From 141fcc2a381a0ccc92a165aa0d1971a3b9d186c0 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Sat, 28 Feb 2026 19:28:37 -0800 Subject: [PATCH] feat(web): wire routine export/import into history page UI --- web/src/app/history/page.tsx | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/web/src/app/history/page.tsx b/web/src/app/history/page.tsx index ac89ac5..8e8b6ec 100644 --- a/web/src/app/history/page.tsx +++ b/web/src/app/history/page.tsx @@ -7,7 +7,8 @@ import { computeStreak } from '@/lib/stats'; import { StatsView } from '@/components/StatsView'; import { StreakCard } from '@/components/StreakCard'; import { TimerCard } from '@/components/TimerCard'; -import { downloadExport, readFileAsText, parseImportData, importTimers } from '@/lib/export'; +import { downloadExportAll, readFileAsText, parseImportData, importTimers, importRoutines } from '@/lib/export'; +import { useRoutineStore } from '@/lib/routine-store'; import { importCalendar } from '@/lib/calendar-import'; import { ArrowLeft, Download, Upload, Calendar, Search, Filter, RotateCcw, FileSpreadsheet, Eye, Check, X } from 'lucide-react'; import { getCategoryById, getAllCategories } from '@/lib/categories'; @@ -17,6 +18,7 @@ import type { Timer } from '@/lib/timer-engine'; export default function HistoryPage() { const timers = useTimerStore((s) => s.timers); const now = useTimerStore((s) => s.now); + const routines = useRoutineStore((s) => s.routines); const [mounted, setMounted] = useState(false); const [search, setSearch] = useState(''); const [filterCategory, setFilterCategory] = useState(''); @@ -50,7 +52,7 @@ export default function HistoryPage() { if (!mounted) return null; - const handleExport = () => downloadExport(timers); + const handleExport = () => downloadExportAll(timers, routines); const handleJsonImport = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; @@ -62,14 +64,28 @@ export default function HistoryPage() { setImportStatus('Invalid file format. Expected a ChronoMind export JSON.'); return; } - const result = importTimers(data, timers); + const timerResult = importTimers(data, timers); // Add the imported timers to the store const existingIds = new Set(timers.map((t) => t.id)); const newTimers = data.timers.filter((t) => !existingIds.has(t.id)); if (newTimers.length > 0) { useTimerStore.setState((s) => ({ timers: [...s.timers, ...newTimers] })); } - setImportStatus(`Imported ${result.imported} timers. ${result.skipped} skipped.${result.errors.length > 0 ? ` Errors: ${result.errors.join(', ')}` : ''}`); + // Import routines if present + const routineResult = importRoutines(data, routines); + if (data.routines && data.routines.length > 0) { + const existingRoutineIds = new Set(routines.map((r) => r.id)); + const newRoutines = data.routines.filter((r) => !existingRoutineIds.has(r.id)); + if (newRoutines.length > 0) { + useRoutineStore.setState((s) => ({ routines: [...s.routines, ...newRoutines] })); + } + } + const parts = [`Imported ${timerResult.imported} timers`]; + if (routineResult.imported > 0) parts.push(`${routineResult.imported} routines`); + const totalSkipped = timerResult.skipped + routineResult.skipped; + if (totalSkipped > 0) parts.push(`${totalSkipped} skipped`); + const allErrors = [...timerResult.errors, ...routineResult.errors]; + setImportStatus(`${parts.join(', ')}.${allErrors.length > 0 ? ` Errors: ${allErrors.join(', ')}` : ''}`); } catch { setImportStatus('Failed to read file.'); } @@ -300,15 +316,15 @@ export default function HistoryPage() { style={{ backgroundColor: 'var(--cm-surface-card)', borderColor: 'var(--cm-border)' }} >

- Export Timers + Export Data

- Download all {timers.length} timers as a JSON file. + Download all {timers.length} timers and {routines.length} routines as a JSON file.