Adds comprehensive mock data repositories and seeding infrastructure
Implements polymorphic data seeding mechanism for initial application setup - Adds Mock repositories for Event, Booking, Customer, and Resource entities - Creates DataSeeder to automatically populate IndexedDB from JSON sources - Enhances index.ts initialization process with data seeding step - Adds mock JSON data files for comprehensive test data Improves offline-first and development testing capabilities
This commit is contained in:
parent
871f5c5682
commit
5648c7c304
11 changed files with 1641 additions and 40 deletions
737
docs/mock-repository-implementation-status.md
Normal file
737
docs/mock-repository-implementation-status.md
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue