managarten/apps/contacts/CLAUDE.md
Till JS 22a73943e1 chore: complete ManaCore → Mana rename (docs, go modules, plists, images)
Final cleanup of references missed in previous rename commits:

- Dockerfiles: PUBLIC_MANA_CORE_AUTH_URL → PUBLIC_MANA_AUTH_URL
- Go modules: github.com/manacore/* → github.com/mana/* (7 go.mod files)
- launchd plists: com.manacore.* → com.mana.* (14 files renamed + content)
- Image assets: *_Manacore_AI_Credits* → *_Mana_AI_Credits* (11 files)
- .env.example files: ManaCore brand strings → Mana
- .prettierignore: stale apps/manacore/* paths → apps/mana/*
- Markdown docs (CLAUDE.md, /docs/*): mana-core-auth → mana-auth, etc.

Excluded from rename: .claude/, devlog/, manascore/ (historical content),
client testimonials, blueprints, npm package refs (@mana-core/*).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:26:10 +02:00

9.8 KiB

Contacts Project Guide

Project Structure

apps/contacts/
├── apps/
│   ├── server/       # Hono/Bun compute server (@contacts/server) - Port 3015
│   ├── landing/      # Astro marketing landing page (@contacts/landing)
│   ├── web/          # SvelteKit web application (@contacts/web) - Port 5184
│   └── mobile/       # Expo/React Native mobile app (@contacts/mobile)
├── packages/
│   └── shared/       # Shared types, utils, configs (@contacts/shared)
└── package.json

Commands

Root Level (from monorepo root)

pnpm contacts:dev                   # Run all contacts apps
pnpm dev:contacts:mobile            # Start mobile app
pnpm dev:contacts:web               # Start web app
pnpm dev:contacts:landing           # Start landing page
pnpm dev:contacts:server            # Start server
pnpm dev:contacts:app               # Start web + server together
pnpm dev:contacts:local             # Start web + sync (no auth needed)

Mobile App (apps/contacts/apps/mobile)

pnpm dev                         # Start Expo dev server
pnpm ios                         # Run on iOS simulator
pnpm android                     # Run on Android emulator

Backend (apps/contacts/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

Web App (apps/contacts/apps/web)

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

Landing Page (apps/contacts/apps/landing)

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

Technology Stack

  • Mobile: React Native 0.81 + Expo SDK 54, NativeWind, Expo Router, Zustand
  • Web: SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS
  • Landing: Astro 5.x, Tailwind CSS
  • Backend: NestJS 10, Drizzle ORM, PostgreSQL
  • Types: TypeScript 5.x

Architecture

Backend API Endpoints

Endpoint Method Description
/api/v1/health GET Health check
/api/v1/contacts GET Get user's contacts
/api/v1/contacts POST Create new contact
/api/v1/contacts/:id GET Get contact details
/api/v1/contacts/:id PATCH Update contact
/api/v1/contacts/:id DELETE Delete contact
/api/v1/contacts/:id/favorite POST Toggle favorite
/api/v1/contacts/:id/archive POST Toggle archive
/api/v1/contacts/:id/photo POST Upload contact photo
/api/v1/tags GET Get user's tags
/api/v1/tags POST Create new tag
/api/v1/tags/:id DELETE Delete tag
/api/v1/contacts/:id/notes GET Get contact notes
/api/v1/contacts/:id/notes POST Add note to contact
/api/v1/notes/:id PATCH Update note
/api/v1/notes/:id DELETE Delete note
/api/v1/contacts/:id/activities GET Get contact activities
/api/v1/contacts/:id/activities POST Log activity
/api/v1/import/preview POST Preview file import (vCard/CSV)
/api/v1/import/execute POST Execute contact import
/api/v1/import/template/csv GET Download CSV template
/api/v1/google/auth-url GET Get Google OAuth URL
/api/v1/google/callback POST Exchange OAuth code
/api/v1/google/status GET Get Google connection status
/api/v1/google/disconnect DELETE Disconnect Google account
/api/v1/google/contacts GET Fetch Google contacts
/api/v1/google/import POST Import from Google
/api/v1/export GET Quick export all contacts
/api/v1/export POST Export with options
/api/v1/organizations/:orgId/contacts GET Get organization contacts
/api/v1/teams/:teamId/contacts GET Get team contacts
/api/v1/contacts/:id/share POST Share contact

Database Schema

contacts - Contact information

  • id (UUID) - Primary key
  • user_id (VARCHAR) - User reference
  • first_name, last_name, display_name, nickname (VARCHAR)
  • email, phone, mobile (VARCHAR)
  • street, city, postal_code, country (VARCHAR)
  • company, job_title, department (VARCHAR)
  • website, birthday, notes, photo_url (VARCHAR/TEXT/DATE)
  • is_favorite, is_archived (BOOLEAN)
  • organization_id, team_id (UUID) - Mana integration
  • visibility (VARCHAR) - private/team/organization/public
  • shared_with (JSONB) - Array of user IDs
  • created_at, updated_at (TIMESTAMP)

contact_tags - Tags for contacts

  • id (UUID) - Primary key
  • user_id (VARCHAR) - User reference
  • name (VARCHAR) - Tag name
  • color (VARCHAR) - Tag color

contact_to_tags - Many-to-many relation

  • contact_id (UUID) - Contact reference
  • tag_id (UUID) - Tag reference

contact_notes - Notes for contacts

  • id (UUID) - Primary key
  • contact_id (UUID) - Contact reference
  • user_id (VARCHAR) - User reference
  • content (TEXT) - Note content
  • is_pinned (BOOLEAN)
  • created_at, updated_at (TIMESTAMP)

contact_activities - Activity log

  • id (UUID) - Primary key
  • contact_id (UUID) - Contact reference
  • user_id (VARCHAR) - User reference
  • activity_type (VARCHAR) - created/updated/called/emailed/met/note_added
  • description (TEXT)
  • metadata (JSONB)
  • created_at (TIMESTAMP)

connected_accounts - OAuth provider connections (Google, etc.)

  • id (UUID) - Primary key
  • user_id (VARCHAR) - User reference
  • provider (VARCHAR) - Provider name (e.g., 'google')
  • provider_account_id (VARCHAR) - Provider's user ID
  • provider_email (VARCHAR) - Provider account email
  • access_token (TEXT) - OAuth access token (encrypted)
  • refresh_token (TEXT) - OAuth refresh token (encrypted)
  • token_expires_at (TIMESTAMP) - Token expiration time
  • scope (TEXT) - Granted OAuth scopes
  • provider_data (JSONB) - Additional provider-specific data
  • created_at, updated_at (TIMESTAMP)

Environment Variables

Backend (.env)

NODE_ENV=development
PORT=3015
DATABASE_URL=postgresql://mana:devpassword@localhost:5432/contacts
MANA_AUTH_URL=http://localhost:3001
CORS_ORIGINS=http://localhost:5173,http://localhost:5184,http://localhost:8081
S3_ENDPOINT=http://localhost:9000
S3_REGION=us-east-1
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_BUCKET=contacts-photos

# Google OAuth (for contacts import)
# Get credentials from https://console.cloud.google.com/apis/credentials
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:5184/data?tab=import&source=google

Mobile (.env)

EXPO_PUBLIC_BACKEND_URL=http://localhost:3015
EXPO_PUBLIC_MANA_AUTH_URL=http://localhost:3001

Web (.env)

PUBLIC_BACKEND_URL=http://localhost:3015
PUBLIC_MANA_AUTH_URL=http://localhost:3001

Shared Packages

@contacts/shared

  • Types: Contact, ContactGroup, ContactTag, ContactNote, ContactActivity
  • Utils: Search, filter, import/export functions
  • Configs: App configuration

Quick Input Syntax

The NewContactModal includes a NL quick-input bar that parses contact info and pre-fills form fields:

"Max Mustermann @ACME Corp max@example.com +49 170 1234567 #kunde"
→ firstName: Max, lastName: Mustermann, company: ACME Corp,
  email: max@example.com, phone: +49 170..., tags: [kunde]

Recognized patterns:

  • Name: First and last name (remaining text after extraction)
  • Company: @CompanyName or bei CompanyName or von CompanyName
  • Email: Standard email format
  • Phone: International (+49...), German (0123...), or 6+ digit numbers
  • Tags: #tag1 #tag2

Type the full text and press Enter to apply. Fields are pre-filled and can be edited before saving.

Live Duplicate Detection

While typing in the name or email fields, the system checks for duplicates against IndexedDB in real-time:

  • Email: Exact match (case-insensitive)
  • Name: Fuzzy match (Levenshtein distance, tolerates typos)
  • Shows warning with matched contact name, company, and match type

Implementation: duplicate-detector.ts — runs fully offline, no server calls.

Code Style Guidelines

  • TypeScript: Strict typing with interfaces
  • Mobile: Functional components with hooks, Zustand for state
  • Web: Svelte 5 runes mode ($state, $derived, $effect)
  • Styling: Tailwind CSS / NativeWind
  • Formatting: Prettier with project config

Important Notes

  1. Authentication: Uses Mana Auth (JWT in Authorization header)
  2. Database: PostgreSQL with Drizzle ORM
  3. Port: Backend runs on port 3015, Web on port 5184 by default
  4. Storage: Uses MinIO/S3 for contact photos via @mana/shared-storage
  5. Mana Integration: Contacts can be linked to Organizations and Teams