admin roadmap csv export, adminactionlog, global search
This commit is contained in:
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* CSV helpers untuk admin export. Simple string-building — bukan streaming —
|
||||
* karena admin export jarang lebih dari 10k row di MVP.
|
||||
*
|
||||
* Escape rule (RFC 4180):
|
||||
* - Field yang berisi koma, quote, CR, atau LF → bungkus quote, escape quote
|
||||
* internal dengan dobel quote.
|
||||
* - Field lain biarkan apa adanya.
|
||||
*/
|
||||
|
||||
/** Escape satu cell sesuai aturan RFC 4180. */
|
||||
export function escapeCsvCell(value: unknown): string {
|
||||
if (value == null) return "";
|
||||
const str = String(value);
|
||||
if (/[",\r\n]/.test(str)) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bangun string CSV lengkap dari headers + rows. Pakai CRLF (RFC 4180) supaya
|
||||
* Excel di Windows happy.
|
||||
*/
|
||||
export function buildCsv(headers: string[], rows: unknown[][]): string {
|
||||
const lines = [headers.map(escapeCsvCell).join(",")];
|
||||
for (const row of rows) {
|
||||
lines.push(row.map(escapeCsvCell).join(","));
|
||||
}
|
||||
return lines.join("\r\n") + "\r\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Bikin Response CSV siap pakai dari Next route handler.
|
||||
* BOM ditambahkan supaya Excel auto-detect UTF-8 untuk karakter non-ASCII
|
||||
* (mis. nama Indonesia dengan diakritik).
|
||||
*/
|
||||
export function csvResponse(filename: string, csv: string): Response {
|
||||
const bom = "";
|
||||
return new Response(bom + csv, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "text/csv; charset=utf-8",
|
||||
"Content-Disposition": `attachment; filename="${filename}"`,
|
||||
"Cache-Control": "no-store",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Format ISO untuk CSV (UTC, sortable). */
|
||||
export function csvDate(d: Date | null | undefined): string {
|
||||
if (!d) return "";
|
||||
return d.toISOString();
|
||||
}
|
||||
|
||||
/** Tanggal Jakarta yang readable di Excel. */
|
||||
export function csvDateJakarta(d: Date | null | undefined): string {
|
||||
if (!d) return "";
|
||||
return d.toLocaleString("id-ID", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
timeZone: "Asia/Jakarta",
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user