P
Propza · Architecture
Angular 18 · Supabase · Vercel · PWA · ZAR
South African landlord SaaS

Property management, made simple.

A standalone Angular 18 SPA that talks directly to Supabase from the browser — no API server. Tracks properties, tenants, rent, and payments in ZAR with timezone-safe (UTC+2) status math and an offline-capable PWA shell. Hosted as a static build on Vercel.

Pages / routes
12
Core services
14
DB tables
5
Archit. issues
17
Stack
  • FrontendAngular 18 · TypeScript 5.5
  • StateSignals + RxJS 7.8
  • UITailwind · Bootstrap · ng-bootstrap
  • BackendSupabase v2.75
  • PWA@angular/service-worker
  • HostVercel (static)
  • TestsKarma · Jasmine
  • i18nEN · AF · ZU · XH

Interactive architecture map

Reads top → bottom: external infrastructure → client entry → app bootstrap → router → shell → features → services → database. Drag to pan, scroll to zoom, click any node for details.

main flow fan-out / spawn uses / consumes
50%
50%
scroll pan · ⌘/ctrl+scroll or pinch zoom · drag pan · esc exit
@supabase/supabase-js DI · constructor(svc)
01 External Infrastructure
Vercel
hosting · CD
Static SPA host. SPA rewrites /(.*) → /index.html. Build via npm run build:vercel; output dist/propza/browser.
Google OAuth
via Supabase Auth
Social login. signInWithOAuth({ provider:'google' }), redirectTo /dashboard?beta=granted.
Google Fonts
font-display: swap
Inter 400/600/700 · Inter Tight 600/700/800 · preconnect declared in index.html.
02 Client Entry
Browser · PWA Shell
index.html → main.ts → bootstrapApplication()
ServiceWorker (prod) View Transitions
03 App Bootstrap
AppComponent
src/app/app.component.ts
standalone
Preloader
Lottie · 4s hold
Confirmation
global modal
Toast Layer
toasts$ stream
04 Router & Guards
Router & Guards
src/app/app.routes.ts · src/app/core/guards/
all routes eagerly loaded
authGuard
requires session → /login
guestGuard
if session → /dashboard
betaAccessGuard
localStorage gate
betaAccessGuard is client-side only; not a security boundary.
/ · /beta-access · /login · /register · /reset-password
┌─ AuthenticatedShellComponent [betaAccessGuard, authGuard]
/dashboard/properties/property/:id
/tenants/tenant/:id/payments/settings
└─ ** → NotFoundComponent
05 Authenticated Shell
AuthenticatedShellComponent
Mobile green hero · Desktop sidebar · <router-outlet> mount
ScrollRestore host MobileShellTitle
06 Feature Modules
auth
5 files
  • · LoginComponent
  • · RegisterComponent
  • · ResetPasswordComponent
  • · BetaAccessComponent
Google OAuth
dashboard
  • · Metrics + 6-month chart
  • · Upcoming rents (4)
  • · Activity stream (6)
  • · Inline search (debounced)
combineLatest
properties
  • · PropertyList · PropertyDetail
  • · PropertyCard component
  • · AddPropertyModal
  • · property-status.config
CRUD Storage upload
tenants
  • · TenantList · TenantDetail
  • · TenantCard component
  • · AddTenantModal (vacant only)
  • · tenant-status.config
filter chips
payments
  • · Filterable ledger
  • · Property + free-text search
  • · Totals · average · count
  • · Mobile launcher pattern
read-only
settings
mobile+desktop split
  • · SettingsDesktop (385 LOC)
  • · SettingsMobile (1145 LOC)
  • · 10 modal components
profile · theme · export
07 Shared UI Library
shared/
imported directly by features & layout (no flow line; cross-cutting)
8 components · 1 pipe
Preloader
Lottie
Confirmation
global
Toast
stream
BottomNav
mobile
HeaderBanner
greeting
IOSInstall
A2HS modal
PwaPrompt
beforeinstall
SummaryCard
stat tile
ShortNumberPipe
R 1.2M · R 1K
08 Core Services
Data3 svc
SupabaseService
client singleton · refreshAll$ bus
PropertyService
properties$ · payments$ · loading$
TenantService
tenants$ · loading$
Both subscribe to SupabaseService.refreshAll$
Identity3 svc
AuthService
signals: session · user · userName
BetaAccessService
localStorage · base64 compare
ThemeService
signal · localStorage · profiles.theme
Auth events drive theme effect & user-scoped queries
Rent Engine2 svc
RentDueService
authoritative · monthly · ZA TZ (UTC+2)
RentHelperService
monthly + weekly · ⚠ legacy
⚠ overlap — consolidation recommended
UX & Modals6 svc
Confirmation
promise modal
ModalOptions
responsive NgbModal
MobileShellTitle
title override
ScrollRestore
per-path Y cache
PwaInstall
A2HS · iOS detect
ToastService
shared/component pair
Utilities4 svc
TranslationService
EN · AF · ZU · XH · 1828 LOC
SanitizationService
XSS · DB write guard
ErrorService
normalize → AppError
LoggerService
env-gated console
09 Supabase Backend
Supabase
Browser ↔ DB direct (no API server)
Auth (JWT) RLS owner-scoped PostgreSQL JWT in localStorage
Active tables
properties
RLS
owner_id · status (vacant/occupied) · rent_amount · currency · lease_url · notes
tenants
RLS (via props)
property_id (CASCADE) · rent_due_date · rent_status · deposit_amount
payments
RLS (via props)
property_id (CASCADE) · period (YYYY-MM) · amount · multi-row partial
Storage
bucket
leases · path {userId}/{ts}_{file}
⚠ bucket policies not version-controlled
RPC
SECURITY DEFINER
delete_current_user() · called by AuthService.deleteAccount()
⚠ function definition not in repo
Legacy & implicit not provisioned by repo migrations
tenancies (legacy)
deprecate
start_date · end_date · rent_due_day · still written by AddPropertyModal, read by PropertyDetail
profiles (implicit)
missing migration
id = auth.users.id · full_name · theme · notifications_enabled · RLS only IF EXISTS