managarten/apps/clock/CLAUDE.md
Till-JS 79b629b820 feat(contacts): add import/export with Google Contacts integration
- Add vCard/CSV file import with duplicate detection and merge options
- Add Google Contacts OAuth2 integration for importing from Google
- Add vCard/CSV export with format selection and filtering options
- Add connected_accounts table for OAuth token storage
- Add FileUploader, ImportPreview, GoogleImport components
- Add ExportModal with format selection (vCard/CSV)
- Add i18n translations for import/export (DE/EN)
2025-12-03 15:54:31 +01:00

11 KiB

Clock Project Guide

Übersicht

Clock ist eine vollständige Uhren-App mit Weltzeituhr, Wecker, Timer, Stoppuhr und Pomodoro. Die App synchronisiert Wecker und Timer zwischen Geräten über ein Backend.

App Port URL
Backend 3017 http://localhost:3017
Web App 5187 http://localhost:5187
Landing Page 4323 http://localhost:4323

Project Structure

apps/clock/
├── apps/
│   ├── backend/          # NestJS API server (@clock/backend)
│   │   └── src/
│   │       ├── main.ts
│   │       ├── app.module.ts
│   │       ├── db/
│   │       │   ├── database.module.ts
│   │       │   ├── connection.ts
│   │       │   ├── schema/
│   │       │   │   ├── alarms.schema.ts
│   │       │   │   ├── timers.schema.ts
│   │       │   │   ├── world-clocks.schema.ts
│   │       │   │   └── presets.schema.ts
│   │       │   └── seed.ts
│   │       ├── alarm/
│   │       ├── timer/
│   │       ├── world-clock/
│   │       ├── preset/
│   │       └── health/
│   │
│   ├── web/              # SvelteKit web app (@clock/web)
│   │   └── src/
│   │       ├── lib/
│   │       │   ├── api/
│   │       │   ├── stores/
│   │       │   ├── components/
│   │       │   └── i18n/
│   │       └── routes/
│   │           ├── +layout.svelte
│   │           ├── +page.svelte          # Dashboard
│   │           ├── alarms/
│   │           ├── timers/
│   │           ├── stopwatch/
│   │           ├── pomodoro/
│   │           ├── world-clock/
│   │           ├── settings/
│   │           ├── feedback/
│   │           └── (auth)/
│   │
│   └── landing/          # Astro landing page (@clock/landing)
│
├── packages/
│   └── shared/           # Shared types & constants (@clock/shared)
│
├── package.json
└── CLAUDE.md

Commands

Root Level (from monorepo root)

# Alle Apps starten
pnpm clock:dev                    # Run all clock apps

# Einzelne Apps starten
pnpm dev:clock:backend            # Start backend server (port 3017)
pnpm dev:clock:web                # Start web app (port 5187)
pnpm dev:clock:landing            # Start landing page (port 4323)
pnpm dev:clock:app                # Start web + backend together

# Datenbank
pnpm clock:db:push                # Push schema to database
pnpm clock:db:studio              # Open Drizzle Studio
pnpm clock:db:seed                # Seed initial data

# Deploy
pnpm deploy:landing:clock         # Deploy landing to Cloudflare Pages

Backend (apps/clock/apps/backend)

pnpm dev                         # Start with hot reload
pnpm build                       # Build for production
pnpm start:prod                  # Start production server
pnpm db:push                     # Push schema to database
pnpm db:studio                   # Open Drizzle Studio
pnpm db:seed                     # Seed initial data

Web App (apps/clock/apps/web)

pnpm dev                         # Start dev server
pnpm build                       # Build for production
pnpm preview                     # Preview production build

Landing Page (apps/clock/apps/landing)

pnpm dev                         # Start dev server (port 4323)
pnpm build                       # Build for production
pnpm preview                     # Preview build

Technology Stack

Layer Technology
Backend NestJS 10, Drizzle ORM, PostgreSQL
Web SvelteKit 2.x, Svelte 5 (runes), Tailwind CSS 4
Landing Astro 5.x, Tailwind CSS
Auth Mana Core Auth (JWT)
i18n svelte-i18n (DE, EN, FR, ES, IT)

Features

1. Weltzeituhr

  • Zeitzonen-Übersicht mit mehreren Städten
  • Zeitdifferenz zum lokalen Standort
  • Tag/Nacht-Indikator
  • Drag & Drop Sortierung

2. Wecker

  • Erstelle/Bearbeite/Lösche Wecker
  • Wiederholende Wecker (Wochentage wählen)
  • Snooze-Funktion (konfigurierbar)
  • Verschiedene Alarm-Sounds
  • Cross-Device Sync

3. Timer

  • Multiple Timer gleichzeitig
  • Timer-Presets (Quick Select)
  • Cross-Device Sync für aktive Timer
  • Start/Pause/Reset Kontrollen

4. Stoppuhr

  • Präzise Zeitmessung
  • Rundenzeiten mit Best/Worst Markierung
  • Lokal-only (kein Sync nötig)

5. Pomodoro

  • Arbeit/Pause Zyklen
  • Anpassbare Intervalle
  • Preset-Auswahl (Klassisch, Kurzer Fokus, Tiefe Arbeit)
  • Push-Benachrichtigungen

API Endpoints

Health

GET    /api/v1/health              # Health check

Alarms

GET    /api/v1/alarms              # List all alarms
POST   /api/v1/alarms              # Create alarm
GET    /api/v1/alarms/:id          # Get alarm
PUT    /api/v1/alarms/:id          # Update alarm
DELETE /api/v1/alarms/:id          # Delete alarm
PATCH  /api/v1/alarms/:id/toggle   # Toggle enabled

Timers

GET    /api/v1/timers              # List all timers
POST   /api/v1/timers              # Create timer
GET    /api/v1/timers/:id          # Get timer
PUT    /api/v1/timers/:id          # Update timer
DELETE /api/v1/timers/:id          # Delete timer
POST   /api/v1/timers/:id/start    # Start timer
POST   /api/v1/timers/:id/pause    # Pause timer
POST   /api/v1/timers/:id/reset    # Reset timer

World Clocks

GET    /api/v1/world-clocks        # List world clocks
POST   /api/v1/world-clocks        # Add city
DELETE /api/v1/world-clocks/:id    # Remove city
PUT    /api/v1/world-clocks/reorder # Reorder cities
GET    /api/v1/timezones/search    # Search timezones

Presets

GET    /api/v1/presets             # List presets
POST   /api/v1/presets             # Create preset
PUT    /api/v1/presets/:id         # Update preset
DELETE /api/v1/presets/:id         # Delete preset

Database Schema

alarms

Column Type Description
id UUID Primary key
user_id UUID Owner
label VARCHAR(255) Alarm name
time TIME Alarm time (HH:MM:SS)
enabled BOOLEAN Active flag
repeat_days INT[] [0-6] für Wochentage
snooze_minutes INTEGER Snooze duration
sound VARCHAR(100) Sound identifier
vibrate BOOLEAN Vibration enabled

timers

Column Type Description
id UUID Primary key
user_id UUID Owner
label VARCHAR(255) Timer name
duration_seconds INTEGER Total duration
remaining_seconds INTEGER Time left
status VARCHAR(20) idle/running/paused/finished
sound VARCHAR(100) Sound identifier

world_clocks

Column Type Description
id UUID Primary key
user_id UUID Owner
timezone VARCHAR(100) IANA timezone
city_name VARCHAR(255) Display name
sort_order INTEGER Display order

presets

Column Type Description
id UUID Primary key
user_id UUID Owner
type VARCHAR(20) timer/pomodoro
name VARCHAR(255) Preset name
duration_seconds INTEGER Duration
settings JSONB Type-specific settings

Environment Variables

Backend (.env)

NODE_ENV=development
PORT=3017
DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/clock
MANA_CORE_AUTH_URL=http://localhost:3001
CORS_ORIGINS=http://localhost:5173,http://localhost:5186,http://localhost:8081
DEV_BYPASS_AUTH=true
DEV_USER_ID=your-test-user-id

Web (.env)

PUBLIC_BACKEND_URL=http://localhost:3017
PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001

Web App Stores (Svelte 5 Runes)

// auth.svelte.ts - Authentication
authStore.isAuthenticated
authStore.user
authStore.signIn(email, password)
authStore.signOut()
authStore.getAccessToken()

// alarms.svelte.ts - Alarms
alarmsStore.alarms
alarmsStore.nextAlarm
alarmsStore.fetchAlarms()
alarmsStore.createAlarm(input)
alarmsStore.toggleAlarm(id)

// timers.svelte.ts - Timers
timersStore.timers
timersStore.activeTimers
timersStore.startTimer(id)
timersStore.pauseTimer(id)
timersStore.resetTimer(id)

// stopwatch.svelte.ts - Stopwatch (local only)
stopwatchStore.isRunning
stopwatchStore.elapsedTime
stopwatchStore.laps
stopwatchStore.start()
stopwatchStore.lap()
stopwatchStore.reset()

// pomodoro.svelte.ts - Pomodoro (local only)
pomodoroStore.phase
pomodoroStore.remainingTime
pomodoroStore.completedSessions
pomodoroStore.start()
pomodoroStore.skip()
pomodoroStore.loadPreset(preset)

// world-clocks.svelte.ts - World Clocks
worldClocksStore.worldClocks
worldClocksStore.addWorldClock(input)
worldClocksStore.removeWorldClock(id)
worldClocksStore.reorderWorldClocks(ids)

Quick Start

1. Datenbank erstellen

# PostgreSQL Container muss laufen
docker compose -f docker-compose.dev.yml up -d postgres

# Datenbank erstellen
PGPASSWORD=devpassword psql -h localhost -U manacore -d postgres -c "CREATE DATABASE clock;"

# Schema pushen
pnpm clock:db:push

2. Apps starten

# Backend + Web zusammen
pnpm dev:clock:app

# Oder einzeln:
pnpm dev:clock:backend  # Terminal 1
pnpm dev:clock:web      # Terminal 2
pnpm dev:clock:landing  # Terminal 3 (optional)

3. URLs öffnen

Testing API (mit curl)

# Health Check
curl http://localhost:3017/api/v1/health

# Login (get token)
TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "password": "password"}' | jq -r '.accessToken')

# Wecker abrufen
curl http://localhost:3017/api/v1/alarms \
  -H "Authorization: Bearer $TOKEN"

# Neuen Wecker erstellen
curl -X POST http://localhost:3017/api/v1/alarms \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"time": "07:00:00", "label": "Aufwachen"}'

# Timer erstellen und starten
TIMER_ID=$(curl -s -X POST http://localhost:3017/api/v1/timers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"durationSeconds": 300, "label": "5 Minuten"}' | jq -r '.id')

curl -X POST http://localhost:3017/api/v1/timers/$TIMER_ID/start \
  -H "Authorization: Bearer $TOKEN"

Important Notes

  1. Authentication: Nutzt Mana Core Auth (JWT im Authorization Header)
  2. Database: PostgreSQL mit Drizzle ORM (Port 5432)
  3. Port: Backend läuft auf Port 3017, Web auf 5187, Landing auf 4323
  4. i18n: 5 Sprachen unterstützt (DE, EN, FR, ES, IT)
  5. Theme: Amber/Orange (#f59e0b) als Primärfarbe
  6. Local Features: Stoppuhr und Pomodoro laufen lokal ohne Backend-Sync