feat: enforce checklist/minibar completion before marking task done
- ApiError now carries errors[] array from 422 responses - updateStatus re-throws 422 so TaskCard can surface errors - TaskCard: minibarCheckedLocal state + 'Подтвердить проверку минибара' button - TaskCard: async handleComplete with completionErrors display - Backend validation errors shown inline in completion panel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Plus, Trash2, ChevronUp, ChevronDown, Pencil, Check, X, Loader2, ListChecks, ToggleLeft, ToggleRight } from 'lucide-react'
|
||||
import { Plus, Trash2, ChevronUp, ChevronDown, Pencil, Check, X, Loader2, ListChecks, ToggleLeft, ToggleRight, ShieldAlert } from 'lucide-react'
|
||||
import { api, type ChecklistTemplate, type ChecklistItem } from '../lib/api'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { cn } from '../lib/utils'
|
||||
@@ -97,6 +97,13 @@ export function ChecklistSettingsPage() {
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
const toggleRequire = async (t: ChecklistTemplate) => {
|
||||
try {
|
||||
await api.checklists.updateTemplate(slug, t.id, { requireBeforeComplete: !t.requireBeforeComplete })
|
||||
setTemplates(prev => prev.map(x => x.id === t.id ? { ...x, requireBeforeComplete: !x.requireBeforeComplete } : x))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
const deleteTemplate = async (id: string) => {
|
||||
if (!confirm('Удалить шаблон вместе со всеми пунктами?')) return
|
||||
try {
|
||||
@@ -323,6 +330,30 @@ export function ChecklistSettingsPage() {
|
||||
Добавить
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Require before complete toggle */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-between mt-3 pt-3 border-t border-slate-100 dark:border-slate-700 px-1 py-1.5 rounded-lg cursor-pointer select-none',
|
||||
tpl.requireBeforeComplete
|
||||
? 'bg-amber-50 dark:bg-amber-900/10'
|
||||
: 'hover:bg-slate-50 dark:hover:bg-slate-800/50',
|
||||
)}
|
||||
onClick={() => toggleRequire(tpl)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ShieldAlert size={14} className={tpl.requireBeforeComplete ? 'text-amber-500' : 'text-slate-400'} />
|
||||
<div>
|
||||
<p className={cn('text-xs font-medium', tpl.requireBeforeComplete ? 'text-amber-700 dark:text-amber-400' : 'text-slate-600 dark:text-slate-400')}>
|
||||
Требовать выполнения перед завершением уборки
|
||||
</p>
|
||||
<p className="text-xs text-slate-400">Горничная не сможет закрыть задачу, пока не отметит все пункты</p>
|
||||
</div>
|
||||
</div>
|
||||
{tpl.requireBeforeComplete
|
||||
? <ToggleRight size={18} className="text-amber-500 shrink-0" />
|
||||
: <ToggleLeft size={18} className="text-slate-300 dark:text-slate-600 shrink-0" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user