Merge pull request #87 from madhura68/feat/a11y-audit-fixes
fix(a11y): static accessibility fixes (v1-readiness #4 — code-side)
This commit is contained in:
commit
04181e54cb
6 changed files with 42 additions and 25 deletions
|
|
@ -5,7 +5,7 @@ import { QrLoginButton } from './qr-login-button'
|
|||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<main className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
|
||||
{/* Logo / titel */}
|
||||
|
|
@ -42,6 +42,6 @@ export default function LoginPage() {
|
|||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { AuthForm } from '@/components/auth/auth-form'
|
|||
|
||||
export default function RegisterPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<main className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
|
||||
{/* Logo / titel */}
|
||||
|
|
@ -26,6 +26,6 @@ export default function RegisterPage() {
|
|||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,10 +220,11 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
|
|||
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-2 block">
|
||||
<label htmlFor="task-title" className="text-sm font-medium mb-2 block">
|
||||
Titel <span className="text-destructive">*</span>
|
||||
</label>
|
||||
<Input
|
||||
id="task-title"
|
||||
{...form.register('title')}
|
||||
aria-invalid={!!form.formState.errors.title}
|
||||
autoFocus
|
||||
|
|
@ -240,13 +241,14 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
|
|||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-2 block">Omschrijving</label>
|
||||
<label htmlFor="task-description" className="text-sm font-medium mb-2 block">Omschrijving</label>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<TextareaAutosize
|
||||
id="task-description"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
aria-invalid={!!form.formState.errors.description}
|
||||
|
|
@ -275,13 +277,14 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
|
|||
|
||||
{/* Implementation plan */}
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-2 block">Implementatieplan</label>
|
||||
<label htmlFor="task-implementation-plan" className="text-sm font-medium mb-2 block">Implementatieplan</label>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="implementation_plan"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<TextareaAutosize
|
||||
id="task-implementation-plan"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
aria-invalid={!!form.formState.errors.implementation_plan}
|
||||
|
|
|
|||
|
|
@ -192,8 +192,9 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
<div className="px-6 py-6 space-y-6">
|
||||
<div className="grid grid-cols-[6rem_1fr] gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Code</label>
|
||||
<label htmlFor="story-code" className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Code</label>
|
||||
<Input
|
||||
id="story-code"
|
||||
name="code"
|
||||
defaultValue={story?.code ?? ''}
|
||||
placeholder={isEdit ? '' : 'auto'}
|
||||
|
|
@ -205,10 +206,11 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
{fieldError('code') && <p className="text-xs text-error">{fieldError('code')}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
<label htmlFor="story-title" className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
Titel <span className="text-error">*</span>
|
||||
</label>
|
||||
<Input
|
||||
id="story-title"
|
||||
ref={titleRef}
|
||||
name="title"
|
||||
defaultValue={story?.title ?? ''}
|
||||
|
|
@ -223,15 +225,16 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Prioriteit</label>
|
||||
<span id="story-priority-label" className="block text-xs font-medium text-muted-foreground uppercase tracking-wide">Prioriteit</span>
|
||||
<PrioritySelect value={priority} onChange={(v) => { setPriority(v); setDirty(true) }} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
<label htmlFor="story-description" className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
Omschrijving <span className="normal-case font-normal">(optioneel)</span>
|
||||
</label>
|
||||
<Textarea
|
||||
id="story-description"
|
||||
name="description"
|
||||
rows={3}
|
||||
defaultValue={story?.description ?? ''}
|
||||
|
|
@ -242,10 +245,11 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
<label htmlFor="story-acceptance" className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
Acceptatiecriteria <span className="normal-case font-normal">(optioneel)</span>
|
||||
</label>
|
||||
<Textarea
|
||||
id="story-acceptance"
|
||||
name="acceptance_criteria"
|
||||
rows={3}
|
||||
defaultValue={story?.acceptance_criteria ?? ''}
|
||||
|
|
|
|||
|
|
@ -63,11 +63,19 @@ export function SoloTaskCard({ task, isDemo, onClick }: SoloTaskCardProps) {
|
|||
{job && (
|
||||
<span
|
||||
className={cn(
|
||||
'text-[10px] px-1.5 py-0 rounded border flex items-center gap-1 shrink-0',
|
||||
'text-[10px] px-1.5 py-0 rounded border flex items-center gap-1 shrink-0 cursor-pointer',
|
||||
JOB_STATUS_COLORS[job.status],
|
||||
)}
|
||||
onClick={(e) => { e.stopPropagation(); onClick() }}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
onClick()
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`Agent-status: ${JOB_STATUS_LABELS[job.status]}`}
|
||||
>
|
||||
{JOB_STATUS_ACTIVE.has(job.status) && <Loader2 className="animate-spin" size={8} />}
|
||||
|
|
|
|||
|
|
@ -113,15 +113,16 @@ function PromotePbiDialog({
|
|||
<form action={formAction} className="space-y-3">
|
||||
<input type="hidden" name="todoId" value={todo.id} />
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Titel</label>
|
||||
<Input name="title" defaultValue={todo.title} required />
|
||||
<label htmlFor="promote-pbi-title" className="text-sm font-medium">Titel</label>
|
||||
<Input id="promote-pbi-title" name="title" defaultValue={todo.title} required />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Product</label>
|
||||
<label htmlFor="promote-pbi-product" className="text-sm font-medium">Product</label>
|
||||
{products.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Maak eerst een product aan.</p>
|
||||
) : (
|
||||
<select
|
||||
id="promote-pbi-product"
|
||||
name="productId"
|
||||
required
|
||||
defaultValue={todo.product_id ?? products[0]?.id}
|
||||
|
|
@ -132,8 +133,8 @@ function PromotePbiDialog({
|
|||
)}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Prioriteit</label>
|
||||
<select name="priority" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
<label htmlFor="promote-pbi-priority" className="text-sm font-medium">Prioriteit</label>
|
||||
<select id="promote-pbi-priority" name="priority" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
<option value="1">Kritiek</option>
|
||||
<option value="2">Hoog</option>
|
||||
<option value="3">Gemiddeld</option>
|
||||
|
|
@ -184,15 +185,16 @@ function PromoteStoryDialog({
|
|||
<input type="hidden" name="todoId" value={todo.id} />
|
||||
<input type="hidden" name="productId" value={selectedProductId} />
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Titel</label>
|
||||
<Input name="title" defaultValue={todo.title} required />
|
||||
<label htmlFor="promote-story-title" className="text-sm font-medium">Titel</label>
|
||||
<Input id="promote-story-title" name="title" defaultValue={todo.title} required />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Product</label>
|
||||
<label htmlFor="promote-story-product" className="text-sm font-medium">Product</label>
|
||||
{products.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Maak eerst een product aan.</p>
|
||||
) : (
|
||||
<select
|
||||
id="promote-story-product"
|
||||
value={selectedProductId}
|
||||
onChange={e => setSelectedProductId(e.target.value)}
|
||||
className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background"
|
||||
|
|
@ -202,18 +204,18 @@ function PromoteStoryDialog({
|
|||
)}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">PBI</label>
|
||||
<label htmlFor="promote-story-pbi" className="text-sm font-medium">PBI</label>
|
||||
{!selectedProduct?.pbis.length ? (
|
||||
<p className="text-sm text-muted-foreground">Maak eerst een PBI aan in dit product.</p>
|
||||
) : (
|
||||
<select name="pbiId" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
<select id="promote-story-pbi" name="pbiId" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
{selectedProduct.pbis.map(p => <option key={p.id} value={p.id}>{p.title}</option>)}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-sm font-medium">Prioriteit</label>
|
||||
<select name="priority" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
<label htmlFor="promote-story-priority" className="text-sm font-medium">Prioriteit</label>
|
||||
<select id="promote-story-priority" name="priority" required className="w-full border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background">
|
||||
<option value="1">Kritiek</option>
|
||||
<option value="2">Hoog</option>
|
||||
<option value="3">Gemiddeld</option>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue