- Add useFocusEffect to calendar for automatic event reload on tab focus - Create ChatStore (Zustand) for persistent chat messages across tab switches - Replace local useState with store in chat screen
14 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
CalChat is a calendar mobile app with AI support. The core concept is managing calendar events through a chat interface with an AI chatbot. Users can add, edit, and delete events via natural language conversation.
This is a fullstack TypeScript monorepo with npm workspaces.
Commands
Root (monorepo)
npm install # Install all dependencies for all workspaces
Client (apps/client) - Expo React Native app
npm run start -w @caldav/client # Start Expo dev server
npm run android -w @caldav/client # Start on Android
npm run ios -w @caldav/client # Start on iOS
npm run web -w @caldav/client # Start web version
npm run lint -w @caldav/client # Run ESLint
Server (apps/server) - Express.js backend
npm run dev -w @caldav/server # Start dev server with hot reload (tsx watch)
npm run build -w @caldav/server # Compile TypeScript
npm run start -w @caldav/server # Run compiled server (port 3000)
Technology Stack
| Area | Technology | Purpose |
|---|---|---|
| Frontend | React Native | Mobile UI Framework |
| Expo | Development platform | |
| Expo-Router | File-based routing | |
| NativeWind | Tailwind CSS for React Native | |
| Zustand | State management | |
| FlashList | High-performance lists | |
| Backend | Express.js | Web framework |
| MongoDB | Database | |
| Mongoose | ODM | |
| Claude (Anthropic) | AI/LLM for chat | |
| JWT | Authentication | |
| Planned | iCalendar | Event export/import |
Architecture
Workspace Structure
apps/client - @caldav/client - Expo React Native app
apps/server - @caldav/server - Express.js backend
packages/shared - @caldav/shared - Shared TypeScript types and models
Frontend Architecture (apps/client)
src/
├── app/ # Expo-Router file-based routing
│ ├── _layout.tsx # Root Stack layout
│ ├── index.tsx # Entry redirect
│ ├── login.tsx # Login screen
│ ├── register.tsx # Registration screen
│ ├── (tabs)/ # Tab navigation group
│ │ ├── _layout.tsx # Tab bar configuration
│ │ ├── chat.tsx # Chat screen (AI conversation)
│ │ └── calendar.tsx # Calendar overview
│ ├── event/
│ │ └── [id].tsx # Event detail screen (dynamic route)
│ └── note/
│ └── [id].tsx # Note editor for event (dynamic route)
├── components/
│ ├── BaseBackground.tsx # Common screen wrapper
│ ├── Header.tsx # Header component
│ ├── EventCard.tsx # Event card for calendar display
│ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal
│ └── ProposedEventCard.tsx # Inline event proposal with confirm/reject buttons
├── Themes.tsx # Centralized color/theme definitions
├── services/
│ ├── index.ts # Re-exports all services
│ ├── ApiClient.ts # HTTP client (get, post, put, delete)
│ ├── AuthService.ts # login(), register(), logout(), refresh()
│ ├── EventService.ts # getAll(), getById(), getByDateRange(), create(), update(), delete()
│ └── ChatService.ts # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation()
└── stores/ # Zustand state management
├── index.ts # Re-exports all stores
├── AuthStore.ts # user, token, isAuthenticated, login(), logout(), setToken()
├── ChatStore.ts # messages[], addMessage(), updateMessage(), clearMessages()
└── EventsStore.ts # events[], setEvents(), addEvent(), updateEvent(), deleteEvent()
Routing: Tab-based navigation with Chat and Calendar as main screens. Auth screens (login, register) outside tabs. Dynamic routes for event detail and note editing.
Backend Architecture (apps/server)
src/
├── app.ts # Entry point, DI setup, Express config
├── controllers/ # Request handlers
│ ├── AuthController.ts # login(), register(), refresh(), logout()
│ ├── ChatController.ts # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation()
│ └── EventController.ts # create(), getById(), getAll(), getByDateRange(), update(), delete()
├── middleware/
│ └── AuthMiddleware.ts # authenticate() - JWT validation
├── routes/ # API endpoint definitions
│ ├── index.ts # Combines all routes under /api
│ ├── auth.routes.ts # /api/auth/*
│ ├── chat.routes.ts # /api/chat/* (protected)
│ └── event.routes.ts # /api/events/* (protected)
├── services/ # Business logic
│ ├── interfaces/ # DB-agnostic interfaces (for dependency injection)
│ │ ├── AIProvider.ts # processMessage()
│ │ ├── UserRepository.ts # + CreateUserData (server-internal DTO)
│ │ ├── EventRepository.ts
│ │ └── ChatRepository.ts
│ ├── AuthService.ts
│ ├── ChatService.ts
│ └── EventService.ts
├── repositories/ # Data access (DB-specific implementations)
│ ├── index.ts # Re-exports from ./mongo
│ └── mongo/ # MongoDB implementation
│ ├── models/ # Mongoose schemas
│ │ ├── types.ts # Shared types (IdVirtual interface)
│ │ ├── UserModel.ts
│ │ ├── EventModel.ts
│ │ └── ChatModel.ts
│ ├── MongoUserRepository.ts
│ ├── MongoEventRepository.ts
│ └── MongoChatRepository.ts
├── ai/
│ └── ClaudeAdapter.ts # Implements AIProvider
└── utils/
├── jwt.ts # signToken(), verifyToken()
├── password.ts # hash(), compare()
├── eventFormatters.ts # getWeeksOverview(), getMonthOverview() - formatted event listings
└── recurrenceExpander.ts # expandRecurringEvents() - expand recurring events into occurrences
API Endpoints:
POST /api/auth/login- User loginPOST /api/auth/register- User registrationPOST /api/auth/refresh- Refresh JWT tokenPOST /api/auth/logout- User logoutGET /api/events- Get all events (protected)GET /api/events/range- Get events by date range (protected)GET /api/events/:id- Get single event (protected)POST /api/events- Create event (protected)PUT /api/events/:id- Update event (protected)DELETE /api/events/:id- Delete event (protected)POST /api/chat/message- Send message to AI (protected)POST /api/chat/confirm/:conversationId/:messageId- Confirm proposed event (protected)POST /api/chat/reject/:conversationId/:messageId- Reject proposed event (protected)GET /api/chat/conversations- Get all conversations (protected)GET /api/chat/conversations/:id- Get messages of a conversation with cursor-based pagination (protected)GET /health- Health checkPOST /api/ai/test- AI test endpoint (development only)
Shared Package (packages/shared)
src/
├── index.ts
├── models/
│ ├── index.ts
│ ├── User.ts # User, CreateUserDTO, LoginDTO, AuthResponse
│ ├── CalendarEvent.ts # CalendarEvent, CreateEventDTO, UpdateEventDTO, ExpandedEvent
│ ├── ChatMessage.ts # ChatMessage, Conversation, SendMessageDTO, CreateMessageDTO,
│ │ # GetMessagesOptions, ChatResponse, ConversationSummary,
│ │ # ProposedEventChange, EventAction
│ └── Constants.ts # DAYS, MONTHS, Day, Month, DAY_INDEX, DAY_INDEX_TO_DAY,
│ # DAY_TO_GERMAN, DAY_TO_GERMAN_SHORT, MONTH_TO_GERMAN
└── utils/
├── index.ts
└── dateHelpers.ts # getDay() - get date for specific weekday relative to today
Key Types:
User: id, email, displayName, passwordHash?, createdAt?, updatedAt?CalendarEvent: id, userId, title, description?, startTime, endTime, note?, isRecurring?, recurrenceRule?ExpandedEvent: Extends CalendarEvent with occurrenceStart, occurrenceEnd (for recurring event instances)ChatMessage: id, conversationId, sender ('user' | 'assistant'), content, proposedChange?ProposedEventChange: action ('create' | 'update' | 'delete'), eventId?, event?, updates?Conversation: id, userId, createdAt?, updatedAt? (messages loaded separately via lazy loading)CreateEventDTO: Used for creating events AND for AI-proposed eventsGetMessagesOptions: Cursor-based pagination withbefore?: stringandlimit?: numberConversationSummary: id, lastMessage?, createdAt? (for conversation list)Day: "Monday" | "Tuesday" | ... | "Sunday"Month: "January" | "February" | ... | "December"
Database Abstraction
The repository pattern allows swapping databases:
- Interfaces (
services/interfaces/) are DB-agnostic - Implementations (
repositories/mongo/) are DB-specific - To add MySQL: create
repositories/mysql/with TypeORM entities
Mongoose Model Pattern
All Mongoose models use a consistent pattern for TypeScript-safe id virtuals:
import { IdVirtual } from './types';
const Schema = new Schema<Doc, Model<Doc, {}, {}, IdVirtual>, {}, {}, IdVirtual>(
{ /* fields */ },
{
virtuals: {
id: {
get() { return this._id.toString(); }
}
},
toJSON: {
virtuals: true,
transform: (_, ret) => {
delete ret._id;
delete ret.__v;
return ret;
}
}
}
);
Repositories use doc.toJSON() as unknown as Type casting (required because Mongoose's TypeScript types don't reflect virtual fields in toJSON output).
MVP Feature Scope
Must-Have
- Chat interface with AI assistant (text input) for event management
- Calendar overview
- Manual event CRUD (without AI)
- View completed events
- Simple reminders
- One note per event
- Recurring events
Nice-to-Have
- iCalendar import/export
- Multiple calendars
- CalDAV synchronization with external services
Development Environment
MongoDB (Docker)
cd apps/server/docker/mongo
docker compose up -d # Start MongoDB + Mongo Express
docker compose down # Stop services
- MongoDB:
localhost:27017(root/mongoose) - Mongo Express UI:
localhost:8083(admin/admin)
Environment Variables
Server requires .env file in apps/server/:
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=1h
MONGODB_URI=mongodb://root:mongoose@localhost:27017/calchat?authSource=admin
Current Implementation Status
Backend:
- Implemented:
AuthController: login(), register() with error handlingAuthService: login(), register() with password validationMongoUserRepository: findByEmail(), create()utils/password: hash(), compare() using bcryptutils/jwt: signToken() (verifyToken() pending)dotenvintegration for environment variablesChatController: sendMessage(), confirmEvent(), rejectEvent()ChatService: processMessage() with test responses (create, update, delete actions), confirmEvent() handles all CRUD actionsMongoEventRepository: Full CRUD implemented (findById, findByUserId, findByDateRange, create, update, delete)EventController: Full CRUD (create, getById, getAll, getByDateRange, update, delete)EventService: Full CRUD with recurring event expansion via recurrenceExpanderutils/eventFormatters: getWeeksOverview(), getMonthOverview() with German localizationutils/recurrenceExpander: expandRecurringEvents() using rrule library for RRULE parsing
- Stubbed (TODO):
AuthMiddleware.authenticate(): Currently uses fake user for testingAuthController: refresh(), logout()AuthService: refreshToken()ChatController: getConversations(), getConversation()MongoChatRepository: Database persistence for chat
- Not started:
ClaudeAdapter(AI integration - currently using test responses)
Shared: Types, DTOs, constants (Day, Month with German translations), ExpandedEvent type, and date utilities defined and exported.
Frontend:
- Tab navigation (Chat, Calendar) implemented with basic UI
- Calendar screen fully functional:
- Month navigation with grid display
- Events loaded from API via EventService.getByDateRange()
- Orange dot indicator for days with events
- Tap-to-open modal overlay showing EventCards for selected day
- Supports events from adjacent months visible in grid
- Uses
useFocusEffectfor automatic reload on tab focus
- Chat screen functional with FlashList, message sending, and event confirm/reject
- Messages persisted via ChatStore (survives tab switches)
ApiClient: get(), post(), put(), delete() implementedEventService: getAll(), getById(), getByDateRange(), create(), update(), delete() - fully implementedChatService: sendMessage(), confirmEvent(convId, msgId, action, event?, eventId?, updates?), rejectEvent() - supports create/update/delete actionsEventCard: Displays event details (title, date, time, duration, recurring indicator) with Feather icons and edit/delete buttonsProposedEventCard: Displays proposed events (title, date, description, recurring indicator) with confirm/reject buttonsThemes.tsx: Centralized color definitions including textPrimary, borderPrimary, eventIndicatorEventsStore: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[]ChatStore: Zustand store with addMessage(), updateMessage(), clearMessages() - persists messages across tab switches- Auth screens (Login, Register), Event Detail, and Note screens exist as skeletons
- AuthStore defined with
throw new Error('Not implemented')
Documentation
Detailed architecture diagrams are in docs/:
api-routes.md- API endpoint overview (German)technisches_brainstorm.tex- Technical concept document (German)architecture-class-diagram.puml- Backend class diagramfrontend-class-diagram.puml- Frontend class diagramcomponent-diagram.puml- System component overview