fix: 4 bugs from regression testing

- BUG-1: Strip HTML tags in sanitizeName (prevent stored XSS)
- BUG-2: Strip HTML tags in notes via sanitizeText across all 3 booking APIs
- BUG-3: Dashboard excludes archived/past MCs and expired Open Day events from counts
- BUG-4: Truncate long names in booking cards to prevent overflow
This commit is contained in:
2026-03-24 16:43:19 +03:00
parent aa0cfe35c3
commit 2c64951cb3
6 changed files with 33 additions and 12 deletions

View File

@@ -568,17 +568,31 @@ function DashboardSummary({ onNavigate }: { onNavigate: (tab: Tab) => void }) {
const [counts, setCounts] = useState<DashboardCounts | null>(null);
useEffect(() => {
const today = new Date().toISOString().split("T")[0];
const tomorrow = new Date(Date.now() + 86400000).toISOString().split("T")[0];
Promise.all([
adminFetch("/api/admin/group-bookings").then((r) => r.json()),
adminFetch("/api/admin/mc-registrations").then((r) => r.json()),
adminFetch("/api/admin/open-day").then((r) => r.json()).then(async (events: { id: number }[]) => {
if (events.length === 0) return [];
return adminFetch(`/api/admin/open-day/bookings?eventId=${events[0].id}`).then((r) => r.json());
// Fetch MC registrations + section data to filter out archived
Promise.all([
adminFetch("/api/admin/mc-registrations").then((r) => r.json()),
adminFetch("/api/admin/sections/masterClasses").then((r) => r.json()),
]).then(([regs, mcData]: [{ status: string; masterClassTitle: string }[], { items?: { title: string; slots: { date: string }[] }[] }]) => {
// Build set of upcoming MC titles
const upcomingTitles = new Set<string>();
for (const mc of mcData.items || []) {
if (mc.slots?.some((s) => s.date >= today)) upcomingTitles.add(mc.title);
}
return regs.filter((r) => upcomingTitles.has(r.masterClassTitle));
}),
// Fetch Open Day — only upcoming events
adminFetch("/api/admin/open-day").then((r) => r.json()).then(async (events: { id: number; date: string }[]) => {
const active = events.find((e) => e.date >= today);
if (!active) return [];
return adminFetch(`/api/admin/open-day/bookings?eventId=${active.id}`).then((r) => r.json());
}),
adminFetch("/api/admin/reminders").then((r) => r.json()).catch(() => []),
]).then(([gb, mc, od, rem]: [{ status: string }[], { status: string }[], { status: string }[], { eventDate: string }[]]) => {
const today = new Date().toISOString().split("T")[0];
const tomorrow = new Date(Date.now() + 86400000).toISOString().split("T")[0];
setCounts({
classesNew: gb.filter((b) => b.status === "new").length,
classesContacted: gb.filter((b) => b.status === "contacted").length,