Files
floci-ui/packages/frontend/src/components/LightsailLaunchPanel.tsx
T
2026-10-11 14:19:00 -04:00

102 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {useState} from 'react'
import {Box, Check, Terminal} from 'lucide-react'
import amazonLinuxImage from '../../../../OS_img/amazon_Linux.png'
import nginxImage from '../../../../OS_img/nginx.svg'
import nodeJsImage from '../../../../OS_img/node-js.svg'
import ubuntuImage from '../../../../OS_img/Ubuntu_Linux.png'
import wordpressImage from '../../../../OS_img/wordpress.svg'
import {ServerLaunchProgress} from './ServerLaunchProgress'
type Blueprint = {
id: string
name: string
description: string
kind: 'app' | 'os'
icon?: string
available: boolean
unavailableReason?: string
}
const blueprints: Blueprint[] = [
{id: 'nginx', name: 'Nginx', description: 'Nginx web server', kind: 'app', icon: nginxImage, available: true},
{id: 'nodejs', name: 'Node.js', description: 'Node.js current LTS application server', kind: 'app', icon: nodeJsImage, available: true},
{id: 'wordpress', name: 'WordPress', description: 'WordPress application stack with managed MariaDB', kind: 'app', icon: wordpressImage, available: true},
{id: 'ubuntu_22_04', name: 'Ubuntu 22.04 LTS', description: 'Ubuntu Linux', kind: 'os', icon: ubuntuImage, available: true},
{id: 'ubuntu_24_04', name: 'Ubuntu 24.04 LTS', description: 'Ubuntu Linux', kind: 'os', icon: ubuntuImage, available: true},
{id: 'amazon_linux_2023', name: 'Amazon Linux 2023', description: 'Amazon Linux', kind: 'os', icon: amazonLinuxImage, available: true},
]
const plans = [
{id: 'nano_3_0', name: 'Nano', detail: '1 vCPU, 512 MB RAM, 20 GB SSD'},
{id: 'micro_3_0', name: 'Micro', detail: '2 vCPU, 1 GB RAM, 40 GB SSD'},
{id: 'small_3_0', name: 'Small', detail: '2 vCPU, 2 GB RAM, 60 GB SSD'},
{id: 'medium_3_0', name: 'Medium', detail: '2 vCPU, 4 GB RAM, 80 GB SSD'},
]
interface LightsailLaunchPanelProps {
isSubmitting: boolean
submitError: string | null
onCancel: () => void
onSubmit: (values: Record<string, unknown>) => void
}
export function LightsailLaunchPanel({isSubmitting, submitError, onCancel, onSubmit}: LightsailLaunchPanelProps) {
const [category, setCategory] = useState<'app' | 'os'>('app')
const [blueprintId, setBlueprintId] = useState('nginx')
const [bundleId, setBundleId] = useState('nano_3_0')
const [name, setName] = useState('')
const [availabilityZone, setAvailabilityZone] = useState('us-east-1a')
const visibleBlueprints = blueprints.filter((blueprint) => blueprint.kind === category)
function selectCategory(next: 'app' | 'os') {
setCategory(next)
setBlueprintId(blueprints.find((blueprint) => blueprint.kind === next && blueprint.available)?.id ?? '')
}
return <section className="lightsail-launch">
<div className="lightsail-launch-header">
<div>
<p className="eyebrow">Amazon Lightsail</p>
<h2>Create an instance</h2>
<p className="muted">Choose a blueprint, a plan, and a name. The instance launches locally through Floci.</p>
</div>
<button className="icon-btn" type="button" onClick={onCancel} aria-label="Close create instance">×</button>
</div>
<div className="lightsail-section">
<h3>Select a platform</h3>
<div className="lightsail-platforms">
<button className="lightsail-platform selected" type="button"><Terminal size={26}/><span><strong>Linux/Unix</strong><small>Supported blueprints</small></span><Check size={18}/></button>
<button className="lightsail-platform" type="button" disabled><Box size={26}/><span><strong>Microsoft Windows</strong><small>Not supported locally</small></span></button>
</div>
</div>
<div className="lightsail-section">
<h3>Select a blueprint</h3>
<div className="lightsail-tabs">
<button className={category === 'app' ? 'active' : ''} type="button" onClick={() => selectCategory('app')}>Apps + OS</button>
<button className={category === 'os' ? 'active' : ''} type="button" onClick={() => selectCategory('os')}>OS Only</button>
</div>
<div className="lightsail-blueprints">
{visibleBlueprints.map((blueprint) => <button key={blueprint.id} type="button" disabled={!blueprint.available} title={blueprint.unavailableReason} className={`lightsail-blueprint ${blueprintId === blueprint.id ? 'selected' : ''}`} onClick={() => setBlueprintId(blueprint.id)}>
{blueprint.icon ? <img src={blueprint.icon} alt=""/> : <span className="lightsail-wordpress">W</span>}
<span><strong>{blueprint.name}</strong><small>{blueprint.available ? blueprint.description : blueprint.unavailableReason}</small></span>
</button>)}
</div>
</div>
<div className="lightsail-section">
<h3>Choose your instance plan</h3>
<div className="lightsail-plans">{plans.map((plan) => <button key={plan.id} type="button" className={bundleId === plan.id ? 'selected' : ''} onClick={() => setBundleId(plan.id)}><strong>{plan.name}</strong><small>{plan.detail}</small></button>)}</div>
</div>
<div className="lightsail-config">
<label>Instance name<input className="input" value={name} onChange={(event) => setName(event.target.value)} placeholder="e.g. web-server" autoFocus/></label>
<label>Availability Zone<input className="input" value={availabilityZone} onChange={(event) => setAvailabilityZone(event.target.value)} /></label>
</div>
{isSubmitting && <ServerLaunchProgress name={name} description="Pulling the server image and preparing the local runtime."/>}
{submitError && <p className="error-text">{submitError}</p>}
<div className="form-actions"><button className="button" type="button" onClick={onCancel}>Cancel</button><button className="button primary" type="button" disabled={isSubmitting || !name.trim()} onClick={() => onSubmit({name, blueprintId, bundleId, availabilityZone})}>{isSubmitting ? 'Creating...' : 'Create instance'}</button></div>
</section>
}