WebVNWrite/src/components/NewProjectButton.tsx

157 lines
5.1 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
export default function NewProjectButton() {
const [isOpen, setIsOpen] = useState(false)
const [projectName, setProjectName] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const router = useRouter()
const handleOpen = () => {
setIsOpen(true)
setProjectName('')
setError(null)
}
const handleClose = () => {
if (!isLoading) {
setIsOpen(false)
setProjectName('')
setError(null)
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!projectName.trim()) {
setError('Project name is required')
return
}
setIsLoading(true)
setError(null)
const supabase = createClient()
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) {
setError('You must be logged in to create a project')
setIsLoading(false)
return
}
const { data, error: insertError } = await supabase
.from('projects')
.insert({
user_id: user.id,
name: projectName.trim(),
flowchart_data: { nodes: [], edges: [] },
})
.select('id')
.single()
if (insertError) {
setError(insertError.message || 'Failed to create project')
setIsLoading(false)
return
}
if (data) {
router.push(`/editor/${data.id}`)
}
}
return (
<>
<button
onClick={handleOpen}
className="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-zinc-900"
>
<svg
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
New Project
</button>
{isOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div
className="absolute inset-0 bg-black/50"
onClick={handleClose}
aria-hidden="true"
/>
<div className="relative w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-zinc-800">
<h2 className="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
Create New Project
</h2>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
Enter a name for your new visual novel project.
</p>
<form onSubmit={handleSubmit} className="mt-4">
<label
htmlFor="projectName"
className="block text-sm font-medium text-zinc-700 dark:text-zinc-300"
>
Project Name
</label>
<input
type="text"
id="projectName"
value={projectName}
onChange={(e) => setProjectName(e.target.value)}
placeholder="My Visual Novel"
disabled={isLoading}
autoFocus
className="mt-1 block w-full rounded-md border border-zinc-300 bg-white px-3 py-2 text-zinc-900 placeholder-zinc-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-50 dark:placeholder-zinc-500"
/>
{error && (
<p className="mt-2 text-sm text-red-600 dark:text-red-400">
{error}
</p>
)}
<div className="mt-6 flex justify-end gap-3">
<button
type="button"
onClick={handleClose}
disabled={isLoading}
className="rounded-md border border-zinc-300 bg-white px-4 py-2 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
>
Cancel
</button>
<button
type="submit"
disabled={isLoading}
className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:focus:ring-offset-zinc-800"
>
{isLoading ? 'Creating...' : 'Create Project'}
</button>
</div>
</form>
</div>
</div>
)}
</>
)
}