Jewelry Rental Management System
Manage ornaments, rentals, customers, and collections — all in one place.
Mobile-first web app for gold and silver ornament rental shops in India.
Built for speed at the counter: book, pickup, WhatsApp bill, return — in seconds.
Dashboard · Inventory · Rentals · Customers · Accounts · Settings
| Screen | What it does |
|---|---|
| Dashboard | Live stats (out on rent, bookings, pickups, returns due, income) with drill-down to filtered lists |
| Inventory | Photo catalog, item codes, search, future-booking badges, PDF export |
| Rentals | Bookings, pickups, returns, overdue tracking — filter by status |
| Customers | Searchable customer database with rental history |
| Accounts | Rental income and deposit ledger — daily, weekly, monthly |
| Settings | Outlets, categories, staff (admin) |
- Product overview
- Features
- Tech stack
- Architecture
- Project structure
- Getting started
- Environment variables
- Rental lifecycle
- API overview
- Production deployment
- Scripts
- Security notes
- Ornament catalog per outlet with auto-generated item codes (
NEC0001,RNG0042, …) - Up to 5 photos per item (JPEG, PNG, WebP)
- Search by name, code, or category
- Future bookings badge per item (upcoming pre-bookings count)
- Date-range availability when creating rentals
- PDF export (full inventory or available-for-date-range)
- Soft delete (admin only)
- 5-step booking wizard: dates → ornaments → customer → pricing → confirm
- Date-overlap conflict detection across
BOOKED,ACTIVE,OVERDUE, andEXTENDEDrentals - Payment plans at booking:
- 50% rent now, balance + deposit on pickup
- Full rent now, deposit on pickup
- Full rent + deposit now
- Flexible “collect now” amounts at confirmation (tracks balance automatically)
- Pickup — convert
BOOKED→ACTIVE, collect remaining payments - Return — mark
RETURNED, record deposit refund - Extend — push due date with optional extra rent charge
- Reschedule — change pickup/return dates on a booking
- Swap item — replace an ornament on an active rental
- Cancel — cancel pre-pickup bookings with optional rent/deposit refunds
- Dedicated pickup queue page for today’s scheduled handovers
- Name, phone, address, optional ID proof
- Full rental history per customer
- Quick-create customer during booking
- Live stats with drill-down links: out on rent, total bookings, pickups today, due today, today’s income
- Pickups today list
- Overdue rentals with one-tap WhatsApp reminders
- Due for return carousel (today + tomorrow)
- Notifications page (pickups and returns due tomorrow)
- Rental income ledger (
RENTAL,RENTAL_ADVANCE,RENTAL_BALANCE, refunds) - Deposit collected / refunded / withheld tracking
- Filter by date range and payment method
- PDF export for accounts
| Role | Access |
|---|---|
| Staff | Rentals, inventory (view), customers, accounts, pickups/returns |
| Admin | Everything staff can do, plus settings, staff management, ornament delete, outlet config |
Each user belongs to one outlet. Data is fully isolated between branches — no cross-branch view.
Pre-filled wa.me links for booking confirmations, rental bills, and overdue reminders. No WhatsApp Business API required.
Installable on phones (Add to Home Screen). Optimised for portrait use at the shop counter.
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18, TypeScript, Tailwind CSS |
| State | Zustand (auth, wizard), TanStack Query (server state) |
| Backend | Node.js, Express, TypeScript |
| Database | PostgreSQL, Prisma ORM |
| Auth | JWT (Bearer token), bcrypt password hashing |
| Files | Multer (local disk storage) |
| jsPDF + jsPDF-AutoTable (client-side export) | |
| Monorepo | pnpm workspaces |
| Production | PM2, Caddy (HTTPS reverse proxy) |
flowchart TB
subgraph client [Browser / PWA]
Web[Next.js :3000]
end
subgraph server [Single server]
Caddy[Caddy HTTPS]
API[Express API :3001]
PG[(PostgreSQL)]
Disk[Uploads directory]
end
Web -->|"/api/v1/*"| Caddy
Web -->|"/uploads/*"| Caddy
Caddy --> API
API --> PG
API --> Disk
Development: Next.js dev server proxies /api/* to http://localhost:3001.
Production: Caddy terminates TLS and routes /api/* and /uploads/* to Express; all other paths go to Next.js.
RentalManagementSystem/
├── apps/
│ ├── api/ # Express REST API
│ │ ├── prisma/ # Schema, migrations, seed
│ │ └── src/
│ │ ├── routes/ # auth, ornaments, rentals, customers, payments, dashboard, settings
│ │ ├── middleware/ # auth, upload, error handling
│ │ └── utils/ # availability, payments, rental calc, date helpers
│ └── web/ # Next.js frontend (PWA)
│ └── src/
│ ├── app/ # App Router pages
│ ├── components/ # UI, rental wizard, shared
│ ├── lib/ # API client, formatters, PDF
│ └── stores/ # Zustand stores
├── packages/
│ └── types/ # Shared TypeScript types (@rental/types)
├── ecosystem.config.js # PM2 process definitions
├── Caddyfile # Production reverse proxy config
├── deploy.sh # One-command production redeploy
└── pnpm-workspace.yaml
- Node.js 20+
- pnpm 10+
- PostgreSQL 14+
git clone https://github.com/Sinu00/RentalManagementSystem.git
cd RentalManagementSystem
pnpm installcp apps/api/.env.example apps/api/.envEdit apps/api/.env — at minimum set DATABASE_URL and JWT_SECRET:
# Generate a JWT secret
openssl rand -hex 32pnpm --filter api exec prisma migrate deploy
pnpm --filter api exec prisma generate
pnpm --filter api exec prisma db seedpnpm devThis starts both services concurrently:
| Service | URL |
|---|---|
| Web app | http://localhost:3000 |
| API | http://localhost:3001 |
| Health check | http://localhost:3001/api/v1/health |
After seeding, use these development-only credentials:
| Outlet | Password | Role | |
|---|---|---|---|
| Branch 1 | admin@branch1.com |
admin123 |
Admin |
| Branch 1 | staff@branch1.com |
staff123 |
Staff |
| Branch 2 | admin@branch2.com |
admin123 |
Admin |
Change all default passwords before any production use.
| Symptom | Likely cause |
|---|---|
| Login fails / network error | API not running — use pnpm dev (both api + web), not web alone |
JWT_SECRET environment variable is required |
Missing apps/api/.env |
| Invalid credentials | Local DB not seeded, or password differs from production (separate databases) |
| Upload fails locally | Ensure apps/api/uploads is writable; API must be running |
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://postgres:password@localhost:5432/rental_db |
JWT_SECRET |
Secret for signing auth tokens (required) | openssl rand -hex 32 |
PORT |
API listen port | 3001 |
UPLOAD_DIR |
Directory for ornament photos | ./uploads (local) or /home/deploy/app/uploads (prod) |
BASE_URL |
Public URL for image links | http://localhost:3001 (local) or https://your-domain.com (prod) |
CORS_ORIGIN |
Allowed browser origin(s), comma-separated | http://localhost:3000 |
TZ |
Timezone for date boundaries | Asia/Kolkata (set in production via PM2) |
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_API_URL |
API base path | /api/v1 (uses Next.js rewrite in dev) |
In production, set NEXT_PUBLIC_API_URL=/api/v1 so requests stay same-origin through Caddy.
stateDiagram-v2
[*] --> BOOKED: Create booking
BOOKED --> ACTIVE: Complete pickup
BOOKED --> CANCELLED: Cancel
ACTIVE --> OVERDUE: Past due date
ACTIVE --> EXTENDED: Extend
OVERDUE --> EXTENDED: Extend
EXTENDED --> OVERDUE: Past new due date
ACTIVE --> RETURNED: Return
OVERDUE --> RETURNED: Return
EXTENDED --> RETURNED: Return
CANCELLED --> [*]
RETURNED --> [*]
| Status | Meaning |
|---|---|
BOOKED |
Pre-booking — dates reserved, ornaments not yet handed over |
ACTIVE |
Out with customer, within due date |
OVERDUE |
Out with customer, past due date |
EXTENDED |
Due date moved forward |
RETURNED |
Closed — items back, deposit handled |
CANCELLED |
Booking cancelled before pickup |
Ornaments are blocked for overlapping dates while a rental is BOOKED, ACTIVE, OVERDUE, or EXTENDED.
Base path: /api/v1
Authentication: Authorization: Bearer <token> on all routes except POST /auth/login.
| Route prefix | Purpose |
|---|---|
/auth |
Login, current user |
/ornaments |
CRUD, images, categories, export |
/customers |
CRUD, rental history |
/rentals |
CRUD, pickup, cancel, return, extend, reschedule, swap-item |
/payments |
Ledger, export |
/dashboard |
Stats, overdue list, pickups today |
/settings |
Outlet, staff, categories (admin) |
Useful rental list filters:
| Query param | Description |
|---|---|
status |
Filter by rental status |
outOnly=true |
ACTIVE + OVERDUE + EXTENDED |
startDate |
Pickup date (YYYY-MM-DD) |
dueDate |
Return date (YYYY-MM-DD) |
search |
Rental number or customer name |
Runs on a single VPS — AWS Lightsail in production — with:
- PM2 — runs API (
apps/api/dist) and web (next start) asrms-apiandrms-web - Caddy — automatic HTTPS, routes
/api/*and/uploads/*to the API - PostgreSQL — on the same server
- Point DNS A record to the server
- Install Node 20, pnpm, PostgreSQL, Caddy, PM2
- Clone repo, copy
apps/api/.env.production.example→apps/api/.env, fill secrets - Copy
apps/web/.env.production.example→apps/web/.env.local - Run migrations, build, seed (first time only), start PM2
- Install
Caddyfileat/etc/caddy/Caddyfileand reload Caddy - Create writable upload directory:
mkdir -p /home/deploy/app/uploads
Critical production values in apps/api/.env:
BASE_URL=https://your-domain.com
UPLOAD_DIR=/home/deploy/app/uploads
CORS_ORIGIN=https://your-domain.com./deploy.shPulls latest code, installs dependencies, runs migrations, builds, and reloads PM2 with zero downtime.
- Database: nightly
pg_dump(not in git — configure on server) - Uploads:
UPLOAD_DIRis outside git; back up regularly
| Command | Description |
|---|---|
pnpm dev |
Start API + web in development |
pnpm build |
Build types → API → web |
pnpm lint |
Lint all packages |
pnpm --filter api dev |
API only (tsx watch) |
pnpm --filter web dev |
Web only (Next.js on :3000) |
pnpm --filter api exec prisma studio |
Open Prisma Studio |
pnpm --filter api exec prisma db seed |
Seed demo outlets and users |
- JWT tokens expire after 7 days
- Login rate-limited (20 attempts per 15 minutes per IP)
- Passwords hashed with bcrypt (cost factor 12)
- Outlet-scoped data access on every API route
- Admin-only routes for staff management, ornament deletion, and settings
- Helmet security headers on the API
- Never commit
.envfiles or upload directories
- Mobile-first — counter staff use phones; desktop is secondary
- Speed at the counter — primary actions in two taps or fewer
- Trust in every number — rupee amounts in Indian formatting (₹1,20,000), dates always visible
- Calm density — enough to act, nothing more
See PRODUCT.md for full product and brand guidelines.
Private / proprietary. All rights reserved unless otherwise specified by the repository owner.

