Modern parcel management for condominiums. Staff receive parcels, residents collect via QR codes. Self-registration, pickup scheduling, photo evidence, Docker + K3s deployment.
-
Staff Dashboard
- Receive & log incoming parcels with photo (camera or file upload)
- Scan QR codes to verify & hand over parcels to residents
- Mark parcels as returned to sender
- View complete parcel history with filtering (room, date range)
- Manage resident accounts (add/register)
- Photo evidence for both inbound & delivery
-
Resident Portal
- View all parcels assigned to their room
- Generate QR codes for easy collection
- Set preferred pickup date/time (sendout scheduling)
- Self-registration — sign up without staff
- Track parcel history with photos
- Share QR code to let others collect
-
Technical Features
- Real-time QR code generation & scanning
- Base64 photo capture (compatible with mobile webcams)
- Image compression via sharp (auto-rotate, resize to 1200px, JPEG quality 80)
- Code splitting with React.lazy for fast initial load
- Error boundaries for graceful failure handling
- SQLite WAL mode + indexes for perf
- Thai language interface
- Role-based access control (JWT)
- Mobile-responsive design
- Docker deployment with nginx SPA proxy
- K3s/Kubernetes orchestration
- React 18 — UI framework
- TypeScript — Type safety
- Vite — Build tool & dev server
- Tailwind CSS — Styling
- React Router v6 — Client-side routing
- Axios — HTTP client
- qrcode.react — QR code generation
- html5-qrcode — QR code scanning
- Express — Web framework
- TypeScript — Type safety
- tsx — TypeScript execution (dev + prod)
- SQLite — Database (WAL mode)
- JWT — Authentication
- Multer — File uploads
- bcrypt — Password hashing
- sharp — Image processing (resize, compress, EXIF rotate)
- qrcode — QR code generation
- CORS — Cross-origin resource sharing
- Docker — Containerized backend + frontend
- nginx — SPA serve + API proxy
- K3s / Kubernetes — Deployment, services, ingress, PVC
- Docker Hub — Image registry (
ppaaul/icondo-*) - kubectl — Rollout management
- Node.js 18+
- npm or yarn
- Modern web browser (Chrome, Firefox, Safari, Edge)
- Camera access (for photo capture & QR scanning)
- Docker (optional — for container deployment)
git clone https://github.com/yourusername/icondo.git
cd icondo# Backend
cd backend
npm install
# Frontend
cd ../frontend
npm installcd backend
npm run db:seedCreates SQLite database (icondo.db) with sample data: staff account, 2 residents, 3 sample parcels.
# Windows
run-dev-windows.bat
# Unix/Bash
./run-dev.bat# Backend (port 3000)
cd backend
npm run dev
# Frontend (port 5173)
cd frontend
npm run devOpen http://localhost:5173
| Username | Password | Role |
|---|---|---|
staff01 |
staff123 |
staff |
| Username | Password | Room |
|---|---|---|
resident101 |
resident123 |
101 |
resident102 |
resident123 |
102 |
icondo/
├── backend/
│ ├── src/
│ │ ├── db/
│ │ │ ├── schema.ts # DB tables, indexes, migration
│ │ │ ├── seed.ts # Sample data seeder
│ │ │ └── sqlite.ts # SQLite connection
│ │ ├── utils/
│ │ │ └── auth.ts # Password hashing helper
│ │ ├── index.ts # Express server (all routes inline)
│ │ └── ...
│ ├── icondo.db # SQLite database (after seed)
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ │ ├── AddResidentModal.tsx # Staff add resident form
│ │ │ ├── CameraCapture.tsx # Camera/webcam capture
│ │ │ ├── ErrorBoundary.tsx # Error fallback UI
│ │ │ ├── HistoryDashboard.tsx # Parcel history with filters
│ │ │ ├── ImageModal.tsx # Full-screen photo viewer
│ │ │ ├── Login.tsx # Auth login form
│ │ │ ├── QRCodeModal.tsx # QR code generator + share
│ │ │ ├── Register.tsx # Resident self-registration
│ │ │ ├── ResidentMyParcels.tsx # Resident parcel list + pickup scheduling
│ │ │ ├── StaffDeliveryOut.tsx # QR scan + handover + return
│ │ │ ├── StaffReceiveParcel.tsx # Receive new parcel form
│ │ │ └── UserList.tsx # Resident list (staff only)
│ │ ├── types/
│ │ │ └── index.ts # TypeScript interfaces
│ │ ├── utils/
│ │ │ ├── api.ts # Axios client with interceptors
│ │ │ └── preload.ts # Lazy loading helpers
│ │ ├── App.tsx # Router + auth state + code splitting
│ │ ├── main.tsx # React entry point
│ │ └── index.css # Tailwind base styles
│ └── package.json
├── diagrams/ # Mermaid architecture diagrams
├── k8s/ # Kubernetes manifests
│ ├── backend-deployment.yaml
│ ├── backend-service.yaml
│ ├── frontend-deployment.yaml
│ ├── frontend-service.yaml
│ ├── ingress.yaml
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── pvc.yaml
│ └── secret.yaml
├── Dockerfile.backend # Express + SQLite (node:20-alpine)
├── Dockerfile.frontend # React build → nginx serve
├── nginx.conf # SPA routing + API proxy to backend
├── build-deploy.bat # Docker build + push + kubectl apply
├── rollout-restart.bat # kubectl rollout restart helper
├── run-dev-windows.bat # Windows dev launcher
├── run-dev.bat # Unix dev launcher
├── run-advanced.bat # Advanced startup options
├── stop.bat # Stop all servers
└── README.md
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/health |
No | Server health + uptime |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/auth/login |
No | Login with username, password, role |
| POST | /api/auth/register-resident |
No | Self-registration |
| POST | /api/users/register |
Staff | Staff registers a resident |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/users/profile |
Any | Get current user profile |
| GET | /api/users/residents |
Staff | List all residents |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/parcels |
Staff | Create new parcel |
| GET | /api/parcels/:id |
Any | Get single parcel details |
| GET | /api/parcels/resident/:id |
Any | Get parcels for a resident (paginated) |
| GET | /api/parcels/history |
Any | Parcel history with filters (room, date) |
| PUT | /api/parcels/:id/collect |
Staff | Mark parcel as collected |
| PUT | /api/parcels/:id/return |
Staff | Mark parcel as returned to sender |
| PUT | /api/parcels/update-parcel |
Resident | Set preferred pickup date (sendout_at) |
| GET | /api/parcels/:id/qrcode |
Any | Generate QR code data URL |
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/upload/parcel-photo |
Staff | Upload inbound photo (multipart) |
| POST | /api/upload/evidence-photo |
Staff | Upload delivery evidence (multipart) |
| POST | /api/upload/base64-photo |
Staff | Upload base64 photo (mobile/webcam) — auto-compressed via sharp |
build-deploy.bat # Build + push + deploy to K3s
build-deploy.bat skipdeploy # Build + push onlykubectl apply -k k8s/rollout-restart.bat # Restart both deployments
rollout-restart.bat backend # Restart backend only
rollout-restart.bat frontend # Restart frontend only
rollout-restart.bat status # Check rollout statusppaaul/icondo-backend:latestppaaul/icondo-frontend:latest
JWT_SECRET=your-secret-key-here
PORT=3000
DATA_DIR=/path/to/data # Default: backend/
VITE_API_URL=/api # Default: /api (nginx proxy)
Serves frontend SPA, proxies /api/ and /uploads/ to backend. Gzip enabled, asset caching 1 year.
- SQLite WAL mode — concurrent reads + writes
- Database indexes — 7 indexes on users + parcels
- Image compression — sharp resizes to 1200px, JPEG quality 80, EXIF auto-rotate
- Code splitting — React.lazy loads components on demand
- Static caching — nginx serves
/assets/withpublic, immutable(1 year) - Upload caching — Express serves
/uploads/withmax-age=86400
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Note: Camera features require HTTPS or localhost.
- Allow camera permissions in browser
- Check no other app using camera
- Refresh page
- Mobile: serve over HTTPS
- Stop all servers
- Delete
backend/icondo.db - Re-run
npm run db:seed
- Ensure both servers running (backend:3000, frontend:5173)
- Check Vite proxy config in
frontend/vite.config.ts
See AGENTS.md for coding conventions, TypeScript patterns, naming, error handling, security, and commands.
MIT
Built with ❤️ for condominium communities