@fayz-ai/plugin-tables 0.2.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/TablesContext.d.ts +35 -0
  4. package/dist/TablesContext.d.ts.map +1 -0
  5. package/dist/TablesPage-5GCFGSX3.cjs +276 -0
  6. package/dist/TablesPage-5GCFGSX3.cjs.map +1 -0
  7. package/dist/TablesPage-IM732NWO.js +274 -0
  8. package/dist/TablesPage-IM732NWO.js.map +1 -0
  9. package/dist/TablesPage.d.ts +13 -0
  10. package/dist/TablesPage.d.ts.map +1 -0
  11. package/dist/data/fayz.d.ts +15 -0
  12. package/dist/data/fayz.d.ts.map +1 -0
  13. package/dist/data/mock.d.ts +3 -0
  14. package/dist/data/mock.d.ts.map +1 -0
  15. package/dist/data/types.d.ts +24 -0
  16. package/dist/data/types.d.ts.map +1 -0
  17. package/dist/index.cjs +887 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.ts +33 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +880 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/locales/en.d.ts +2 -0
  24. package/dist/locales/en.d.ts.map +1 -0
  25. package/dist/locales/index.d.ts +2 -0
  26. package/dist/locales/index.d.ts.map +1 -0
  27. package/dist/locales/pt-BR.d.ts +2 -0
  28. package/dist/locales/pt-BR.d.ts.map +1 -0
  29. package/dist/registries.d.ts +3 -0
  30. package/dist/registries.d.ts.map +1 -0
  31. package/dist/store.d.ts +28 -0
  32. package/dist/store.d.ts.map +1 -0
  33. package/dist/types.d.ts +84 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/views/FloorPlanView.d.ts +3 -0
  36. package/dist/views/FloorPlanView.d.ts.map +1 -0
  37. package/package.json +55 -0
  38. package/src/TablesContext.tsx +41 -0
  39. package/src/TablesPage.tsx +20 -0
  40. package/src/data/fayz.ts +348 -0
  41. package/src/data/mock.ts +322 -0
  42. package/src/data/types.ts +31 -0
  43. package/src/index.ts +179 -0
  44. package/src/locales/en.ts +45 -0
  45. package/src/locales/index.ts +4 -0
  46. package/src/locales/pt-BR.ts +45 -0
  47. package/src/registries.ts +32 -0
  48. package/src/store.ts +178 -0
  49. package/src/types.ts +116 -0
  50. package/src/views/FloorPlanView.tsx +388 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,887 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var vanilla = require('zustand/vanilla');
5
+ var saas = require('@fayz-ai/saas');
6
+ var sonner = require('sonner');
7
+ var sdk = require('@fayz-ai/sdk');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var React__default = /*#__PURE__*/_interopDefault(React);
12
+
13
+ // src/index.ts
14
+
15
+ // src/data/mock.ts
16
+ var uid = 0;
17
+ function nextId(prefix) {
18
+ return prefix + "-" + ++uid;
19
+ }
20
+ var TENANT = "mock-tenant";
21
+ var NOW = (/* @__PURE__ */ new Date()).toISOString();
22
+ function makeTable(id, number, seats, zone, zoneName, shape, gridCol, gridRow) {
23
+ return {
24
+ id,
25
+ name: `Table ${number}`,
26
+ number,
27
+ seats,
28
+ status: "available",
29
+ zone,
30
+ zoneName,
31
+ shape,
32
+ gridCol,
33
+ gridRow,
34
+ isActive: true,
35
+ tenantId: TENANT,
36
+ createdAt: NOW,
37
+ updatedAt: NOW
38
+ };
39
+ }
40
+ function seedZones() {
41
+ return [
42
+ { id: nextId("zone"), name: "Indoor", color: "#3b82f6", sortOrder: 0, isActive: true, tenantId: TENANT, createdAt: NOW },
43
+ { id: nextId("zone"), name: "Outdoor", color: "#22c55e", sortOrder: 1, isActive: true, tenantId: TENANT, createdAt: NOW },
44
+ { id: nextId("zone"), name: "Bar", color: "#f59e0b", sortOrder: 2, isActive: true, tenantId: TENANT, createdAt: NOW }
45
+ ];
46
+ }
47
+ function seedTables(zones) {
48
+ const [indoor, outdoor, bar] = zones;
49
+ const tables = [];
50
+ for (let i = 0; i < 8; i++) {
51
+ const num = i + 1;
52
+ tables.push(makeTable(
53
+ nextId("table"),
54
+ num,
55
+ i % 2 === 0 ? 4 : 6,
56
+ indoor.id,
57
+ indoor.name,
58
+ i % 2 === 0 ? "square" : "rectangle",
59
+ i % 4,
60
+ Math.floor(i / 4)
61
+ ));
62
+ }
63
+ tables.push(makeTable(nextId("table"), 9, 4, outdoor.id, outdoor.name, "round", 0, 0));
64
+ tables.push(makeTable(nextId("table"), 10, 4, outdoor.id, outdoor.name, "round", 1, 0));
65
+ tables.push(makeTable(nextId("table"), 11, 2, bar.id, bar.name, "bar", 0, 0));
66
+ tables.push(makeTable(nextId("table"), 12, 2, bar.id, bar.name, "bar", 1, 0));
67
+ return tables;
68
+ }
69
+ function createMockTablesProvider() {
70
+ const zones = seedZones();
71
+ const tables = seedTables(zones);
72
+ const sessions = [];
73
+ function findTable(id) {
74
+ return tables.find((t) => t.id === id) ?? null;
75
+ }
76
+ function matchesQuery(table, query) {
77
+ if (!query) return true;
78
+ if (query.zone && table.zone !== query.zone) return false;
79
+ if (query.status) {
80
+ const statuses = Array.isArray(query.status) ? query.status : [query.status];
81
+ if (!statuses.includes(table.status)) return false;
82
+ }
83
+ if (query.search) {
84
+ const s = query.search.toLowerCase();
85
+ if (!table.name.toLowerCase().includes(s) && !String(table.number).includes(s) && !(table.zoneName ?? "").toLowerCase().includes(s)) return false;
86
+ }
87
+ return true;
88
+ }
89
+ return {
90
+ // ---- Tables ----
91
+ async getTables(query) {
92
+ return tables.filter((t) => matchesQuery(t, query));
93
+ },
94
+ async getTableById(id) {
95
+ return findTable(id);
96
+ },
97
+ async createTable(input) {
98
+ const zone = zones.find((z) => z.id === input.zone);
99
+ const table = {
100
+ id: nextId("table"),
101
+ name: input.name,
102
+ number: input.number,
103
+ seats: input.seats,
104
+ status: "available",
105
+ zone: input.zone,
106
+ zoneName: zone?.name,
107
+ shape: input.shape ?? "square",
108
+ gridCol: input.gridCol ?? 0,
109
+ gridRow: input.gridRow ?? 0,
110
+ isActive: true,
111
+ tenantId: TENANT,
112
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
113
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
114
+ };
115
+ tables.push(table);
116
+ return table;
117
+ },
118
+ async updateTable(id, data) {
119
+ const table = findTable(id);
120
+ if (!table) throw new Error(`Table ${id} not found`);
121
+ Object.assign(table, data, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
122
+ return table;
123
+ },
124
+ async deleteTable(id) {
125
+ const idx = tables.findIndex((t) => t.id === id);
126
+ if (idx !== -1) tables.splice(idx, 1);
127
+ },
128
+ async updateTableStatus(input) {
129
+ const table = findTable(input.tableId);
130
+ if (!table) throw new Error(`Table ${input.tableId} not found`);
131
+ table.status = input.status;
132
+ table.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
133
+ return table;
134
+ },
135
+ async updateTablePositions(positions) {
136
+ for (const pos of positions) {
137
+ const table = findTable(pos.id);
138
+ if (table) {
139
+ table.gridCol = pos.gridCol;
140
+ table.gridRow = pos.gridRow;
141
+ table.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
142
+ }
143
+ }
144
+ },
145
+ // ---- Sessions ----
146
+ async seatGuests(input) {
147
+ const table = findTable(input.tableId);
148
+ if (!table) throw new Error(`Table ${input.tableId} not found`);
149
+ const session = {
150
+ id: nextId("session"),
151
+ tableId: input.tableId,
152
+ tableName: table.name,
153
+ guests: input.guests,
154
+ waiterId: input.waiterId,
155
+ waiterName: input.waiterId ? `Waiter ${input.waiterId}` : void 0,
156
+ seatedAt: (/* @__PURE__ */ new Date()).toISOString(),
157
+ status: "active",
158
+ notes: input.notes,
159
+ tenantId: TENANT,
160
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
161
+ };
162
+ sessions.push(session);
163
+ table.status = "occupied";
164
+ table.currentSessionId = session.id;
165
+ table.currentGuests = input.guests;
166
+ table.currentWaiterName = session.waiterName;
167
+ table.currentElapsedMinutes = 0;
168
+ table.currentTotal = 0;
169
+ table.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
170
+ return session;
171
+ },
172
+ async closeSession(sessionId) {
173
+ const session = sessions.find((s) => s.id === sessionId);
174
+ if (!session) throw new Error(`Session ${sessionId} not found`);
175
+ session.status = "closed";
176
+ session.closedAt = (/* @__PURE__ */ new Date()).toISOString();
177
+ const table = findTable(session.tableId);
178
+ if (table) {
179
+ table.status = "cleaning";
180
+ table.currentSessionId = void 0;
181
+ table.currentOrderId = void 0;
182
+ table.currentGuests = void 0;
183
+ table.currentWaiterName = void 0;
184
+ table.currentElapsedMinutes = void 0;
185
+ table.currentTotal = void 0;
186
+ table.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
187
+ }
188
+ return session;
189
+ },
190
+ async getActiveSessions() {
191
+ return sessions.filter((s) => s.status === "active");
192
+ },
193
+ async getSessionHistory(tableId) {
194
+ const closed = sessions.filter((s) => s.status === "closed");
195
+ if (tableId) return closed.filter((s) => s.tableId === tableId);
196
+ return closed;
197
+ },
198
+ // ---- Zones ----
199
+ async getZones() {
200
+ return [...zones];
201
+ },
202
+ async createZone(data) {
203
+ const zone = {
204
+ id: nextId("zone"),
205
+ name: data.name ?? "New Zone",
206
+ color: data.color,
207
+ sortOrder: data.sortOrder ?? zones.length,
208
+ isActive: data.isActive ?? true,
209
+ tenantId: TENANT,
210
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
211
+ };
212
+ zones.push(zone);
213
+ return zone;
214
+ },
215
+ async updateZone(id, data) {
216
+ const zone = zones.find((z) => z.id === id);
217
+ if (!zone) throw new Error(`Zone ${id} not found`);
218
+ Object.assign(zone, data);
219
+ return zone;
220
+ },
221
+ async deleteZone(id) {
222
+ const idx = zones.findIndex((z) => z.id === id);
223
+ if (idx !== -1) zones.splice(idx, 1);
224
+ },
225
+ // ---- Summary ----
226
+ async getSummary() {
227
+ const available = tables.filter((t) => t.status === "available").length;
228
+ const occupied = tables.filter((t) => t.status === "occupied").length;
229
+ const reserved = tables.filter((t) => t.status === "reserved").length;
230
+ const cleaning = tables.filter((t) => t.status === "cleaning").length;
231
+ const totalSeats = tables.reduce((sum, t) => sum + t.seats, 0);
232
+ const occupiedSeats = tables.filter((t) => t.status === "occupied").reduce((sum, t) => sum + (t.currentGuests ?? t.seats), 0);
233
+ const closedSessions = sessions.filter((s) => s.status === "closed" && s.closedAt);
234
+ let averageSessionMinutes = 0;
235
+ if (closedSessions.length > 0) {
236
+ const totalMinutes = closedSessions.reduce((sum, s) => {
237
+ const seated = new Date(s.seatedAt).getTime();
238
+ const closed = new Date(s.closedAt).getTime();
239
+ return sum + (closed - seated) / 6e4;
240
+ }, 0);
241
+ averageSessionMinutes = Math.round(totalMinutes / closedSessions.length);
242
+ }
243
+ return {
244
+ totalTables: tables.length,
245
+ availableCount: available,
246
+ occupiedCount: occupied,
247
+ reservedCount: reserved,
248
+ cleaningCount: cleaning,
249
+ totalSeats,
250
+ occupiedSeats,
251
+ averageSessionMinutes
252
+ };
253
+ }
254
+ };
255
+ }
256
+ function createTablesStore(provider) {
257
+ return vanilla.createStore((set, get) => ({
258
+ tables: [],
259
+ tablesLoading: false,
260
+ zones: [],
261
+ zonesLoading: false,
262
+ activeSessions: [],
263
+ sessionsLoading: false,
264
+ sessionHistory: [],
265
+ historyLoading: false,
266
+ summary: null,
267
+ summaryLoading: false,
268
+ selectedTableId: null,
269
+ async fetchTables(query) {
270
+ return saas.dedup("tables:tables:" + JSON.stringify(query), async () => {
271
+ set({ tablesLoading: true });
272
+ const tables = await provider.getTables(query);
273
+ set({ tables, tablesLoading: false });
274
+ });
275
+ },
276
+ async fetchZones() {
277
+ return saas.dedup("tables:zones", async () => {
278
+ set({ zonesLoading: true });
279
+ const zones = await provider.getZones();
280
+ set({ zones, zonesLoading: false });
281
+ });
282
+ },
283
+ async fetchActiveSessions() {
284
+ return saas.dedup("tables:activeSessions", async () => {
285
+ set({ sessionsLoading: true });
286
+ const activeSessions = await provider.getActiveSessions();
287
+ set({ activeSessions, sessionsLoading: false });
288
+ });
289
+ },
290
+ async fetchSessionHistory(tableId) {
291
+ return saas.dedup("tables:history:" + (tableId ?? ""), async () => {
292
+ set({ historyLoading: true });
293
+ const sessionHistory = await provider.getSessionHistory(tableId);
294
+ set({ sessionHistory, historyLoading: false });
295
+ });
296
+ },
297
+ async fetchSummary() {
298
+ return saas.dedup("tables:summary", async () => {
299
+ set({ summaryLoading: true });
300
+ const summary = await provider.getSummary();
301
+ set({ summary, summaryLoading: false });
302
+ });
303
+ },
304
+ selectTable(id) {
305
+ set({ selectedTableId: id });
306
+ },
307
+ async seatGuests(input) {
308
+ try {
309
+ const session = await provider.seatGuests(input);
310
+ const [tables, activeSessions, summary] = await Promise.all([
311
+ provider.getTables(),
312
+ provider.getActiveSessions(),
313
+ provider.getSummary()
314
+ ]);
315
+ set({ tables, activeSessions, summary });
316
+ sonner.toast.success("Guests seated");
317
+ return session;
318
+ } catch (err) {
319
+ sonner.toast.error("Failed to seat guests", { description: err?.message });
320
+ throw err;
321
+ }
322
+ },
323
+ async closeSession(sessionId) {
324
+ try {
325
+ await provider.closeSession(sessionId);
326
+ const [tables, activeSessions, summary] = await Promise.all([
327
+ provider.getTables(),
328
+ provider.getActiveSessions(),
329
+ provider.getSummary()
330
+ ]);
331
+ set({ tables, activeSessions, summary });
332
+ sonner.toast.success("Session closed");
333
+ } catch (err) {
334
+ sonner.toast.error("Failed to close session", { description: err?.message });
335
+ throw err;
336
+ }
337
+ },
338
+ async updateTableStatus(input) {
339
+ try {
340
+ await provider.updateTableStatus(input);
341
+ const [tables, summary] = await Promise.all([
342
+ provider.getTables(),
343
+ provider.getSummary()
344
+ ]);
345
+ set({ tables, summary });
346
+ sonner.toast.success("Table status updated");
347
+ } catch (err) {
348
+ sonner.toast.error("Failed to update table status", { description: err?.message });
349
+ throw err;
350
+ }
351
+ },
352
+ async createTable(input) {
353
+ try {
354
+ const table = await provider.createTable(input);
355
+ const [tables, summary] = await Promise.all([
356
+ provider.getTables(),
357
+ provider.getSummary()
358
+ ]);
359
+ set({ tables, summary });
360
+ sonner.toast.success("Table created");
361
+ return table;
362
+ } catch (err) {
363
+ sonner.toast.error("Failed to create table", { description: err?.message });
364
+ throw err;
365
+ }
366
+ }
367
+ }));
368
+ }
369
+
370
+ // src/registries.ts
371
+ var zoneEntity = {
372
+ name: "Zone",
373
+ namePlural: "Zones",
374
+ icon: "MapPin",
375
+ displayField: "name",
376
+ defaultSort: "sortOrder",
377
+ fields: [
378
+ { key: "name", label: "Name", type: "text", required: true, showInTable: true },
379
+ { key: "color", label: "Color", type: "text", showInTable: true },
380
+ { key: "sortOrder", label: "Order", type: "number", showInTable: true, defaultValue: 0 },
381
+ { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
382
+ ],
383
+ data: { table: "restaurant_zones", tenantScoped: true }
384
+ };
385
+ var tablesRegistries = [
386
+ {
387
+ id: "zones",
388
+ entity: zoneEntity,
389
+ icon: "MapPin",
390
+ description: "Floor plan zones (indoor, outdoor, bar, etc.)",
391
+ display: "table",
392
+ seedData: [
393
+ { id: "z-indoor", name: "Indoor", color: "#3b82f6", sortOrder: 0, isActive: true },
394
+ { id: "z-outdoor", name: "Outdoor", color: "#22c55e", sortOrder: 1, isActive: true },
395
+ { id: "z-bar", name: "Bar", color: "#f59e0b", sortOrder: 2, isActive: true }
396
+ ]
397
+ }
398
+ ];
399
+
400
+ // src/locales/en.ts
401
+ var en = {
402
+ "tables.title": "Tables",
403
+ "tables.nav.floorPlan": "Floor Plan",
404
+ "tables.nav.zones": "Zones",
405
+ "tables.nav.history": "Session History",
406
+ "tables.floorPlan.title": "Floor Plan",
407
+ "tables.floorPlan.subtitle": "Manage table layout and seating",
408
+ "tables.floorPlan.available": "Available",
409
+ "tables.floorPlan.occupied": "Occupied",
410
+ "tables.floorPlan.reserved": "Reserved",
411
+ "tables.floorPlan.cleaning": "Cleaning",
412
+ "tables.floorPlan.seats": "seats",
413
+ "tables.floorPlan.noTables": "No tables configured",
414
+ "tables.floorPlan.noTablesDesc": "Add your first table to get started",
415
+ "tables.floorPlan.addTable": "Add Table",
416
+ "tables.floorPlan.occupiedOf": "{occupied} occupied, {available} available of {total} tables",
417
+ "tables.floorPlan.selectTable": "Select a table",
418
+ "tables.floorPlan.selectTableDesc": "Click on any table to see details and actions",
419
+ "tables.floorPlan.min": "min",
420
+ "tables.floorPlan.zone": "Zone",
421
+ "tables.floorPlan.order": "Order",
422
+ "tables.floorPlan.checkIn": "Check In",
423
+ "tables.detail.currentSession": "Current Session",
424
+ "tables.detail.guests": "Guests",
425
+ "tables.detail.waiter": "Waiter",
426
+ "tables.detail.elapsed": "Elapsed",
427
+ "tables.detail.total": "Running Total",
428
+ "tables.detail.viewOrder": "View Order",
429
+ "tables.detail.seatGuests": "Seat Guests",
430
+ "tables.detail.closeTable": "Close Table",
431
+ "tables.detail.markClean": "Mark Ready",
432
+ "tables.detail.cancel": "Cancel",
433
+ "tables.seat.title": "Seat Guests",
434
+ "tables.seat.guestCount": "Number of guests",
435
+ "tables.seat.waiter": "Waiter",
436
+ "tables.seat.notes": "Notes",
437
+ "tables.seat.confirm": "Seat",
438
+ "tables.history.title": "Session History",
439
+ "tables.history.table": "Table",
440
+ "tables.history.guests": "Guests",
441
+ "tables.history.waiter": "Waiter",
442
+ "tables.history.duration": "Duration",
443
+ "tables.history.noSessions": "No session history yet",
444
+ "tables.settings.title": "Tables Settings"
445
+ };
446
+
447
+ // src/locales/pt-BR.ts
448
+ var ptBR = {
449
+ "tables.title": "Mesas",
450
+ "tables.nav.floorPlan": "Mapa de Mesas",
451
+ "tables.nav.zones": "\xC1reas",
452
+ "tables.nav.history": "Hist\xF3rico",
453
+ "tables.floorPlan.title": "Mapa de Mesas",
454
+ "tables.floorPlan.subtitle": "Gerencie o layout e a ocupa\xE7\xE3o das mesas",
455
+ "tables.floorPlan.available": "Dispon\xEDvel",
456
+ "tables.floorPlan.occupied": "Ocupada",
457
+ "tables.floorPlan.reserved": "Reservada",
458
+ "tables.floorPlan.cleaning": "Limpeza",
459
+ "tables.floorPlan.seats": "lugares",
460
+ "tables.floorPlan.noTables": "Nenhuma mesa configurada",
461
+ "tables.floorPlan.noTablesDesc": "Adicione a primeira mesa para come\xE7ar",
462
+ "tables.floorPlan.addTable": "Adicionar Mesa",
463
+ "tables.floorPlan.occupiedOf": "{occupied} ocupadas, {available} dispon\xEDveis de {total} mesas",
464
+ "tables.floorPlan.selectTable": "Selecione uma mesa",
465
+ "tables.floorPlan.selectTableDesc": "Clique em uma mesa para ver detalhes e a\xE7\xF5es",
466
+ "tables.floorPlan.min": "min",
467
+ "tables.floorPlan.zone": "\xC1rea",
468
+ "tables.floorPlan.order": "Pedido",
469
+ "tables.floorPlan.checkIn": "Check-in",
470
+ "tables.detail.currentSession": "Sess\xE3o Atual",
471
+ "tables.detail.guests": "Pessoas",
472
+ "tables.detail.waiter": "Gar\xE7om",
473
+ "tables.detail.elapsed": "Tempo",
474
+ "tables.detail.total": "Total Parcial",
475
+ "tables.detail.viewOrder": "Ver Pedido",
476
+ "tables.detail.seatGuests": "Sentar Clientes",
477
+ "tables.detail.closeTable": "Fechar Mesa",
478
+ "tables.detail.markClean": "Marcar Pronta",
479
+ "tables.detail.cancel": "Cancelar",
480
+ "tables.seat.title": "Sentar Clientes",
481
+ "tables.seat.guestCount": "N\xFAmero de pessoas",
482
+ "tables.seat.waiter": "Gar\xE7om",
483
+ "tables.seat.notes": "Observa\xE7\xF5es",
484
+ "tables.seat.confirm": "Sentar",
485
+ "tables.history.title": "Hist\xF3rico de Sess\xF5es",
486
+ "tables.history.table": "Mesa",
487
+ "tables.history.guests": "Pessoas",
488
+ "tables.history.waiter": "Gar\xE7om",
489
+ "tables.history.duration": "Dura\xE7\xE3o",
490
+ "tables.history.noSessions": "Nenhum hist\xF3rico de sess\xF5es",
491
+ "tables.settings.title": "Configura\xE7\xF5es de Mesas"
492
+ };
493
+
494
+ // src/locales/index.ts
495
+ var tablesLocales = { en, "pt-BR": ptBR };
496
+ var DEFAULT_TABLE_NAME = "restaurant_tables";
497
+ function nowIso() {
498
+ return (/* @__PURE__ */ new Date()).toISOString();
499
+ }
500
+ function rowToTable(row) {
501
+ const number = row.number ?? 0;
502
+ const zone = row.zone ?? "indoor";
503
+ return {
504
+ id: row.id,
505
+ name: row.name ?? `Table ${number}`,
506
+ number,
507
+ seats: row.seats ?? 4,
508
+ status: row.status ?? "available",
509
+ zone,
510
+ zoneName: row.zone_name ?? zone,
511
+ shape: row.shape ?? "square",
512
+ gridCol: row.grid_col ?? 0,
513
+ gridRow: row.grid_row ?? 0,
514
+ isActive: row.is_active ?? true,
515
+ currentSessionId: row.current_session_id ?? void 0,
516
+ currentOrderId: row.current_order_id ?? void 0,
517
+ currentGuests: row.current_guests ?? void 0,
518
+ currentWaiterName: row.current_waiter_name ?? void 0,
519
+ currentElapsedMinutes: row.current_elapsed_minutes ?? void 0,
520
+ currentTotal: row.current_total ?? void 0,
521
+ metadata: row.metadata ?? {},
522
+ tenantId: row.tenant_id ?? "runtime-tenant",
523
+ createdAt: row.created_at ?? nowIso(),
524
+ updatedAt: row.updated_at ?? row.created_at ?? nowIso()
525
+ };
526
+ }
527
+ function tableCreatePayload(input) {
528
+ return {
529
+ name: input.name,
530
+ number: input.number,
531
+ seats: input.seats,
532
+ zone: input.zone,
533
+ shape: input.shape ?? "square",
534
+ grid_col: input.gridCol ?? 0,
535
+ grid_row: input.gridRow ?? 0,
536
+ status: "available",
537
+ is_active: true
538
+ };
539
+ }
540
+ function tableUpdatePayload(data) {
541
+ const payload = {};
542
+ if (data.name !== void 0) payload.name = data.name;
543
+ if (data.number !== void 0) payload.number = data.number;
544
+ if (data.seats !== void 0) payload.seats = data.seats;
545
+ if (data.status !== void 0) payload.status = data.status;
546
+ if (data.zone !== void 0) payload.zone = data.zone;
547
+ if (data.zoneName !== void 0) payload.zone_name = data.zoneName;
548
+ if (data.shape !== void 0) payload.shape = data.shape;
549
+ if (data.gridCol !== void 0) payload.grid_col = data.gridCol;
550
+ if (data.gridRow !== void 0) payload.grid_row = data.gridRow;
551
+ if (data.isActive !== void 0) payload.is_active = data.isActive;
552
+ if (data.currentSessionId !== void 0) payload.current_session_id = data.currentSessionId;
553
+ if (data.currentOrderId !== void 0) payload.current_order_id = data.currentOrderId;
554
+ if (data.currentGuests !== void 0) payload.current_guests = data.currentGuests;
555
+ if (data.currentWaiterName !== void 0) payload.current_waiter_name = data.currentWaiterName;
556
+ if (data.currentElapsedMinutes !== void 0) payload.current_elapsed_minutes = data.currentElapsedMinutes;
557
+ if (data.currentTotal !== void 0) payload.current_total = data.currentTotal;
558
+ if (data.metadata !== void 0) payload.metadata = data.metadata;
559
+ return payload;
560
+ }
561
+ function filtersFromQuery(query) {
562
+ const filters = [{ column: "is_active", operator: "eq", value: true }];
563
+ if (!query) return filters;
564
+ if (query.zone) filters.push({ column: "zone", operator: "eq", value: query.zone });
565
+ if (query.status && !Array.isArray(query.status)) {
566
+ filters.push({ column: "status", operator: "eq", value: query.status });
567
+ }
568
+ return filters;
569
+ }
570
+ function matchesClientQuery(table, query) {
571
+ if (!query) return true;
572
+ if (Array.isArray(query.status) && !query.status.includes(table.status)) return false;
573
+ if (query.search) {
574
+ const search = query.search.toLowerCase();
575
+ return table.name.toLowerCase().includes(search) || String(table.number).includes(search) || (table.zoneName?.toLowerCase().includes(search) ?? false);
576
+ }
577
+ return true;
578
+ }
579
+ function summarize(tables) {
580
+ const availableCount = tables.filter((table) => table.status === "available").length;
581
+ const occupiedCount = tables.filter((table) => table.status === "occupied").length;
582
+ const reservedCount = tables.filter((table) => table.status === "reserved").length;
583
+ const cleaningCount = tables.filter((table) => table.status === "cleaning").length;
584
+ const totalSeats = tables.reduce((sum, table) => sum + table.seats, 0);
585
+ const occupiedSeats = tables.filter((table) => table.status === "occupied").reduce((sum, table) => sum + (table.currentGuests ?? table.seats), 0);
586
+ return {
587
+ totalTables: tables.length,
588
+ availableCount,
589
+ occupiedCount,
590
+ reservedCount,
591
+ cleaningCount,
592
+ totalSeats,
593
+ occupiedSeats,
594
+ averageSessionMinutes: 0
595
+ };
596
+ }
597
+ function createFayzTablesProvider(options = {}) {
598
+ const client = sdk.createFayzClient(options);
599
+ const table = options.tableName ?? DEFAULT_TABLE_NAME;
600
+ const baseOptions = {
601
+ projectId: options.projectId,
602
+ table,
603
+ schema: options.schema,
604
+ runtime: options.runtime
605
+ };
606
+ function resolveTenant() {
607
+ const value = typeof options.tenantId === "function" ? options.tenantId() : options.tenantId;
608
+ return value ?? void 0;
609
+ }
610
+ function withTenant(filters) {
611
+ const tenantId = resolveTenant();
612
+ return tenantId ? [{ column: "tenant_id", operator: "eq", value: tenantId }, ...filters] : filters;
613
+ }
614
+ async function listTables(query) {
615
+ const response = await client.data.listRows({
616
+ ...baseOptions,
617
+ filters: withTenant(filtersFromQuery(query)),
618
+ sortColumn: "number",
619
+ sortDirection: "asc",
620
+ limit: 500
621
+ });
622
+ return response.rows.map(rowToTable).filter((item) => matchesClientQuery(item, query));
623
+ }
624
+ async function getById(id) {
625
+ const response = await client.data.listRows({
626
+ ...baseOptions,
627
+ filters: withTenant([{ column: "id", operator: "eq", value: id }]),
628
+ limit: 1
629
+ });
630
+ return response.rows[0] ? rowToTable(response.rows[0]) : null;
631
+ }
632
+ return {
633
+ async getTables(query) {
634
+ return listTables(query);
635
+ },
636
+ async getTableById(id) {
637
+ return getById(id);
638
+ },
639
+ async createTable(input) {
640
+ const tenantId = resolveTenant();
641
+ const row = await client.data.createRow({
642
+ ...baseOptions,
643
+ row: tenantId ? { ...tableCreatePayload(input), tenant_id: tenantId } : tableCreatePayload(input)
644
+ });
645
+ return rowToTable(row);
646
+ },
647
+ async updateTable(id, data) {
648
+ const row = await client.data.updateRow({
649
+ ...baseOptions,
650
+ primaryKeys: { id },
651
+ row: tableUpdatePayload(data)
652
+ });
653
+ return rowToTable(row);
654
+ },
655
+ async deleteTable(id) {
656
+ await client.data.deleteRows({ ...baseOptions, rows: [{ id }] });
657
+ },
658
+ async updateTableStatus(input) {
659
+ return this.updateTable(input.tableId, { status: input.status });
660
+ },
661
+ async updateTablePositions(positions) {
662
+ await Promise.all(positions.map((position) => this.updateTable(position.id, {
663
+ gridCol: position.gridCol,
664
+ gridRow: position.gridRow
665
+ })));
666
+ },
667
+ async seatGuests(input) {
668
+ const table2 = await this.updateTable(input.tableId, {
669
+ status: "occupied",
670
+ currentGuests: input.guests,
671
+ currentWaiterName: input.waiterId ? `Waiter ${input.waiterId}` : void 0,
672
+ currentElapsedMinutes: 0,
673
+ currentTotal: 0
674
+ });
675
+ const session = {
676
+ id: table2.currentSessionId ?? `runtime-session-${table2.id}`,
677
+ tableId: table2.id,
678
+ tableName: table2.name,
679
+ guests: input.guests,
680
+ waiterId: input.waiterId,
681
+ waiterName: table2.currentWaiterName,
682
+ seatedAt: nowIso(),
683
+ status: "active",
684
+ notes: input.notes,
685
+ tenantId: table2.tenantId,
686
+ createdAt: nowIso()
687
+ };
688
+ return session;
689
+ },
690
+ async closeSession(sessionId) {
691
+ const tables = await listTables();
692
+ const table2 = tables.find((item) => item.currentSessionId === sessionId);
693
+ if (table2) await this.updateTable(table2.id, {
694
+ status: "cleaning",
695
+ currentSessionId: void 0,
696
+ currentOrderId: void 0,
697
+ currentGuests: void 0,
698
+ currentWaiterName: void 0,
699
+ currentElapsedMinutes: void 0,
700
+ currentTotal: void 0
701
+ });
702
+ return {
703
+ id: sessionId,
704
+ tableId: table2?.id ?? "",
705
+ tableName: table2?.name,
706
+ guests: table2?.currentGuests ?? 0,
707
+ seatedAt: nowIso(),
708
+ closedAt: nowIso(),
709
+ status: "closed",
710
+ tenantId: table2?.tenantId ?? "runtime-tenant",
711
+ createdAt: nowIso()
712
+ };
713
+ },
714
+ async getActiveSessions() {
715
+ const tables = await listTables({ status: "occupied" });
716
+ return tables.map((table2) => ({
717
+ id: table2.currentSessionId ?? `runtime-session-${table2.id}`,
718
+ tableId: table2.id,
719
+ tableName: table2.name,
720
+ guests: table2.currentGuests ?? table2.seats,
721
+ waiterName: table2.currentWaiterName,
722
+ seatedAt: nowIso(),
723
+ status: "active",
724
+ tenantId: table2.tenantId,
725
+ createdAt: nowIso()
726
+ }));
727
+ },
728
+ async getSessionHistory() {
729
+ return [];
730
+ },
731
+ async getZones() {
732
+ const tables = await listTables();
733
+ const zones = /* @__PURE__ */ new Map();
734
+ for (const table2 of tables) {
735
+ if (zones.has(table2.zone)) continue;
736
+ zones.set(table2.zone, {
737
+ id: table2.zone,
738
+ name: table2.zoneName ?? table2.zone,
739
+ sortOrder: zones.size,
740
+ isActive: true,
741
+ tenantId: table2.tenantId,
742
+ createdAt: table2.createdAt
743
+ });
744
+ }
745
+ return [...zones.values()];
746
+ },
747
+ async createZone() {
748
+ throw new Error("Restaurant zone writes require a dedicated Fayz table/broker contract.");
749
+ },
750
+ async updateZone() {
751
+ throw new Error("Restaurant zone writes require a dedicated Fayz table/broker contract.");
752
+ },
753
+ async deleteZone() {
754
+ throw new Error("Restaurant zone writes require a dedicated Fayz table/broker contract.");
755
+ },
756
+ async getSummary() {
757
+ return summarize(await listTables());
758
+ }
759
+ };
760
+ }
761
+
762
+ // src/index.ts
763
+ var TablesPage = React__default.default.lazy(() => import('./TablesPage-5GCFGSX3.cjs').then((m) => ({ default: m.TablesPage })));
764
+ var DEFAULT_LABELS = {
765
+ pageTitle: "Tables",
766
+ floorPlan: "Floor Plan",
767
+ zones: "Zones",
768
+ sessionHistory: "Session History"
769
+ };
770
+ function resolveConfig(options) {
771
+ return {
772
+ modules: {
773
+ reservations: options?.modules?.reservations === true,
774
+ sessionHistory: options?.modules?.sessionHistory !== false
775
+ },
776
+ labels: { ...DEFAULT_LABELS, ...options?.labels },
777
+ defaultZones: options?.defaultZones ?? [
778
+ { name: "Indoor", color: "#3b82f6" },
779
+ { name: "Outdoor", color: "#22c55e" },
780
+ { name: "Bar", color: "#f59e0b" }
781
+ ],
782
+ onTableSeated: options?.onTableSeated,
783
+ onTableClosed: options?.onTableClosed
784
+ };
785
+ }
786
+ function createTablesPlugin(options) {
787
+ const config = resolveConfig(options);
788
+ const provider = options?.dataProvider ?? createMockTablesProvider();
789
+ const store = createTablesStore(provider);
790
+ const PageComponent = () => React__default.default.createElement(
791
+ React__default.default.Suspense,
792
+ { fallback: null },
793
+ React__default.default.createElement(TablesPage, { config, provider, store, registries: tablesRegistries })
794
+ );
795
+ return {
796
+ id: "tables",
797
+ name: config.labels.pageTitle,
798
+ icon: "MapPin",
799
+ version: "1.0.0",
800
+ scope: options?.scope ?? "vertical",
801
+ verticalId: options?.verticalId,
802
+ defaultEnabled: true,
803
+ dependencies: [],
804
+ navigation: [
805
+ {
806
+ section: options?.navSection ?? "main",
807
+ position: options?.navPosition ?? 4,
808
+ label: config.labels.pageTitle,
809
+ route: "/tables",
810
+ icon: "MapPin",
811
+ permission: { feature: "tables", action: "read" }
812
+ }
813
+ ],
814
+ routes: [
815
+ {
816
+ path: "/tables",
817
+ component: PageComponent,
818
+ permission: { feature: "tables", action: "read" }
819
+ }
820
+ ],
821
+ widgets: [],
822
+ aiTools: [
823
+ {
824
+ id: "tables.availability",
825
+ name: "getTableAvailability",
826
+ description: "Returns which tables are available, occupied, reserved, or being cleaned.",
827
+ icon: "MapPin",
828
+ mode: "read",
829
+ category: "Tables",
830
+ parameters: {
831
+ type: "object",
832
+ properties: {
833
+ zone: { type: "string", description: "Filter by zone name" },
834
+ status: { type: "string", enum: ["available", "occupied", "reserved", "cleaning"] }
835
+ }
836
+ },
837
+ suggestions: [
838
+ { label: "Which tables are available right now?" },
839
+ { label: "How many tables are occupied?" }
840
+ ]
841
+ },
842
+ {
843
+ id: "tables.seat-guests",
844
+ name: "seatGuests",
845
+ description: "Seats guests at a specific table.",
846
+ icon: "UserPlus",
847
+ mode: "persist",
848
+ category: "Tables",
849
+ parameters: {
850
+ type: "object",
851
+ properties: {
852
+ tableNumber: { type: "number", description: "Table number" },
853
+ guests: { type: "number", description: "Number of guests" }
854
+ },
855
+ required: ["tableNumber", "guests"]
856
+ },
857
+ permission: { feature: "tables", action: "edit" }
858
+ }
859
+ ],
860
+ registries: tablesRegistries,
861
+ settings: [
862
+ {
863
+ id: "tables",
864
+ label: config.labels.pageTitle,
865
+ icon: "MapPin",
866
+ component: (() => {
867
+ const TablesSettingsTab = () => React__default.default.createElement(saas.PluginSettingsPanel, {
868
+ title: "Tables Settings",
869
+ subtitle: "Zones and floor plan configuration",
870
+ registries: tablesRegistries,
871
+ routeBase: "/settings/tables"
872
+ });
873
+ TablesSettingsTab.displayName = "TablesSettingsTab";
874
+ return TablesSettingsTab;
875
+ })(),
876
+ order: 21,
877
+ permission: { feature: "tables", action: "read" }
878
+ }
879
+ ],
880
+ locales: tablesLocales
881
+ };
882
+ }
883
+
884
+ exports.createFayzTablesProvider = createFayzTablesProvider;
885
+ exports.createTablesPlugin = createTablesPlugin;
886
+ //# sourceMappingURL=index.cjs.map
887
+ //# sourceMappingURL=index.cjs.map