# 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<string, any>;
}
```

### 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<string, any>?` | ✅ 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<string, any>` (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<string, any>;
  [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<string, any>;
}
```

### 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<string, any>?` | ✅ `Record<string, any>?` | ✅ 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<string, any>;
  [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<string, any>;
}
```

### 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<string, any>?` | ✅ `Record<string, any>?` | ✅ 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<string, any>;  // 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
