From 89e6a202df497aedd0a7b9c4236f8ce57ee1f333 Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 29 Mar 2026 14:28:02 +0200 Subject: [PATCH] docs(citycorners): update CLAUDE.md for multi-city platform Rewrite project docs to reflect the new multi-city architecture: local-first data layer, city-scoped routes, and community-driven content model. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/citycorners/CLAUDE.md | 123 +++++++++++++++---------------------- 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/apps/citycorners/CLAUDE.md b/apps/citycorners/CLAUDE.md index d35cf7a70..c95f87a1a 100644 --- a/apps/citycorners/CLAUDE.md +++ b/apps/citycorners/CLAUDE.md @@ -1,13 +1,12 @@ # CityCorners -City guide for Konstanz (Bodensee) – showcasing locations, restaurants, museums, and sights. +Open platform for city guides worldwide — users create cities and add locations, growing the platform organically from the community. ## Live URLs | Service | URL | |---------|-----| | **Web App** | https://citycorners.mana.how | -| **API** | https://citycorners-api.mana.how | | **Landing** | https://citycorners-landing.pages.dev | ## Architecture @@ -16,83 +15,70 @@ City guide for Konstanz (Bodensee) – showcasing locations, restaurants, museum apps/citycorners/ ├── apps/ │ ├── landing/ # Astro static site (Tailwind, Cloudflare Pages) -│ ├── backend/ # NestJS API (port 3025 dev, 3041 prod) │ └── web/ # SvelteKit web app (port 5196 dev, 5022 prod) └── CLAUDE.md ``` ### Tech Stack -- **Backend:** NestJS 10, Drizzle ORM, PostgreSQL, mana-core-auth (JWT) +- **Data Layer:** Local-first via @manacore/local-store (Dexie.js/IndexedDB) +- **Sync:** mana-sync (Go, WebSocket) for server synchronization - **Web:** SvelteKit 2, Svelte 5 runes, Tailwind 4, Leaflet maps, svelte-i18n (DE/EN), PWA - **Landing:** Astro 5, Tailwind 3, static site generation -- **Search:** mana-search integration (SearXNG + content extraction) +- **Auth:** mana-core-auth (JWT, guest mode supported) ## Development ```bash -# Full stack (auth + backend + web) +# Full stack (auth + web) pnpm dev:citycorners:full # Individual apps pnpm dev:citycorners:landing -pnpm dev:citycorners:backend pnpm dev:citycorners:web - -# Database -pnpm citycorners:db:push # Push schema -pnpm citycorners:db:studio # Drizzle Studio -pnpm citycorners:db:seed # Seed 41 sample locations - -# Tests -pnpm --filter @citycorners/backend test # Run all tests (31 tests) -pnpm --filter @citycorners/backend test:watch # Watch mode -pnpm --filter @citycorners/backend test:cov # Coverage report - -# Deploy -pnpm deploy:landing:citycorners # Landing to Cloudflare Pages ``` -## Database +## Data Model (Local-First) -PostgreSQL database `citycorners` with Drizzle ORM. +Three IndexedDB collections managed by `@manacore/local-store`: -### Schema - -- **locations** – name, category (enum: sight/restaurant/shop/museum/cafe/bar/park/beach/hotel/event_venue/viewpoint), description, address, lat/lng, imageUrl, timeline (JSONB array of {year, event}) -- **favorites** – userId, locationId (FK → locations, cascade delete), unique constraint on (userId, locationId) - -## API Endpoints - -All endpoints are prefixed with `/api/v1/` in production (via shared-nestjs-setup). +### Cities +- **id** (string, PK) +- **name** (string) — city/village/town name +- **slug** (string, indexed) — URL-friendly name +- **country** (string, indexed) +- **state** (string, optional) — state/region +- **description** (string, optional) +- **latitude** (number) — center coordinates +- **longitude** (number) +- **imageUrl** (string, optional) +- **createdBy** (string, optional) — user ID ### Locations - -| Method | Path | Auth | Description | -|--------|------|------|-------------| -| GET | `/locations` | No | List all (optional `?category=sight\|restaurant\|shop\|museum`) | -| GET | `/locations/search?q=` | No | Text search (ILIKE on name, description, address) | -| GET | `/locations/lookup?q=` | No | Web lookup via mana-search (scrapes info, auto-fills form) | -| GET | `/locations/:id` | No | Get single location | -| POST | `/locations` | Yes | Create location | -| PUT | `/locations/:id` | Yes | Update location | -| DELETE | `/locations/:id` | Yes | Delete location | +- **id** (string, PK) +- **cityId** (string, indexed, FK → cities) +- **name** (string, indexed) +- **category** (enum, indexed: sight/restaurant/shop/museum/cafe/bar/park/beach/hotel/event_venue/viewpoint) +- **description** (string, optional) +- **address** (string, optional) +- **latitude/longitude** (number, optional) +- **imageUrl** (string, optional) +- **timeline** (JSON array of {year, event}, optional) ### Favorites +- **id** (string, PK) +- **locationId** (string, indexed, FK → locations) -| Method | Path | Auth | Description | -|--------|------|------|-------------| -| GET | `/favorites` | Yes | List user's favorite location IDs | -| POST | `/favorites/:locationId` | Yes | Add to favorites | -| DELETE | `/favorites/:locationId` | Yes | Remove from favorites | - -## Web App Pages +## Web App Routes | Route | Description | |-------|-------------| -| `/` | Location grid with category filter pills | -| `/map` | Leaflet map with color-coded markers | -| `/locations/:id` | Detail page with mini-map, timeline, favorite button | -| `/add` | Two-step flow: web lookup → edit form → submit | +| `/` | City discovery — search & browse cities | +| `/add-city` | Create a new city (auth required) | +| `/cities/:slug` | City home — location grid with category filters | +| `/cities/:slug/map` | Leaflet map with color-coded markers | +| `/cities/:slug/add` | Add a location to city (auth required) | +| `/cities/:slug/locations/:id` | Location detail with map, timeline, nearby | +| `/cities/:slug/locations/:id/edit` | Edit location (creator only) | | `/favorites` | User's saved locations | | `/settings` | Theme mode/variant, account, about | | `/login`, `/register` | Auth via shared-auth-ui | @@ -100,16 +86,20 @@ All endpoints are prefixed with `/api/v1/` in production (via shared-nestjs-setu ## Features -- **PWA:** Installable, offline fallback, service worker caching (API: NetworkFirst, images: CacheFirst) -- **i18n:** German + English, language switcher in PillNav, localStorage persistence -- **Favorites:** Optimistic updates, auth-gated heart button on cards + detail page -- **Search:** QuickInputBar in PillNav, backend ILIKE search -- **Web Lookup:** mana-search integration for auto-filling location data from the web -- **Branding:** Registered in shared-branding (AppId, icon, APP_URLS, app switcher) +- **Multi-City Platform:** Users create cities/villages and add locations within them +- **Local-First:** All CRUD via IndexedDB, works offline, syncs to server +- **Guest Mode:** Browse with seed data (Konstanz, Zürich, Berlin) +- **PWA:** Installable, offline fallback, service worker caching +- **i18n:** German + English, language switcher +- **Context-Aware Navigation:** Nav items change based on city context +- **Categories:** 11 location types with color-coded markers +- **Favorites:** Heart button on cards, auth-gated +- **Geocoding:** Auto-coordinates from city/address names (Nominatim) +- **Slug Generation:** Auto-generated URL-safe slugs with umlaut handling ## Categories -| DB Value | Label (DE) | Label (EN) | Card Color | +| DB Value | Label (DE) | Label (EN) | Marker Color | |----------|------------|------------|------------| | `sight` | Sehenswürdigkeit | Sight | Blue | | `restaurant` | Restaurant | Restaurant | Red | @@ -123,27 +113,14 @@ All endpoints are prefixed with `/api/v1/` in production (via shared-nestjs-setu | `event_venue` | Veranstaltungsort | Event Venue | Pink | | `viewpoint` | Aussichtspunkt | Viewpoint | Sky | -## Tests - -4 test suites, 31 tests covering: -- `LocationService` – CRUD, search, category filtering -- `FavoriteService` – add/remove/check, conflict handling -- `LocationLookupService` – web search, content extraction, address/category detection, error handling -- `LocationController` – endpoint routing, query params, auth guards - ## Docker -- **Backend:** `apps/citycorners/apps/backend/Dockerfile` (multi-stage, port 3041 prod) - **Web:** `apps/citycorners/apps/web/Dockerfile` (multi-stage, port 5022 prod) -- **Entrypoints:** Auto schema push, optional seed on start -- **docker-compose.macmini.yml:** Both services configured with health checks +- **docker-compose.macmini.yml:** Web service with health check ## Environment Variables | Variable | Used by | Description | |----------|---------|-------------| -| `DATABASE_URL` | Backend | PostgreSQL connection string | -| `MANA_CORE_AUTH_URL` | Backend | Auth service URL | -| `MANA_SEARCH_URL` | Backend | mana-search service URL | -| `PUBLIC_BACKEND_URL` | Web | Backend API URL | | `PUBLIC_MANA_CORE_AUTH_URL` | Web | Auth service URL (client) | +| `PUBLIC_SYNC_SERVER_URL` | Web | mana-sync WebSocket URL |