Act as a senior FastAPI + Jinja dev inside this Replit project. Goal: Make the VWFS "Data Upload" flow send a required `bookMonth` field (YYYY-MM-01) to the backend and then on to the Supabase Edge Function. We will use a simple month selector (Option A). There are two main changes: 1) Update `app/templates/dashboard.html` so the upload form includes a month input and the JS appends `bookMonth` as `YYYY-MM-01` to the FormData. 2) Update the FastAPI `/upload-book` endpoint in `app/main.py` to accept `bookMonth` from the form and forward it to the ingest function using `httpx`. Please make the following concrete edits. ------------------------------------------------ 1) Update the Data Upload form in app/templates/dashboard.html ------------------------------------------------ In `app/templates/dashboard.html`, find the `
Modify it so that it includes a **second input** for the snapshot month, using an HTML `month` input. The structure should become: - Left: file chooser - Middle: month picker - Right: button + status Replace the existing ` Note: the new input has `id="book-month"` (no `name` attribute, we’ll manually append the value in JS). ------------------------------------------------ 2) Update the upload JavaScript in app/templates/dashboard.html ------------------------------------------------ In the same file, near the bottom inside: document.addEventListener("DOMContentLoaded", () => { ... if (uploadForm) { uploadForm.addEventListener("submit", async (e) => { e.preventDefault(); uploadStatus.textContent = "Uploading…"; const formData = new FormData(uploadForm); try { const res = await fetch("/upload-book", { ... }) We need to: - Grab the value from the `book-month` input. - Validate it's present. - Append as `bookMonth` with "-01" at the end (YYYY-MM-01) to the FormData. Replace the body of the submit handler with this: uploadForm.addEventListener("submit", async (e) => { e.preventDefault(); if (!uploadStatus) return; const monthInput = document.getElementById("book-month"); if (!monthInput || !monthInput.value) { uploadStatus.textContent = "❌ Please select the snapshot month."; return; } uploadStatus.textContent = "Uploading…"; const formData = new FormData(uploadForm); // Convert YYYY-MM from the into YYYY-MM-01 const bookMonth = monthInput.value + "-01"; formData.append("bookMonth", bookMonth); try { const res = await fetch("/upload-book", { method: "POST", body: formData, }); const data = await res.json().catch(() => ({})); if (!res.ok) { const message = data.detail || data.error || "Upload failed — please try again."; uploadStatus.textContent = `❌ Upload failed: ${message}`; return; } uploadStatus.textContent = data.detail || "✅ Upload complete — Book imported"; } catch (err) { uploadStatus.textContent = `❌ ${err.message || "Upload failed"}`; } }); Make sure the fetch URL is `/upload-book` (relative, not the Supabase URL). ------------------------------------------------ 3) Update the FastAPI endpoint in app/main.py ------------------------------------------------ Open `app/main.py` and find the `/upload-book` endpoint that was recently rewritten to forward the CSV to the ingest function using `httpx`. We need to: - Accept `bookMonth` as form data. - Forward it in the POST body to the Supabase Edge Function. Ensure the imports at the top of `main.py` include: from fastapi import FastAPI, Request, UploadFile, File, Form import httpx from . import config Now update the endpoint to look like this (replace the existing `/upload-book` function): @app.post("/upload-book") async def upload_book( file: UploadFile = File(...), bookMonth: str = Form(...) ): """ Receive VWFS snapshot CSV + bookMonth from the dashboard, forward them securely to the Supabase Edge Function. """ if not config.INGEST_FUNCTION_URL or not config.INGEST_FUNCTION_TOKEN: raise HTTPException(status_code=500, detail="Ingest function not configured") contents = await file.read() files = { "file": (file.filename or "vwfs_snapshot.csv", contents, file.content_type or "text/csv") } data = { "bookMonth": bookMonth } headers = { "x-ingest-token": config.INGEST_FUNCTION_TOKEN } async with httpx.AsyncClient(timeout=60.0) as client: resp = await client.post( config.INGEST_FUNCTION_URL, headers=headers, files=files, data=data, ) try: payload = resp.json() except Exception: payload = None if resp.status_code >= 400: # Edge function returned an error msg = (payload or {}).get("error") if isinstance(payload, dict) else resp.text raise HTTPException( status_code=resp.status_code, detail=msg or "Ingest function error", ) return {"detail": "Upload complete — Book imported"} ------------------------------------------------ 4) Restart and test ------------------------------------------------ After making these changes: 1. Restart the FastAPI server in Replit (or let the Agent restart it). 2. Open the dashboard (Preview tab). 3. Go to **Data Upload**. 4. Choose a CSV file. 5. Select a month in the new "Snapshot month" field (e.g. 2025-11). 6. Click **Upload & Queue**. You should now see a green message similar to: "✅ Upload complete — Book imported" or any success message returned by the Edge Function. If anything fails, show me the exact error text in the upload status so we can refine the endpoint.