feat: Lightsail + svg animation

This commit is contained in:
Zakaria
2026-10-11 14:19:00 -04:00
parent 0036ef7e51
commit ca1e845cb6
40 changed files with 1887 additions and 34 deletions
@@ -0,0 +1,101 @@
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>
}