# Mock Data Migration Guide

**Purpose:** This document explains the changes required to existing mock data files to support the new booking architecture with service-level resource assignment.

**Date:** 2025-11-14
**Related:** [Booking & Resource Architecture Redesign](../coding-sessions/2025-11-14-booking-resource-architecture-redesign.md)

---

## Overview of Changes

### What Changed?

The booking architecture has been fundamentally redesigned to support:
- **Split-resource bookings** (student + master working on same booking)
- **Equal-split bookings** (two masters, no primary resource)
- **Service-level resource assignment** (resources assigned per service, not per booking)
- **Clear separation** between Booking (business) and CalendarEvent (scheduling)

### Key Architectural Principles

1. **Booking = Customer Services ONLY**
   - Bookings are ALWAYS for customers
   - Bookings ALWAYS have services
   - Vacation/break/meeting are NOT bookings

2. **Resources Assigned Per Service**
   - No `Booking.resourceId` (removed)
   - Each `IBookingService` has `resourceId`
   - Enables split-resource and equal-split scenarios

3. **CalendarEvent.type Determines Booking Existence**
   - `type: 'customer'` → has `bookingId`
   - `type: 'vacation' | 'break' | 'meeting' | 'blocked'` → no `bookingId`

4. **Denormalization for Performance**
   - `CalendarEvent` denormalizes `resourceId`, `customerId` for fast IndexedDB queries
   - Backend performs JOIN, frontend receives denormalized data

---

## Data Structure Changes

### 1. CalendarEvent Changes

**Before:**
```json
{
  "id": "1",
  "title": "Balayage langt hår",
  "start": "2025-08-05T10:00:00",
  "end": "2025-08-05T11:00:00",
  "type": "work",
  "allDay": false,
  "syncStatus": "synced",
  "metadata": { "duration": 60 }
}
```

**After:**
```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 }
}
```

**Changes:**
- `type`: Changed from `"work"` to `"customer"` (using CalendarEventType)
- **NEW:** `bookingId` - Reference to booking (required if type = 'customer')
- **NEW:** `resourceId` - Resource who owns this time slot (denormalized)
- **NEW:** `customerId` - Customer for this event (denormalized from Booking)

### 2. New Entity: Booking

**Structure:**
```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"
}
```

**Key Points:**
- NO `resourceId` at booking level (moved to service level)
- NO `type` field (always customer-related)
- `customerId` is REQUIRED
- `services` is REQUIRED (array with at least one service)

### 3. New Entity: Customer

**Structure:**
```json
{
  "id": "CUST001",
  "name": "Maria Jensen",
  "phone": "+45 12 34 56 78",
  "email": "maria.jensen@example.com",
  "metadata": {
    "preferredStylist": "EMP001",
    "allergies": ["ammonia"]
  }
}
```

### 4. New Entity: Resource

**Structure:**
```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"]
  }
}
```

---

## Complete JSON Examples

### Example 1: Simple Customer Booking (Single Resource)

**Scenario:** One customer, one service, one resource, one time slot.

**Customer:**
```json
{
  "id": "CUST001",
  "name": "Maria Jensen",
  "phone": "+45 12 34 56 78",
  "email": "maria.jensen@example.com"
}
```

**Resource:**
```json
{
  "id": "EMP001",
  "name": "karina.knudsen",
  "displayName": "Karina Knudsen",
  "type": "person",
  "avatarUrl": "/avatars/karina.jpg",
  "isActive": true
}
```

**Booking:**
```json
{
  "id": "BOOK001",
  "customerId": "CUST001",
  "status": "created",
  "createdAt": "2025-08-05T09:00:00",
  "services": [
    {
      "serviceId": "SRV001",
      "serviceName": "Klipning og styling",
      "baseDuration": 60,
      "basePrice": 500,
      "customPrice": 500,
      "resourceId": "EMP001"
    }
  ],
  "totalPrice": 500
}
```

**CalendarEvent:**
```json
{
  "id": "EVT001",
  "title": "Maria Jensen - Klipning og styling",
  "start": "2025-08-05T10:00:00",
  "end": "2025-08-05T11:00:00",
  "type": "customer",
  "allDay": false,
  "syncStatus": "synced",

  "bookingId": "BOOK001",
  "resourceId": "EMP001",
  "customerId": "CUST001"
}
```

**Relationship:**
- 1 Customer → 1 Booking → 1 Event → 1 Resource

---

### Example 2: Split-Resource Booking (Student + Master)

**Scenario:** One customer, two services split between student (hair wash) and master (color treatment).

**Resources:**
```json
[
  {
    "id": "STUDENT001",
    "name": "anne.elev",
    "displayName": "Anne (Elev)",
    "type": "person",
    "isActive": true,
    "metadata": { "role": "student" }
  },
  {
    "id": "EMP001",
    "name": "karina.knudsen",
    "displayName": "Karina Knudsen",
    "type": "person",
    "isActive": true,
    "metadata": { "role": "master stylist" }
  }
]
```

**Customer:**
```json
{
  "id": "CUST002",
  "name": "Louise Andersen",
  "phone": "+45 23 45 67 89",
  "email": "louise@example.com"
}
```

**Booking (Single booking with 2 services, 2 different resources):**
```json
{
  "id": "BOOK002",
  "customerId": "CUST002",
  "status": "created",
  "createdAt": "2025-08-05T12:00:00",
  "services": [
    {
      "serviceId": "SRV002",
      "serviceName": "Hårvask",
      "baseDuration": 30,
      "basePrice": 100,
      "customPrice": 100,
      "resourceId": "STUDENT001"
    },
    {
      "serviceId": "SRV003",
      "serviceName": "Bundfarve",
      "baseDuration": 90,
      "basePrice": 800,
      "customPrice": 800,
      "resourceId": "EMP001"
    }
  ],
  "totalPrice": 900,
  "notes": "Hårvask udføres af elev, farve af master stylist"
}
```

**CalendarEvents (2 events, same booking, different resources):**
```json
[
  {
    "id": "EVT002",
    "title": "Louise Andersen - Hårvask",
    "start": "2025-08-05T13:00:00",
    "end": "2025-08-05T13:30:00",
    "type": "customer",
    "allDay": false,
    "syncStatus": "synced",

    "bookingId": "BOOK002",
    "resourceId": "STUDENT001",
    "customerId": "CUST002"
  },
  {
    "id": "EVT003",
    "title": "Louise Andersen - Bundfarve",
    "start": "2025-08-05T13:30:00",
    "end": "2025-08-05T15:00:00",
    "type": "customer",
    "allDay": false,
    "syncStatus": "synced",

    "bookingId": "BOOK002",
    "resourceId": "EMP001",
    "customerId": "CUST002"
  }
]
```

**Relationship:**
- 1 Customer → 1 Booking → 2 Events → 2 Resources (student + master)

**Key Points:**
- Both events reference the SAME `bookingId`
- Each event has a DIFFERENT `resourceId`
- No "primary" resource concept - both are equal in the booking
- Student's calendar shows EVT002, Master's calendar shows EVT003

---

### Example 3: Equal Split Booking (Two Masters)

**Scenario:** One customer, one large service split equally between two master stylists.

**Resources:**
```json
[
  {
    "id": "EMP001",
    "name": "karina.knudsen",
    "displayName": "Karina Knudsen",
    "type": "person",
    "isActive": true
  },
  {
    "id": "EMP002",
    "name": "nanna.nielsen",
    "displayName": "Nanna Nielsen",
    "type": "person",
    "isActive": true
  }
]
```

**Customer:**
```json
{
  "id": "CUST003",
  "name": "Sofie Hansen",
  "phone": "+45 34 56 78 90"
}
```

**Booking:**
```json
{
  "id": "BOOK003",
  "customerId": "CUST003",
  "status": "created",
  "createdAt": "2025-08-05T08:00:00",
  "services": [
    {
      "serviceId": "SRV004",
      "serviceName": "Bryllupsfrisure - Del 1",
      "baseDuration": 60,
      "basePrice": 750,
      "customPrice": 750,
      "resourceId": "EMP001"
    },
    {
      "serviceId": "SRV005",
      "serviceName": "Bryllupsfrisure - Del 2",
      "baseDuration": 60,
      "basePrice": 750,
      "customPrice": 750,
      "resourceId": "EMP002"
    }
  ],
  "totalPrice": 1500,
  "notes": "To stylister arbejder sammen om bryllupsfrisure"
}
```

**CalendarEvents:**
```json
[
  {
    "id": "EVT004",
    "title": "Sofie Hansen - Bryllupsfrisure (Karina)",
    "start": "2025-08-05T10:00:00",
    "end": "2025-08-05T11:00:00",
    "type": "customer",
    "allDay": false,
    "syncStatus": "synced",

    "bookingId": "BOOK003",
    "resourceId": "EMP001",
    "customerId": "CUST003"
  },
  {
    "id": "EVT005",
    "title": "Sofie Hansen - Bryllupsfrisure (Nanna)",
    "start": "2025-08-05T10:00:00",
    "end": "2025-08-05T11:00:00",
    "type": "customer",
    "allDay": false,
    "syncStatus": "synced",

    "bookingId": "BOOK003",
    "resourceId": "EMP002",
    "customerId": "CUST003"
  }
]
```

**Relationship:**
- 1 Customer → 1 Booking → 2 Events (SAME time, different resources)
- Both masters work simultaneously - no "primary" resource

---

### Example 4: Vacation Event (No Booking)

**Scenario:** Resource on vacation - CalendarEvent only, NO booking.

**Resource:**
```json
{
  "id": "EMP001",
  "name": "karina.knudsen",
  "displayName": "Karina Knudsen",
  "type": "person",
  "isActive": true
}
```

**CalendarEvent:**
```json
{
  "id": "EVT006",
  "title": "Ferie - Spanien",
  "start": "2025-08-10T00:00:00",
  "end": "2025-08-17T23:59:59",
  "type": "vacation",
  "allDay": true,
  "syncStatus": "synced",

  "resourceId": "EMP001",

  "metadata": {
    "destination": "Mallorca",
    "emergency_contact": "+45 12 34 56 78"
  }
}
```

**Key Points:**
- `type: "vacation"` → NO `bookingId`
- `resourceId` present (who is on vacation)
- NO `customerId` (not customer-related)
- NO Booking object exists for this event

---

### Example 5: Break Event (No Booking)

**Scenario:** Lunch break - CalendarEvent only, NO booking.

**CalendarEvent:**
```json
{
  "id": "EVT007",
  "title": "Frokostpause",
  "start": "2025-08-05T12:00:00",
  "end": "2025-08-05T12:30:00",
  "type": "break",
  "allDay": false,
  "syncStatus": "synced",

  "resourceId": "EMP001"
}
```

**Key Points:**
- `type: "break"` → NO `bookingId`
- NO `customerId`
- NO Booking object

---

### Example 6: Meeting Event (No Booking)

**Scenario:** Team meeting - CalendarEvent only, NO booking.

**CalendarEvent:**
```json
{
  "id": "EVT008",
  "title": "Team møde - Salgsmål Q3",
  "start": "2025-08-05T16:00:00",
  "end": "2025-08-05T17:00:00",
  "type": "meeting",
  "allDay": false,
  "syncStatus": "synced",

  "resourceId": "EMP001",

  "metadata": {
    "attendees": ["EMP001", "EMP002", "EMP003"],
    "location": "Mødelokale 1"
  }
}
```

**Key Points:**
- `type: "meeting"` → NO `bookingId`
- NO `customerId`
- Can have multiple attendees in metadata, but `resourceId` is the "owner"

---

### Example 7: Queued Booking (No Event Yet)

**Scenario:** Customer booked services but no time slot assigned yet.

**Booking:**
```json
{
  "id": "BOOK004",
  "customerId": "CUST004",
  "status": "created",
  "createdAt": "2025-08-05T14:00:00",
  "services": [
    {
      "serviceId": "SRV001",
      "serviceName": "Klipning",
      "baseDuration": 45,
      "basePrice": 450,
      "resourceId": "EMP001"
    }
  ],
  "totalPrice": 450,
  "notes": "Kunde venter på ledig tid"
}
```

**CalendarEvents:**
```json
[]
```

**Key Points:**
- Booking EXISTS without CalendarEvent
- No time slot assigned yet (queued state)
- When time slot is assigned, CalendarEvent will be created with `bookingId: "BOOK004"`

---

## Migration Checklist

### For Existing `mock-events.json`

- [ ] Update all `type` values to use CalendarEventType:
  - `"work"` → `"customer"`
  - `"meeting"` → `"meeting"` (unchanged)
  - Add `"vacation"`, `"break"`, `"blocked"` as needed

- [ ] Add denormalized fields to customer events:
  - [ ] Add `bookingId` (if type = 'customer')
  - [ ] Add `resourceId` (which resource owns this slot)
  - [ ] Add `customerId` (if type = 'customer')

- [ ] Create corresponding Booking objects for all customer events

- [ ] Create Customer objects for all unique customers

- [ ] Create Resource objects for all unique resources

### For Existing `mock-resource-events.json`

- [ ] Extract resource data to separate `resources.json` file using IResource interface

- [ ] Update event structure (same changes as above)

- [ ] Link events to bookings and customers

### New Files to Create

1. **`customers.json`** - Array of ICustomer objects
2. **`resources.json`** - Array of IResource objects
3. **`bookings.json`** - Array of IBooking objects
4. **`services.json`** (optional) - Array of service definitions

---

## Type Reference

### CalendarEventType

```typescript
type CalendarEventType =
  | 'customer'    // Customer appointment (HAS booking)
  | 'vacation'    // Vacation/time off (NO booking)
  | 'break'       // Lunch/break (NO booking)
  | 'meeting'     // Meeting (NO booking)
  | 'blocked';    // Blocked time (NO booking)
```

### BookingStatus

```typescript
type BookingStatus =
  | 'created'     // AftaleOprettet
  | 'arrived'     // Ankommet
  | 'paid'        // Betalt
  | 'noshow'      // Udeblevet
  | 'cancelled';  // Aflyst
```

### ResourceType

```typescript
type ResourceType =
  | 'person'      // Employee, stylist, etc.
  | 'room'        // Meeting room, treatment room
  | 'equipment'   // Chair, equipment
  | 'vehicle'     // Company car
  | 'custom';     // Custom resource type
```

---

## Quick Decision Guide

**Question:** Where does this data belong?

| Data | Entity | Example |
|------|--------|---------|
| Customer name, phone, email | Customer | `{ "id": "CUST001", "name": "Maria" }` |
| Service name, price, duration | Booking.services | `{ "serviceId": "SRV001", "basePrice": 500 }` |
| Which resource performs service | BookingService.resourceId | `{ "resourceId": "EMP001" }` |
| When service happens | CalendarEvent | `{ "start": "...", "end": "..." }` |
| Resource on vacation | CalendarEvent (type: vacation) | `{ "type": "vacation", "resourceId": "EMP001" }` |
| Break/lunch | CalendarEvent (type: break) | `{ "type": "break", "resourceId": "EMP001" }` |
| Team meeting | CalendarEvent (type: meeting) | `{ "type": "meeting" }` |

---

## Common Mistakes to Avoid

### ❌ Wrong: Adding resourceId to Booking

```json
{
  "id": "BOOK001",
  "resourceId": "EMP001",  // ❌ WRONG - removed from booking level
  "customerId": "CUST001",
  "services": [...]
}
```

**Why wrong:** Resources are assigned per service, not per booking.

### ❌ Wrong: Adding type to Booking

```json
{
  "id": "BOOK001",
  "type": "customer",  // ❌ WRONG - Booking has no type field
  "customerId": "CUST001"
}
```

**Why wrong:** Bookings are ALWAYS customer-related. Type belongs on CalendarEvent.

### ❌ Wrong: Creating Booking for Vacation

```json
{
  "id": "BOOK999",
  "type": "vacation",  // ❌ WRONG - Vacation is NOT a booking
  "resourceId": "EMP001"
}
```

**Why wrong:** Only customer services are bookings. Vacation is a CalendarEvent only.

### ❌ Wrong: Customer Event Without bookingId

```json
{
  "id": "EVT001",
  "type": "customer",
  "resourceId": "EMP001",
  // ❌ MISSING: bookingId
}
```

**Why wrong:** If `type: 'customer'`, there MUST be a `bookingId`.

---

## Summary

**Before:**
- Events had basic structure with `type: "work" | "meeting"`
- No separation between business (booking) and scheduling (event)
- Resources implicitly tied to events

**After:**
- Clean separation: **Booking** (business) ↔ **CalendarEvent** (scheduling)
- Resources assigned at **service level** (enables splits)
- Denormalized data in events for performance (resourceId, customerId)
- Type-safe with CalendarEventType, BookingStatus, ResourceType
- Supports all business scenarios: split resources, equal splits, queued bookings

**Next Steps:**
1. Create new entity files (customers.json, resources.json, bookings.json)
2. Update existing event files with new structure
3. Test data loading with new interfaces
4. Verify UI displays correctly with denormalized data

---

**Related Documentation:**
- [Booking & Resource Architecture Redesign](../coding-sessions/2025-11-14-booking-resource-architecture-redesign.md)
- [Booking Event Architecture](./booking-event-architecture.md)
