Close create wizard modal as soon as ddev config succeeds
handleSubmit awaited the entire post-create chain (start, then for WordPress: download core + install) before ever calling onClose(), so the modal overlay sat there for the whole multi-step process with no way to see what was actually happening — the terminal panel that already tracks this operation's live output was hidden behind it. Close right after the quick config step succeeds and let post-create continue as a tracked background operation; failures still surface via the existing toast/terminal-panel error handling regardless of modal state. Also extracts the project-name validation helpers out of CreateProjectModal.tsx into their own module — mixing component and non-component exports in one file breaks React Fast Refresh. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5beb70f1e2
commit
c2b95d057f
@@ -19,25 +19,11 @@ import { getTypeLabel, PROJECT_TYPES } from './types/registry'
|
|||||||
import { GenericSetup } from './types/GenericSetup'
|
import { GenericSetup } from './types/GenericSetup'
|
||||||
import { WordpressSetup } from './types/WordpressSetup'
|
import { WordpressSetup } from './types/WordpressSetup'
|
||||||
import type { TypeSetupHandle } from './types/shared'
|
import type { TypeSetupHandle } from './types/shared'
|
||||||
|
import { isValidProjectName, slugifyProjectName } from './projectName'
|
||||||
import docksideIcon from '../../assets/dockside-icon.png'
|
import docksideIcon from '../../assets/dockside-icon.png'
|
||||||
|
|
||||||
type Step = 'site' | 'setup'
|
type Step = 'site' | 'setup'
|
||||||
|
|
||||||
// DDEV project names must be valid hostname labels: alphanumeric and
|
|
||||||
// hyphens only, can't start/end with a hyphen.
|
|
||||||
const PROJECT_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/
|
|
||||||
|
|
||||||
export function isValidProjectName(name: string): boolean {
|
|
||||||
return PROJECT_NAME_PATTERN.test(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function slugifyProjectName(raw: string): string {
|
|
||||||
return raw
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[^a-z0-9]+/g, '-')
|
|
||||||
.replace(/^-+|-+$/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldClass =
|
const fieldClass =
|
||||||
'w-full rounded-lg border border-neutral-300 bg-white/80 px-3 py-2 text-sm shadow-sm transition placeholder:text-neutral-400 focus:border-cyan-400 dark:border-white/10 dark:bg-neutral-950/70 dark:placeholder:text-neutral-600'
|
'w-full rounded-lg border border-neutral-300 bg-white/80 px-3 py-2 text-sm shadow-sm transition placeholder:text-neutral-400 focus:border-cyan-400 dark:border-white/10 dark:bg-neutral-950/70 dark:placeholder:text-neutral-600'
|
||||||
|
|
||||||
@@ -92,12 +78,20 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }): React.
|
|||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
try {
|
try {
|
||||||
await createProject.mutateAsync({ directory, projectName: name, projectType, docroot })
|
await createProject.mutateAsync({ directory, projectName: name, projectType, docroot })
|
||||||
await setupRef.current?.runPostCreate({ directory, projectName: name })
|
} catch {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post-create (starting the project, downloading/installing WordPress,
|
||||||
|
// etc.) can run long. Close the modal as soon as the quick `ddev
|
||||||
|
// config` step succeeds instead of blocking the whole wizard on it —
|
||||||
|
// the terminal panel already tracks and surfaces this operation's
|
||||||
|
// progress and any failure independently of the modal.
|
||||||
|
const runPostCreate = setupRef.current?.runPostCreate
|
||||||
selectProject(name)
|
selectProject(name)
|
||||||
onClose()
|
onClose()
|
||||||
} finally {
|
runPostCreate?.({ directory, projectName: name }).catch(() => {})
|
||||||
setIsSubmitting(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { isValidProjectName, slugifyProjectName } from './CreateProjectModal'
|
import { isValidProjectName, slugifyProjectName } from './projectName'
|
||||||
|
|
||||||
describe('isValidProjectName', () => {
|
describe('isValidProjectName', () => {
|
||||||
it('accepts hostname-safe names', () => {
|
it('accepts hostname-safe names', () => {
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// DDEV project names must be valid hostname labels: alphanumeric and
|
||||||
|
// hyphens only, can't start/end with a hyphen.
|
||||||
|
const PROJECT_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/
|
||||||
|
|
||||||
|
export function isValidProjectName(name: string): boolean {
|
||||||
|
return PROJECT_NAME_PATTERN.test(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function slugifyProjectName(raw: string): string {
|
||||||
|
return raw
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, '-')
|
||||||
|
.replace(/^-+|-+$/g, '')
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user