inventory-pro / README.md
MetiMiester's picture
Update README.md
96e840a verified
---
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