91ce11274d
CI / checks (pull_request) Successful in 25m47s
Labels, empty states, error messages and defaults the console shows now name TheCloud or its runtimes. Identifiers that must match the emulator (FLOCI_* settings, the X-Floci-CSRF header, emulator-returned host names) are unchanged.
102 lines
6.0 KiB
TypeScript
102 lines
6.0 KiB
TypeScript
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 on TheCloud.</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>
|
||
}
|