- type
- concept
- created
- Mon Apr 06 2026 02:00:00 GMT+0200 (Central European Summer Time)
- updated
- Mon Apr 06 2026 02:00:00 GMT+0200 (Central European Summer Time)
- sources
- raw/notes/systemPatterns
- tags
- routing frontend architecture
Slug-Based URLs
abstract
Human-readable URLs using property name slugs and unit numbers instead of numeric IDs and UUIDs.URL Format
| Page | Old URL | New URL |
|---|---|---|
| Property detail | /property-management/42 |
/property-management/ashford-farms-ii |
| Unit detail | /property-management/42/buildings/15/units/d4d48f48-... |
/property-management/ashford-farms-ii/units/1535 |
How It Works
Slugify Function
A shared slugify() utility in property.service.ts converts property names to URL-safe slugs:
"Ashford Farms II" → "ashford-farms-ii"
"Aspen at Brookfield" → "aspen-at-brookfield"
Route Definition
// app.routes.ts
{ path: 'property-management/:propertySlug/units/:unitId', ... }
{ path: 'property-management/:id', ... }
Property Detail Resolution
Property detail accepts both IDs and slugs (backward compatible). It fetches all properties and matches:
- First by
property.id === param - Fallback by
slugify(property.name) === param
Unit Detail Resolution
The unit-detail component detects whether the unitId param is a UUID or a unit number:
- UUID format → calls
/api/units/:uuiddirectly - Unit number → calls
/api/units/by-name/:propertySlug/:unitNumber
Backend Endpoint
server.js provides /api/units/by-name/:propertySlug/:unitNumber:
- Fetches all properties, finds one whose slugified name matches
- Queries
units_unitbyproperty_id+unit_number - Falls through to the standard UUID detail handler
Navigation Flow
Properties list (slugified links)
→ Property detail (resolves by ID or slug)
→ Unit square click → goToUnit() uses slugify(property.name) + unit_number
→ Unit detail (resolves by UUID or property-slug + unit-number)
→ Back button uses slugify(property.name) to return
Key Files
| File | Role |
|---|---|
src/app/core/services/property.service.ts |
slugify() utility + getUnitByName() API |
src/app/app.routes.ts |
Route: /:propertySlug/units/:unitId |
src/app/features/properties/unit-detail.component.ts |
UUID vs unit-number detection in ngOnInit() |
src/app/features/properties/property-detail.component.ts |
goToUnit() uses slug + unit number |
src/app/features/properties/properties.component.ts |
Property card links use slug |
server.js |
/api/units/by-name/ endpoint |
Enforcement
This is a non-negotiable project rule (CLAUDE.md rule #11). It is enforced at three layers:
| Layer | File | What it says |
|---|---|---|
| Project rules | CLAUDE.md |
Rule #11: slug-based URLs only, never raw IDs/UUIDs |
| Frontend agent | agents/angular-frontend.md |
Routing section: slug patterns, slugify() usage, backward-compat resolution |
| Backend agent | agents/django-backend.md |
URLs section: every entity needs a by-name resolution endpoint |
For any new entity that gets its own detail page, you must:
- Choose a human-readable identifier (name slug, number, code — never database ID)
- Add a
by-nameendpoint inserver.js(or Django) to resolve the slug - Use
slugify()in frontend navigation - Handle both slug and UUID in the component for backward compatibility
Sources
- raw/notes/systemPatterns — architecture patterns
Related
- wiki/concepts/tech-stack — Angular 19 architecture
- wiki/entities/unitcycle — the product
- wiki/summaries/platform-blueprint — system architecture