import { Workspace, DocEntity, TodoEntity } from "./types"; import { OutputData } from "@editorjs/editorjs"; /** * Parse JSON file content back to Workspace */ export function importWorkspaceFromJson(jsonContent: string): Workspace { try { const parsed = JSON.parse(jsonContent); // Basic validation if ( typeof parsed !== "object" || parsed === null || typeof parsed.id !== "string" || typeof parsed.name !== "string" ) { throw new Error("Invalid workspace JSON structure"); } return parsed as Workspace; } catch (err) { throw new Error(`Failed to parse workspace JSON: ${err}`); } } /** * Parse JSON file content back to DocEntity */ export function importDocFromJson(jsonContent: string): DocEntity { try { const parsed = JSON.parse(jsonContent); // Basic validation if ( typeof parsed !== "object" || parsed === null || typeof parsed.name !== "string" || parsed.type !== "doc" || typeof parsed.content !== "object" ) { throw new Error("Invalid doc JSON structure"); } return parsed as DocEntity; } catch (err) { throw new Error(`Failed to parse doc JSON: ${err}`); } } /** * Parse JSON file content back to TodoEntity */ export function importTodoFromJson(jsonContent: string): TodoEntity { try { const parsed = JSON.parse(jsonContent); // Basic validation if ( typeof parsed !== "object" || parsed === null || typeof parsed.name !== "string" || parsed.type !== "todo" || typeof parsed.config !== "object" ) { throw new Error("Invalid todo JSON structure"); } return parsed as TodoEntity; } catch (err) { throw new Error(`Failed to parse todo JSON: ${err}`); } } /** * Parse CSV file content to extract tasks */ export function importTodoFromCsv( csvContent: string, todoEntity: TodoEntity, ): { tasks: Record; errors: string[]; } { const lines = csvContent.trim().split("\n"); const tasks: Record = {}; const errors: string[] = []; if (lines.length < 2) { errors.push("CSV file must have at least a header row"); return { tasks, errors }; } const headers = lines[0].split(",").map((h) => h.trim().toLowerCase()); const titleIndex = headers.indexOf("title"); const descriptionIndex = headers.indexOf("description"); const statusIndex = headers.indexOf("status"); const tagsIndex = headers.indexOf("tags"); const docIndex = headers.indexOf("doc"); if (titleIndex === -1) { errors.push("CSV must have a 'Title' column"); return { tasks, errors }; } for (let i = 1; i < lines.length; i++) { try { const line = lines[i]; if (!line.trim()) continue; const values = line.split(",").map((v) => v.trim().replace(/^"|"$/g, "")); const title = values[titleIndex]; if (!title) { errors.push(`Row ${i + 1}: Missing title`); continue; } // Find status by name let statusId = todoEntity.config.statuses[0].id; if (statusIndex !== -1 && values[statusIndex]) { const status = todoEntity.config.statuses.find( (s) => s.name.toLowerCase() === values[statusIndex].toLowerCase(), ); if (status) { statusId = status.id; } } // Parse tags let tagIds: string[] = []; if (tagsIndex !== -1 && values[tagsIndex]) { const tagNames = values[tagsIndex].split(";").map((t) => t.trim()); tagIds = tagNames .map((name) => { const tag = Object.entries(todoEntity.tags).find( ([, t]) => t.name.toLowerCase() === name.toLowerCase(), ); return tag ? tag[0] : null; }) .filter((id) => id !== null) as string[]; } const taskId = `task_${Math.random().toString(36).slice(2, 9)}`; tasks[taskId] = { id: taskId, title, description: descriptionIndex !== -1 ? values[descriptionIndex] : "", doc: docIndex !== -1 && values[docIndex] ? JSON.parse(values[docIndex]) : undefined, statusId, tagIds, createdAt: Date.now(), updatedAt: Date.now(), }; } catch (err) { errors.push(`Row ${i + 1}: ${err}`); } } return { tasks, errors }; } /** * Open file picker and read file content */ export function openFileDialog( accept: string = ".json", ): Promise<{ content: string; filename: string }> { return new Promise((resolve, reject) => { const input = document.createElement("input"); input.type = "file"; input.accept = accept; input.onchange = (e: any) => { const file = e.target.files?.[0]; if (!file) { reject(new Error("No file selected")); return; } const reader = new FileReader(); reader.onload = (event: any) => { try { const content = event.target?.result as string; resolve({ content, filename: file.name }); } catch (err) { reject(err); } }; reader.onerror = () => reject(new Error("Failed to read file")); reader.readAsText(file); }; input.click(); }); }