mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 22:41:09 +02:00
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) <noreply@anthropic.com>
This commit is contained in:
parent
e73d64c999
commit
89e6a202df
1 changed files with 50 additions and 73 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue