UnitCycle — Tech Context
Stack
| Layer |
Technology |
Version |
| Frontend |
Angular (standalone) |
19.2 |
| CSS |
Tailwind CSS |
4 |
| Backend |
Django + DRF |
5.x |
| Database |
PostgreSQL |
17 |
| Server |
Node.js (server.js) |
— |
| Process Manager |
PM2 |
— |
| Tests |
Playwright |
— |
| Maps |
MapLibre GL JS |
5 |
| AI |
Claude API |
claude-sonnet-4-20250514 |
| PDF Extraction |
LlamaParse |
— |
Infrastructure
- Server: Contabo VPS at 77.237.235.106
- Nginx:
demo.unitcycle.com + t.unitcycle.com → port 4400, /media/ → port 4400 (NOT Django)
- PM2 processes:
unitcycle (id 45, server.js, port 4400), property-api-django (Django, port 3001)
- ⚠️ NEVER touch PM2 without Rafael's approval
- pgAdmin: Docker on port 8928 →
db.unitcycle.com
Project Structure
/home/claude/projects/unitcycle-demo/
├── src/app/
│ ├── features/ # Angular feature modules
│ │ ├── dashboard/
│ │ ├── invoices/
│ │ ├── lease-abstraction/
│ │ ├── lease-generation/
│ │ ├── move-in-inspection/
│ │ ├── properties/
│ │ ├── property-map/
│ │ ├── settings/
│ │ ├── tenant-screening/
│ │ └── work-orders/
│ └── core/ # Services, theme, guards
├── src/styles/
│ └── theme-tokens.css # 208 CSS variables
├── backend/
│ ├── django_api/ # Django project root
│ └── media/invoices/ # Uploaded invoice PDFs
├── dist/unitcycle/browser/ # Angular build output
├── server.js # Node server (frontend + raw SQL API)
├── tests/ # Playwright E2E tests
├── memory-bank/ # Memory Bank (this folder)
├── CLAUDE.md # Agent rules
└── public/assets/docs/ # AI spec documents
Database
- Host: localhost
- Database:
unitcycle_demo
- Users:
postgres / postgres123 (superuser), propintel (DML only)
- Data: ~254 tables, ~483K rows, ~15 properties, ~5K units, ~5K leases, ~4,617 tenants
Build & Deploy
cd /home/claude/projects/unitcycle-demo
npx ng build
cp backend/media/property_images/real_*.jpg dist/unitcycle/browser/media/property_images/
pm2 restart unitcycle && pm2 save
Git
- Repo:
rafael-private/unitcycle-demo (master branch)
- Auth as:
deploystaff
- RULE: Commit + push after EVERY feature/fix — no exceptions
- Old repos (
deploystaff/townx-demo, unitcycle-demo-new) are DEAD
Known Constraints & Pitfalls
propintel = DML only — all DDL via sudo -u postgres
managed=False on Yardi models — never ALTER those tables
_aed → _usd naming — Django model fields use _aed, API responses use _usd (legacy Dubai/TownX)
server.js fragile — agents overwrite it during delegated tasks, guard carefully
- Angular cache clearing can cause full site outage
PortalUser ≠ Django User — login uses custom model, NOT auth_user
*ngTemplateOutlet silent failure — standalone components need NgTemplateOutlet in imports
- Multi-project confusion —
unitcycle-demo/ and unitcycle/ are DIFFERENT projects, don't mix