// utils/export.ts export type ExportParams = { baseUrl?: string; // e.g. process.env.NEXT_PUBLIC_API_URL site: string; // PROJ-0028 suffix?: "grid" | "solar"; // default "grid" serial?: string | null; // device id like "01" day?: string; // "YYYY-MM-DD" (preferred) start?: string; // ISO string (if not using day) end?: string; // ISO string (if not using day) columns?: string[]; // optional list of columns localTz?: string; // default "Asia/Kuala_Lumpur" }; export function buildExportUrl(p: ExportParams): string { const { baseUrl = "", site, suffix = "grid", serial, day, start, end, columns, localTz = "Asia/Kuala_Lumpur", } = p; const params = new URLSearchParams(); params.set("site", site); params.set("suffix", suffix); params.set("local_tz", localTz); const s = serial?.trim(); if (s) params.set("serial", s); if (day) { params.set("day", day); // simple whole-day export } else { if (!start || !end) throw new Error("Provide either day=YYYY-MM-DD or both start and end."); params.set("start", start); // URLSearchParams will encode '+' correctly params.set("end", end); } if (columns?.length) { // backend expects ?columns=... repeated; append each columns.forEach(c => params.append("columns", c)); } // ensure there's a single slash join for /export/xlsx const root = baseUrl.replace(/\/+$/, ""); return `${root}/export/xlsx?${params.toString()}`; } /** Parse filename from Content-Disposition (handles RFC5987 filename*) */ export function getFilenameFromCD(cd: string | null): string | null { if (!cd) return null; // filename*=UTF-8''encoded-name.xlsx const star = /filename\*\s*=\s*([^']*)''([^;]+)/i.exec(cd); if (star && star[2]) { try { return decodeURIComponent(star[2]); } catch { return star[2]; } } // filename="name.xlsx" OR filename=name.xlsx const plain = /filename\s*=\s*("?)([^";]+)\1/i.exec(cd); return plain ? plain[2] : null; }