104 lines
2.8 KiB
TypeScript
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>
|
|
))
|
|
}
|