toes/src/client/modals/RenameApp.tsx

104 lines
2.8 KiB
TypeScript

import type { App } from '../../shared/types'
import { closeModal, openModal, rerenderModal } from '../components/modal'
import { navigate } from '../router'
import { apps } from '../state'
import { Button, Form, FormActions, FormError, FormField, FormInput, FormLabel } from '../styles'
let renameAppError = ''
let renameAppRenaming = false
let renameAppTarget: App | null = null
async function doRenameApp(input: HTMLInputElement) {
if (!renameAppTarget) return
const newName = input.value.trim().toLowerCase().replace(/\s+/g, '-')
if (!newName) {
renameAppError = 'App name is required'
rerenderModal()
return
}
if (!/^[a-z][a-z0-9-]*$/.test(newName)) {
renameAppError = 'Name must start with a letter and contain only lowercase letters, numbers, and hyphens'
rerenderModal()
return
}
if (newName === renameAppTarget.name) {
closeModal()
return
}
if (apps.some(a => a.name === newName)) {
renameAppError = 'An app with this name already exists'
rerenderModal()
return
}
renameAppRenaming = true
renameAppError = ''
rerenderModal()
try {
const res = await fetch(`/api/apps/${renameAppTarget.name}/rename`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName }),
})
const text = await res.text()
let data: { ok?: boolean, error?: string, name?: string }
try {
data = JSON.parse(text)
} catch {
throw new Error(`Server error: ${text.slice(0, 100)}`)
}
if (!res.ok || !data.ok) {
throw new Error(data.error || 'Failed to rename app')
}
// Success - close modal and navigate to renamed app
closeModal()
navigate(`/app/${data.name || newName}`)
} catch (err) {
renameAppError = err instanceof Error ? err.message : 'Failed to rename app'
renameAppRenaming = false
rerenderModal()
}
}
export function openRenameAppModal(app: App) {
renameAppError = ''
renameAppRenaming = false
renameAppTarget = app
openModal('Rename App', () => (
<Form onSubmit={(e: Event) => {
e.preventDefault()
const input = (e.target as HTMLFormElement).querySelector('input') as HTMLInputElement
doRenameApp(input)
}}>
<FormField>
<FormLabel for="rename-app">App Name</FormLabel>
<FormInput
id="rename-app"
type="text"
value={renameAppTarget?.name ?? ''}
autofocus
/>
{renameAppError && <FormError>{renameAppError}</FormError>}
</FormField>
<FormActions>
<Button type="button" onClick={closeModal} disabled={renameAppRenaming}>
Cancel
</Button>
<Button type="submit" variant="primary" disabled={renameAppRenaming}>
{renameAppRenaming ? 'Renaming...' : 'Rename'}
</Button>
</FormActions>
</Form>
))
}