diff --git a/App/pelagia-portal/app/api/po/[id]/export/route.ts b/App/pelagia-portal/app/api/po/[id]/export/route.ts index 63f2d23..c1e8aa8 100644 --- a/App/pelagia-portal/app/api/po/[id]/export/route.ts +++ b/App/pelagia-portal/app/api/po/[id]/export/route.ts @@ -1,22 +1,28 @@ import { auth } from "@/auth"; import { db } from "@/lib/db"; import { NextRequest, NextResponse } from "next/server"; -import * as XLSX from "xlsx"; +import ExcelJS from "exceljs"; import { TC_FIXED_LINE, TC_DEFAULTS } from "@/lib/validations/po"; -const COMPANY_NAME = "PELAGIA MARINE SERVICES PVT. LTD"; -const COMPANY_ADDR = "409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210"; -const COMPANY_TEL = "Tel: +91-22-6909 9028 / Email: technical@pelagiamarine.com / Mob: +91 74000 60772"; -const INVOICE_ADDR = "Pelagia Marine Services Pvt Ltd, 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210 (MH)"; -const INVOICE_GST = "Email: accounts@pelagiamarine.com GST NO: 27AAHCP5787B1Z6"; +// ── Company constants ───────────────────────────────────────────────────────── -function fmt(n: number, dec = 2) { - return n.toLocaleString("en-IN", { minimumFractionDigits: dec, maximumFractionDigits: dec }); -} -function fmtDate(d: Date | null | undefined) { +const CO_NAME = "PELAGIA MARINE SERVICES PVT. LTD"; +const CO_ADDR = "Office address: 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210"; +const CO_TEL = "Tel: +91-22-6909 9028 / Email: technical@pelagiamarine.com / Mob: +91 74000 60772"; +const INV_ADDR = "Pelagia Marine Services Pvt Ltd, 409-410, ZION, Plot 273, Sector-10, Kharghar, Navi Mumbai- 410210 (MH)"; +const INV_GST = "Email: accounts@pelagiamarine.com GST NO: 27AAHCP5787B1Z6"; + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function fmtDate(d: Date | null | undefined): string { if (!d) return ""; return new Date(d).toLocaleDateString("en-IN", { day: "2-digit", month: "short", year: "numeric" }); } +function fmtNum(n: number, dec = 2): string { + return n.toLocaleString("en-IN", { minimumFractionDigits: dec, maximumFractionDigits: dec }); +} + +// ── Route ───────────────────────────────────────────────────────────────────── interface Props { params: Promise<{ id: string }> } @@ -28,17 +34,13 @@ export async function GET(request: NextRequest, { params }: Props) { const po = await db.purchaseOrder.findUnique({ where: { id }, include: { - submitter: true, - vessel: true, - account: true, - vendor: true, + submitter: true, vessel: true, account: true, vendor: true, lineItems: { orderBy: { sortOrder: "asc" } }, actions: { include: { actor: true }, orderBy: { createdAt: "asc" } }, }, }); if (!po) return NextResponse.json({ error: "Not found" }, { status: 404 }); - // Permission: submitter can always export their own PO const canViewAll = ["ACCOUNTS", "MANAGER", "SUPERUSER", "AUDITOR", "ADMIN"].includes(session.user.role); if (!canViewAll && po.submitterId !== session.user.id) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); @@ -46,38 +48,39 @@ export async function GET(request: NextRequest, { params }: Props) { const format = request.nextUrl.searchParams.get("format") ?? "pdf"; - // Compute totals - const items = po.lineItems.map((li) => ({ - sn: li.sortOrder + 1, - desc: li.description, - unit: li.unit, - qty: Number(li.quantity), - unitPrice: Number(li.unitPrice), - gstRate: Number((li as { gstRate?: unknown }).gstRate ?? 0.18), - taxable: Number(li.totalPrice), - get gstAmt() { return this.taxable * this.gstRate; }, - get total() { return this.taxable + this.gstAmt; }, - })); + // ── Computed data ───────────────────────────────────────────────────────── + + const items = po.lineItems.map((li, i) => { + const qty = Number(li.quantity); + const unitPrice = Number(li.unitPrice); + const gstRate = Number((li as { gstRate?: unknown }).gstRate ?? 0.18); + const taxable = Number(li.totalPrice); + const gstAmt = taxable * gstRate; + return { sn: i + 1, desc: li.description, unit: li.unit, qty, unitPrice, gstRate, taxable, gstAmt, total: taxable + gstAmt }; + }); const totalTaxable = items.reduce((s, i) => s + i.taxable, 0); - const totalGst = items.reduce((s, i) => s + i.gstAmt, 0); - const grandTotal = totalTaxable + totalGst; + const totalGst = items.reduce((s, i) => s + i.gstAmt, 0); + const grandTotal = totalTaxable + totalGst; const approvalAction = [...po.actions].reverse() - .find((a) => a.actionType === "APPROVED" || a.actionType === "APPROVED_WITH_NOTE"); + .find(a => a.actionType === "APPROVED" || a.actionType === "APPROVED_WITH_NOTE"); const approvedBy = approvalAction?.actor.name ?? ""; - const piNo = (po as { piQuotationNo?: string | null }).piQuotationNo ?? ""; - const piDate = fmtDate((po as { piQuotationDate?: Date | null }).piQuotationDate); - const reqNo = (po as { requisitionNo?: string | null }).requisitionNo ?? ""; - const reqDate = fmtDate((po as { requisitionDate?: Date | null }).requisitionDate); - const placeOfDelivery = (po as { placeOfDelivery?: string | null }).placeOfDelivery ?? ""; const ext = po as { + piQuotationNo?: string | null; piQuotationDate?: Date | null; + requisitionNo?: string | null; requisitionDate?: Date | null; + placeOfDelivery?: string | null; tcDelivery?: string | null; tcDispatch?: string | null; tcInspection?: string | null; tcTransitInsurance?: string | null; tcPaymentTerms?: string | null; tcOthers?: string | null; }; - // Build ordered TC lines: [number, label, value] + const piNo = ext.piQuotationNo ?? ""; + const piDate = fmtDate(ext.piQuotationDate); + const reqNo = ext.requisitionNo ?? ""; + const reqDate = fmtDate(ext.requisitionDate); + const delivery = ext.placeOfDelivery ?? ""; + const tcLines: [number, string, string][] = [ [1, "", TC_FIXED_LINE], [2, "DELIVERY", ext.tcDelivery ?? TC_DEFAULTS.tcDelivery], @@ -88,296 +91,561 @@ export async function GET(request: NextRequest, { params }: Props) { ...(ext.tcOthers ? [[7, "OTHERS", ext.tcOthers] as [number, string, string]] : []), ]; - // ── XLSX ──────────────────────────────────────────────────────────────────── + const vendorAddr = [ + po.vendor?.address, + po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : null, + ].filter(Boolean).join(" "); + const vendorContact = [po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail] + .filter(Boolean).join(" "); + + // ── GST summary row label ───────────────────────────────────────────────── + // If all items share the same GST rate, show it; otherwise show "GST" + const gstRates = [...new Set(items.map(i => Math.round(i.gstRate * 100)))]; + const gstLabel = gstRates.length === 1 ? `GST (${gstRates[0]}%)` : "GST"; + + // ═══════════════════════════════════════════════════════════════════════════ + // XLSX — ExcelJS with full formatting + // ═══════════════════════════════════════════════════════════════════════════ + if (format === "xlsx") { - const wb = XLSX.utils.book_new(); - const ws: XLSX.WorkSheet = {}; - - function s(r: number, c: number, v: string | number | Date, opts?: XLSX.CellObject) { - const addr = XLSX.utils.encode_cell({ r, c }); - ws[addr] = { v, t: typeof v === "number" ? "n" : v instanceof Date ? "d" : "s", ...opts }; - } - function n(v: number): XLSX.CellObject { return { v, t: "n" }; } - - // Header - s(0, 0, COMPANY_NAME); - s(1, 0, COMPANY_ADDR); - s(2, 0, COMPANY_TEL); - s(3, 0, "PURCHASE ORDER"); - // PO meta - s(4, 0, "Purchase Order No:"); - s(4, 2, po.poNumber); - s(4, 7, "Date:"); - s(4, 8, new Date(po.createdAt).toLocaleDateString("en-IN")); - s(5, 0, "Performa Invoice / Quotation No:"); - s(5, 2, piNo); - s(5, 6, "P I / Quotation Date:"); - s(5, 8, piDate); - // Vessel / Requisition - s(6, 0, "Vessel Owner Name"); - s(6, 2, "Pelagia Marine Services Pvt. Ltd."); - s(6, 3, "Budget head"); - s(6, 5, po.account.code); - s(6, 6, "Requested By"); - s(6, 7, po.submitter.name); - s(7, 0, "Vessel/Office Requisition Number"); - s(7, 2, reqNo); - s(7, 3, "Reqn. Date"); - s(7, 5, reqDate); - s(7, 6, "Approved By"); - s(7, 7, approvedBy); - // Delivery - s(8, 0, "Place of Delivery"); - s(8, 2, placeOfDelivery); - // Invoice details - s(10, 0, "Invoice Details"); - s(10, 2, INVOICE_ADDR); - s(11, 2, INVOICE_GST); - // Vendor - s(12, 0, "Vendor Name & Address"); - s(12, 2, po.vendor?.name ?? ""); - s(12, 3, [po.vendor?.address ?? "", po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : ""].filter(Boolean).join(" ")); - s(13, 0, "Contact Person name / mobile no."); - s(13, 2, [po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail].filter(Boolean).join(" ")); - // Line item header - const LH = 14; - s(LH, 0, "S.N."); - s(LH, 1, "Description"); - s(LH, 3, "Unit"); - s(LH, 4, "Qnty"); - s(LH, 5, "Unit price"); - s(LH, 6, "Taxable cost"); - s(LH, 7, "GST%"); - s(LH, 8, "Total cost"); - // Line items - items.forEach((item, idx) => { - const r = LH + 1 + idx; - ws[XLSX.utils.encode_cell({ r, c: 0 })] = n(item.sn); - s(r, 1, item.desc); - s(r, 3, item.unit); - ws[XLSX.utils.encode_cell({ r, c: 4 })] = n(item.qty); - ws[XLSX.utils.encode_cell({ r, c: 5 })] = n(item.unitPrice); - ws[XLSX.utils.encode_cell({ r, c: 6 })] = n(item.taxable); - ws[XLSX.utils.encode_cell({ r, c: 7 })] = n(item.gstRate); - ws[XLSX.utils.encode_cell({ r, c: 8 })] = n(item.total); - }); - // Totals - const TR = LH + items.length + 1 + 6; // leave blank rows up to row 23 (0-indexed) - s(TR, 5, "Total taxable value"); - ws[XLSX.utils.encode_cell({ r: TR, c: 7 })] = n(totalTaxable); - s(TR + 1, 5, `GST (${Math.round(items[0]?.gstRate * 100 || 18)}%)`); - ws[XLSX.utils.encode_cell({ r: TR + 1, c: 7 })] = n(totalGst); - s(TR + 2, 5, "GRAND TOTAL"); - ws[XLSX.utils.encode_cell({ r: TR + 2, c: 7 })] = n(grandTotal); - - // Instructions - const IR = TR + 4; - s(IR, 0, "INSTRUCTIONS TO VENDORS"); - tcLines.forEach(([num, label, value], idx) => { - ws[XLSX.utils.encode_cell({ r: IR + 1 + idx, c: 0 })] = { v: num, t: "n" }; - s(IR + 1 + idx, 1, label ? `${label}: ${value}` : value); + const wb = new ExcelJS.Workbook(); + wb.creator = "Pelagia Portal"; + const ws = wb.addWorksheet(po.poNumber.replace(/\//g, "-"), { + pageSetup: { paperSize: 9, orientation: "portrait", fitToPage: true, fitToWidth: 1, fitToHeight: 0 }, }); - // Signature - const SR = IR + tcLines.length + 3; - s(SR, 0, po.submitter.name); - s(SR + 1, 0, "Authorized Signatory & Stamp"); - s(SR + 1, 6, "Authorized Signatory & Stamp"); - s(SR + 2, 0, "For, Pelagia Marine Services Pvt. Ltd."); - s(SR + 2, 5, "For"); - s(SR + 2, 6, po.vendor?.name ?? ""); - - ws["!ref"] = XLSX.utils.encode_range({ r: 0, c: 0 }, { r: SR + 3, c: 8 }); - ws["!cols"] = [ - { wch: 22 }, { wch: 30 }, { wch: 22 }, { wch: 8 }, { wch: 8 }, - { wch: 14 }, { wch: 14 }, { wch: 8 }, { wch: 14 }, + // ── Column widths (A-I) ───────────────────────────────────────────────── + ws.columns = [ + { width: 20 }, // A + { width: 4 }, // B + { width: 22 }, // C + { width: 10 }, // D + { width: 7 }, // E + { width: 16 }, // F + { width: 7 }, // G + { width: 7 }, // H + { width: 16 }, // I ]; - XLSX.utils.book_append_sheet(wb, ws, po.poNumber.replace(/\//g, "-")); - const buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" }); + // ── Style constants ───────────────────────────────────────────────────── + const thin = (argb = "FF999999") => ({ style: "thin" as const, color: { argb } }); + const med = (argb = "FF555555") => ({ style: "medium" as const, color: { argb } }); + const bordAll = { top: thin(), left: thin(), bottom: thin(), right: thin() }; + const bordMed = { top: med(), left: med(), bottom: med(), right: med() }; - return new NextResponse(buf, { + const ARIAL = "Arial"; + const fBase = { name: ARIAL, size: 9 }; + const fBold = { name: ARIAL, size: 9, bold: true }; + const fTitle = { name: ARIAL, size: 13, bold: true }; + const fH2 = { name: ARIAL, size: 11, bold: true }; + const fSmall = { name: ARIAL, size: 8 }; + + const fillHdr = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } }; + const fillLbl = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF2F2F2" } }; + const fillTot = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFF0F0F0" } }; + const fillGT = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFD8D8D8" } }; + const fillInst = { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFEAEAEA" } }; + + const alignC: Partial = { horizontal: "center", vertical: "middle", wrapText: true }; + const alignL: Partial = { horizontal: "left", vertical: "middle", wrapText: true }; + const alignR: Partial = { horizontal: "right", vertical: "middle" }; + + // Helper: set cell value + style + function sc(row: number, col: number, value: ExcelJS.CellValue, opts: { + font?: Partial; + fill?: ExcelJS.Fill; + border?: Partial; + align?: Partial; + numFmt?: string; + } = {}) { + const cell = ws.getCell(row, col); + cell.value = value; + if (opts.font) cell.font = opts.font as ExcelJS.Font; + if (opts.fill) cell.fill = opts.fill; + if (opts.border) cell.border = opts.border; + if (opts.align) cell.alignment = opts.align; + if (opts.numFmt) cell.numFmt = opts.numFmt; + } + + // Helper: apply border to a whole row range + function rowBorder(row: number, c1: number, c2: number, border: Partial) { + for (let c = c1; c <= c2; c++) ws.getCell(row, c).border = border; + } + + // ── Row heights ───────────────────────────────────────────────────────── + ws.getRow(1).height = 22; + ws.getRow(2).height = 14; + ws.getRow(3).height = 14; + ws.getRow(4).height = 18; + + // ══ ROW 1: Company name ═════════════════════════════════════════════════ + sc(1, 1, CO_NAME, { font: fTitle, align: alignC }); + ws.mergeCells("A1:I1"); + ws.getRow(1).getCell(1).border = { bottom: thin() }; + + // ══ ROW 2: Address ══════════════════════════════════════════════════════ + sc(2, 1, CO_ADDR, { font: fBase, align: alignC }); + ws.mergeCells("A2:I2"); + + // ══ ROW 3: Tel / Email ══════════════════════════════════════════════════ + sc(3, 1, CO_TEL, { font: fSmall, align: alignC }); + ws.mergeCells("A3:I3"); + + // ══ ROW 4: PURCHASE ORDER ════════════════════════════════════════════════ + sc(4, 1, "PURCHASE ORDER", { font: { ...fH2, underline: true }, align: alignC }); + ws.mergeCells("A4:I4"); + ws.getRow(4).border = { top: thin(), bottom: thin() }; + + // ══ ROW 5: PO Number & Date ══════════════════════════════════════════════ + ws.getRow(5).height = 16; + sc(5, 1, "Purchase Order No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A5:B5"); + sc(5, 3, po.poNumber, { font: { ...fBold, color: { argb: "FF1A1A1A" } }, border: bordAll, align: alignL }); + ws.mergeCells("C5:G5"); + sc(5, 8, "Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR }); + sc(5, 9, fmtDate(po.createdAt), { font: fBase, border: bordAll, align: alignL }); + + // ══ ROW 6: PI / Quotation ════════════════════════════════════════════════ + ws.getRow(6).height = 14; + sc(6, 1, "Performa Invoice / Quotation No:", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A6:B6"); + sc(6, 3, piNo, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("C6:F6"); + sc(6, 7, "P I / Quotation Date:", { font: fBold, fill: fillLbl, border: bordAll, align: alignR }); + ws.mergeCells("G6:H6"); + sc(6, 9, piDate, { font: fBase, border: bordAll, align: alignL }); + + // ══ ROW 7: Vessel Owner / Budget / Requested By ═══════════════════════ + ws.getRow(7).height = 14; + sc(7, 1, "Vessel Owner Name", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A7:B7"); + sc(7, 3, "Pelagia Marine Services Pvt. Ltd.", { font: fBase, border: bordAll, align: alignL }); + sc(7, 4, "Budget head", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("D7:E7"); + sc(7, 6, po.account.code, { font: fBase, border: bordAll, align: alignC }); + sc(7, 7, "Requested By", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + sc(7, 8, po.submitter.name, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("H7:I7"); + + // ══ ROW 8: Requisition / Approved By ═════════════════════════════════ + ws.getRow(8).height = 14; + sc(8, 1, "Vessel/Office Requisition Number", { font: fBold, fill: fillLbl, border: bordAll, align: alignL }); + ws.mergeCells("A8:B8"); + sc(8, 3, reqNo, { font: fBase, border: bordAll, align: alignL }); + sc(8, 4, "Reqn. Date", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("D8:E8"); + sc(8, 6, reqDate, { font: fBase, border: bordAll, align: alignC }); + sc(8, 7, "Approved By", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + sc(8, 8, approvedBy, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("H8:I8"); + + // ══ ROWS 9-10: Place of Delivery (2-row span) ═══════════════════════ + ws.getRow(9).height = 14; + ws.getRow(10).height = 14; + sc(9, 1, "Place of Delivery", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A9:B10"); + sc(9, 3, delivery, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells("C9:I10"); + + // ══ ROWS 11-12: Invoice Details (2-row span) ════════════════════════ + ws.getRow(11).height = 14; + ws.getRow(12).height = 13; + sc(11, 1, "Invoice Details", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A11:B12"); + sc(11, 3, INV_ADDR, { font: fSmall, border: { top: thin(), left: thin(), right: thin() }, align: alignL }); + ws.mergeCells("C11:I11"); + sc(12, 3, INV_GST, { font: fSmall, border: { bottom: thin(), left: thin(), right: thin() }, align: alignL }); + ws.mergeCells("C12:I12"); + + // ══ ROW 13: Vendor Name & Address ═══════════════════════════════════ + ws.getRow(13).height = 28; + sc(13, 1, "Vendor Name & Address", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A13:B13"); + sc(13, 3, po.vendor?.name ?? "—", { font: fBold, border: bordAll, align: alignL }); + sc(13, 4, vendorAddr, { font: fSmall, border: bordAll, align: alignL }); + ws.mergeCells("D13:I13"); + + // ══ ROW 14: Contact ═════════════════════════════════════════════════ + ws.getRow(14).height = 14; + sc(14, 1, "Contact Person name / mobile no.", { font: fBold, fill: fillLbl, border: bordAll, align: alignC }); + ws.mergeCells("A14:B14"); + sc(14, 3, vendorContact, { font: fSmall, border: bordAll, align: alignL }); + ws.mergeCells("C14:I14"); + + // ══ ROW 15: Line item header ═════════════════════════════════════════ + const HDR_ROW = 15; + ws.getRow(HDR_ROW).height = 16; + const hdrStyle = { font: fBold, fill: fillHdr, border: bordAll }; + sc(HDR_ROW, 1, "S.N.", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 2, "Description", { ...hdrStyle, align: alignC }); + ws.mergeCells(`B${HDR_ROW}:C${HDR_ROW}`); + sc(HDR_ROW, 4, "Unit", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 5, "Qnty", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 6, "Unit price", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 7, "Taxable cost",{ ...hdrStyle, align: alignC }); + sc(HDR_ROW, 8, "GST%", { ...hdrStyle, align: alignC }); + sc(HDR_ROW, 9, "Total cost", { ...hdrStyle, align: alignC }); + + // ══ Line items ═══════════════════════════════════════════════════════ + const NUM_FMT = '#,##0.00'; + const BODY_ROWS = Math.max(items.length, 7); // reserve at least 7 rows + for (let idx = 0; idx < BODY_ROWS; idx++) { + const r = HDR_ROW + 1 + idx; + ws.getRow(r).height = 15; + const item = items[idx]; + const fillAlt = idx % 2 === 1 + ? { type: "pattern" as const, pattern: "solid" as const, fgColor: { argb: "FFFAFAFA" } } + : undefined; + + sc(r, 1, item?.sn ?? null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC }); + sc(r, 2, item?.desc ?? "", { font: fBase, fill: fillAlt, border: bordAll, align: alignL }); + ws.mergeCells(`B${r}:C${r}`); + sc(r, 4, item?.unit ?? "", { font: fBase, fill: fillAlt, border: bordAll, align: alignC }); + sc(r, 5, item ? item.qty : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 6, item ? item.unitPrice : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 7, item ? item.taxable : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + sc(r, 8, item ? item.gstRate : null, { font: fBase, fill: fillAlt, border: bordAll, align: alignC, numFmt: '0%' }); + sc(r, 9, item ? item.total : null, { font: { ...fBase, bold: !!item }, fill: fillAlt, border: bordAll, align: alignR, numFmt: NUM_FMT }); + } + + // ══ Totals ════════════════════════════════════════════════════════════ + const TOT_ROW = HDR_ROW + 1 + BODY_ROWS + 1; + ws.getRow(TOT_ROW).height = 14; + ws.getRow(TOT_ROW + 1).height = 14; + ws.getRow(TOT_ROW + 2).height = 16; + + // "Total taxable value" + sc(TOT_ROW, 6, "Total taxable value", { font: fBold, fill: fillTot, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW}:G${TOT_ROW}`); + sc(TOT_ROW, 8, totalTaxable, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW}:I${TOT_ROW}`); + + // "GST" + sc(TOT_ROW + 1, 6, gstLabel, { font: fBold, fill: fillTot, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW + 1}:G${TOT_ROW + 1}`); + sc(TOT_ROW + 1, 8, totalGst, { font: fBold, fill: fillTot, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW + 1}:I${TOT_ROW + 1}`); + + // "GRAND TOTAL" + sc(TOT_ROW + 2, 6, "GRAND TOTAL", { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR }); + ws.mergeCells(`F${TOT_ROW + 2}:G${TOT_ROW + 2}`); + sc(TOT_ROW + 2, 8, grandTotal, { font: { ...fBold, size: 10 }, fill: fillGT, border: bordAll, align: alignR, numFmt: NUM_FMT }); + ws.mergeCells(`H${TOT_ROW + 2}:I${TOT_ROW + 2}`); + + // ══ Instructions ═════════════════════════════════════════════════════ + const INST_ROW = TOT_ROW + 4; + ws.getRow(INST_ROW).height = 16; + sc(INST_ROW, 1, "INSTRUCTIONS TO VENDORS", { font: { ...fBold, size: 10 }, fill: fillInst, border: bordAll, align: alignC }); + ws.mergeCells(`A${INST_ROW}:I${INST_ROW}`); + + tcLines.forEach(([num, label, value], i) => { + const r = INST_ROW + 1 + i; + ws.getRow(r).height = 14; + sc(r, 1, num, { font: fBase, border: bordAll, align: alignC }); + const text = label ? `${label}: ${value}` : value; + sc(r, 2, text, { font: fBase, border: bordAll, align: alignL }); + ws.mergeCells(`B${r}:I${r}`); + }); + + // ══ Signatures ═══════════════════════════════════════════════════════ + const SIG_ROW = INST_ROW + tcLines.length + 3; + ws.getRow(SIG_ROW).height = 40; + ws.getRow(SIG_ROW + 1).height = 14; + ws.getRow(SIG_ROW + 2).height = 14; + + // Left sig block (submitter) + sc(SIG_ROW, 1, po.submitter.name, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW}:D${SIG_ROW}`); + sc(SIG_ROW + 1, 1, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW + 1}:D${SIG_ROW + 1}`); + sc(SIG_ROW + 2, 1, "For, Pelagia Marine Services Pvt. Ltd.", { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`A${SIG_ROW + 2}:D${SIG_ROW + 2}`); + + // Right sig block (vendor) + const vName = po.vendor?.name ?? ""; + sc(SIG_ROW, 6, vName, { font: fBold, border: { top: thin(), left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW}:I${SIG_ROW}`); + sc(SIG_ROW + 1, 6, "Authorized Signatory & Stamp", { font: fSmall, border: { left: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW + 1}:I${SIG_ROW + 1}`); + sc(SIG_ROW + 2, 6, `For, ${vName}`, { font: fSmall, border: { left: thin(), bottom: thin(), right: thin() }, align: alignC }); + ws.mergeCells(`F${SIG_ROW + 2}:I${SIG_ROW + 2}`); + + // ── Serialise ───────────────────────────────────────────────────────── + const buf = await wb.xlsx.writeBuffer(); + const slug = po.poNumber.replace(/\//g, "-"); + return new NextResponse(Buffer.from(buf), { headers: { "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Content-Disposition": `attachment; filename="${po.poNumber.replace(/\//g, "-")}.xlsx"`, + "Content-Disposition": `attachment; filename="${slug}.xlsx"`, }, }); } - // ── PDF (HTML print page) ─────────────────────────────────────────────────── + // ═══════════════════════════════════════════════════════════════════════════ + // PDF — HTML print page matching sample layout + // ═══════════════════════════════════════════════════════════════════════════ + const itemRows = items.map((item, i) => ` ${item.sn} ${item.desc} ${item.unit} - ${fmt(item.qty, 3)} - ${fmt(item.unitPrice)} - ${fmt(item.taxable)} + ${fmtNum(item.qty, item.qty % 1 === 0 ? 0 : 3)} + ${fmtNum(item.unitPrice)} + ${fmtNum(item.taxable)} ${Math.round(item.gstRate * 100)}% - ${fmt(item.total)} + ${fmtNum(item.total)} `).join(""); - const termLines = tcLines.map(([num, label, value]) => - `
  • ${num}. ${label ? `${label}: ` : ""}${value}
  • ` + // Pad to at least 7 rows so the table doesn't collapse + const blankRows = Math.max(0, 7 - items.length); + const padRows = Array(blankRows).fill( + '' ).join(""); + const termRows = tcLines.map(([num, label, value]) => ` + + ${num}. + ${label ? `${label}: ` : ""}${value} + `).join(""); + const html = ` ${po.poNumber} — Purchase Order -
    - + +
    +
    - -

    ${COMPANY_NAME}

    -

    ${COMPANY_ADDR}

    -

    ${COMPANY_TEL}

    -
    -

    PURCHASE ORDER

    -
    + +
    ${CO_NAME}
    +
    ${CO_ADDR}
    +
    ${CO_TEL}
    +
    PURCHASE ORDER
    - - + +
    - - - + + + - + - +
    Purchase Order No:${po.poNumber}Date:Purchase Order No:${po.poNumber}Date: ${fmtDate(po.createdAt)}
    PI / Quotation No:Performa Invoice / Quotation No: ${piNo}PI / Quotation Date:P I / Quotation Date: ${piDate}
    - - + +
    - - - - - - + + + + + + - + - + - +
    Vessel Owner NamePelagia Marine Services Pvt. Ltd.Budget Head${po.account.code}Requested By${po.submitter.name}Vessel Owner NamePelagia Marine Services Pvt. Ltd.Budget head${po.account.code}Requested By${po.submitter.name}
    Vessel/Office Req. No.Vessel/Office Requisition Number ${reqNo}Reqn. DateReqn. Date ${reqDate}Approved ByApproved By ${approvedBy}
    - - + +
    - - + +
    Place of Delivery${placeOfDelivery}Place of Delivery${delivery}
    - - + +
    - - + + + + +
    Invoice Details${INVOICE_ADDR}
    ${INVOICE_GST}
    Invoice Details${INV_ADDR}
    ${INV_GST}
    - - + +
    - - - + + + - - + +
    Vendor Name & Address${po.vendor?.name ?? "—"}${[po.vendor?.address, po.vendor?.gstin ? `GSTIN: ${po.vendor.gstin}` : ""].filter(Boolean).join(" ")}Vendor Name & Address${po.vendor?.name ?? "—"}${vendorAddr}
    Contact Person / Mobile${[po.vendor?.contactName, po.vendor?.contactMobile, po.vendor?.contactEmail].filter(Boolean).join(" ")}Contact Person name / mobile no.${vendorContact}
    - - + +
    - - - - + + + + - + ${itemRows} - ${Array(Math.max(0, 6 - items.length)).fill('').join("")} + ${padRows}
    S.N. DescriptionUnitQtyUnit PriceTaxable CostUnitQntyUnit priceTaxable cost GST%Total CostTotal cost
     
    - - - - - + +
    Total Taxable Value${fmt(totalTaxable)}
    + + + - - - + + + - - - + + +
    Total taxable value${fmtNum(totalTaxable)}
    GST (${Math.round((items[0]?.gstRate ?? 0.18) * 100)}%)${fmt(totalGst)}
    ${gstLabel}${fmtNum(totalGst)}
    GRAND TOTAL (INR)${fmt(grandTotal)}
    GRAND TOTAL${fmtNum(grandTotal)}
    - - - - +
    + + +
    INSTRUCTIONS TO VENDORS
      ${termLines}
    + + ${termRows}
    INSTRUCTIONS TO VENDORS
    - -
    + +
    -

    ${po.submitter.name}

    -

    Authorized Signatory & Stamp

    -

    For, Pelagia Marine Services Pvt. Ltd.

    +
    ${po.submitter.name}
    +
    +
    Authorized Signatory & Stamp
    +
    For, Pelagia Marine Services Pvt. Ltd.
    +
    -

    ${po.vendor?.name ?? ""}

    -

    Authorized Signatory & Stamp

    -

    For, ${po.vendor?.name ?? ""}

    +
    ${po.vendor?.name ?? ""}
    +
    +
    Authorized Signatory & Stamp
    +
    For, ${po.vendor?.name ?? ""}
    +
    @@ -385,7 +653,5 @@ export async function GET(request: NextRequest, { params }: Props) { `; - return new NextResponse(html, { - headers: { "Content-Type": "text/html; charset=utf-8" }, - }); + return new NextResponse(html, { headers: { "Content-Type": "text/html; charset=utf-8" } }); } diff --git a/App/pelagia-portal/package.json b/App/pelagia-portal/package.json index 04ec12e..4feacc7 100644 --- a/App/pelagia-portal/package.json +++ b/App/pelagia-portal/package.json @@ -43,6 +43,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "exceljs": "^4.4.0", "lucide-react": "^0.468.0", "next": "^15.1.0", "next-auth": "^5.0.0-beta.25", diff --git a/App/pelagia-portal/pnpm-lock.yaml b/App/pelagia-portal/pnpm-lock.yaml index 350aa16..41a35c1 100644 --- a/App/pelagia-portal/pnpm-lock.yaml +++ b/App/pelagia-portal/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + exceljs: + specifier: ^4.4.0 + version: 4.4.0 lucide-react: specifier: ^0.468.0 version: 0.468.0(react@19.2.5) @@ -998,6 +1001,12 @@ packages: '@noble/hashes': optional: true + '@fast-csv/format@4.3.5': + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + + '@fast-csv/parse@4.3.6': + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -2620,6 +2629,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + '@types/node@22.19.17': resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} @@ -2879,6 +2891,18 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2936,6 +2960,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2973,9 +3000,19 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} @@ -2998,9 +3035,20 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -3036,6 +3084,9 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -3096,6 +3147,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -3109,6 +3164,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} @@ -3118,6 +3176,10 @@ packages: engines: {node: '>=0.8'} hasBin: true + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3198,6 +3260,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + debounce@2.0.0: resolution: {integrity: sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==} engines: {node: '>=18'} @@ -3297,6 +3362,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -3314,6 +3382,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} @@ -3522,10 +3593,18 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} @@ -3596,6 +3675,12 @@ packages: resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} engines: {node: '>=0.8'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3606,6 +3691,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3662,6 +3752,10 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3726,6 +3820,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -3738,6 +3835,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3873,6 +3974,9 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3949,6 +4053,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3959,6 +4066,10 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -3966,6 +4077,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -4040,13 +4154,56 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -4131,6 +4288,10 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -4142,6 +4303,10 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4272,6 +4437,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -4299,6 +4467,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4320,6 +4491,10 @@ packages: resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4404,6 +4579,9 @@ packages: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -4489,10 +4667,16 @@ packages: resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -4547,6 +4731,11 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rollup@4.60.2: resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -4559,6 +4748,9 @@ packages: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4570,6 +4762,10 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -4601,6 +4797,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4713,6 +4912,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -4773,6 +4975,10 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -4805,6 +5011,10 @@ packages: resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} hasBin: true + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4817,6 +5027,9 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -4876,6 +5089,9 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -4913,6 +5129,11 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -5046,6 +5267,9 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -5077,6 +5301,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -6051,6 +6279,25 @@ snapshots: '@exodus/bytes@1.15.0': {} + '@fast-csv/format@4.3.5': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + + '@fast-csv/parse@4.3.6': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -7516,6 +7763,8 @@ snapshots: '@types/json5@0.0.29': {} + '@types/node@14.18.63': {} + '@types/node@22.19.17': dependencies: undici-types: 6.21.0 @@ -7770,6 +8019,42 @@ snapshots: ansi-styles@6.2.3: {} + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -7855,6 +8140,8 @@ snapshots: async-function@1.0.0: {} + async@3.2.6: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -7879,12 +8166,21 @@ snapshots: dependencies: require-from-string: 2.0.2 + big-integer@1.6.52: {} + + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + bluebird@3.4.7: {} + bowser@2.14.1: {} brace-expansion@1.1.14: @@ -7912,11 +8208,17 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) + buffer-crc32@0.2.13: {} + + buffer-indexof-polyfill@1.0.2: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + buffers@0.1.1: {} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -7957,6 +8259,10 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chainsaw@0.1.0: + dependencies: + traverse: 0.3.9 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -8008,6 +8314,13 @@ snapshots: commander@11.1.0: {} + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + concat-map@0.0.1: {} config-chain@1.1.13: @@ -8019,6 +8332,8 @@ snapshots: cookie@0.7.2: {} + core-util-is@1.0.3: {} + cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -8026,6 +8341,11 @@ snapshots: crc-32@1.2.2: {} + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -8108,6 +8428,8 @@ snapshots: date-fns@4.1.0: {} + dayjs@1.11.20: {} + debounce@2.0.0: {} debug@3.2.7: @@ -8191,6 +8513,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + eastasianwidth@0.2.0: {} editorconfig@1.0.7: @@ -8206,6 +8532,10 @@ snapshots: emoji-regex@9.2.2: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + engine.io-parser@5.2.3: {} engine.io@6.6.7: @@ -8629,8 +8959,25 @@ snapshots: eventemitter3@4.0.7: {} + exceljs@4.4.0: + dependencies: + archiver: 5.3.2 + dayjs: 1.11.20 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.5 + unzipper: 0.10.14 + uuid: 8.3.2 + expect-type@1.3.0: {} + fast-csv@4.3.6: + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + fast-deep-equal@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -8699,12 +9046,23 @@ snapshots: frac@1.1.2: {} + fs-constants@1.0.0: {} + + fs.realpath@1.0.0: {} + fsevents@2.3.2: optional: true fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -8777,6 +9135,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@14.0.0: {} globalthis@1.0.4: @@ -8837,6 +9204,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -8846,6 +9215,11 @@ snapshots: indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@1.3.8: {} @@ -8981,6 +9355,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} @@ -9073,6 +9449,13 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -9083,6 +9466,10 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + leac@0.6.0: {} levn@0.4.1: @@ -9090,6 +9477,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-android-arm64@1.32.0: optional: true @@ -9139,12 +9530,40 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + listenercount@1.0.1: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash.defaults@4.2.0: {} + + lodash.difference@4.5.0: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.flatten@4.4.0: {} + + lodash.groupby@4.6.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + + lodash.isfunction@3.0.9: {} + + lodash.isnil@4.0.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isundefined@3.0.1: {} + lodash.merge@4.6.2: {} + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + lodash@4.18.1: {} log-symbols@4.1.0: @@ -9212,6 +9631,10 @@ snapshots: dependencies: brace-expansion: 1.1.14 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + minimatch@9.0.9: dependencies: brace-expansion: 2.1.0 @@ -9220,6 +9643,10 @@ snapshots: minipass@7.1.3: {} + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + ms@2.1.3: {} nanoid@3.3.11: {} @@ -9345,6 +9772,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -9386,6 +9817,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -9407,6 +9840,8 @@ snapshots: path-expression-matcher@1.5.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -9474,6 +9909,8 @@ snapshots: prismjs@1.29.0: {} + process-nextick-args@2.0.1: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -9577,12 +10014,26 @@ snapshots: react@19.2.5: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@4.1.2: {} recharts-scale@0.4.5: @@ -9656,6 +10107,10 @@ snapshots: reusify@1.1.0: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rollup@4.60.2: dependencies: '@types/estree': 1.0.8 @@ -9699,6 +10154,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -9712,6 +10169,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -9748,6 +10209,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setimmediate@1.0.5: {} + sharp@0.33.5: dependencies: color: 4.2.3 @@ -9963,6 +10426,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -10006,6 +10473,14 @@ snapshots: tapable@2.3.3: {} + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -10029,6 +10504,8 @@ snapshots: dependencies: tldts-core: 7.0.30 + tmp@0.2.5: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10041,6 +10518,8 @@ snapshots: dependencies: punycode: 2.3.1 + traverse@0.3.9: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -10137,6 +10616,19 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -10168,6 +10660,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@8.3.2: {} + vary@1.1.2: {} victory-vendor@36.9.2: @@ -10339,6 +10833,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.2.0 + wrappy@1.0.2: {} + ws@8.18.3: {} xlsx@0.18.5: @@ -10359,4 +10855,10 @@ snapshots: yocto-queue@0.1.0: {} + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + zod@3.25.76: {}