2025-11-07 15:12:05 +01:00
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
2025-12-03 14:43:25 +01:00
Calendar Plantempus is a professional TypeScript calendar component with offline-first architecture, drag-and-drop functionality, and real-time synchronization capabilities. Supports both date-based (day/week/month) and resource-based (people, rooms) calendar views.
2025-11-07 15:12:05 +01:00
## Build & Development Commands
```bash
# Build the project (bundles to wwwroot/js/calendar.js)
npm run build
# Watch mode for development
npm run watch
# Clean build output
npm run clean
# Type check only
npx tsc --noEmit
2025-12-03 14:43:25 +01:00
# Run all tests (watch mode)
2025-11-07 15:12:05 +01:00
npm test
# Run tests once and exit
npm run test:run
# Run tests with UI
npm run test:ui
2025-12-03 14:43:25 +01:00
# Run single test file
npm test -- < test-file-name >
2025-11-07 15:12:05 +01:00
# CSS Development
npm run css:build # Build CSS
npm run css:watch # Watch and rebuild CSS
npm run css:build:prod # Build minified production CSS
npm run css:analyze # Analyze CSS metrics
```
## Architecture
### Core Design Pattern: Dependency Injection with NovaDI
The application uses **NovaDI** (@novadi/core ) for dependency injection. All managers, services, and repositories are registered in `src/index.ts` and resolved through the DI container.
**Key principle**: Never instantiate managers or services directly with `new` . Always use constructor injection and register types in the container.
### Event-Driven Architecture
The application uses a **centralized EventBus** (`src/core/EventBus.ts` ) built on DOM CustomEvents for all inter-component communication. This is the ONLY way components should communicate.
- All event types are defined in `src/constants/CoreEvents.ts` (reduced from 102+ to ~20 core events)
- Components emit events via `eventBus.emit(CoreEvents.EVENT_NAME, payload)`
- Components subscribe via `eventBus.on(CoreEvents.EVENT_NAME, handler)`
- Never call methods directly between managers - always use events
2025-12-03 14:43:25 +01:00
### Calendar Modes: Date vs Resource
The calendar supports two column modes, configured at initialization in `src/index.ts` :
**Date Mode** (`DateColumnDataSource` ):
- Columns represent dates (day/week/month views)
- Uses `DateHeaderRenderer` and `DateColumnRenderer`
**Resource Mode** (`ResourceColumnDataSource` ):
- Columns represent resources (people, rooms, equipment)
- Uses `ResourceHeaderRenderer` and `ResourceColumnRenderer`
- Events filtered per-resource via `IColumnInfo.events`
Both modes implement `IColumnDataSource` interface, allowing polymorphic column handling.
2025-11-07 15:12:05 +01:00
### Manager Hierarchy
**CalendarManager** (`src/managers/CalendarManager.ts` ) - Top-level coordinator
- Manages calendar state (current view, current date)
- Orchestrates initialization sequence
- Coordinates other managers via EventBus
**Key Managers**:
- **EventManager** - Event CRUD operations, data loading from repository
- **GridManager** - Renders time grid structure
- **NavigationManager** - Date navigation and period calculations
- **DragDropManager** - Advanced drag-and-drop with smooth animations, type conversion (timed ↔ all-day), scroll compensation
- **ResizeHandleManager** - Event resizing with visual feedback
- **AllDayManager** - All-day event layout and rendering
- **HeaderManager** - Date headers and all-day event container
- **ScrollManager** - Scroll behavior and position management
- **EdgeScrollManager** - Automatic scrolling at viewport edges during drag
### Repository Pattern
2025-12-03 14:43:25 +01:00
Data access is abstracted through **IApiRepository<T>** interface (`src/repositories/IApiRepository.ts` ):
- **MockEventRepository**, **MockBookingRepository** , **MockCustomerRepository** , **MockResourceRepository** - Development: Load from JSON files
- **ApiEventRepository**, **ApiBookingRepository** , **ApiCustomerRepository** , **ApiResourceRepository** - Production: Backend API calls
2025-11-07 15:12:05 +01:00
All repository methods accept an `UpdateSource` parameter ('local' | 'remote') to distinguish user actions from remote updates.
2025-12-03 14:43:25 +01:00
### Entity Service Pattern
**IEntityService< T > ** (`src/storage/IEntityService.ts` ) provides polymorphic entity handling:
- `EventService` , `BookingService` , `CustomerService` , `ResourceService` implement this interface
- Encapsulates sync status manipulation (SyncManager delegates, never manipulates directly)
- Uses `entityType` discriminator for runtime routing without switch statements
- Enables polymorphic operations: `Array<IEntityService<any>>` works across all entity types
2025-11-07 15:12:05 +01:00
### Offline-First Sync Architecture
**SyncManager** (`src/workers/SyncManager.ts` ) provides background synchronization:
1. Local changes are written to **IndexedDB** immediately
2. Operations are queued in **OperationQueue**
3. SyncManager processes queue when online (5-second polling)
4. Failed operations retry with exponential backoff (max 5 retries)
5. Events have `syncStatus` : 'synced' | 'pending' | 'error'
### Rendering Strategy Pattern
**EventRenderingService** (`src/renderers/EventRendererManager.ts` ) uses strategy pattern:
- **IEventRenderer** interface defines rendering contract
- **DateEventRenderer** - Renders timed events in day columns
- **AllDayEventRenderer** - Renders all-day events in header
- Strategies can be swapped without changing core logic
### Layout Engines
**EventStackManager** (`src/managers/EventStackManager.ts` ) - Uses CSS flexbox for overlapping events:
- Groups overlapping events into stacks
- Calculates flex positioning (basis, grow, shrink)
- Handles multi-column spanning events
**AllDayLayoutEngine** (`src/utils/AllDayLayoutEngine.ts` ) - Row-based layout for all-day events:
- Detects overlaps and assigns row positions
- Supports collapsed view (max 4 rows) with "+N more" indicator
- Calculates container height dynamically
### Configuration System
Configuration is loaded from `wwwroot/data/calendar-config.json` via **ConfigManager** :
- **GridSettings** - Hour height, work hours, snap interval
- **DateViewSettings** - Period type, first day of week
- **TimeFormatConfig** - Timezone, locale, 12/24-hour format
- **WorkWeekSettings** - Configurable work week presets
- **Interaction** - Enable/disable drag, resize, create
Access via injected `Configuration` instance, never load config directly.
## Important Patterns & Conventions
### Event Type Conversion (Drag & Drop)
When dragging events between timed grid and all-day area:
- **Timed → All-day**: `DragDropManager` emits `drag:mouseenter-header` , `AllDayManager` creates all-day clone
- **All-day → Timed**: `DragDropManager` emits `drag:mouseenter-column` , `EventRenderingService` creates timed clone
- Original element is marked with `data-conversion-source="true"`
- Clone is marked with `data-converted-clone="true"`
### Scroll Compensation During Drag
`DragDropManager` tracks scroll delta during edge-scrolling:
1. Listens to `edge-scroll:scrolling` events
2. Accumulates `scrollDeltaY` from scroll events
3. Compensates dragged element position: `targetY = mouseY - scrollDeltaY - mouseOffset.y`
4. Prevents visual "jumping" during scroll
### Grid Snapping
When dropping events, snap to time grid:
1. Get mouse Y position relative to column
2. Convert to time using `PositionUtils.getTimeAtPosition()`
3. Account for `mouseOffset.y` (click position within event)
4. Snap to nearest `snapInterval` (default 15 minutes)
### Testing with Vitest
Tests use **Vitest** with **jsdom** environment:
2025-12-03 14:43:25 +01:00
- Config: `vitest.config.ts`
2025-11-07 15:12:05 +01:00
- Setup file: `test/setup.ts`
2025-12-03 14:43:25 +01:00
- Test helpers: `test/helpers/dom-helpers.ts` , `test/helpers/config-helpers.ts`
2025-11-07 15:12:05 +01:00
- Run single test: `npm test -- <test-file-name>`
## Key Files to Know
2025-12-03 14:43:25 +01:00
- `src/index.ts` - DI container setup and initialization (also sets calendar mode: date vs resource)
2025-11-07 15:12:05 +01:00
- `src/core/EventBus.ts` - Central event dispatcher
2025-12-03 14:43:25 +01:00
- `src/constants/CoreEvents.ts` - All event type constants (~34 core events)
2025-11-07 15:12:05 +01:00
- `src/types/CalendarTypes.ts` - Core type definitions
2025-12-03 14:43:25 +01:00
- `src/types/ColumnDataSource.ts` - Column abstraction for date/resource modes
- `src/storage/IEntityService.ts` - Entity service interface for polymorphic sync
2025-11-07 15:12:05 +01:00
- `src/managers/CalendarManager.ts` - Main coordinator
- `src/managers/DragDropManager.ts` - Detailed drag-drop architecture docs
- `src/configurations/CalendarConfig.ts` - Configuration schema
- `wwwroot/data/calendar-config.json` - Runtime configuration
## Common Tasks
### Adding a New Event Type to CoreEvents
1. Add constant to `src/constants/CoreEvents.ts`
2. Define payload type in `src/types/EventTypes.ts`
3. Emit with `eventBus.emit(CoreEvents.NEW_EVENT, payload)`
4. Subscribe with `eventBus.on(CoreEvents.NEW_EVENT, handler)`
### Adding a New Manager
1. Create in `src/managers/`
2. Inject dependencies via constructor (EventBus, Configuration, other managers)
3. Register in DI container in `src/index.ts` : `builder.registerType(NewManager).as<NewManager>()`
4. Communicate via EventBus only, never direct method calls
5. Initialize in CalendarManager if needed
### Modifying Event Data
Always go through EventManager:
- Create: `eventManager.createEvent(eventData)`
- Update: `eventManager.updateEvent(id, updates)`
- Delete: `eventManager.deleteEvent(id)`
EventManager handles repository calls, event emission, and UI updates.
### Debugging
Debug mode is enabled in development:
```javascript
eventBus.setDebug(true); // In src/index.ts
```
Access debug interface in browser console:
```javascript
window.calendarDebug.eventBus.getEventLog()
window.calendarDebug.calendarManager
window.calendarDebug.eventManager
2025-12-03 14:43:25 +01:00
window.calendarDebug.auditService
window.calendarDebug.syncManager
window.calendarDebug.app // Full DI container
2025-11-07 15:12:05 +01:00
```
## Dependencies
- **@novadi/core ** - Dependency injection framework
2025-12-03 14:43:25 +01:00
- **dayjs** - Date manipulation and formatting
2025-11-07 15:12:05 +01:00
- **fuse.js** - Fuzzy search for event filtering
- **esbuild** - Fast bundler for development
- **vitest** - Testing framework
- **postcss** - CSS processing and optimization