diff --git a/apps/web/package.json b/apps/web/package.json index 4f330d9..edc6d3d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,7 @@ "@trpc/server": "^11.0.0", "clsx": "^2.1.1", "dompurify": "^3.3.3", + "exceljs": "^4.4.0", "framer-motion": "^12.38.0", "next": "^15.1.7", "next-auth": "^5.0.0-beta.25", diff --git a/apps/web/src/components/admin/BatchSkillImport.tsx b/apps/web/src/components/admin/BatchSkillImport.tsx index 6eaf7b3..05a200f 100644 --- a/apps/web/src/components/admin/BatchSkillImport.tsx +++ b/apps/web/src/components/admin/BatchSkillImport.tsx @@ -3,6 +3,7 @@ import { useState, useRef } from "react"; import { trpc } from "~/lib/trpc/client.js"; import { parseSkillMatrixWorkbook, matchRoleName } from "~/lib/skillMatrixParser.js"; +import { assertSpreadsheetFile } from "~/lib/excel.js"; import type { SkillEntry } from "@capakraken/shared"; interface ParsedEntry { @@ -54,6 +55,7 @@ export function BatchSkillImport() { ); try { + assertSpreadsheetFile(file, { allowCsv: false, contextLabel: "skill matrix import" }); const buffer = await file.arrayBuffer(); const result = await parseSkillMatrixWorkbook(buffer); @@ -152,7 +154,7 @@ export function BatchSkillImport() {

Click to select multiple .xlsx files

Name files after resource EID or display name for automatic matching

- + {/* Summary */} diff --git a/apps/web/src/components/estimates/EstimateWizard.tsx b/apps/web/src/components/estimates/EstimateWizard.tsx index 35de50e..e1eb1a6 100644 --- a/apps/web/src/components/estimates/EstimateWizard.tsx +++ b/apps/web/src/components/estimates/EstimateWizard.tsx @@ -269,7 +269,7 @@ export function EstimateWizard({ onClose }: { onClose: () => void }) { event.target.value = ""; if (!isSpreadsheetFile(file)) { - setScopeImportWarnings(["Unsupported file type. Please upload .xlsx, .xls, or .csv."]); + setScopeImportWarnings(["Unsupported file type. Please upload .xlsx or .csv."]); return; } @@ -586,7 +586,7 @@ export function EstimateWizard({ onClose }: { onClose: () => void }) {