User management

This commit is contained in:
Louis Knight-Webb
2025-06-14 16:26:48 -04:00
parent e099269ed2
commit ca231bd6be
31 changed files with 2581 additions and 56 deletions

View File

@@ -0,0 +1,127 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Project, CreateProject, UpdateProject } from 'shared/types'
import { AlertCircle } from 'lucide-react'
interface ProjectFormProps {
open: boolean
onClose: () => void
onSuccess: () => void
project?: Project | null
}
export function ProjectForm({ open, onClose, onSuccess, project }: ProjectFormProps) {
const [name, setName] = useState(project?.name || '')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const isEditing = !!project
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setLoading(true)
try {
if (isEditing) {
const updateData: UpdateProject = { name }
const response = await fetch(`/api/projects/${project.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updateData),
})
if (!response.ok) {
throw new Error('Failed to update project')
}
} else {
// For now, using a placeholder owner_id - this should come from auth
const createData: CreateProject = {
name,
owner_id: '00000000-0000-0000-0000-000000000000'
}
const response = await fetch('/api/projects', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(createData),
})
if (!response.ok) {
throw new Error('Failed to create project')
}
}
onSuccess()
setName('')
} catch (error) {
setError(error instanceof Error ? error.message : 'An error occurred')
} finally {
setLoading(false)
}
}
const handleClose = () => {
setName(project?.name || '')
setError('')
onClose()
}
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>
{isEditing ? 'Edit Project' : 'Create New Project'}
</DialogTitle>
<DialogDescription>
{isEditing
? 'Make changes to your project here. Click save when you\'re done.'
: 'Add a new project to your workspace. You can always edit it later.'
}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Project Name</Label>
<Input
id="name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter project name"
required
/>
</div>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
{error}
</AlertDescription>
</Alert>
)}
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={handleClose}
disabled={loading}
>
Cancel
</Button>
<Button type="submit" disabled={loading || !name.trim()}>
{loading ? 'Saving...' : isEditing ? 'Save Changes' : 'Create Project'}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}