toes/src/client/modals/NewApp.tsx
2026-01-30 15:26:58 -08:00

87 lines
2.3 KiB
TypeScript

import { closeModal, openModal, rerenderModal } from '../components/modal'
import { apps, setSelectedApp } from '../state'
import { Button, Form, FormActions, FormError, FormField, FormInput, FormLabel } from '../styles'
let newAppError = ''
let newAppCreating = false
async function createNewApp(input: HTMLInputElement) {
const name = input.value.trim().toLowerCase().replace(/\s+/g, '-')
if (!name) {
newAppError = 'App name is required'
rerenderModal()
return
}
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
newAppError = 'Name must start with a letter and contain only lowercase letters, numbers, and hyphens'
rerenderModal()
return
}
if (apps.some(a => a.name === name)) {
newAppError = 'An app with this name already exists'
rerenderModal()
return
}
newAppCreating = true
newAppError = ''
rerenderModal()
try {
const res = await fetch('/api/apps', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
})
const data = await res.json()
if (!res.ok || !data.ok) {
throw new Error(data.error || 'Failed to create app')
}
// Success - close modal and select the new app
setSelectedApp(name)
closeModal()
} catch (err) {
newAppError = err instanceof Error ? err.message : 'Failed to create app'
newAppCreating = false
rerenderModal()
}
}
export function openNewAppModal() {
newAppError = ''
newAppCreating = false
openModal('New App', () => (
<Form onSubmit={(e: Event) => {
e.preventDefault()
const input = (e.target as HTMLFormElement).querySelector('input') as HTMLInputElement
createNewApp(input)
}}>
<FormField>
<FormLabel for="app-name">App Name</FormLabel>
<FormInput
id="app-name"
type="text"
placeholder="my-app"
autofocus
/>
{newAppError && <FormError>{newAppError}</FormError>}
</FormField>
<FormActions>
<Button type="button" onClick={closeModal} disabled={newAppCreating}>
Cancel
</Button>
<Button type="submit" variant="primary" disabled={newAppCreating}>
{newAppCreating ? 'Creating...' : 'Create App'}
</Button>
</FormActions>
</Form>
))
}