datewise/app/routes/app.bookings.export.tsx
metatroncubeswdev e56182e0e3 chore: initial Datewise app (rebranded from SlotSure prototype)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:06:49 -04:00

52 lines
2.2 KiB
TypeScript

import type { LoaderFunctionArgs } from "react-router";
import prisma from "../db.server";
import { toCsv } from "../engine/runsheet";
import { localParts } from "../engine/time";
import { adminContext } from "../lib/admin.server";
import { bookingWhere } from "../lib/bookings-query.server";
/** Excel-friendly CSV (UTF-8 BOM) of the bookings matching the list filters. */
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { cfg } = await adminContext(request);
const sp = new URL(request.url).searchParams;
const today = localParts(Date.now(), cfg.shop.timezone).date;
const rows = await prisma.booking.findMany({
where: bookingWhere(cfg.shop.id, sp, today),
orderBy: [{ date: "asc" }, { slotStart: "asc" }],
take: 20000,
});
const loc = Object.fromEntries(cfg.locations.map((l) => [l.id, l.name]));
const csv = toCsv(
rows.map((b) => {
const a = (b.address ?? {}) as { line1?: string; city?: string; postal?: string };
return {
order: b.orderName ?? "",
status: b.status,
review_note: b.reviewReason ?? "",
method: b.method,
location: b.locationId ? loc[b.locationId] ?? b.locationId : "",
date: b.date ?? "",
slot_start: b.slotStart ?? "",
slot_end: b.slotEnd ?? "",
timezone: b.timezone,
start_utc: b.startUtc?.toISOString() ?? "",
end_utc: b.endUtc?.toISOString() ?? "",
customer: b.customerName ?? "",
email: b.customerEmail ?? "",
phone: b.customerPhone ?? "",
address: [a.line1, a.city, a.postal].filter(Boolean).join(", "),
items: ((b.items ?? []) as { title: string; quantity: number }[]).map((i) => `${i.quantity}x ${i.title}`).join("; "),
fee: (b.fee / 100).toFixed(2),
rescheduled: b.rescheduleCount,
};
}),
["order", "status", "review_note", "method", "location", "date", "slot_start", "slot_end", "timezone", "start_utc", "end_utc", "customer", "email", "phone", "address", "items", "fee", "rescheduled"],
);
return new Response(csv, {
headers: {
"Content-Type": "text/csv; charset=utf-8",
"Content-Disposition": `attachment; filename="datewise-bookings-${today}.csv"`,
},
});
};