@fayz-ai/plugin-agenda 0.8.4 → 0.9.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/store.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createStore, type StoreApi } from 'zustand/vanilla'
2
+ import { emitDataChanged } from '@fayz-ai/core'
2
3
  import { dedup } from '@fayz-ai/saas'
3
4
  import { toast } from '@fayz-ai/ui'
4
5
  import { getScheduleBlockConfig, setScheduleBlockConfig } from '@fayz-ai/saas'
@@ -181,6 +182,7 @@ export function createAgendaStore(provider: AgendaDataProvider, financialBridge?
181
182
  const bookings = await provider.getBookings(getCurrentBookingQuery(state))
182
183
  set({ bookings })
183
184
  }
185
+ emitDataChanged({ table: 'appointments', op: 'create', id: booking.id, source: 'ui' })
184
186
  toast.success('Appointment created')
185
187
  return booking
186
188
  } catch (err: any) {
@@ -197,6 +199,7 @@ export function createAgendaStore(provider: AgendaDataProvider, financialBridge?
197
199
  const bookings = await provider.getBookings(getCurrentBookingQuery(state))
198
200
  set({ bookings })
199
201
  }
202
+ emitDataChanged({ table: 'appointments', op: 'update', id, source: 'ui' })
200
203
  toast.success('Appointment updated')
201
204
  return booking
202
205
  } catch (err: any) {
@@ -213,6 +216,7 @@ export function createAgendaStore(provider: AgendaDataProvider, financialBridge?
213
216
  const bookings = await provider.getBookings(getCurrentBookingQuery(state))
214
217
  set({ bookings })
215
218
  }
219
+ emitDataChanged({ table: 'appointments', op: 'delete', id, source: 'ui' })
216
220
  toast.success('Appointment deleted')
217
221
  } catch (err: any) {
218
222
  toast.error('Failed to delete appointment', { description: err?.message })
@@ -232,6 +236,7 @@ export function createAgendaStore(provider: AgendaDataProvider, financialBridge?
232
236
  const bookings = await provider.getBookings(getCurrentBookingQuery(state))
233
237
  set({ bookings })
234
238
  }
239
+ emitDataChanged({ table: 'appointments', op: 'update', id, source: 'ui' })
235
240
  toast.success('Status updated')
236
241
  } catch (err: any) {
237
242
  // Rollback on failure
@@ -1,483 +0,0 @@
1
- 'use strict';
2
-
3
- var core = require('@fayz-ai/core');
4
-
5
- // src/data/mock.ts
6
- var nextId = 1;
7
- function uid() {
8
- return `mock-${nextId++}`;
9
- }
10
- function now() {
11
- return (/* @__PURE__ */ new Date()).toISOString();
12
- }
13
- function addMinutes(date, minutes) {
14
- const d = new Date(date);
15
- d.setMinutes(d.getMinutes() + minutes);
16
- return d.toISOString();
17
- }
18
- function timeOverlaps(aStart, aEnd, bStart, bEnd) {
19
- return aStart < bEnd && aEnd > bStart;
20
- }
21
- var SAMPLE_PROFESSIONALS = [
22
- { id: "prof-1", name: "Ana Silva", avatarUrl: null, locationId: "loc-1", locationName: "Main Branch", isActive: true },
23
- { id: "prof-2", name: "Carlos Santos", avatarUrl: null, locationId: "loc-1", locationName: "Main Branch", isActive: true },
24
- { id: "prof-3", name: "Maria Oliveira", avatarUrl: null, locationId: "loc-1", locationName: "Main Branch", isActive: true }
25
- ];
26
- function createSampleBookings() {
27
- const today = /* @__PURE__ */ new Date();
28
- const year = today.getFullYear();
29
- const month = today.getMonth();
30
- const day = today.getDate();
31
- function dt(hour, minute = 0) {
32
- return new Date(year, month, day, hour, minute).toISOString();
33
- }
34
- return [
35
- {
36
- id: "bk-1",
37
- tenantId: "mock",
38
- kind: "appointment",
39
- startsAt: dt(9, 0),
40
- endsAt: dt(10, 30),
41
- status: "confirmed",
42
- notes: null,
43
- orderId: "ord-1",
44
- locationId: "loc-1",
45
- metadata: {},
46
- clientId: "cl-1",
47
- clientName: "Sarah Johnson",
48
- clientPhone: "(11) 99999-1111",
49
- clientEmail: "sarah@email.com",
50
- clientAvatarUrl: null,
51
- professionalId: "prof-1",
52
- professionalName: "Ana Silva",
53
- professionalAvatarUrl: null,
54
- locationName: "Main Branch",
55
- orderTotal: 250,
56
- orderStatus: "draft",
57
- services: [{ id: "bi-1", serviceId: "svc-1", name: "Balayage", durationMinutes: 90, price: 250, assigneeId: "prof-1" }],
58
- totalDurationMinutes: 90,
59
- createdAt: now(),
60
- updatedAt: now()
61
- },
62
- {
63
- id: "bk-2",
64
- tenantId: "mock",
65
- kind: "appointment",
66
- startsAt: dt(10, 0),
67
- endsAt: dt(11, 0),
68
- status: "scheduled",
69
- notes: null,
70
- orderId: "ord-2",
71
- locationId: "loc-1",
72
- metadata: {},
73
- clientId: "cl-2",
74
- clientName: "Emily Carter",
75
- clientPhone: "(11) 99999-2222",
76
- clientEmail: "emily@email.com",
77
- clientAvatarUrl: null,
78
- professionalId: "prof-2",
79
- professionalName: "Carlos Santos",
80
- professionalAvatarUrl: null,
81
- locationName: "Main Branch",
82
- orderTotal: 80,
83
- orderStatus: "draft",
84
- services: [{ id: "bi-2", serviceId: "svc-2", name: "Haircut", durationMinutes: 60, price: 80, assigneeId: "prof-2" }],
85
- totalDurationMinutes: 60,
86
- createdAt: now(),
87
- updatedAt: now()
88
- },
89
- {
90
- id: "bk-3",
91
- tenantId: "mock",
92
- kind: "appointment",
93
- startsAt: dt(14, 0),
94
- endsAt: dt(15, 0),
95
- status: "confirmed",
96
- notes: "First visit",
97
- orderId: "ord-3",
98
- locationId: "loc-1",
99
- metadata: {},
100
- clientId: "cl-3",
101
- clientName: "Rachel Kim",
102
- clientPhone: "(11) 99999-3333",
103
- clientEmail: null,
104
- clientAvatarUrl: null,
105
- professionalId: "prof-3",
106
- professionalName: "Maria Oliveira",
107
- professionalAvatarUrl: null,
108
- locationName: "Main Branch",
109
- orderTotal: 120,
110
- orderStatus: "draft",
111
- services: [{ id: "bi-3", serviceId: "svc-3", name: "Facial Treatment", durationMinutes: 60, price: 120, assigneeId: "prof-3" }],
112
- totalDurationMinutes: 60,
113
- createdAt: now(),
114
- updatedAt: now()
115
- },
116
- {
117
- id: "bk-4",
118
- tenantId: "mock",
119
- kind: "appointment",
120
- startsAt: dt(11, 0),
121
- endsAt: dt(12, 0),
122
- status: "scheduled",
123
- notes: null,
124
- orderId: "ord-4",
125
- locationId: "loc-1",
126
- metadata: {},
127
- clientId: "cl-4",
128
- clientName: "Jessica Lima",
129
- clientPhone: "(11) 99999-4444",
130
- clientEmail: null,
131
- clientAvatarUrl: null,
132
- professionalId: "prof-1",
133
- professionalName: "Ana Silva",
134
- professionalAvatarUrl: null,
135
- locationName: "Main Branch",
136
- orderTotal: 60,
137
- orderStatus: "draft",
138
- services: [{ id: "bi-4", serviceId: "svc-4", name: "Manicure", durationMinutes: 60, price: 60, assigneeId: "prof-1" }],
139
- totalDurationMinutes: 60,
140
- createdAt: now(),
141
- updatedAt: now()
142
- },
143
- {
144
- id: "bk-5",
145
- tenantId: "mock",
146
- kind: "appointment",
147
- startsAt: dt(15, 30),
148
- endsAt: dt(17, 0),
149
- status: "confirmed",
150
- notes: null,
151
- orderId: "ord-5",
152
- locationId: "loc-1",
153
- metadata: {},
154
- clientId: "cl-5",
155
- clientName: "Amanda Wright",
156
- clientPhone: "(11) 99999-5555",
157
- clientEmail: "amanda@email.com",
158
- clientAvatarUrl: null,
159
- professionalId: "prof-2",
160
- professionalName: "Carlos Santos",
161
- professionalAvatarUrl: null,
162
- locationName: "Main Branch",
163
- orderTotal: 350,
164
- orderStatus: "draft",
165
- services: [
166
- { id: "bi-5", serviceId: "svc-1", name: "Highlights", durationMinutes: 60, price: 200, assigneeId: "prof-2" },
167
- { id: "bi-6", serviceId: "svc-2", name: "Haircut", durationMinutes: 30, price: 150, assigneeId: "prof-2" }
168
- ],
169
- totalDurationMinutes: 90,
170
- createdAt: now(),
171
- updatedAt: now()
172
- }
173
- ];
174
- }
175
- function createSampleSchedules() {
176
- const schedules = [];
177
- for (const prof of SAMPLE_PROFESSIONALS) {
178
- for (let day = 1; day <= 5; day++) {
179
- schedules.push({
180
- id: uid(),
181
- tenantId: "mock",
182
- kind: "working_hours",
183
- assigneeId: prof.id,
184
- locationId: prof.locationId,
185
- dayOfWeek: day,
186
- specificDate: null,
187
- startsAt: "08:00",
188
- endsAt: "12:00",
189
- isActive: true,
190
- metadata: {}
191
- });
192
- schedules.push({
193
- id: uid(),
194
- tenantId: "mock",
195
- kind: "working_hours",
196
- assigneeId: prof.id,
197
- locationId: prof.locationId,
198
- dayOfWeek: day,
199
- specificDate: null,
200
- startsAt: "13:00",
201
- endsAt: "18:00",
202
- isActive: true,
203
- metadata: {}
204
- });
205
- }
206
- schedules.push({
207
- id: uid(),
208
- tenantId: "mock",
209
- kind: "working_hours",
210
- assigneeId: prof.id,
211
- locationId: prof.locationId,
212
- dayOfWeek: 6,
213
- specificDate: null,
214
- startsAt: "08:00",
215
- endsAt: "14:00",
216
- isActive: true,
217
- metadata: {}
218
- });
219
- }
220
- return schedules;
221
- }
222
- function createMockAgendaProvider(options) {
223
- const seed = options?.seed;
224
- const bookings = seed?.bookings ? [...seed.bookings] : createSampleBookings();
225
- const schedules = seed?.schedules ? [...seed.schedules] : createSampleSchedules();
226
- const professionals = seed?.professionals ? [...seed.professionals] : [...SAMPLE_PROFESSIONALS];
227
- return {
228
- async getLocations() {
229
- return [];
230
- },
231
- async getBookings(query) {
232
- return bookings.filter((b) => {
233
- if (query.kind && b.kind !== query.kind) return false;
234
- if (query.statuses?.length && !query.statuses.includes(b.status)) return false;
235
- if (query.professionalIds?.length && !query.professionalIds.includes(b.professionalId ?? "")) return false;
236
- if (query.locationId && b.locationId !== query.locationId) return false;
237
- if (query.dateRange) {
238
- if (b.startsAt > query.dateRange.end || (b.endsAt ?? b.startsAt) < query.dateRange.start) return false;
239
- }
240
- return true;
241
- });
242
- },
243
- async getBookingById(id) {
244
- return bookings.find((b) => b.id === id) ?? null;
245
- },
246
- async createBooking(input) {
247
- const totalDuration = input.services.reduce((sum, s) => sum + s.durationMinutes, 0);
248
- const totalPrice = input.services.reduce((sum, s) => sum + s.price, 0);
249
- const booking = {
250
- id: uid(),
251
- tenantId: "mock",
252
- kind: input.kind ?? "appointment",
253
- startsAt: input.startsAt,
254
- endsAt: addMinutes(input.startsAt, totalDuration || 30),
255
- status: "scheduled",
256
- notes: input.notes ?? null,
257
- orderId: input.services.length > 0 ? uid() : null,
258
- locationId: input.locationId ?? null,
259
- metadata: {},
260
- clientId: input.clientId,
261
- clientName: input.title?.trim() || "New Client",
262
- clientPhone: null,
263
- clientEmail: null,
264
- clientAvatarUrl: null,
265
- professionalId: input.professionalId,
266
- professionalName: professionals.find((p) => p.id === input.professionalId)?.name ?? "Unknown",
267
- professionalAvatarUrl: null,
268
- locationName: null,
269
- orderTotal: totalPrice,
270
- orderStatus: "draft",
271
- services: input.services.map((s) => ({
272
- id: uid(),
273
- serviceId: s.serviceId,
274
- name: s.name,
275
- durationMinutes: s.durationMinutes,
276
- price: s.price,
277
- assigneeId: s.assigneeId ?? input.professionalId
278
- })),
279
- totalDurationMinutes: totalDuration,
280
- createdAt: now(),
281
- updatedAt: now()
282
- };
283
- bookings.push(booking);
284
- return booking;
285
- },
286
- async updateBooking(id, data) {
287
- const idx = bookings.findIndex((b) => b.id === id);
288
- if (idx === -1) throw new Error("Booking not found");
289
- const existing = bookings[idx];
290
- const serviceUpdates = {};
291
- if (data.services) {
292
- serviceUpdates.services = data.services.map((s) => ({
293
- id: uid(),
294
- serviceId: s.serviceId,
295
- name: s.name,
296
- durationMinutes: s.durationMinutes,
297
- price: s.price,
298
- assigneeId: s.assigneeId ?? existing.professionalId ?? ""
299
- }));
300
- serviceUpdates.totalDurationMinutes = data.services.reduce((sum, s) => sum + s.durationMinutes, 0);
301
- serviceUpdates.orderTotal = data.services.reduce((sum, s) => sum + s.price, 0);
302
- }
303
- const updated = {
304
- ...existing,
305
- ...data.startsAt && { startsAt: data.startsAt },
306
- ...data.endsAt && { endsAt: data.endsAt },
307
- ...data.title !== void 0 && { clientName: data.title },
308
- ...data.status && { status: data.status },
309
- ...data.notes !== void 0 && { notes: data.notes ?? null },
310
- ...data.professionalId && { professionalId: data.professionalId },
311
- ...data.clientId && { clientId: data.clientId },
312
- ...data.locationId && { locationId: data.locationId },
313
- ...serviceUpdates,
314
- updatedAt: now()
315
- };
316
- bookings[idx] = updated;
317
- return updated;
318
- },
319
- async deleteBooking(id) {
320
- const idx = bookings.findIndex((b) => b.id === id);
321
- if (idx !== -1) bookings.splice(idx, 1);
322
- },
323
- async updateBookingStatus(id, status) {
324
- const idx = bookings.findIndex((b) => b.id === id);
325
- if (idx === -1) throw new Error("Booking not found");
326
- bookings[idx] = { ...bookings[idx], status, updatedAt: now() };
327
- return bookings[idx];
328
- },
329
- async checkConflict(params) {
330
- return bookings.some((b) => {
331
- if (params.excludeBookingId && b.id === params.excludeBookingId) return false;
332
- if (b.professionalId !== params.assigneeId) return false;
333
- if (b.status === "cancelled" || b.status === "no_show") return false;
334
- return timeOverlaps(b.startsAt, b.endsAt ?? b.startsAt, params.startsAt, params.endsAt);
335
- });
336
- },
337
- async getAvailableSlots(params) {
338
- const [yy, mm, dd] = params.date.split("-").map(Number);
339
- const date = new Date(yy, (mm ?? 1) - 1, dd ?? 1);
340
- const dayOfWeek = date.getDay();
341
- const interval = params.slotInterval ?? 30;
342
- const daySchedules = schedules.filter(
343
- (s) => s.assigneeId === params.assigneeId && s.isActive && (s.dayOfWeek === dayOfWeek || s.specificDate === params.date)
344
- );
345
- const slots = [];
346
- for (const sched of daySchedules) {
347
- const [startH, startM] = sched.startsAt.split(":").map(Number);
348
- const [endH, endM] = sched.endsAt.split(":").map(Number);
349
- const schedStart = new Date(date);
350
- schedStart.setHours(startH, startM, 0, 0);
351
- const schedEnd = new Date(date);
352
- schedEnd.setHours(endH, endM, 0, 0);
353
- let slotStart = new Date(schedStart);
354
- while (true) {
355
- const slotEnd = new Date(slotStart.getTime() + params.durationMinutes * 6e4);
356
- if (slotEnd > schedEnd) break;
357
- const startIso = slotStart.toISOString();
358
- const endIso = slotEnd.toISOString();
359
- const hasConflict = bookings.some((b) => {
360
- if (b.professionalId !== params.assigneeId) return false;
361
- if (b.status === "cancelled" || b.status === "no_show") return false;
362
- return timeOverlaps(b.startsAt, b.endsAt ?? b.startsAt, startIso, endIso);
363
- });
364
- if (!hasConflict) {
365
- slots.push({ start: startIso, end: endIso });
366
- }
367
- slotStart = new Date(slotStart.getTime() + interval * 6e4);
368
- }
369
- }
370
- return slots;
371
- },
372
- async getSchedules(professionalId) {
373
- if (professionalId) return schedules.filter((s) => s.assigneeId === professionalId);
374
- return [...schedules];
375
- },
376
- async saveSchedule(input) {
377
- const schedule = {
378
- id: uid(),
379
- tenantId: "mock",
380
- kind: "working_hours",
381
- assigneeId: input.assigneeId,
382
- locationId: input.locationId ?? null,
383
- dayOfWeek: input.dayOfWeek ?? null,
384
- specificDate: input.specificDate ?? null,
385
- startsAt: input.startsAt,
386
- endsAt: input.endsAt,
387
- isActive: input.isActive !== false,
388
- metadata: input.metadata ?? {}
389
- };
390
- schedules.push(schedule);
391
- return schedule;
392
- },
393
- async deleteSchedule(id) {
394
- const idx = schedules.findIndex((s) => s.id === id);
395
- if (idx !== -1) schedules.splice(idx, 1);
396
- },
397
- async getProfessionals() {
398
- return [...professionals];
399
- },
400
- async getConfirmationsPending(daysAhead = 2) {
401
- const cutoff = /* @__PURE__ */ new Date();
402
- cutoff.setDate(cutoff.getDate() + daysAhead);
403
- return bookings.filter(
404
- (b) => b.status === "scheduled" && new Date(b.startsAt) <= cutoff && new Date(b.startsAt) >= /* @__PURE__ */ new Date()
405
- );
406
- },
407
- async sendConfirmation(bookingId, channel) {
408
- const booking = bookings.find((b) => b.id === bookingId);
409
- if (!booking) throw new Error("Booking not found");
410
- const confirmations = booking.metadata.confirmations ?? [];
411
- confirmations.push({ channel, sentAt: now(), status: "sent" });
412
- booking.metadata = { ...booking.metadata, confirmations };
413
- },
414
- async completeBooking(bookingId) {
415
- const idx = bookings.findIndex((b) => b.id === bookingId);
416
- if (idx === -1) throw new Error("Booking not found");
417
- bookings[idx] = { ...bookings[idx], status: "completed", updatedAt: now() };
418
- return { booking: bookings[idx], orderId: bookings[idx].orderId ?? "" };
419
- }
420
- };
421
- }
422
- function getClient() {
423
- const client = core.getSupabaseClientOptional();
424
- if (!client) throw new Error("No Supabase client registered \u2014 call setGlobalSupabaseClient() at app startup.");
425
- return client;
426
- }
427
- function toPhoneDigits(phone) {
428
- return (phone ?? "").replace(/\D/g, "");
429
- }
430
- function createSupabasePublicBookingProvider(options) {
431
- const { tenantId } = options;
432
- return {
433
- async getServices() {
434
- const { data, error } = await getClient().from("v_public_services").select("id, name, description, price, currency, duration_minutes, sort_order").eq("tenant_id", tenantId).order("sort_order", { ascending: true });
435
- if (error) throw new Error(`[plugin-agenda/public] getServices: ${error.message}`);
436
- return (data ?? []).map((row) => ({
437
- id: String(row.id),
438
- name: String(row.name),
439
- description: row.description ?? void 0,
440
- price: Number(row.price ?? 0),
441
- durationMinutes: Number(row.duration_minutes ?? 30)
442
- }));
443
- },
444
- async getAvailableSlots(query) {
445
- const { data, error } = await getClient().rpc("get_available_slots", {
446
- p_tenant_id: tenantId,
447
- p_assignee_id: query.assigneeId,
448
- p_date: query.date,
449
- p_duration_minutes: query.durationMinutes,
450
- p_slot_interval: query.slotInterval
451
- });
452
- if (error) throw new Error(`[plugin-agenda/public] getAvailableSlots: ${error.message}`);
453
- return (data ?? []).map((row) => ({
454
- start: row.slot_start,
455
- end: row.slot_end
456
- }));
457
- },
458
- async createBooking(input) {
459
- const phone = toPhoneDigits(input.contact.phone);
460
- const { data, error } = await getClient().rpc("create_public_booking", {
461
- p_tenant_id: tenantId,
462
- p_service_id: input.serviceId,
463
- p_starts_at: input.startsAt,
464
- p_name: input.contact.name.trim(),
465
- p_phone: phone,
466
- p_email: input.contact.email?.trim() || null,
467
- p_notes: input.contact.notes?.trim() || null,
468
- // Only sent when pinned — keeps compatibility with pools still running
469
- // the pre-004 7-arg function (unknown named arg would 404 on PostgREST).
470
- ...input.assigneeId ? { p_assignee_id: input.assigneeId } : {}
471
- });
472
- if (error) throw new Error(`[plugin-agenda/public] createBooking: ${error.message}`);
473
- const row = Array.isArray(data) ? data[0] : data;
474
- if (!row) throw new Error("[plugin-agenda/public] createBooking: empty RPC response");
475
- return { bookingId: row.booking_id, startsAt: row.starts_at, endsAt: row.ends_at };
476
- }
477
- };
478
- }
479
-
480
- exports.createMockAgendaProvider = createMockAgendaProvider;
481
- exports.createSupabasePublicBookingProvider = createSupabasePublicBookingProvider;
482
- //# sourceMappingURL=chunk-RX2HMWXS.cjs.map
483
- //# sourceMappingURL=chunk-RX2HMWXS.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/data/mock.ts","../src/public/data.supabase.ts"],"names":["getSupabaseClientOptional"],"mappings":"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","file":"chunk-RX2HMWXS.cjs","sourcesContent":["import type { AgendaDataProvider } from './types'\nimport type {\n CalendarBooking, Professional, Schedule, TimeSlot,\n CreateBookingInput, UpdateBookingInput, SaveScheduleInput,\n BookingQuery, ConflictCheckParams, AvailableSlotsParams, BookingStatus,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nlet nextId = 1\nfunction uid(): string { return `mock-${nextId++}` }\nfunction now(): string { return new Date().toISOString() }\n\nfunction addMinutes(date: string, minutes: number): string {\n const d = new Date(date)\n d.setMinutes(d.getMinutes() + minutes)\n return d.toISOString()\n}\n\nfunction timeOverlaps(aStart: string, aEnd: string, bStart: string, bEnd: string): boolean {\n return aStart < bEnd && aEnd > bStart\n}\n\n// ---------------------------------------------------------------------------\n// Sample data\n// ---------------------------------------------------------------------------\n\nconst SAMPLE_PROFESSIONALS: Professional[] = [\n { id: 'prof-1', name: 'Ana Silva', avatarUrl: null, locationId: 'loc-1', locationName: 'Main Branch', isActive: true },\n { id: 'prof-2', name: 'Carlos Santos', avatarUrl: null, locationId: 'loc-1', locationName: 'Main Branch', isActive: true },\n { id: 'prof-3', name: 'Maria Oliveira', avatarUrl: null, locationId: 'loc-1', locationName: 'Main Branch', isActive: true },\n]\n\nfunction createSampleBookings(): CalendarBooking[] {\n const today = new Date()\n const year = today.getFullYear()\n const month = today.getMonth()\n const day = today.getDate()\n\n function dt(hour: number, minute = 0): string {\n return new Date(year, month, day, hour, minute).toISOString()\n }\n\n return [\n {\n id: 'bk-1', tenantId: 'mock', kind: 'appointment',\n startsAt: dt(9, 0), endsAt: dt(10, 30), status: 'confirmed',\n notes: null, orderId: 'ord-1', locationId: 'loc-1', metadata: {},\n clientId: 'cl-1', clientName: 'Sarah Johnson', clientPhone: '(11) 99999-1111', clientEmail: 'sarah@email.com', clientAvatarUrl: null,\n professionalId: 'prof-1', professionalName: 'Ana Silva', professionalAvatarUrl: null,\n locationName: 'Main Branch', orderTotal: 250, orderStatus: 'draft',\n services: [{ id: 'bi-1', serviceId: 'svc-1', name: 'Balayage', durationMinutes: 90, price: 250, assigneeId: 'prof-1' }],\n totalDurationMinutes: 90, createdAt: now(), updatedAt: now(),\n },\n {\n id: 'bk-2', tenantId: 'mock', kind: 'appointment',\n startsAt: dt(10, 0), endsAt: dt(11, 0), status: 'scheduled',\n notes: null, orderId: 'ord-2', locationId: 'loc-1', metadata: {},\n clientId: 'cl-2', clientName: 'Emily Carter', clientPhone: '(11) 99999-2222', clientEmail: 'emily@email.com', clientAvatarUrl: null,\n professionalId: 'prof-2', professionalName: 'Carlos Santos', professionalAvatarUrl: null,\n locationName: 'Main Branch', orderTotal: 80, orderStatus: 'draft',\n services: [{ id: 'bi-2', serviceId: 'svc-2', name: 'Haircut', durationMinutes: 60, price: 80, assigneeId: 'prof-2' }],\n totalDurationMinutes: 60, createdAt: now(), updatedAt: now(),\n },\n {\n id: 'bk-3', tenantId: 'mock', kind: 'appointment',\n startsAt: dt(14, 0), endsAt: dt(15, 0), status: 'confirmed',\n notes: 'First visit', orderId: 'ord-3', locationId: 'loc-1', metadata: {},\n clientId: 'cl-3', clientName: 'Rachel Kim', clientPhone: '(11) 99999-3333', clientEmail: null, clientAvatarUrl: null,\n professionalId: 'prof-3', professionalName: 'Maria Oliveira', professionalAvatarUrl: null,\n locationName: 'Main Branch', orderTotal: 120, orderStatus: 'draft',\n services: [{ id: 'bi-3', serviceId: 'svc-3', name: 'Facial Treatment', durationMinutes: 60, price: 120, assigneeId: 'prof-3' }],\n totalDurationMinutes: 60, createdAt: now(), updatedAt: now(),\n },\n {\n id: 'bk-4', tenantId: 'mock', kind: 'appointment',\n startsAt: dt(11, 0), endsAt: dt(12, 0), status: 'scheduled',\n notes: null, orderId: 'ord-4', locationId: 'loc-1', metadata: {},\n clientId: 'cl-4', clientName: 'Jessica Lima', clientPhone: '(11) 99999-4444', clientEmail: null, clientAvatarUrl: null,\n professionalId: 'prof-1', professionalName: 'Ana Silva', professionalAvatarUrl: null,\n locationName: 'Main Branch', orderTotal: 60, orderStatus: 'draft',\n services: [{ id: 'bi-4', serviceId: 'svc-4', name: 'Manicure', durationMinutes: 60, price: 60, assigneeId: 'prof-1' }],\n totalDurationMinutes: 60, createdAt: now(), updatedAt: now(),\n },\n {\n id: 'bk-5', tenantId: 'mock', kind: 'appointment',\n startsAt: dt(15, 30), endsAt: dt(17, 0), status: 'confirmed',\n notes: null, orderId: 'ord-5', locationId: 'loc-1', metadata: {},\n clientId: 'cl-5', clientName: 'Amanda Wright', clientPhone: '(11) 99999-5555', clientEmail: 'amanda@email.com', clientAvatarUrl: null,\n professionalId: 'prof-2', professionalName: 'Carlos Santos', professionalAvatarUrl: null,\n locationName: 'Main Branch', orderTotal: 350, orderStatus: 'draft',\n services: [\n { id: 'bi-5', serviceId: 'svc-1', name: 'Highlights', durationMinutes: 60, price: 200, assigneeId: 'prof-2' },\n { id: 'bi-6', serviceId: 'svc-2', name: 'Haircut', durationMinutes: 30, price: 150, assigneeId: 'prof-2' },\n ],\n totalDurationMinutes: 90, createdAt: now(), updatedAt: now(),\n },\n ]\n}\n\nfunction createSampleSchedules(): Schedule[] {\n const schedules: Schedule[] = []\n for (const prof of SAMPLE_PROFESSIONALS) {\n // Mon-Fri 08:00-12:00, 13:00-18:00\n for (let day = 1; day <= 5; day++) {\n schedules.push({\n id: uid(), tenantId: 'mock', kind: 'working_hours',\n assigneeId: prof.id, locationId: prof.locationId,\n dayOfWeek: day, specificDate: null,\n startsAt: '08:00', endsAt: '12:00', isActive: true, metadata: {},\n })\n schedules.push({\n id: uid(), tenantId: 'mock', kind: 'working_hours',\n assigneeId: prof.id, locationId: prof.locationId,\n dayOfWeek: day, specificDate: null,\n startsAt: '13:00', endsAt: '18:00', isActive: true, metadata: {},\n })\n }\n // Saturday 08:00-14:00\n schedules.push({\n id: uid(), tenantId: 'mock', kind: 'working_hours',\n assigneeId: prof.id, locationId: prof.locationId,\n dayOfWeek: 6, specificDate: null,\n startsAt: '08:00', endsAt: '14:00', isActive: true, metadata: {},\n })\n }\n return schedules\n}\n\n// ---------------------------------------------------------------------------\n// Mock provider factory\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-populated agenda data. Any omitted collection falls back to the built-in\n * salon-style sample. Pass `bookings` (and optionally an empty `professionals`)\n * to render a calendar seeded with app-specific events.\n */\nexport interface MockAgendaSeed {\n bookings?: CalendarBooking[]\n professionals?: Professional[]\n schedules?: Schedule[]\n}\n\nexport interface MockAgendaProviderOptions {\n seed?: MockAgendaSeed\n}\n\nexport function createMockAgendaProvider(options?: MockAgendaProviderOptions): AgendaDataProvider {\n const seed = options?.seed\n const bookings: CalendarBooking[] = seed?.bookings ? [...seed.bookings] : createSampleBookings()\n const schedules: Schedule[] = seed?.schedules ? [...seed.schedules] : createSampleSchedules()\n const professionals: Professional[] = seed?.professionals ? [...seed.professionals] : [...SAMPLE_PROFESSIONALS]\n\n return {\n async getLocations(): Promise<Array<{ id: string; name: string }>> {\n return []\n },\n async getBookings(query: BookingQuery): Promise<CalendarBooking[]> {\n return bookings.filter((b) => {\n if (query.kind && b.kind !== query.kind) return false\n if (query.statuses?.length && !query.statuses.includes(b.status)) return false\n if (query.professionalIds?.length && !query.professionalIds.includes(b.professionalId ?? '')) return false\n if (query.locationId && b.locationId !== query.locationId) return false\n if (query.dateRange) {\n if (b.startsAt > query.dateRange.end || (b.endsAt ?? b.startsAt) < query.dateRange.start) return false\n }\n return true\n })\n },\n\n async getBookingById(id: string): Promise<CalendarBooking | null> {\n return bookings.find((b) => b.id === id) ?? null\n },\n\n async createBooking(input: CreateBookingInput): Promise<CalendarBooking> {\n const totalDuration = input.services.reduce((sum, s) => sum + s.durationMinutes, 0)\n const totalPrice = input.services.reduce((sum, s) => sum + s.price, 0)\n const booking: CalendarBooking = {\n id: uid(), tenantId: 'mock', kind: input.kind ?? 'appointment',\n startsAt: input.startsAt, endsAt: addMinutes(input.startsAt, totalDuration || 30),\n status: 'scheduled', notes: input.notes ?? null,\n orderId: input.services.length > 0 ? uid() : null, locationId: input.locationId ?? null, metadata: {},\n clientId: input.clientId, clientName: input.title?.trim() || 'New Client', clientPhone: null, clientEmail: null, clientAvatarUrl: null,\n professionalId: input.professionalId,\n professionalName: professionals.find((p) => p.id === input.professionalId)?.name ?? 'Unknown',\n professionalAvatarUrl: null,\n locationName: null, orderTotal: totalPrice, orderStatus: 'draft',\n services: input.services.map((s) => ({\n id: uid(), serviceId: s.serviceId, name: s.name,\n durationMinutes: s.durationMinutes, price: s.price,\n assigneeId: s.assigneeId ?? input.professionalId,\n })),\n totalDurationMinutes: totalDuration, createdAt: now(), updatedAt: now(),\n }\n bookings.push(booking)\n return booking\n },\n\n async updateBooking(id: string, data: UpdateBookingInput): Promise<CalendarBooking> {\n const idx = bookings.findIndex((b) => b.id === id)\n if (idx === -1) throw new Error('Booking not found')\n const existing = bookings[idx]\n const serviceUpdates: Partial<CalendarBooking> = {}\n if (data.services) {\n serviceUpdates.services = data.services.map((s) => ({\n id: uid(), serviceId: s.serviceId, name: s.name,\n durationMinutes: s.durationMinutes, price: s.price,\n assigneeId: s.assigneeId ?? existing.professionalId ?? '',\n }))\n serviceUpdates.totalDurationMinutes = data.services.reduce((sum, s) => sum + s.durationMinutes, 0)\n serviceUpdates.orderTotal = data.services.reduce((sum, s) => sum + s.price, 0)\n }\n const updated: CalendarBooking = {\n ...existing,\n ...(data.startsAt && { startsAt: data.startsAt }),\n ...(data.endsAt && { endsAt: data.endsAt }),\n ...(data.title !== undefined && { clientName: data.title }),\n ...(data.status && { status: data.status }),\n ...(data.notes !== undefined && { notes: data.notes ?? null }),\n ...(data.professionalId && { professionalId: data.professionalId }),\n ...(data.clientId && { clientId: data.clientId }),\n ...(data.locationId && { locationId: data.locationId }),\n ...serviceUpdates,\n updatedAt: now(),\n }\n bookings[idx] = updated\n return updated\n },\n\n async deleteBooking(id: string): Promise<void> {\n const idx = bookings.findIndex((b) => b.id === id)\n if (idx !== -1) bookings.splice(idx, 1)\n },\n\n async updateBookingStatus(id: string, status: string): Promise<CalendarBooking> {\n const idx = bookings.findIndex((b) => b.id === id)\n if (idx === -1) throw new Error('Booking not found')\n bookings[idx] = { ...bookings[idx], status: status as BookingStatus, updatedAt: now() }\n return bookings[idx]\n },\n\n async checkConflict(params: ConflictCheckParams): Promise<boolean> {\n return bookings.some((b) => {\n if (params.excludeBookingId && b.id === params.excludeBookingId) return false\n if (b.professionalId !== params.assigneeId) return false\n if (b.status === 'cancelled' || b.status === 'no_show') return false\n return timeOverlaps(b.startsAt, b.endsAt ?? b.startsAt, params.startsAt, params.endsAt)\n })\n },\n\n async getAvailableSlots(params: AvailableSlotsParams): Promise<TimeSlot[]> {\n // Parse 'YYYY-MM-DD' as LOCAL midnight (not UTC) so the weekday and the\n // generated slot instants land on the requested calendar day in any tz.\n const [yy, mm, dd] = params.date.split('-').map(Number)\n const date = new Date(yy, (mm ?? 1) - 1, dd ?? 1)\n const dayOfWeek = date.getDay()\n const interval = params.slotInterval ?? 30\n\n // Get working hours for this professional on this day\n const daySchedules = schedules.filter((s) =>\n s.assigneeId === params.assigneeId && s.isActive &&\n (s.dayOfWeek === dayOfWeek || s.specificDate === params.date)\n )\n\n const slots: TimeSlot[] = []\n for (const sched of daySchedules) {\n const [startH, startM] = sched.startsAt.split(':').map(Number)\n const [endH, endM] = sched.endsAt.split(':').map(Number)\n const schedStart = new Date(date); schedStart.setHours(startH, startM, 0, 0)\n const schedEnd = new Date(date); schedEnd.setHours(endH, endM, 0, 0)\n\n let slotStart = new Date(schedStart)\n while (true) {\n const slotEnd = new Date(slotStart.getTime() + params.durationMinutes * 60000)\n if (slotEnd > schedEnd) break\n const startIso = slotStart.toISOString()\n const endIso = slotEnd.toISOString()\n const hasConflict = bookings.some((b) => {\n if (b.professionalId !== params.assigneeId) return false\n if (b.status === 'cancelled' || b.status === 'no_show') return false\n return timeOverlaps(b.startsAt, b.endsAt ?? b.startsAt, startIso, endIso)\n })\n if (!hasConflict) {\n slots.push({ start: startIso, end: endIso })\n }\n slotStart = new Date(slotStart.getTime() + interval * 60000)\n }\n }\n return slots\n },\n\n async getSchedules(professionalId?: string): Promise<Schedule[]> {\n if (professionalId) return schedules.filter((s) => s.assigneeId === professionalId)\n return [...schedules]\n },\n\n async saveSchedule(input: SaveScheduleInput): Promise<Schedule> {\n const schedule: Schedule = {\n id: uid(), tenantId: 'mock', kind: 'working_hours',\n assigneeId: input.assigneeId, locationId: input.locationId ?? null,\n dayOfWeek: input.dayOfWeek ?? null, specificDate: input.specificDate ?? null,\n startsAt: input.startsAt, endsAt: input.endsAt,\n isActive: input.isActive !== false, metadata: input.metadata ?? {},\n }\n schedules.push(schedule)\n return schedule\n },\n\n async deleteSchedule(id: string): Promise<void> {\n const idx = schedules.findIndex((s) => s.id === id)\n if (idx !== -1) schedules.splice(idx, 1)\n },\n\n async getProfessionals(): Promise<Professional[]> {\n return [...professionals]\n },\n\n async getConfirmationsPending(daysAhead = 2): Promise<CalendarBooking[]> {\n const cutoff = new Date()\n cutoff.setDate(cutoff.getDate() + daysAhead)\n return bookings.filter((b) =>\n b.status === 'scheduled' && new Date(b.startsAt) <= cutoff && new Date(b.startsAt) >= new Date()\n )\n },\n\n async sendConfirmation(bookingId: string, channel: string): Promise<void> {\n const booking = bookings.find((b) => b.id === bookingId)\n if (!booking) throw new Error('Booking not found')\n const confirmations = (booking.metadata.confirmations as any[] ?? [])\n confirmations.push({ channel, sentAt: now(), status: 'sent' })\n booking.metadata = { ...booking.metadata, confirmations }\n },\n\n async completeBooking(bookingId: string): Promise<{ booking: CalendarBooking; orderId: string }> {\n const idx = bookings.findIndex((b) => b.id === bookingId)\n if (idx === -1) throw new Error('Booking not found')\n bookings[idx] = { ...bookings[idx], status: 'completed', updatedAt: now() }\n return { booking: bookings[idx], orderId: bookings[idx].orderId ?? '' }\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Supabase implementation of PublicBookingDataProvider — anon-safe.\n//\n// Public booking pages run with the publishable (anon) key and NO user session,\n// so the canonical tenant RLS (`tenant_id IN user_tenant_ids()`) yields nothing.\n// This provider therefore only touches objects designed for anon access:\n// - public.v_public_services (column-whitelisted view of active services)\n// - public.get_available_slots (SECURITY DEFINER RPC, mirrors the mock algo)\n// - public.create_public_booking (SECURITY DEFINER RPC: validates the slot,\n// upserts the customer person, writes order+booking+order_item atomically)\n// Shipped by plugins/plugin-agenda/src/migrations/001_public_booking.sql.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport type { TimeSlot } from '../types'\nimport type { PublicService } from './types'\nimport type { PublicBookingDataProvider } from './data'\n\nexport interface SupabasePublicBookingOptions {\n /** The tenant this public page books against (RPC arg — required). */\n tenantId: string\n}\n\ninterface SupabaseLikeClient {\n from(table: string): any\n rpc(fn: string, args: Record<string, unknown>): PromiseLike<{ data: unknown; error: { message: string } | null }>\n}\n\nfunction getClient(): SupabaseLikeClient {\n const client = getSupabaseClientOptional()\n if (!client) throw new Error('No Supabase client registered — call setGlobalSupabaseClient() at app startup.')\n return client as SupabaseLikeClient\n}\n\n/** Digits-only phone (RPC normalizes again server-side; keep the dial code). */\nfunction toPhoneDigits(phone: string | undefined): string {\n return (phone ?? '').replace(/\\D/g, '')\n}\n\nexport function createSupabasePublicBookingProvider(options: SupabasePublicBookingOptions): PublicBookingDataProvider {\n const { tenantId } = options\n\n return {\n async getServices(): Promise<PublicService[]> {\n const { data, error } = await getClient()\n .from('v_public_services')\n .select('id, name, description, price, currency, duration_minutes, sort_order')\n .eq('tenant_id', tenantId)\n .order('sort_order', { ascending: true })\n if (error) throw new Error(`[plugin-agenda/public] getServices: ${error.message}`)\n return ((data ?? []) as Array<Record<string, unknown>>).map((row) => ({\n id: String(row.id),\n name: String(row.name),\n description: (row.description as string | null) ?? undefined,\n price: Number(row.price ?? 0),\n durationMinutes: Number(row.duration_minutes ?? 30),\n }))\n },\n\n async getAvailableSlots(query): Promise<TimeSlot[]> {\n const { data, error } = await getClient().rpc('get_available_slots', {\n p_tenant_id: tenantId,\n p_assignee_id: query.assigneeId,\n p_date: query.date,\n p_duration_minutes: query.durationMinutes,\n p_slot_interval: query.slotInterval,\n })\n if (error) throw new Error(`[plugin-agenda/public] getAvailableSlots: ${error.message}`)\n return ((data ?? []) as Array<{ slot_start: string; slot_end: string }>).map((row) => ({\n start: row.slot_start,\n end: row.slot_end,\n }))\n },\n\n async createBooking(input) {\n const phone = toPhoneDigits(input.contact.phone)\n const { data, error } = await getClient().rpc('create_public_booking', {\n p_tenant_id: tenantId,\n p_service_id: input.serviceId,\n p_starts_at: input.startsAt,\n p_name: input.contact.name.trim(),\n p_phone: phone,\n p_email: input.contact.email?.trim() || null,\n p_notes: input.contact.notes?.trim() || null,\n // Only sent when pinned — keeps compatibility with pools still running\n // the pre-004 7-arg function (unknown named arg would 404 on PostgREST).\n ...(input.assigneeId ? { p_assignee_id: input.assigneeId } : {}),\n })\n if (error) throw new Error(`[plugin-agenda/public] createBooking: ${error.message}`)\n const row = (Array.isArray(data) ? data[0] : data) as\n | { booking_id: string; starts_at: string; ends_at: string }\n | undefined\n if (!row) throw new Error('[plugin-agenda/public] createBooking: empty RPC response')\n return { bookingId: row.booking_id, startsAt: row.starts_at, endsAt: row.ends_at }\n },\n }\n}\n"]}