refactor: simplify team bio — replace complex achievements with simple list, remove experience
- Replace VictoryItem (type/place/category/competition/city/date) with RichListItem (text + optional link/image) - Remove VictoryItemListField, DateRangeField, CityField and related helpers - Remove experience field from admin form and user profile (can be in bio text) - Simplify TeamProfile: remove victory tabs, show achievements as RichCards - Fix auto-save: snapshot comparison prevents false saves on focus/blur - Add save on tab leave (visibilitychange) and page close (sendBeacon) - Add save after image uploads (main photo, achievements, education) - Auto-migrate old VictoryItem data to RichListItem format in DB parser
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import Database from "better-sqlite3";
|
||||
import path from "path";
|
||||
import type { SiteContent, TeamMember, RichListItem, VictoryItem } from "@/types/content";
|
||||
import type { SiteContent, TeamMember, RichListItem } from "@/types/content";
|
||||
import { MS_PER_DAY } from "@/lib/constants";
|
||||
|
||||
const DB_PATH =
|
||||
@@ -382,16 +382,18 @@ function parseRichList(val: string | null): RichListItem[] | undefined {
|
||||
} catch { return undefined; }
|
||||
}
|
||||
|
||||
function parseVictories(val: string | null): VictoryItem[] | undefined {
|
||||
function parseVictoriesAsRichList(val: string | null): RichListItem[] | undefined {
|
||||
if (!val) return undefined;
|
||||
try {
|
||||
const arr = JSON.parse(val);
|
||||
if (!Array.isArray(arr) || arr.length === 0) return undefined;
|
||||
// Handle old string[], old RichListItem[], and new VictoryItem[] formats
|
||||
// Migrate old VictoryItem[] → RichListItem[]
|
||||
return arr.map((item: string | Record<string, unknown>) => {
|
||||
if (typeof item === "string") return { place: "", category: "", competition: item };
|
||||
if ("text" in item && !("competition" in item)) return { place: "", category: "", competition: item.text as string, image: item.image as string | undefined, link: item.link as string | undefined };
|
||||
return item as unknown as VictoryItem;
|
||||
if (typeof item === "string") return { text: item };
|
||||
if ("text" in item) return { text: item.text as string, image: item.image as string | undefined, link: item.link as string | undefined };
|
||||
// Old VictoryItem format: combine place + category + competition into text
|
||||
const parts = [item.place, item.category, item.competition].filter(Boolean);
|
||||
return { text: parts.join(" · "), image: item.image as string | undefined, link: item.link as string | undefined };
|
||||
});
|
||||
} catch { return undefined; }
|
||||
}
|
||||
@@ -409,8 +411,7 @@ export function getTeamMembers(): (TeamMember & { id: number })[] {
|
||||
instagram: r.instagram ?? undefined,
|
||||
shortDescription: r.short_description ?? undefined,
|
||||
description: r.description ?? undefined,
|
||||
experience: parseJsonArray(r.experience),
|
||||
victories: parseVictories(r.victories),
|
||||
victories: parseVictoriesAsRichList(r.victories),
|
||||
education: parseRichList(r.education),
|
||||
}));
|
||||
}
|
||||
@@ -431,8 +432,7 @@ export function getTeamMember(
|
||||
instagram: r.instagram ?? undefined,
|
||||
shortDescription: r.short_description ?? undefined,
|
||||
description: r.description ?? undefined,
|
||||
experience: parseJsonArray(r.experience),
|
||||
victories: parseVictories(r.victories),
|
||||
victories: parseVictoriesAsRichList(r.victories),
|
||||
education: parseRichList(r.education),
|
||||
};
|
||||
}
|
||||
@@ -456,7 +456,7 @@ export function createTeamMember(
|
||||
data.instagram ?? null,
|
||||
data.shortDescription ?? null,
|
||||
data.description ?? null,
|
||||
data.experience?.length ? JSON.stringify(data.experience) : null,
|
||||
null,
|
||||
data.victories?.length ? JSON.stringify(data.victories) : null,
|
||||
data.education?.length ? JSON.stringify(data.education) : null,
|
||||
maxOrder.max + 1
|
||||
@@ -478,7 +478,6 @@ export function updateTeamMember(
|
||||
if (data.instagram !== undefined) { fields.push("instagram = ?"); values.push(data.instagram || null); }
|
||||
if (data.shortDescription !== undefined) { fields.push("short_description = ?"); values.push(data.shortDescription || null); }
|
||||
if (data.description !== undefined) { fields.push("description = ?"); values.push(data.description || null); }
|
||||
if (data.experience !== undefined) { fields.push("experience = ?"); values.push(data.experience?.length ? JSON.stringify(data.experience) : null); }
|
||||
if (data.victories !== undefined) { fields.push("victories = ?"); values.push(data.victories?.length ? JSON.stringify(data.victories) : null); }
|
||||
if (data.education !== undefined) { fields.push("education = ?"); values.push(data.education?.length ? JSON.stringify(data.education) : null); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user