import { type Component, createSignal, For, Show } from "solid-js"; import { addTask, calculateDateFromUrgency } from "@/store"; import { Button } from "@/components/ui/button"; import { TagPicker } from "@/components/TagPicker"; import { Badge } from "@/components/ui/badge"; import { toast } from "solid-sonner"; import { PRIORITY_OPTIONS, URGENCY_OPTIONS, SIZE_OPTIONS } from "@/lib/constants"; import { X, ArrowRight, Upload, Loader2 } from "lucide-solid"; interface ImportedTask { id: number; title: string; priority: string; urgency: string; size: string; tags: string[]; } /** * Strips common list prefixes from a line of text: * - Checkboxes: ☐ ☑ ✅ ✓ ✗ ✘ ❌ - [ ] - [x] - [X] * - Bullets: • ‣ ◦ ▪ ▸ ► ○ ● - * + * - Numbered: 1. 2) 3 - */ const stripPrefix = (line: string): string => { // Regex matches everything from the start of the string that is NOT a letter or a number // \p{L} matches any kind of letter from any language // \p{N} matches any kind of numeric character // The 'u' flag is required for unicode property escapes return line.replace(/^[^\p{L}\p{N}]*/u, "").trim(); }; export const ImportTool: Component = () => { const [step, setStep] = createSignal<"paste" | "configure">("paste"); const [rawText, setRawText] = createSignal(""); const [tasks, setTasks] = createSignal([]); const [importing, setImporting] = createSignal(false); const [importProgress, setImportProgress] = createSignal(0); // Bulk defaults const [bulkPriority, setBulkPriority] = createSignal("5"); const [bulkUrgency, setBulkUrgency] = createSignal("5"); const [bulkSize, setBulkSize] = createSignal("5"); const [bulkTags, setBulkTags] = createSignal([]); const parseText = () => { const lines = rawText() .split("\n") .map(l => stripPrefix(l)) .filter(l => l.length > 0); if (lines.length === 0) { toast.error("No tasks found in pasted text."); return; } const parsed: ImportedTask[] = lines.map((title, i) => ({ id: i, title, priority: bulkPriority(), urgency: bulkUrgency(), size: bulkSize(), tags: [...bulkTags()] })); setTasks(parsed); setStep("configure"); }; const applyBulkDefaults = () => { setTasks(prev => prev.map(t => ({ ...t, priority: bulkPriority(), urgency: bulkUrgency(), size: bulkSize(), tags: [...bulkTags()] })) ); }; const updateTask = (id: number, field: keyof ImportedTask, value: any) => { setTasks(prev => prev.map(t => t.id === id ? { ...t, [field]: value } : t)); }; const removeTask = (id: number) => { setTasks(prev => prev.filter(t => t.id !== id)); }; const handleImport = async () => { const taskList = tasks(); if (taskList.length === 0) return; setImporting(true); setImportProgress(0); let successCount = 0; for (let i = 0; i < taskList.length; i++) { const t = taskList[i]; try { const urgencyLevel = parseInt(t.urgency); const dueDate = new Date(calculateDateFromUrgency(urgencyLevel)); await addTask(t.title, { priority: parseInt(t.priority), dueDate, tags: t.tags.length > 0 ? t.tags : ["work"], size: parseInt(t.size) }); successCount++; } catch (err) { console.error(`Failed to import task: ${t.title}`, err); } setImportProgress(i + 1); } setImporting(false); toast.success(`Imported ${successCount} task${successCount !== 1 ? "s" : ""} successfully!`); // Reset setRawText(""); setTasks([]); setStep("paste"); setBulkPriority("5"); setBulkUrgency("5"); setBulkSize("5"); setBulkTags([]); }; const reset = () => { setStep("paste"); setTasks([]); }; return (