import React, { useState, useEffect, useCallback } from 'react'; import { INITIAL_TASKS, TaskEntity } from '../../mock/mockData'; import { getStoreItem, putStoreItem } from '../../services/storage/indexedDb'; import { Badge, MaturityBadge, Button, Modal } from '../../shared/ui'; import { Clock, Plus, CheckCircle2, Send, Link, Check, } from 'lucide-react'; interface SubtaskItem { id: string; text: string; completed: boolean; } interface CommentItem { id: string; author: string; text: string; time: string; } export const TasksModule: React.FC = () => { const [tasks, setTasks] = useState(INITIAL_TASKS); const [activeView, setActiveView] = useState<'kanban' | 'list'>('kanban'); // Load persisted tasks on mount useEffect(() => { async function loadTasks() { try { const saved = await getStoreItem<{ key: string; tasks: TaskEntity[] }>('settings', 'krona_persisted_tasks'); if (saved?.tasks && Array.isArray(saved.tasks) && saved.tasks.length > 0) { setTasks(saved.tasks); } } catch { // Keep initial } } loadTasks(); }, []); const persistTasks = useCallback((updated: TaskEntity[]) => { setTasks(updated); void putStoreItem('settings', { key: 'krona_persisted_tasks', tasks: updated }).catch(() => {}); }, []); // Selected task detail state const [selectedTask, setSelectedTask] = useState(null); const [subtasks, setSubtasks] = useState([ { id: 'st-1', text: 'Сверить расчетную себестоимость и маржинальность', completed: true }, { id: 'st-2', text: 'Согласовать тайминг подачи с шеф-поваром', completed: false }, { id: 'st-3', text: 'Выставить предварительный счет клиенту', completed: false }, ]); const [comments, setComments] = useState([ { id: 'c-1', author: 'Ольга Власова', text: 'Клиент подтвердил количество 60 персон. Ждем аванс.', time: 'Вчера, 16:20' }, { id: 'c-2', author: 'Артур Ким (Шеф)', text: 'Продукты зарезервированы на основном складе.', time: 'Сегодня, 10:15' }, ]); const [newComment, setNewComment] = useState(''); const handleStatusChange = (taskId: string, nextStatus: TaskEntity['status']) => { const updated = tasks.map((t) => (t.id === taskId ? { ...t, status: nextStatus } : t)); persistTasks(updated); if (selectedTask && selectedTask.id === taskId) { setSelectedTask({ ...selectedTask, status: nextStatus }); } }; const handleToggleSubtask = (id: string) => { setSubtasks((prev) => prev.map((s) => (s.id === id ? { ...s, completed: !s.completed } : s)) ); }; const handleAddComment = (e: React.FormEvent) => { e.preventDefault(); if (!newComment.trim()) return; setComments((prev) => [ ...prev, { id: `c-${Date.now()}`, author: 'Вы (Текущий пользователь)', text: newComment, time: 'Только что', }, ]); setNewComment(''); }; return (

Задачи, поручения и проекты команды

Канбан-доска исполнения, дедлайны, привязка к CRM и ответственные

{activeView === 'kanban' ? ( /* Kanban Board */
{(['todo', 'in_progress', 'review', 'done'] as const).map((st) => { const colTasks = tasks.filter(t => t.status === st); const colTitles: Record = { todo: 'К выполнению', in_progress: 'В работе', review: 'На проверке', done: 'Выполнено', }; return (
e.preventDefault()} onDrop={(e) => { e.preventDefault(); const taskId = e.dataTransfer.getData('text/plain'); if (taskId) handleStatusChange(taskId, st); }} className="bg-krona-workspace/80 border border-krona-border rounded-xl p-3 flex flex-col min-h-[420px]" >
{colTitles[st]} {colTasks.length}
{colTasks.map((t) => (
{ e.dataTransfer.setData('text/plain', t.id); }} onClick={() => setSelectedTask(t)} className="p-3 rounded-lg border border-krona-border bg-krona-surface shadow-krona-soft space-y-2 cursor-pointer hover:border-krona-green/70 transition-all group active:scale-[0.99]" >
{t.project} {t.priority === 'high' ? 'Срочно' : 'Обычный'}
{t.title}
{t.assignee} {t.dueDate}
{st !== 'done' && (
)}
))}
); })}
) : ( /* List View */
{tasks.map((t) => ( setSelectedTask(t)} className="hover:bg-krona-workspace/50 transition-colors cursor-pointer" > ))}
Название задачи Проект Ответственный Срок Приоритет Статус
{t.title} {t.project} {t.assignee} {t.dueDate} {t.priority === 'high' ? 'Высокий' : 'Обычный'} {t.status}
)} {/* Task Detail Modal (GAP 5) */} {selectedTask && ( setSelectedTask(null)} title={`Задача: ${selectedTask.title}`} subtitle={`Проект: ${selectedTask.project} • Исполнитель: ${selectedTask.assignee}`} maxWidth="lg" footer={
} >
{/* Meta attributes */}
Ответственный: {selectedTask.assignee}
Срок завершения: {selectedTask.dueDate}
Приоритет: {selectedTask.priority === 'high' ? 'Высокий (Срочно)' : 'Обычный'}
{/* Linked CRM Entity */}
Связанный CRM контрагент:
ООО "Сибирский Ритейл" (Сделка: Банкет на 60 персон)
CRM Link
{/* Checklist of Subtasks */}
Чек-лист подзадач ({subtasks.filter(s => s.completed).length} из {subtasks.length}):
{subtasks.map((st) => (
handleToggleSubtask(st.id)} className="flex items-center gap-2 cursor-pointer p-1 rounded hover:bg-krona-surface/60 transition-colors select-none" >
{st.completed && }
{st.text}
))}
{/* Comments Stream & Input */}
Обсуждение задачи ({comments.length}):
{comments.map((cm) => (
{cm.author} {cm.time}

{cm.text}

))}
setNewComment(e.target.value)} placeholder="Добавить комментарий к задаче..." className="flex-1 bg-krona-workspace border border-krona-border rounded-lg px-3 py-1.5 text-xs text-krona-primary placeholder:text-krona-secondary focus:outline-none focus:border-krona-green" />
)}
); };