Seven call sites used fetch() directly instead of the api client in src/api/client.js, so they sent no Authorization header. Four of them hit endpoints the backend guards with a permission, and every one of those returned 401 for any signed-in user: AdminPage /api/admin/training/upload-dataset (upload_train_test) AdminPage /api/admin/training/allocate-discounts (allocate_discounts) UserPage /api/user/products/add (add_product) UserPage /api/user/products/upload-file (upload_batch_products) The remaining three hit public GETs and worked, but bypassed VITE_API_BASE_URL and the central 401 handler just the same. Route all seven through the client and add the endpoints it was missing. Multipart uploads still need a raw fetch, because the browser has to set its own Content-Type to carry the boundary, so that is now one upload() helper that attaches the auth header and routes 401s to the logout handler rather than three copies that did neither. handleAllocateDiscounts only acted on res.ok, so a 401 or 403 left the panel looking idle with no indication that nothing had been allocated. It surfaces the error now. Take VITE_API_BASE_URL as a Docker build arg. The app is served from catalogue.nearle.ai.in and the API from mcp.catalogue.nearle.ai.in, and Vite inlines env vars at build time, so setting this on the running container does nothing - it has to reach npm run build. Defaults to empty, which keeps requests relative for the same-origin nginx proxy. Also revoke the object URL after the sample-CSV download. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Frontend - Kirana AI (React + Vite)
React 19 + Vite + Tailwind CSS v4 single-page app for browsing the catalog,
running semantic search, chatting with the RAG assistant, and (on
/admin) triggering ingestion of new brands.
Full setup steps are in the project documentation (../docs/). This file
is a fast local reference.
Quick start
One command (from the project root)
python run_project.py
Starts the FastAPI backend, waits until /api/health answers, then starts
this Vite dev server - so the API is always up before the UI first calls it.
Use --frontend-only to start just Vite against a backend you're already
running yourself.
Manual
cd frontend
npm install
npm run dev
Open http://localhost:5173 - the Vite dev server proxies /api/* to
http://localhost:8000 automatically (see vite.config.js), so make
sure the backend is running there first (see ../backend/README.md).
Note that Vite binds localhost as IPv6 ([::1]), which browsers handle
fine. Only relevant if you probe it with a tool pinned to IPv4 - use
http://[::1]:5173 there, or pass --host 127.0.0.1.
If your backend runs somewhere else, set VITE_API_BASE_URL in a local
.env file (copy .env.example) instead of relying on the proxy.
Production build
npm run build # outputs to dist/
npm run preview # serve the production build locally to sanity-check it
Pages
/- Browse & Search (sidebar brand/category filters, product grid, semantic search bar) with a toggle to Ask AI, a conversational RAG chat panel that cites which catalog products it used for each answer./admin- Trigger ingestion of a new brand and watch job status.
Structure
src/
├── api/client.js # fetch wrapper for every backend endpoint
├── components/
│ ├── Sidebar.jsx / TopBar.jsx
│ ├── ProductCard.jsx / ProductGrid.jsx / ProductModal.jsx
│ ├── ChatPanel.jsx / ChatMessage.jsx
│ └── Atoms.jsx # Spinner, EmptyState
├── pages/
│ ├── HomePage.jsx
│ └── AdminPage.jsx
├── App.jsx # routes
├── main.jsx
└── index.css # Tailwind v4 theme tokens ("Kirana Ledger" palette)