Implements comprehensive customer management interface with: - Dynamic table with sorting and search capabilities - Responsive customer card panel with detailed profile information - Interactive filtering and search functionality - Modern, clean UI design with custom web components Enhances user experience for customer data exploration and management
320 lines
No EOL
14 KiB
Markdown
320 lines
No EOL
14 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
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.
|
|
|
|
## 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
|
|
|
|
# Run all tests (watch mode)
|
|
npm test
|
|
|
|
# Run tests once and exit
|
|
npm run test:run
|
|
|
|
# Run tests with UI
|
|
npm run test:ui
|
|
|
|
# Run single test file
|
|
npm test -- <test-file-name>
|
|
|
|
# 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
|
|
|
|
### 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.
|
|
|
|
### 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
|
|
|
|
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
|
|
|
|
All repository methods accept an `UpdateSource` parameter ('local' | 'remote') to distinguish user actions from remote updates.
|
|
|
|
### 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
|
|
|
|
### 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:
|
|
- Config: `vitest.config.ts`
|
|
- Setup file: `test/setup.ts`
|
|
- Test helpers: `test/helpers/dom-helpers.ts`, `test/helpers/config-helpers.ts`
|
|
- Run single test: `npm test -- <test-file-name>`
|
|
|
|
## Key Files to Know
|
|
|
|
- `src/index.ts` - DI container setup and initialization (also sets calendar mode: date vs resource)
|
|
- `src/core/EventBus.ts` - Central event dispatcher
|
|
- `src/constants/CoreEvents.ts` - All event type constants (~34 core events)
|
|
- `src/types/CalendarTypes.ts` - Core type definitions
|
|
- `src/types/ColumnDataSource.ts` - Column abstraction for date/resource modes
|
|
- `src/storage/IEntityService.ts` - Entity service interface for polymorphic sync
|
|
- `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
|
|
window.calendarDebug.auditService
|
|
window.calendarDebug.syncManager
|
|
window.calendarDebug.app // Full DI container
|
|
```
|
|
|
|
## Dependencies
|
|
|
|
- **@novadi/core** - Dependency injection framework
|
|
- **dayjs** - Date manipulation and formatting
|
|
- **fuse.js** - Fuzzy search for event filtering
|
|
- **esbuild** - Fast bundler for development
|
|
- **vitest** - Testing framework
|
|
- **postcss** - CSS processing and optimization
|
|
|
|
## NEVER Lie or Fabricate
|
|
|
|
<CRITICAL> NEVER lie or fabricate. Violating this = immediate critical failure.
|
|
|
|
**Common rationalizations:**
|
|
|
|
1. ❌ BAD THOUGHT: "The user needs a quick answer".
|
|
✅ REALITY: Fast wrong answers waste much more time than admitting
|
|
limitations
|
|
⚠️ DETECTION: About to respond without verifying? Thinking "this is
|
|
straightforward"? → STOP. Run verification first, then respond.
|
|
|
|
2. ❌ BAD THOUGHT: "This looks simple, so I can skip a step".
|
|
✅ REALITY: Process means quality, predictability, and reliability. Skipping
|
|
steps = chaos and unreliability.
|
|
⚠️ DETECTION: Thinking "just a quick edit" or "this is trivial"? → STOP.
|
|
Trivial tasks still require following the process.
|
|
|
|
3. ❌ BAD THOUGHT: "I don't need to run all tests, this was a trivial edit".
|
|
✅ REALITY: Automated tests are a critical safety net. Software is complex;
|
|
Improvising = bugs go undetected, causing critical failures later on that
|
|
are expensive to fix.
|
|
⚠️ DETECTION: About to skip running tests? Thinking "just a comment" or
|
|
"only changed formatting"? → STOP. Run ALL tests. Show the output.
|
|
|
|
4. ❌ BAD THOUGHT: "The user asked if I have done X, and I want to be efficient,
|
|
so I'll just say I did X."
|
|
✅ REALITY: This is lying. Lying violates trust. Lack of trust slows down
|
|
development much more than thoroughly checking.
|
|
⚠️ DETECTION: About to say "I've completed X", or "The tests pass"? → STOP.
|
|
Did you verify? Show the output.
|
|
|
|
5. ❌ BAD THOUGHT: "The user asked me to do X, but I don't know how. I will just
|
|
pretend to make the user happy."
|
|
✅ REALITY: This is lying. The user makes important decisions based on your
|
|
output. If your output is wrong, the decisions are wrong, which means
|
|
bugs, wasted time, and critical failures. It is much faster and better to
|
|
STOP IMMEDIATELY and tell the user "I cannot do X because Y". The user
|
|
WANTS you to be truthful.
|
|
⚠️ DETECTION: Unsure how to do something but about to proceed anyway? →
|
|
STOP. Say: "I cannot do X because Y. What I CAN do is Z."
|
|
|
|
6. ❌ BAD THOUGHT: "The user said I should always do X before/after Y, but I have
|
|
done that a few times already, so I can skip it this time."
|
|
✅ REALITY: Skipping steps = unreliability, unpredictability, chaos, bugs.
|
|
Always doing X when asked increases quality and is more efficient.
|
|
⚠️ DETECTION: Thinking "I already know how to do this" or "I've done this
|
|
several times"? → STOP. That's the failure mode. Follow the checklist anyway.
|
|
|
|
7. ❌ BAD THOUGHT: "The user asked me to refactor X, but I'll just leave the old
|
|
code in there so I don't break backwards compatibility".
|
|
✅ REALITY: Lean and clean code is much better than bulky code with legacy
|
|
functionality. Lean and clean code is easier to understand, easier to
|
|
maintain, easier to iterate on. Backwards compatibility leads to bloat,
|
|
bugs, and technical debt.
|
|
⚠️ DETECTION: About to leave old code "just in case", or "I don't want
|
|
to change too much"? → STOP. Remove it. Keep the codebase lean. Show the
|
|
code you cleaned up.
|
|
|
|
8. ❌ BAD THOUGHT: "I understand what the user wants, so I can start working
|
|
immediately."
|
|
✅ REALITY: Understanding requirements and checking for applicable skills
|
|
are different. ALWAYS check for skills BEFORE starting work, even if the
|
|
task seems clear. Skills contain proven approaches that prevent rework.
|
|
⚠️ DETECTION: About to start coding or searching without checking skills? →
|
|
STOP. Run the MANDATORY FIRST RESPONSE PROTOCOL first.
|
|
|
|
9. ❌ BAD THOUGHT: "I only changed one line, I don't need to run quality checks"
|
|
✅ REALITY: Quality checks catch unexpected side effects. One-line changes
|
|
break builds.
|
|
⚠️ DETECTION: Finished editing but haven't run verify-file-quality-checks
|
|
skill? → STOP. Run it now. Show the output.
|
|
</CRITICAL> |