diff --git a/package.json b/package.json index 204d5bc..979bf60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.81.19", + "version": "1.81.20", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/app/api/tasks/import/route.ts b/src/app/api/tasks/import/route.ts index b7562f0..9dcf8db 100644 --- a/src/app/api/tasks/import/route.ts +++ b/src/app/api/tasks/import/route.ts @@ -40,13 +40,17 @@ export async function POST(req: NextRequest) { } const user = await prisma.user.findUnique({ - where: { email: session.user.email } + where: { email: session.user.email }, + select: { id: true, viewSettings: true }, }); if (!user) { return NextResponse.json({ error: 'User not found' }, { status: 404 }); } + // Restore tab assignments from stored preferences (survives disconnect/reconnect) + const somedayTabPrefs: Record = (user.viewSettings as any)?.somedayTabPrefs || {}; + // Normalize sourceLists let lists: SourceList[] = []; if (Array.isArray(sourceLists)) { @@ -241,6 +245,7 @@ export async function POST(req: NextRequest) { } if (!somedayList) { + const restoredTab = somedayTabPrefs[listTitle] || null; somedayList = await prisma.somedayList.create({ data: { userId: user.id, @@ -249,21 +254,25 @@ export async function POST(req: NextRequest) { externalId: sourceInfo?.id ?? null, externalProvider: sourceInfo ? provider : null, lastSyncedAt: new Date(), + ...(restoredTab && { tab: restoredTab }), } }); listsCreated++; - console.log(`[IMPORT] Created SomedayList "${listTitle}" (${somedayList.id}) with externalId=${sourceInfo?.id}`); + console.log(`[IMPORT] Created SomedayList "${listTitle}" (${somedayList.id}) with externalId=${sourceInfo?.id}${restoredTab ? `, tab=${restoredTab}` : ''}`); } else if (sourceInfo && !somedayList.externalId) { - // Update existing list with external link if missing + // Update existing list with external link if missing; + // also restore tab if it got lost and we have a preference saved + const restoredTab = !somedayList.tab && somedayTabPrefs[listTitle] ? somedayTabPrefs[listTitle] : undefined; somedayList = await prisma.somedayList.update({ where: { id: somedayList.id }, data: { externalId: sourceInfo.id, externalProvider: provider, lastSyncedAt: new Date(), + ...(restoredTab && { tab: restoredTab }), } }); - console.log(`[IMPORT] Linked SomedayList "${listTitle}" to external ${sourceInfo.id}`); + console.log(`[IMPORT] Linked SomedayList "${listTitle}" to external ${sourceInfo.id}${restoredTab ? `, restored tab=${restoredTab}` : ''}`); } // First pass: create/update all tasks (parents first via sorting) diff --git a/src/components/GridTaskBlock.tsx b/src/components/GridTaskBlock.tsx index 2dacb5c..3143e65 100644 --- a/src/components/GridTaskBlock.tsx +++ b/src/components/GridTaskBlock.tsx @@ -307,7 +307,11 @@ export function GridTaskBlock({ )} {showProjectIcons && task.project && (() => { - const found = allIcons.find(i => i.name === task.project!.icon); + const rawIcon = task.project!.icon || ""; + const normalised = rawIcon.startsWith("fa") && rawIcon.length > 2 && rawIcon[2] === rawIcon[2].toUpperCase() + ? rawIcon.slice(2, 3).toLowerCase() + rawIcon.slice(3) + : rawIcon; + const found = allIcons.find(i => i.name === normalised || i.name === rawIcon); const iconStyle = { marginRight: "4px", verticalAlign: "middle" } as const; if (found) { return found.type === "fa" diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index 72239b7..85ad2aa 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -705,6 +705,15 @@ export default function WeeklyView() { setCustomTabs(tabs); const email = session?.user?.email; if (email) localStorage.setItem(`weekly_custom_tabs_${email}`, JSON.stringify(tabs)); + // Also persist to DB so tabs survive on other devices and reconnects + const updated = { ...(viewSettingsRef.current as any), somedayCustomTabs: tabs }; + viewSettingsRef.current = updated; + setViewSettings(updated); + fetch("/api/user/profile", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ viewSettings: updated }), + }).catch(e => console.error("[tabs] Failed to save custom tabs to DB:", e)); }; const somedayTabs = useMemo(() => { @@ -722,6 +731,20 @@ export default function WeeklyView() { const assignListToTab = async (listId: string, tab: string | null) => { setSomedayLists(prev => prev.map(l => l.id === listId ? { ...l, tab } : l)); + // Persist title→tab preference so it survives reconnects + const list = somedayLists.find(l => l.id === listId); + if (list) { + const prefs: Record = { ...((viewSettingsRef.current as any).somedayTabPrefs || {}) }; + if (tab) { prefs[list.title] = tab; } else { delete prefs[list.title]; } + const updated = { ...(viewSettingsRef.current as any), somedayTabPrefs: prefs }; + viewSettingsRef.current = updated; + setViewSettings(updated); + fetch("/api/user/profile", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ viewSettings: updated }), + }).catch(e => console.error("[tabs] Failed to save tab pref:", e)); + } try { await fetch("/api/someday-lists", { method: "PATCH", @@ -1288,7 +1311,17 @@ export default function WeeklyView() { if (profileData.hourLabelFormat) setHourLabelFormat(profileData.hourLabelFormat); if (profileData.showSubHourSlots !== undefined) setShowSubHourSlots(profileData.showSubHourSlots); if (profileData.allDayPosition) setAllDayPosition(profileData.allDayPosition); - if (profileData.viewSettings) setViewSettings(profileData.viewSettings); + if (profileData.viewSettings) { + setViewSettings(profileData.viewSettings); + // Load customTabs from DB (cross-device, survives reconnects) + const dbTabs = (profileData.viewSettings as any).somedayCustomTabs; + if (Array.isArray(dbTabs) && dbTabs.length > 0) { + setCustomTabs(prev => { + const merged = new Set([...dbTabs, ...prev]); + return Array.from(merged); + }); + } + } // Show onboarding wizard for new users if (profileData.hasCompletedOnboarding === false) {