fix: restore project icons and persist someday tab assignments across reconnects
Project icons (faHeart, faBriefcase etc.) were rendering as literal text in GridTaskBlock because the lookup skipped the legacy "faX" → "x" normalization that ProjectIcon does. Added the same normalization before allIcons.find(). Someday list tabs now survive provider disconnects and reconnects: - customTabs (empty tabs) saved to viewSettings in DB instead of localStorage only - assignListToTab saves a title→tab mapping in viewSettings.somedayTabPrefs - Profile load merges DB customTabs with localStorage for migration - Import route reads somedayTabPrefs and auto-applies tab when creating/ relinking a SomedayList by title match v1.81.20
This commit is contained in:
parent
0371532e7d
commit
f94f83af73
@ -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": {
|
||||
|
||||
@ -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<string, string> = (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)
|
||||
|
||||
@ -307,7 +307,11 @@ export function GridTaskBlock({
|
||||
)}
|
||||
<span style={task.completed && showTaskCheckboxes ? { opacity: 0.5, flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" } : { flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" }}>
|
||||
<span style={{ marginRight: "4px" }}>{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"
|
||||
|
||||
@ -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<string, string> = { ...((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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user