/
DKoko
/
task-manager-light
Обзор
Документация
Войти
/
DKoko
/
task-manager-light
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/components/TaskForm.tsx
125 строк
4 KB
NMitin
feat: исполнитель задачи (assignee)
21 июн 2026, 09:54
21 июн 2026, 09:54
cbf633a
Код
Авторство
О чём код?
'use client' import { useState, type FormEvent } from 'react' import { useRouter } from 'next/navigation' import { STATUSES, STATUS_LABELS, msToDate, type Status } from '@/lib/tasks-shared' import { RichTextEditor } from './RichTextEditor' interface TaskFormProps { mode: 'create' | 'edit' taskId?: string assignees: string[] initial?: { title: string; description: string; status: Status; dueDate: number | null; assignee?: string } } export function TaskForm({ mode, taskId, assignees, initial }: TaskFormProps) { const router = useRouter() const [title, setTitle] = useState(initial?.title ?? '') const [description, setDescription] = useState(initial?.description ?? '') const [status, setStatus] = useState<Status>(initial?.status ?? 'new') const [dueDate, setDueDate] = useState(msToDate(initial?.dueDate ?? null) ?? '') const [assignee, setAssignee] = useState(initial?.assignee ?? assignees[0] ?? 'Я') const [error, setError] = useState('') const [saving, setSaving] = useState(false) async function handleSubmit(e: FormEvent<HTMLFormElement>) { e.preventDefault() if (!title.trim()) { setError('Введите название') return } setSaving(true) setError('') const payload: Record<string, unknown> = { title, description, dueDate: dueDate || null, assignee, } if (mode === 'edit') payload.status = status const url = mode === 'create' ? '/api/tasks' : `/api/tasks/${taskId}` const method = mode === 'create' ? 'POST' : 'PATCH' try { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }) if (res.ok) { router.push('/tasks') router.refresh() return } const data = await res.json().catch(() => ({})) setError(data.error ?? 'Не удалось сохранить') } finally { setSaving(false) } } return ( <form onSubmit={handleSubmit} className="task-form"> <label className="field"> <span>Название</span> <input value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Что нужно сделать?" autoFocus /> </label> <label className="field"> <span>Описание</span> <RichTextEditor value={description} onChange={setDescription} /> </label> <div className="row"> <label className="field"> <span>Срок</span> <input type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} /> </label> <label className="field"> <span>Исполнитель</span> <select value={assignee} onChange={(e) => setAssignee(e.target.value)}> {assignees.map((a) => ( <option key={a} value={a}> {a} </option> ))} </select> </label> {mode === 'edit' && ( <label className="field"> <span>Статус</span> <select value={status} onChange={(e) => setStatus(e.target.value as Status)}> {STATUSES.map((s) => ( <option key={s} value={s}> {STATUS_LABELS[s]} </option> ))} </select> </label> )} </div> {error && <div className="error">{error}</div>} <div className="actions"> <button type="submit" disabled={saving}> {saving ? 'Сохранение…' : mode === 'create' ? 'Создать' : 'Сохранить'} </button> <button type="button" className="secondary" onClick={() => router.push('/tasks')}> Отмена </button> </div> </form> ) }