/
alexefan136
/
flowstack
Обзор
Документация
Войти
/
alexefan136
/
flowstack
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
ui/src/lib/utils/task-export.ts
165 строк
5 KB
Alexander Efanov
upd fix
29 июл 2026, 23:26
29 июл 2026, 23:26
d8ac8ee
Код
Авторство
О чём код?
// src/lib/utils/task-export.ts import type { Task, TaskPriority, TaskStatus } from "../types/task-types"; const STATUS_LABELS: Record<TaskStatus, string> = { todo: "К выполнению", in_progress: "В работе", done: "Завершено", cancelled: "Отменено", }; const PRIORITY_LABELS: Record<TaskPriority, string> = { low: "Низкий", medium: "Средний", high: "Высокий", urgent: "Срочный", }; const STATUS_ORDER: TaskStatus[] = ["in_progress", "todo", "done", "cancelled"]; function formatDate(iso?: string): string { if (!iso) return "—"; return new Date(iso).toLocaleString("ru-RU", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", }); } /** Экранирование значения для CSV */ function csvEscape(value: unknown): string { const str = value == null ? "" : String(value); if (str.includes(",") || str.includes('"') || str.includes("\n")) { return `"${str.replace(/"/g, '""')}"`; } return str; } /** Скачать файл в браузере */ export function downloadFile( content: string, filename: string, mimeType: string, ): void { const blob = new Blob(["\uFEFF" + content], { type: `${mimeType};charset=utf-8`, }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); } /** Экспорт в Markdown (сгруппировано по статусам) */ export function exportTasksToMarkdown( tasks: Task[], getAgentName?: (id: string) => string, ): string { const lines: string[] = []; lines.push("# 📋 Задачи FlowStack"); lines.push(""); lines.push(`**Экспортировано:** ${formatDate(new Date().toISOString())}`); lines.push(`**Всего задач:** ${tasks.length}`); const done = tasks.filter((t) => t.status === "done").length; const overdue = tasks.filter( (t) => t.deadline && t.status !== "done" && t.status !== "cancelled" && new Date(t.deadline) < new Date(), ).length; lines.push(`**Завершено:** ${done} · **Просрочено:** ${overdue}`); lines.push(""); lines.push("---"); lines.push(""); for (const status of STATUS_ORDER) { const group = tasks.filter((t) => t.status === status); if (group.length === 0) continue; lines.push(`## ${STATUS_LABELS[status]} (${group.length})`); lines.push(""); for (const task of group) { lines.push(`### [${PRIORITY_LABELS[task.priority]}] ${task.title}`); if (task.description) lines.push(`\n${task.description}`); const meta: string[] = []; if (task.assigned_agent_id) meta.push( `**Агент:** ${getAgentName?.(task.assigned_agent_id) ?? task.assigned_agent_id}`, ); if (task.deadline) meta.push(`**Дедлайн:** ${formatDate(task.deadline)}`); if (task.tags && task.tags.length > 0) meta.push(`**Теги:** ${task.tags.join(", ")}`); if (task.estimated_duration_minutes) meta.push(`**Оценка:** ${task.estimated_duration_minutes} мин`); if (task.recurrence_pattern) meta.push(`**Повторение:** \`${task.recurrence_pattern}\``); if (meta.length > 0) lines.push("", ...meta.map((m) => `- ${m}`)); // Подзадачи if (task.subtasks && task.subtasks.length > 0) { const doneCount = task.subtasks.filter((s) => s.completed).length; lines.push("", `**Чеклист (${doneCount}/${task.subtasks.length}):**`); for (const st of task.subtasks) { lines.push(`- [${st.completed ? "x" : " "}] ${st.title}`); } } lines.push(""); } } return lines.join("\n"); } /** Экспорт в CSV (таблица) */ export function exportTasksToCSV( tasks: Task[], getAgentName?: (id: string) => string, ): string { const headers = [ "Название", "Описание", "Статус", "Приоритет", "Тип", "Агент", "Дедлайн", "Теги", "Подзадачи", "Создана", ]; const rows = tasks.map((task) => { const subtasksProgress = task.subtasks && task.subtasks.length > 0 ? `${task.subtasks.filter((s) => s.completed).length}/${task.subtasks.length}` : ""; return [ task.title, task.description, STATUS_LABELS[task.status], PRIORITY_LABELS[task.priority], task.type, task.assigned_agent_id ? (getAgentName?.(task.assigned_agent_id) ?? task.assigned_agent_id) : "", task.deadline ? formatDate(task.deadline) : "", task.tags?.join("; ") ?? "", subtasksProgress, formatDate(task.created_at), ] .map(csvEscape) .join(","); }); return [headers.map(csvEscape).join(","), ...rows].join("\n"); }