Spaces:
Runtime error
Runtime error
| title: Inventory Pro (Monolith) | |
| emoji: π¦ | |
| colorFrom: gray | |
| colorTo: indigo | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| # Inventory Pro | |
| Single-domain deployment of a MERN inventory app on a Hugging Face **Docker Space**. | |
| --- | |
| # Inventory Pro β MERN (Assignment) | |
| A full-stack inventory app that hits the assignment spec end-to-end: | |
| * π Search & filter, π₯ Import / π€ Export CSV, βοΈ inline row editing, π§Ύ inventory history | |
| * π Clean, responsive UI (table on desktop, cards on mobile) | |
| * π§° Robust CSV parser (BOM, comma/semicolon/tab),optimistic updates | |
| * π Production-friendly CORS, Express **v5** compatible preflight | |
| * βοΈ Ready to deploy: Backend β Render, Frontend β Netlify | |
| --- | |
| ## Tech Stack | |
| * **Backend:** Node.js, Express **v5**, Mongoose, Multer, csv-parse, json2csv, express-validator, Helmet, Morgan, CORS | |
| * **Frontend:** React + TypeScript + Vite, Tailwind CSS, @tanstack/react-query v5, axios, lucide-react | |
| * **Database:** MongoDB (Atlas or local) | |
| --- | |
| ## Project Structure | |
| ``` | |
| inventory-pro/ | |
| backend/ | |
| src/ | |
| config/ | |
| controllers/ | |
| middleware/ | |
| models/ | |
| routes/ | |
| utils/ | |
| server.js | |
| .env | |
| package.json | |
| frontend/ | |
| src/ | |
| components/ | |
| lib/ | |
| App.tsx | |
| main.tsx | |
| index.css | |
| .env | |
| index.html | |
| netlify.toml # (optional, for Netlify) | |
| package.json | |
| README.md | |
| ``` | |
| --- | |
| ## Prerequisites | |
| * Node.js 18+ (recommended 20+) | |
| * MongoDB: **Atlas** connection string OR local `mongod` | |
| --- | |
| ## 1) Run Locally | |
| ### A) Backend | |
| 1. Create `backend/.env`: | |
| ```env | |
| PORT=4000 | |
| # Choose ONE: | |
| # MONGODB_URI=mongodb+srv://<username>:<password>@cluster0.no4gl81.mongodb.net/inventorydb?retryWrites=true&w=majority&appName=Cluster0 | |
| MONGODB_URI=mongodb://127.0.0.1:27017/inventorydb | |
| # Allow both common dev origins | |
| CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173 | |
| ``` | |
| > If your password has special characters (`@`, `#`, `!`, etc.), URL-encode them (`@` β `%40`). | |
| 2. Install & run: | |
| ```bash | |
| cd backend | |
| npm i | |
| npm run dev | |
| ``` | |
| Expected log: | |
| ``` | |
| β MongoDB connected | |
| π API running on http://localhost:4000 | |
| ``` | |
| Health checks in browser: | |
| * [http://localhost:4000/health](http://localhost:4000/health) β `{"ok":true,"db":"connected"}` | |
| * [http://localhost:4000/api/products](http://localhost:4000/api/products) β JSON list | |
| > **Express v5 note:** Preflight is handled safely (no wildcard `*` route needed). | |
| --- | |
| ### B) Frontend | |
| 1. Create `frontend/.env`: | |
| ```env | |
| VITE_API_BASE=http://localhost:4000 | |
| ``` | |
| 2. Install & run: | |
| ```bash | |
| cd frontend | |
| npm i | |
| npm run dev | |
| ``` | |
| Open the app (Vite prints the URL, usually [http://localhost:5173](http://localhost:5173)). | |
| * Top-right **API / DB** pills should be green β when backend is running. | |
| * Hover them to see the API base URL in use. | |
| --- | |
| ## 2) CSV Format (Import/Export) | |
| **Headers (required):** | |
| `name,unit,category,brand,stock,status,image` | |
| * `status` is optional; if omitted, itβs derived from `stock` (0 β OUT\_OF\_STOCK, >0 β IN\_STOCK) | |
| * Parser accepts **comma**, **semicolon**, or **tab** delimited CSV | |
| * BOM/Excel CSVs are handled | |
| **Example:** | |
| ```csv | |
| name,unit,category,brand,stock,status,image | |
| Coffee Beans,kg,Beverages,Acme,50,IN_STOCK,https://picsum.photos/seed/coffee/200 | |
| Tea Bags,box,Beverages,Acme,0,OUT_OF_STOCK,https://picsum.photos/seed/tea/200 | |
| ``` | |
| --- | |
| ## 3) Quick Tests (curl / PowerShell) | |
| **Create a CSV quickly (PowerShell):** | |
| ```powershell | |
| @' | |
| name,unit,category,brand,stock,status,image | |
| Coffee Beans,kg,Beverages,Acme,50,IN_STOCK,https://picsum.photos/seed/coffee/200 | |
| Tea Bags,box,Beverages,Acme,0,OUT_OF_STOCK,https://picsum.photos/seed/tea/200 | |
| Sugar,kg,Grocery,SweetCo,100,IN_STOCK,https://picsum.photos/seed/sugar/200 | |
| Milk,litre,Dairy,DairyBest,20,IN_STOCK,https://picsum.photos/seed/milk/200 | |
| '@ | Set-Content -Encoding UTF8 ".\products.csv" | |
| ``` | |
| **Import:** | |
| ```bash | |
| curl.exe -s -X POST "http://localhost:4000/api/products/import" -F "file=@products.csv" | |
| # β {"addedCount":4,"skipped":[...if duplicates...]} | |
| ``` | |
| **List:** | |
| ```bash | |
| curl.exe -s "http://localhost:4000/api/products?page=1&limit=10" | |
| ``` | |
| **Search (assignment alias):** | |
| ```bash | |
| curl.exe -s "http://localhost:4000/api/products/search?name=Tea" | |
| ``` | |
| **Update stock (PUT):** | |
| ```powershell | |
| # get first id | |
| $resp = Invoke-RestMethod -Uri "http://localhost:4000/api/products?limit=1" -Method GET | |
| $id = $resp.data[0]._id | |
| Invoke-RestMethod -Uri "http://localhost:4000/api/products/$id" -Method PUT -ContentType "application/json" -Body '{"stock":75}' | |
| ``` | |
| **History:** | |
| ```bash | |
| curl.exe -s "http://localhost:4000/api/products/<id>/history" | |
| ``` | |
| **Export:** | |
| ```bash | |
| curl.exe -L "http://localhost:4000/api/products/export" -o exported.csv | |
| ``` | |
| --- | |
| ## 4) API Reference | |
| Base URL (dev): `http://localhost:4000/api` | |
| | Method | Path | Description | | |
| | -----: | ----------------------- | ------------------------------------------------------ | | |
| | GET | `/products` | List (pagination, sorting; filters `name`, `category`) | | |
| | GET | `/products/search` | Alias of list supporting `?name=` | | |
| | POST | `/products/import` | Import CSV (multipart: `file`) | | |
| | GET | `/products/export` | Export all products as CSV | | |
| | PUT | `/products/:id` | Update editable fields (validates) | | |
| | GET | `/products/:id/history` | Inventory change logs (sorted desc) | | |
| --- | |
| ## 5) Assignment Mapping (Scoring) | |
| **Frontend (50 pts)** | |
| * Search bar (`/api/products/search?name=` alias) + category filter β | |
| * βAdd New Productβ button (CSV one-row import) β | |
| * Import/Export buttons aligned right (Export downloads; Import uploads & refreshes) β | |
| * Products table columns: Image, Name, Unit, Category, Brand, Stock, Status, Actions β | |
| * Stock labels: green βIn Stockβ when `>0`, red βOut of Stockβ when `0` β | |
| * Actions: Edit/Delete; inline editing with Save β PUT `/api/products/:id` β | |
| * Table updates without full page refresh (optimistic) β | |
| * Inventory history sidebar (fetch logs desc by date) β | |
| * Responsive/mobile layout β | |
| **Backend (50 pts)** | |
| * Import CSV: accept file, parse columns, insert only new, return `addedCount` + `skipped` (duplicate info for editing) β | |
| * Export CSV: returns all products β | |
| * Products list: JSON with pagination & sorting β | |
| * Update product: validation (unique name, numeric stock, status enum), updates only editable fields β | |
| * Inventory history: logs stock changes; fetch API returns desc β | |
| --- | |
| ## 6) Deployment | |
| ### A) Backend β Render | |
| 1. Push repo to GitHub. | |
| 2. Render: **New β Web Service** β connect `backend/` | |
| 3. **Build Command:** `npm install` | |
| **Start Command:** `node src/server.js` | |
| 4. **Environment variables:** | |
| * `MONGODB_URI` β your Atlas URI (e.g., `mongodb+srv://.../inventorydb?...`) | |
| * `CORS_ORIGIN` β `https://<your-netlify>.netlify.app` | |
| * (Optionally add dev: `http://localhost:5173,http://127.0.0.1:5173`) | |
| 5. Deploy. Verify: `https://<render-app>.onrender.com/health` β `{ "ok": true, "db": "connected" }`. | |
| > Express reads `process.env.PORT` automatically on Render. | |
| ### B) Frontend β Netlify | |
| 1. In `frontend/.env` set: | |
| ```env | |
| VITE_API_BASE=https://<render-app>.onrender.com | |
| ``` | |
| 2. (Optional) Add `frontend/netlify.toml`: | |
| ```toml | |
| [build] | |
| command = "npm run build" | |
| publish = "dist" | |
| [[redirects]] | |
| from = "/*" | |
| to = "/index.html" | |
| status = 200 | |
| ``` | |
| 3. Netlify β **New site from Git** | |
| * Base directory: `frontend` | |
| * Build command: `npm run build` | |
| * Publish directory: `dist` | |
| * Env var: `VITE_API_BASE=https://<render-app>.onrender.com` | |
| --- | |
| ## 7) Troubleshooting | |
| * **Frontend βfailed to fetchβ / red API pill** | |
| * Backend not running or URL mismatch. Check `frontend/.env` and restart `npm run dev`. | |
| * CORS: ensure your frontend origin is in `CORS_ORIGIN`. | |
| * **Import says βNo valid rows found in CSVβ** | |
| * Headers must match exactly: `name,unit,category,brand,stock,status,image`. | |
| * At least `name`, `unit`, `category` must be non-empty for a row to import. | |
| * Excel CSVs with semicolons are supported. | |
| * **History not updating** | |
| * Only changes to `stock` create history entries. | |
| --- | |
| ## 8) Scripts | |
| **Backend** | |
| ```bash | |
| npm run dev # nodemon dev server | |
| npm start # node src/server.js | |
| ``` | |
| **Frontend** | |
| ```bash | |
| npm run dev # Vite dev server | |
| npm run build # production build | |
| npm run preview # serve built app locally | |
| ``` | |
| --- | |
| ## License | |
| MIT | |