95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useActionState } from 'react'
|
|
import { useFormStatus } from 'react-dom'
|
|
import { useRouter } from 'next/navigation'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Textarea } from '@/components/ui/textarea'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog'
|
|
import { createSprintAction } from '@/actions/sprints'
|
|
|
|
interface StartSprintButtonProps {
|
|
productId: string
|
|
}
|
|
|
|
function SubmitButton() {
|
|
const { pending } = useFormStatus()
|
|
return (
|
|
<Button type="submit" disabled={pending}>
|
|
{pending ? 'Aanmaken…' : 'Sprint starten'}
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
export function StartSprintButton({ productId }: StartSprintButtonProps) {
|
|
const [open, setOpen] = useState(false)
|
|
const router = useRouter()
|
|
|
|
const [state, formAction] = useActionState(
|
|
async (_prev: unknown, fd: FormData) => {
|
|
const result = await createSprintAction(_prev, fd)
|
|
if (result.success) {
|
|
setOpen(false)
|
|
router.push(`/products/${productId}/sprint`)
|
|
}
|
|
return result
|
|
},
|
|
undefined
|
|
)
|
|
|
|
const globalError = typeof state?.error === 'string' ? state.error : undefined
|
|
|
|
return (
|
|
<>
|
|
<Button size="sm" onClick={() => setOpen(true)}>
|
|
Sprint starten
|
|
</Button>
|
|
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogContent className="sm:max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>Nieuwe Sprint starten</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<form action={formAction} className="space-y-4 p-1">
|
|
<input type="hidden" name="productId" value={productId} />
|
|
|
|
<div className="space-y-1.5">
|
|
<label className="text-sm font-medium text-foreground">
|
|
Sprint Goal <span className="text-error">*</span>
|
|
</label>
|
|
<Textarea
|
|
name="sprint_goal"
|
|
required
|
|
rows={3}
|
|
placeholder="Wat wil je aan het einde van deze Sprint bereikt hebben?"
|
|
autoFocus
|
|
/>
|
|
{typeof state?.error === 'object' && (state.error as Record<string, string[]>).sprint_goal && (
|
|
<p className="text-xs text-error">{(state.error as Record<string, string[]>).sprint_goal[0]}</p>
|
|
)}
|
|
</div>
|
|
|
|
{globalError && (
|
|
<div className="bg-error-container text-error-container-foreground rounded-lg px-3 py-2 text-sm border-l-4 border-error">
|
|
{globalError}
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex gap-2 justify-end">
|
|
<Button type="button" variant="ghost" onClick={() => setOpen(false)}>
|
|
Annuleren
|
|
</Button>
|
|
<SubmitButton />
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
)
|
|
}
|