debugging file upload ui

This commit is contained in:
Khalim Conn-Kowlessar 2025-07-21 09:04:45 +01:00
parent bc9ddbd7e2
commit bcaaaab391
3 changed files with 66 additions and 33 deletions

View file

@ -19,6 +19,7 @@ const PresignedUrlBodySchema = z.object({
already_installed_file_path: z.string().optional(),
// optional scenario_id to link the plan to an existing scenario
scenario_id: z.string().optional().nullable(),
file_type: z.enum(["csv", "xlsx"]).optional(), // Specify the file type
});
export async function POST(request: NextRequest) {
@ -54,8 +55,6 @@ export async function POST(request: NextRequest) {
console.log("Triggering plan with body: ", validatedBody);
const url = `${process.env.FASTAPI_API_URL}/v1/plan/trigger`;
console.log("Triggering plan with url: ", url);
console.log("Triggering plan with headers: ", headers);
const response = await fetch(url, {
method: "POST",

View file

@ -10,41 +10,70 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
}
const arrayBuffer = await file.arrayBuffer();
const workbook = XLSX.read(arrayBuffer, { type: "array" });
const filename = file.name.toLowerCase();
const mimeType = file.type;
const sheetNames = workbook.SheetNames;
let fileType: "csv" | "xlsx" | "unknown" = "unknown";
if (
filename.endsWith(".csv") ||
mimeType === "text/csv" ||
mimeType === "application/vnd.ms-excel"
) {
fileType = "csv";
} else if (
filename.endsWith(".xlsx") ||
filename.endsWith(".xls") ||
mimeType ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
) {
fileType = "xlsx";
}
if (fileType === "unknown") {
return NextResponse.json(
{ error: "Unsupported file type", file_type: fileType },
{ status: 400 }
);
}
let isStandardised = false;
let sheetNames: string[] = [];
for (const sheetName of sheetNames) {
if (sheetName !== "Standardised Asset List") continue;
// Handle CSV
if (fileType === "csv") {
const text = await file.text();
const lines = text.split("\n").map((line) => line.split(","));
const headers = lines[0].map((h) => h.trim());
isStandardised = headers.includes("domna_property_id");
}
const worksheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) as (
| string
| number
| null
)[][];
// Handle Excel
if (fileType === "xlsx") {
const arrayBuffer = await file.arrayBuffer();
const headers = jsonData.find(
(row): row is string[] =>
Array.isArray(row) &&
row.length > 0 &&
row.every((cell) => typeof cell === "string" || cell === null)
);
// Only load specific sheet to reduce memory usage
const workbook = XLSX.read(arrayBuffer, {
type: "array",
sheets: ["Standardised Asset List"],
});
if (headers?.includes("domna_property_id")) {
isStandardised = true;
break;
sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets["Standardised Asset List"];
if (worksheet) {
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
const headers = jsonData[0] as string[];
isStandardised = headers?.includes("domna_property_id");
}
}
return NextResponse.json({
message: isStandardised
? "Standardised Asset List format detected. Please select which tab to use."
? "Standardised Asset List format detected."
: "Valid file. No standardised format detected.",
sheetNames: isStandardised ? sheetNames : undefined,
isStandardised,
file_type: fileType,
...(fileType === "xlsx" && isStandardised ? { sheetNames } : {}),
});
}

View file

@ -51,9 +51,13 @@ const goalValueOptions = [
{ label: "A", value: "A", disabled: false },
];
function generateS3Key(userId: string, portfolioId: string) {
function generateS3Key(
userId: string,
portfolioId: string,
fileType: "csv" | "xlsx"
) {
const timestamp = new Date().toISOString().replace(/[:.-]/g, "");
return `${userId}/${portfolioId}/${timestamp}/asset_list.csv`;
return `${userId}/${portfolioId}/${timestamp}/asset_list.${fileType}`;
}
async function uploadCsvToS3({
@ -103,6 +107,7 @@ export function useUploadCsvPlan({
ashpCop,
measures,
onSuccessRedirect,
fileType,
}: {
file: File;
portfolioId: string;
@ -115,13 +120,14 @@ export function useUploadCsvPlan({
selectedSheet: string;
measures: (typeof measuresList)[number][];
onSuccessRedirect: (path: string) => void;
fileType: "csv" | "xlsx";
}) {
const session = useSession();
const userId = String(session.data?.user.dbId);
const fileKey = useMemo(
() => generateS3Key(userId, portfolioId),
[userId, portfolioId]
() => generateS3Key(userId, portfolioId, fileType),
[userId, portfolioId, fileType]
);
const { mutateAsync: uploadFileToS3, isLoading: isUploadLoading } =
@ -154,6 +160,7 @@ export function useUploadCsvPlan({
event_type: "remote_assessment",
sheet_name: selectedSheet,
ashp_cop: ashpCop,
file_type: fileType, // Pass the file type for backend processing
};
const triggerRes = await fetch("/api/plan/trigger", {
@ -211,6 +218,7 @@ export default function UploadCsvModal({
const [csvFile, setCsvFile] = useState<File | null>(null);
const [selectedScenario, setSelectedScenario] = useState<string | null>(null);
const [showMeasures, setShowMeasures] = useState(false);
const [fileType, setFileType] = useState<"csv" | "xlsx">("csv");
const scenarioOptions = useMemo(
() =>
@ -242,6 +250,7 @@ export default function UploadCsvModal({
setSheetNames([]);
setSelectedSheet("");
}
setFileType(data.file_type); // capture file type
},
onError: () => {
setSheetNames([]);
@ -282,6 +291,7 @@ export default function UploadCsvModal({
ashpCop: form.watch("ashpCop"),
scenarioName: form.watch("scenario"),
measures: form.watch("measures"),
fileType: fileType,
portfolioId,
selectedSheet,
onSuccessRedirect: (path) => router.push(path),
@ -496,11 +506,6 @@ export default function UploadCsvModal({
/>
</div>
<pre>
{JSON.stringify(form.formState.errors, null, 2)}
</pre>
<pre>{JSON.stringify(form.watch(), null, 2)}</pre>
<div className="grid grid-cols-2 gap-4">
{/* Heat Pump COP */}
<FormField