feat: centralize popup texts in new admin tab /admin/popups

- New admin page for shared popup texts (success, waiting list, error, Instagram hint)
- Removed popup fields from MC and Open Day admin editors
- All SignupModals now read from centralized popups config
- Stored as "popups" section in DB with fallback defaults
This commit is contained in:
2026-03-25 23:48:06 +03:00
parent 983bf296fc
commit 6c485872b0
11 changed files with 100 additions and 62 deletions
+7 -8
View File
@@ -519,6 +519,7 @@ const SECTION_KEYS = [
"schedule",
"news",
"contact",
"popups",
] as const;
export function getSiteContent(): SiteContent | null {
@@ -549,6 +550,12 @@ export function getSiteContent(): SiteContent | null {
pricing: sections.pricing,
schedule: sections.schedule,
news: sections.news ?? { title: "Новости", items: [] },
popups: sections.popups ?? {
successMessage: "Вы записаны!",
waitingListText: "Все места заняты, но мы добавили вас в лист ожидания.\nЕсли кто-то откажется — мы предложим место вам.",
errorMessage: "Не удалось отправить заявку. Свяжитесь с нами через Instagram — мы запишем вас!",
instagramHint: "По вопросам пишите в Instagram",
},
contact: sections.contact,
team: {
title: teamSection.title || "",
@@ -1014,8 +1021,6 @@ export interface OpenDayEvent {
discountThreshold: number;
minBookings: number;
maxParticipants: number;
successMessage?: string;
waitingListText?: string;
active: boolean;
}
@@ -1098,8 +1103,6 @@ function mapEventRow(r: OpenDayEventRow): OpenDayEvent {
discountThreshold: r.discount_threshold,
minBookings: r.min_bookings,
maxParticipants: r.max_participants ?? 0,
successMessage: r.success_message ?? undefined,
waitingListText: r.waiting_list_text ?? undefined,
active: !!r.active,
};
}
@@ -1219,8 +1222,6 @@ export function updateOpenDayEvent(
discountThreshold: number;
minBookings: number;
maxParticipants: number;
successMessage: string;
waitingListText: string;
active: boolean;
}>
): void {
@@ -1235,8 +1236,6 @@ export function updateOpenDayEvent(
if (data.discountThreshold !== undefined) { sets.push("discount_threshold = ?"); vals.push(data.discountThreshold); }
if (data.minBookings !== undefined) { sets.push("min_bookings = ?"); vals.push(data.minBookings); }
if (data.maxParticipants !== undefined) { sets.push("max_participants = ?"); vals.push(data.maxParticipants); }
if (data.successMessage !== undefined) { sets.push("success_message = ?"); vals.push(data.successMessage || null); }
if (data.waitingListText !== undefined) { sets.push("waiting_list_text = ?"); vals.push(data.waitingListText || null); }
if (data.active !== undefined) { sets.push("active = ?"); vals.push(data.active ? 1 : 0); }
if (sets.length === 0) return;
sets.push("updated_at = datetime('now')");