# Mock Data Repository Implementation - Status Documentation **Document Generated:** 2025-11-19 **Analysis Scope:** Mock Repository Implementation vs Target Architecture **Files Analyzed:** 4 repositories, 4 type files, 2 architecture docs ## Executive Summary This document compares the current Mock Repository implementation against the documented target architecture. The analysis covers 4 entity types: Event, Booking, Customer, and Resource. **Overall Status:** Implementation is structurally correct but Event entity is missing critical fields required for the booking architecture. **Compliance Score:** 84% --- ## 1. Event Entity Comparison ### Current RawEventData Interface **Location:** `src/repositories/MockEventRepository.ts` ```typescript interface RawEventData { id: string; title: string; start: string | Date; end: string | Date; type: string; color?: string; allDay?: boolean; [key: string]: unknown; } ``` ### Target ICalendarEvent Interface **Location:** `src/types/CalendarTypes.ts` ```typescript export interface ICalendarEvent extends ISync { id: string; title: string; description?: string; start: Date; end: Date; type: CalendarEventType; allDay: boolean; bookingId?: string; resourceId?: string; customerId?: string; recurringId?: string; metadata?: Record; } ``` ### Documented JSON Format **Source:** `docs/mock-data-migration-guide.md`, `docs/booking-event-architecture.md` ```json { "id": "EVT001", "title": "Balayage langt hår", "start": "2025-08-05T10:00:00", "end": "2025-08-05T11:00:00", "type": "customer", "allDay": false, "syncStatus": "synced", "bookingId": "BOOK001", "resourceId": "EMP001", "customerId": "CUST001", "metadata": { "duration": 60 } } ``` ### Field-by-Field Comparison - Event Entity | Field | Current RawData | Target Interface | Documented JSON | Status | |-------|----------------|------------------|----------------|--------| | `id` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `title` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `description` | ❌ Missing | ✅ `string?` | ❌ Missing | **MISSING** | | `start` | ✅ `string \| Date` | ✅ `Date` | ✅ Present | **MATCH** | | `end` | ✅ `string \| Date` | ✅ `Date` | ✅ Present | **MATCH** | | `type` | ✅ `string` | ✅ `CalendarEventType` | ✅ `"customer"` | **MATCH** (needs cast) | | `allDay` | ✅ `boolean?` | ✅ `boolean` | ✅ `false` | **MATCH** | | `bookingId` | ❌ Missing | ✅ `string?` | ✅ Present | **CRITICAL MISSING** | | `resourceId` | ❌ Missing | ✅ `string?` | ✅ Present | **CRITICAL MISSING** | | `customerId` | ❌ Missing | ✅ `string?` | ✅ Present | **CRITICAL MISSING** | | `recurringId` | ❌ Missing | ✅ `string?` | ❌ Not in example | **MISSING** | | `metadata` | ✅ Via `[key: string]` | ✅ `Record?` | ✅ Present | **MATCH** | | `syncStatus` | ❌ Missing | ✅ `SyncStatus` (via ISync) | ✅ `"synced"` | **MISSING (added in processing)** | | `color` | ✅ `string?` | ❌ Not in interface | ❌ Not documented | **EXTRA (legacy)** | ### Critical Missing Fields - Event Entity #### 1. bookingId (CRITICAL) **Impact:** Cannot link customer events to booking data **Required For:** - Type `'customer'` events MUST have `bookingId` - Loading booking details when event is clicked - Cascading deletes (cancel booking → delete events) - Backend JOIN queries between CalendarEvent and Booking tables **Example:** ```json { "id": "EVT001", "type": "customer", "bookingId": "BOOK001", // ← CRITICAL - Links to booking ... } ``` #### 2. resourceId (CRITICAL) **Impact:** Cannot filter events by resource (calendar columns) **Required For:** - Denormalized query performance (no JOIN needed) - Resource calendar views (week view with resource columns) - Resource utilization analytics - Quick filtering: "Show all events for EMP001" **Example:** ```json { "id": "EVT001", "resourceId": "EMP001", // ← CRITICAL - Which stylist ... } ``` #### 3. customerId (CRITICAL) **Impact:** Cannot query customer events without loading booking **Required For:** - Denormalized query performance - Customer history views - Quick customer lookup: "Show all events for CUST001" - Analytics and reporting **Example:** ```json { "id": "EVT001", "type": "customer", "customerId": "CUST001", // ← CRITICAL - Which customer ... } ``` #### 4. description (OPTIONAL) **Impact:** Cannot add detailed event notes **Required For:** - Event details panel - Additional context beyond title - Notes and instructions ### Action Items for Events 1. **Add to RawEventData:** - `description?: string` - `bookingId?: string` (CRITICAL) - `resourceId?: string` (CRITICAL) - `customerId?: string` (CRITICAL) - `recurringId?: string` - `metadata?: Record` (make explicit) 2. **Update Processing:** - Explicitly map all new fields in `processCalendarData()` - Remove or document legacy `color` field - Ensure `allDay` defaults to `false` if missing - Validate that `type: 'customer'` events have `bookingId` 3. **JSON File Requirements:** - Customer events MUST include `bookingId`, `resourceId`, `customerId` - Vacation/break/meeting events MUST NOT have `bookingId` or `customerId` - Vacation/break events SHOULD have `resourceId` --- ## 2. Booking Entity Comparison ### Current RawBookingData Interface **Location:** `src/repositories/MockBookingRepository.ts` ```typescript interface RawBookingData { id: string; customerId: string; status: string; createdAt: string | Date; services: RawBookingService[]; totalPrice?: number; tags?: string[]; notes?: string; [key: string]: unknown; } interface RawBookingService { serviceId: string; serviceName: string; baseDuration: number; basePrice: number; customPrice?: number; resourceId: string; } ``` ### Target IBooking Interface **Location:** `src/types/BookingTypes.ts` ```typescript export interface IBooking extends ISync { id: string; customerId: string; status: BookingStatus; createdAt: Date; services: IBookingService[]; totalPrice?: number; tags?: string[]; notes?: string; } export interface IBookingService { serviceId: string; serviceName: string; baseDuration: number; basePrice: number; customPrice?: number; resourceId: string; } ``` ### Documented JSON Format ```json { "id": "BOOK001", "customerId": "CUST001", "status": "created", "createdAt": "2025-08-05T09:00:00", "services": [ { "serviceId": "SRV001", "serviceName": "Balayage langt hår", "baseDuration": 60, "basePrice": 800, "customPrice": 800, "resourceId": "EMP001" } ], "totalPrice": 800, "notes": "Kunde ønsker lys blond" } ``` ### Field-by-Field Comparison - Booking Entity **Main Booking:** | Field | Current RawData | Target Interface | Documented JSON | Status | |-------|----------------|------------------|----------------|--------| | `id` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `customerId` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `status` | ✅ `string` | ✅ `BookingStatus` | ✅ `"created"` | **MATCH** (needs cast) | | `createdAt` | ✅ `string \| Date` | ✅ `Date` | ✅ Present | **MATCH** | | `services` | ✅ `RawBookingService[]` | ✅ `IBookingService[]` | ✅ Present | **MATCH** | | `totalPrice` | ✅ `number?` | ✅ `number?` | ✅ Present | **MATCH** | | `tags` | ✅ `string[]?` | ✅ `string[]?` | ❌ Not in example | **MATCH** | | `notes` | ✅ `string?` | ✅ `string?` | ✅ Present | **MATCH** | | `syncStatus` | ❌ Missing | ✅ `SyncStatus` (via ISync) | ❌ Not in example | **MISSING (added in processing)** | **BookingService:** | Field | Current RawData | Target Interface | Documented JSON | Status | |-------|----------------|------------------|----------------|--------| | `serviceId` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `serviceName` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `baseDuration` | ✅ `number` | ✅ `number` | ✅ Present | **MATCH** | | `basePrice` | ✅ `number` | ✅ `number` | ✅ Present | **MATCH** | | `customPrice` | ✅ `number?` | ✅ `number?` | ✅ Present | **MATCH** | | `resourceId` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | ### Status - Booking Entity **RawBookingData: PERFECT MATCH** ✅ **RawBookingService: PERFECT MATCH** ✅ All fields present and correctly typed. `syncStatus` correctly added during processing. ### Validation Recommendations - Ensure `customerId` is not null/empty (REQUIRED) - Ensure `services` array has at least one service (REQUIRED) - Validate `status` is valid BookingStatus enum value - Validate each service has `resourceId` (REQUIRED) --- ## 3. Customer Entity Comparison ### Current RawCustomerData Interface **Location:** `src/repositories/MockCustomerRepository.ts` ```typescript interface RawCustomerData { id: string; name: string; phone: string; email?: string; metadata?: Record; [key: string]: unknown; } ``` ### Target ICustomer Interface **Location:** `src/types/CustomerTypes.ts` ```typescript export interface ICustomer extends ISync { id: string; name: string; phone: string; email?: string; metadata?: Record; } ``` ### Documented JSON Format ```json { "id": "CUST001", "name": "Maria Jensen", "phone": "+45 12 34 56 78", "email": "maria.jensen@example.com", "metadata": { "preferredStylist": "EMP001", "allergies": ["ammonia"] } } ``` ### Field-by-Field Comparison - Customer Entity | Field | Current RawData | Target Interface | Documented JSON | Status | |-------|----------------|------------------|----------------|--------| | `id` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `name` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `phone` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `email` | ✅ `string?` | ✅ `string?` | ✅ Present | **MATCH** | | `metadata` | ✅ `Record?` | ✅ `Record?` | ✅ Present | **MATCH** | | `syncStatus` | ❌ Missing | ✅ `SyncStatus` (via ISync) | ❌ Not in example | **MISSING (added in processing)** | ### Status - Customer Entity **RawCustomerData: PERFECT MATCH** ✅ All fields present and correctly typed. `syncStatus` correctly added during processing. --- ## 4. Resource Entity Comparison ### Current RawResourceData Interface **Location:** `src/repositories/MockResourceRepository.ts` ```typescript interface RawResourceData { id: string; name: string; displayName: string; type: string; avatarUrl?: string; color?: string; isActive?: boolean; metadata?: Record; [key: string]: unknown; } ``` ### Target IResource Interface **Location:** `src/types/ResourceTypes.ts` ```typescript export interface IResource extends ISync { id: string; name: string; displayName: string; type: ResourceType; avatarUrl?: string; color?: string; isActive?: boolean; metadata?: Record; } ``` ### Documented JSON Format ```json { "id": "EMP001", "name": "karina.knudsen", "displayName": "Karina Knudsen", "type": "person", "avatarUrl": "/avatars/karina.jpg", "color": "#9c27b0", "isActive": true, "metadata": { "role": "master stylist", "specialties": ["balayage", "color", "bridal"] } } ``` ### Field-by-Field Comparison - Resource Entity | Field | Current RawData | Target Interface | Documented JSON | Status | |-------|----------------|------------------|----------------|--------| | `id` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `name` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `displayName` | ✅ `string` | ✅ `string` | ✅ Present | **MATCH** | | `type` | ✅ `string` | ✅ `ResourceType` | ✅ `"person"` | **MATCH** (needs cast) | | `avatarUrl` | ✅ `string?` | ✅ `string?` | ✅ Present | **MATCH** | | `color` | ✅ `string?` | ✅ `string?` | ✅ Present | **MATCH** | | `isActive` | ✅ `boolean?` | ✅ `boolean?` | ✅ Present | **MATCH** | | `metadata` | ✅ `Record?` | ✅ `Record?` | ✅ Present | **MATCH** | | `syncStatus` | ❌ Missing | ✅ `SyncStatus` (via ISync) | ❌ Not in example | **MISSING (added in processing)** | ### Status - Resource Entity **RawResourceData: PERFECT MATCH** ✅ All fields present and correctly typed. `syncStatus` correctly added during processing. ### Validation Recommendations - Validate `type` is valid ResourceType enum value (`'person' | 'room' | 'equipment' | 'vehicle' | 'custom'`) --- ## Summary Table | Entity | Core Fields Status | Missing Critical Fields | Extra Fields | Overall Status | |--------|-------------------|-------------------------|--------------|----------------| | **Event** | ✅ Basic fields OK | ❌ 4 critical fields missing | ⚠️ `color` (legacy) | **NEEDS UPDATES** | | **Booking** | ✅ All fields present | ✅ None | ✅ None | **COMPLETE ✅** | | **Customer** | ✅ All fields present | ✅ None | ✅ None | **COMPLETE ✅** | | **Resource** | ✅ All fields present | ✅ None | ✅ None | **COMPLETE ✅** | --- ## Architecture Validation Rules ### Event Type Constraints From `docs/booking-event-architecture.md`: ```typescript // Rule 1: Customer events MUST have booking reference if (event.type === 'customer') { assert(event.bookingId !== undefined, "Customer events require bookingId"); assert(event.customerId !== undefined, "Customer events require customerId"); assert(event.resourceId !== undefined, "Customer events require resourceId"); } // Rule 2: Non-customer events MUST NOT have booking reference if (event.type !== 'customer') { assert(event.bookingId === undefined, "Only customer events have bookingId"); assert(event.customerId === undefined, "Only customer events have customerId"); } // Rule 3: Vacation/break events MUST have resource assignment if (event.type === 'vacation' || event.type === 'break') { assert(event.resourceId !== undefined, "Vacation/break events require resourceId"); } // Rule 4: Meeting/blocked events MAY have resource assignment if (event.type === 'meeting' || event.type === 'blocked') { // resourceId is optional } ``` ### Booking Constraints ```typescript // Rule 5: Booking ALWAYS has customer assert(booking.customerId !== "", "Booking requires customer"); // Rule 6: Booking ALWAYS has services assert(booking.services.length > 0, "Booking requires at least one service"); // Rule 7: Each service MUST have resource assignment booking.services.forEach(service => { assert(service.resourceId !== undefined, "Service requires resourceId"); }); // Rule 8: Service resourceId becomes Event resourceId // When a booking has ONE service: // event.resourceId = booking.services[0].resourceId // When a booking has MULTIPLE services: // ONE event per service, each with different resourceId ``` ### Denormalization Rules From `docs/booking-event-architecture.md` (lines 532-547): **Backend performs JOIN and denormalizes:** ```sql SELECT e.Id, e.Type, e.Title, e.Start, e.End, e.AllDay, e.BookingId, e.ResourceId, -- Already on CalendarEvent (denormalized) b.CustomerId -- Joined from Booking table FROM CalendarEvent e LEFT JOIN Booking b ON e.BookingId = b.Id WHERE e.Start >= @start AND e.Start <= @end ``` **Why denormalization:** - **Performance:** No JOIN needed in frontend queries - **Resource filtering:** Quick "show all events for EMP001" - **Customer filtering:** Quick "show all events for CUST001" - **Offline-first:** Complete event data available without JOIN --- ## Recommended Implementation ### Phase 1: Update RawEventData Interface (HIGH PRIORITY) **File:** `src/repositories/MockEventRepository.ts` ```typescript interface RawEventData { // Core fields (required) id: string; title: string; start: string | Date; end: string | Date; type: string; allDay?: boolean; // Denormalized references (NEW - CRITICAL for booking architecture) bookingId?: string; // Reference to booking (customer events only) resourceId?: string; // Which resource owns this slot customerId?: string; // Customer reference (denormalized from booking) // Optional fields description?: string; // Detailed event notes recurringId?: string; // For recurring events metadata?: Record; // Flexible metadata // Legacy (deprecated, keep for backward compatibility) color?: string; // UI-specific field } ``` ### Phase 2: Update processCalendarData() Method ```typescript private processCalendarData(data: RawEventData[]): ICalendarEvent[] { return data.map((event): ICalendarEvent => { // Validate event type constraints if (event.type === 'customer') { if (!event.bookingId) { console.warn(`Customer event ${event.id} missing bookingId`); } if (!event.resourceId) { console.warn(`Customer event ${event.id} missing resourceId`); } if (!event.customerId) { console.warn(`Customer event ${event.id} missing customerId`); } } return { id: event.id, title: event.title, description: event.description, start: new Date(event.start), end: new Date(event.end), type: event.type as CalendarEventType, allDay: event.allDay || false, // Denormalized references (CRITICAL) bookingId: event.bookingId, resourceId: event.resourceId, customerId: event.customerId, // Optional fields recurringId: event.recurringId, metadata: event.metadata, syncStatus: 'synced' as const }; }); } ``` ### Phase 3: Testing (RECOMMENDED) 1. **Test customer event with booking reference** - Verify `bookingId`, `resourceId`, `customerId` are preserved - Verify type is correctly cast to `CalendarEventType` 2. **Test vacation event without booking** - Verify `bookingId` and `customerId` are `undefined` - Verify `resourceId` IS present (required for vacation/break) 3. **Test split-resource booking scenario** - Booking with 2 services (different resources) - Should create 2 events with different `resourceId` 4. **Test event-booking relationship queries** - Load event by `bookingId` - Load all events for `resourceId` - Load all events for `customerId` --- ## Architecture Compliance Score | Aspect | Score | Notes | |--------|-------|-------| | Repository Pattern | 100% | Correctly implements IApiRepository | | Entity Interfaces | 75% | Booking/Customer/Resource perfect, Event missing 4 critical fields | | Data Processing | 90% | Correct date/type conversions, needs explicit field mapping | | Type Safety | 85% | Good type assertions, needs validation | | Documentation Alignment | 70% | Partially matches documented examples | | **Overall** | **84%** | **Good foundation, Event entity needs updates** | --- ## Gap Analysis Summary ### What's Working ✅ - **Repository pattern:** Correctly implements IApiRepository interface - **Booking entity:** 100% correct (all fields match) - **Customer entity:** 100% correct (all fields match) - **Resource entity:** 100% correct (all fields match) - **Date processing:** string | Date → Date correctly handled - **Type assertions:** string → enum types correctly cast - **SyncStatus injection:** Correctly added during processing - **Error handling:** Unsupported operations (create/update/delete) throw errors - **fetchAll() implementation:** Correctly loads from JSON and processes data ### What's Missing ❌ **Event Entity - 4 Critical Fields:** 1. **bookingId** - Cannot link events to bookings - Impact: Cannot load booking details when event is clicked - Impact: Cannot cascade delete when booking is cancelled - Impact: Cannot query events by booking 2. **resourceId** - Cannot query by resource - Impact: Cannot filter calendar by resource (columns) - Impact: Cannot show resource utilization - Impact: Denormalization benefit lost (requires JOIN) 3. **customerId** - Cannot query by customer - Impact: Cannot show customer history - Impact: Cannot filter events by customer - Impact: Denormalization benefit lost (requires JOIN) 4. **description** - Cannot add detailed notes - Impact: Limited event details - Impact: No additional context beyond title ### What Needs Validation ⚠️ - **Event type constraints:** Customer events require `bookingId` - **Booking constraints:** Must have `customerId` and `services[]` - **Resource assignment:** Vacation/break events require `resourceId` - **Enum validation:** Validate `type`, `status` match enum values ### What Needs Cleanup 🧹 - **Legacy `color` field:** Present in RawEventData but not in ICalendarEvent - **Index signature:** Consider removing `[key: string]: unknown` once all fields are explicit --- ## Next Steps ### Immediate (HIGH PRIORITY) 1. **Update Event Entity** - Add 4 missing fields to `RawEventData` - Update `processCalendarData()` with explicit mapping - Add validation for type constraints ### Short-term (MEDIUM PRIORITY) 2. **Create Mock Data Files** - Update `wwwroot/data/mock-events.json` with denormalized fields - Ensure `mock-bookings.json`, `mock-customers.json`, `mock-resources.json` exist - Verify relationships (event.bookingId → booking.id) 3. **Add Validation Layer** - Validate event-booking relationships - Validate required fields per event type - Log warnings for data integrity issues ### Long-term (LOW PRIORITY) 4. **Update Tests** - Test new fields in event processing - Test validation rules - Test cross-entity relationships 5. **Documentation** - Update CLAUDE.md with Mock repository usage - Document validation rules - Document denormalization strategy --- ## Conclusion The Mock Repository implementation has a **strong foundation** with 3 out of 4 entities (Booking, Customer, Resource) perfectly matching the target architecture. The **Event entity** needs critical updates to support the booking architecture's denormalization strategy. Adding the 4 missing fields (`bookingId`, `resourceId`, `customerId`, `description`) will bring the implementation to **100% compliance** with the documented architecture. **Estimated effort:** 1-2 hours for updates + testing **Risk:** Low - changes are additive (no breaking changes to existing code) **Priority:** HIGH - required for booking architecture to function correctly