feat: ST-501-ST-506 M5 todo-lijst en rolbeheer

- Todo-lijst met snelle invoer via Enter (ST-501)
- Todo afvinken met visuele doorstreping (ST-502)
- Archiveer afgeronde todos (ST-503)
- Promoveer todo naar PBI met product en prioriteit keuze (ST-504)
- Promoveer todo naar story met product, PBI en prioriteit keuze (ST-505)
- Rolbeheer in instellingen: Product Owner, Scrum Master, Developer (ST-506)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-24 11:59:25 +02:00
parent b71a1a7328
commit 8bb8754d01
5 changed files with 568 additions and 0 deletions

View file

@ -0,0 +1,70 @@
'use client'
import { useState, useTransition } from 'react'
import { Button } from '@/components/ui/button'
import { updateRolesAction } from '@/actions/todos'
const ALL_ROLES = [
{ value: 'PRODUCT_OWNER', label: 'Product Owner' },
{ value: 'SCRUM_MASTER', label: 'Scrum Master' },
{ value: 'DEVELOPER', label: 'Developer' },
]
interface RoleManagerProps {
currentRoles: string[]
isDemo: boolean
}
export function RoleManager({ currentRoles, isDemo }: RoleManagerProps) {
const [selected, setSelected] = useState<Set<string>>(new Set(currentRoles))
const [error, setError] = useState<string | null>(null)
const [saved, setSaved] = useState(false)
const [, startTransition] = useTransition()
function toggle(role: string) {
setSelected(prev => {
const next = new Set(prev)
next.has(role) ? next.delete(role) : next.add(role)
return next
})
setSaved(false)
setError(null)
}
function handleSave() {
if (selected.size === 0) {
setError('Minimaal één rol is verplicht')
return
}
startTransition(async () => {
const result = await updateRolesAction([...selected])
if (result.success) setSaved(true)
else setError(result.error ?? 'Opslaan mislukt')
})
}
return (
<div className="bg-surface-container-low border border-border rounded-xl p-5 space-y-4">
<h2 className="text-sm font-medium text-foreground">Mijn rollen</h2>
<div className="flex flex-wrap gap-3">
{ALL_ROLES.map(role => (
<label key={role.value} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={selected.has(role.value)}
onChange={() => toggle(role.value)}
disabled={isDemo}
className="w-4 h-4 rounded accent-primary"
/>
<span className="text-sm">{role.label}</span>
</label>
))}
</div>
{error && <p className="text-xs text-error">{error}</p>}
{saved && <p className="text-xs text-success">Rollen opgeslagen.</p>}
{!isDemo && (
<Button size="sm" onClick={handleSave}>Opslaan</Button>
)}
</div>
)
}