EventBuzz_Console/src/app/components/panels/badgeBatchFormState.ts

143 lines
5.8 KiB
TypeScript

/**
* Map a saved BadgeBatch (config) into CreateBadgePanel form state.
*/
import type { BadgeBatchRecord, BadgeLayoutElementPayload } from "../../../services/api/badgeBatches";
import type { BadgeBackgroundImageLayout, BadgeLayoutElement } from "./badgeLayout";
import { defaultBackgroundImageLayout, defaultLayoutForFields, parseBackgroundImageLayout } from "./badgeLayout";
export type BadgeFormState = {
formName: string;
formEventId: string;
sizePreset: "a6" | "a7" | "4x3in" | "4x6in" | "custom";
customW: string;
customH: string;
orientation: "portrait" | "landscape";
includeFaculty: boolean;
includeParticipants: boolean;
participantScope: "all" | "participant_list";
participantListId: string;
backgroundMode: "image" | "color";
backgroundColor: string;
backgroundImageUrl: string;
backgroundImageLayout: BadgeBackgroundImageLayout;
enabledFields: string[];
layoutElements: BadgeLayoutElement[];
customTexts: { id: string; text: string }[];
customImages: { id: string; imageUrl: string }[];
outputFormat: "pdf" | "png" | "jpeg";
badgeTypeLabel: string;
};
function eventIdFromBatch(batch: BadgeBatchRecord): string {
const ev = batch.event;
if (typeof ev === "string") return ev;
if (ev && typeof ev === "object" && "_id" in ev) return String(ev._id);
return "";
}
function duplicateExportName(name: string): string {
const base = (name || "Badge export").trim();
const copy = `Copy of ${base}`;
return copy.length > 120 ? `${copy.slice(0, 117)}…` : copy;
}
function asConfig(batch: BadgeBatchRecord): Record<string, unknown> {
return (batch.config && typeof batch.config === "object" ? batch.config : {}) as Record<string, unknown>;
}
function str(v: unknown, fallback = ""): string {
return typeof v === "string" ? v : fallback;
}
function bool(v: unknown, fallback: boolean): boolean {
return typeof v === "boolean" ? v : fallback;
}
function numStr(v: unknown, fallback: string): string {
if (typeof v === "number" && Number.isFinite(v)) return String(v);
return fallback;
}
function sizePresetFromConfig(c: Record<string, unknown>): BadgeFormState["sizePreset"] {
const p = str(c.sizePreset, "a6");
if (p === "a7" || p === "4x3in" || p === "4x6in" || p === "custom") return p;
return "a6";
}
function layoutFromConfig(c: Record<string, unknown>): BadgeLayoutElement[] {
const raw = c.layout;
if (!Array.isArray(raw) || raw.length === 0) return [];
return raw
.filter((el): el is BadgeLayoutElementPayload => el != null && typeof el === "object" && typeof (el as BadgeLayoutElementPayload).id === "string")
.map((el) => ({ ...el })) as BadgeLayoutElement[];
}
function customTextsFromConfig(c: Record<string, unknown>, layout: BadgeLayoutElement[]): { id: string; text: string }[] {
const fromConfig = Array.isArray(c.customTexts)
? c.customTexts
.filter((x): x is { id: string; text: string } => x != null && typeof x === "object" && typeof (x as { id: string }).id === "string")
.map((x) => ({ id: String(x.id), text: String((x as { text?: string }).text ?? "") }))
: [];
if (fromConfig.length) return fromConfig;
return layout
.filter((el) => el.kind === "custom_text")
.map((el) => ({ id: el.id, text: str(el.text, "Your text") }));
}
function customImagesFromConfig(c: Record<string, unknown>, layout: BadgeLayoutElement[]): { id: string; imageUrl: string }[] {
const fromConfig = Array.isArray(c.customImages)
? c.customImages
.filter((x): x is { id: string; imageUrl: string } => x != null && typeof x === "object" && typeof (x as { id: string }).id === "string")
.map((x) => ({ id: String(x.id), imageUrl: String((x as { imageUrl?: string }).imageUrl ?? "") }))
: [];
if (fromConfig.length) return fromConfig;
return layout
.filter((el) => el.kind === "custom_image")
.map((el) => ({ id: el.id, imageUrl: str(el.imageUrl, "") }))
.filter((x) => x.imageUrl);
}
export function formStateFromBadgeBatch(batch: BadgeBatchRecord): BadgeFormState {
const c = asConfig(batch);
const bgColor = str(c.backgroundColor, "#0B4DD1");
const bgImageUrl = str(c.backgroundImageUrl, "");
const bgMode = bgImageUrl.trim() ? "image" : str(c.backgroundMode, "color") === "image" ? "image" : "color";
const backgroundImageLayout =
parseBackgroundImageLayout(c.backgroundImageLayout) ??
(bgImageUrl.trim() ? defaultBackgroundImageLayout() : defaultBackgroundImageLayout());
const fields = Array.isArray(c.fields) ? c.fields.map((f) => String(f)) : ["name", "title", "org", "qr"];
let layoutElements = layoutFromConfig(c);
if (!layoutElements.length && fields.length) {
layoutElements = defaultLayoutForFields(fields, bgColor, bgMode);
}
const customTexts = customTextsFromConfig(c, layoutElements);
const customImages = customImagesFromConfig(c, layoutElements);
const scope = str(c.participantScope, "all") === "participant_list" ? "participant_list" : "all";
const out = str(c.outputFormat, "pdf");
const outputFormat: BadgeFormState["outputFormat"] =
out === "png" || out === "jpeg" ? out : "pdf";
return {
formName: duplicateExportName(batch.name),
formEventId: eventIdFromBatch(batch),
sizePreset: sizePresetFromConfig(c),
customW: numStr(c.widthMm, "105"),
customH: numStr(c.heightMm, "148"),
orientation: str(c.orientation, "portrait") === "landscape" ? "landscape" : "portrait",
includeFaculty: bool(c.includeFaculty, true),
includeParticipants: bool(c.includeParticipants, true),
participantScope: scope,
participantListId: c.participantListId != null ? String(c.participantListId) : "",
backgroundMode: bgMode,
backgroundColor: bgColor,
backgroundImageUrl: bgImageUrl,
backgroundImageLayout,
enabledFields: fields,
layoutElements,
customTexts,
customImages,
outputFormat,
badgeTypeLabel: str(c.badgeTypeLabel, "Attendee"),
};
}