--- 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://:@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//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://.netlify.app` * (Optionally add dev: `http://localhost:5173,http://127.0.0.1:5173`) 5. Deploy. Verify: `https://.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://.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://.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