@fayz-ai/plugin-orders 0.12.28 → 0.14.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 (39) hide show
  1. package/dist/OrderDetailRoute-LTPDQXQU.js +193 -0
  2. package/dist/OrderDetailRoute-LTPDQXQU.js.map +1 -0
  3. package/dist/OrdersPage-5326V62H.js +291 -0
  4. package/dist/OrdersPage-5326V62H.js.map +1 -0
  5. package/dist/chunk-YVC7R552.js +6 -0
  6. package/dist/chunk-YVC7R552.js.map +1 -0
  7. package/dist/context.d.ts +0 -46
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/data/core-supabase.d.ts.map +1 -1
  10. package/dist/index.d.ts +4 -34
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1108 -934
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/detail-slot.d.ts +13 -0
  15. package/dist/lib/detail-slot.d.ts.map +1 -0
  16. package/dist/lib/list-query.d.ts +21 -0
  17. package/dist/lib/list-query.d.ts.map +1 -0
  18. package/dist/migrations/index.d.ts +6 -0
  19. package/dist/migrations/index.d.ts.map +1 -0
  20. package/dist/server/index.d.ts +17 -0
  21. package/dist/server/index.d.ts.map +1 -0
  22. package/dist/server/index.js +48 -0
  23. package/dist/server/index.js.map +1 -0
  24. package/dist/types.d.ts +10 -2
  25. package/dist/types.d.ts.map +1 -1
  26. package/dist/views/OrderDetailRoute.d.ts +12 -0
  27. package/dist/views/OrderDetailRoute.d.ts.map +1 -0
  28. package/dist/views/OrdersPage.d.ts +1 -7
  29. package/dist/views/OrdersPage.d.ts.map +1 -1
  30. package/dist/views/OrdersScreen.d.ts.map +1 -1
  31. package/package.json +12 -9
  32. package/dist/OrdersPage-QV2YWARF.js +0 -483
  33. package/dist/OrdersPage-QV2YWARF.js.map +0 -1
  34. package/dist/chunk-TYS3LCKX.js +0 -57
  35. package/dist/chunk-TYS3LCKX.js.map +0 -1
  36. package/dist/lib/floor-plan-id.d.ts +0 -13
  37. package/dist/lib/floor-plan-id.d.ts.map +0 -1
  38. package/dist/views/CommercialOrdersScreen.d.ts +0 -3
  39. package/dist/views/CommercialOrdersScreen.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,428 +1,23 @@
1
- import { getNextStatus, STATUS_FLOW } from './chunk-TYS3LCKX.js';
2
- export { ADVANCE_VERB, DEFAULT_STATUS_CONFIGS, KANBAN_COLUMNS, STATUS_FLOW, getNextStatus, getPreviousStatus, mapToKanbanColumn } from './chunk-TYS3LCKX.js';
3
- import * as React2 from 'react';
4
- import React2__default from 'react';
5
- import { fayzConnectorOAuth, OrderDetailPage, orderIsEditable, PageActions, adminNavigateTo, PluginSettingsPanel, dedup, usePermissionOptional, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, SettingsGroup, ToggleRow, AddConnectorButton, ConnectorRow, AddConnectorPanel, ConnectorDetail, TextRow } from '@fayz-ai/admin';
6
- import { Pencil, AlertTriangle, Loader2, Check } from 'lucide-react';
7
- import { createConnectionStore, connectionRuns, connectionSettings, connectionStatus, settingsStatus, getSupabaseClientOptional, usePluginRuntimeOptional, emitDomainEvent, errorMessage, describeError } from '@fayz-ai/core';
1
+ export { ORDER_DETAIL_COMPONENT_ID } from './chunk-YVC7R552.js';
2
+ import React from 'react';
3
+ import { createConnectionStore, connectionRuns, connectionSettings, connectionStatus, settingsStatus, getSupabaseClientOptional, emitDomainEvent, errorMessage } from '@fayz-ai/core';
8
4
  import { createStore } from 'zustand/vanilla';
5
+ import { fayzConnectorOAuth, PluginSettingsPanel, dedup, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, SettingsGroup, ToggleRow, AddConnectorButton, ConnectorRow, AddConnectorPanel, ConnectorDetail, TextRow } from '@fayz-ai/admin';
9
6
  import { toast } from 'sonner';
10
- import { ReceivePaymentModal } from '@fayz-ai/plugin-financial';
11
- import { Button, Skeleton, toast as toast$1 } from '@fayz-ai/ui';
12
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
7
+ import { Skeleton } from '@fayz-ai/ui';
8
+ import { AlertTriangle, Loader2, Check } from 'lucide-react';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
13
10
  import { createFayzClient } from '@fayz-ai/sdk';
14
11
 
15
- // src/data/mock.ts
16
- var uid = 100;
17
- function nextId(prefix) {
18
- return prefix + "-" + ++uid;
19
- }
20
- var now = (/* @__PURE__ */ new Date()).toISOString();
21
- function makeItem(orderId, name, quantity, unitPrice, sortOrder, productId) {
22
- const id = nextId("item");
23
- return {
24
- id,
25
- orderId,
26
- productId,
27
- name,
28
- quantity,
29
- unitPrice,
30
- discount: 0,
31
- total: quantity * unitPrice,
32
- sortOrder,
33
- createdAt: now
34
- };
35
- }
36
- function createSeedOrders() {
37
- const order1Id = nextId("order");
38
- const order1Items = [
39
- makeItem(order1Id, "Classic Burger", 1, 32, 0, "prod-burger-classic"),
40
- makeItem(order1Id, "Fries", 1, 15, 1, "prod-fries")
41
- ];
42
- const order1 = {
43
- id: order1Id,
44
- referenceNumber: "ORD-0001",
45
- kind: "dine_in",
46
- status: "preparing",
47
- source: "pos",
48
- tableId: "table-3",
49
- tableName: "Table 3",
50
- subtotal: 47,
51
- discount: 0,
52
- tax: 0,
53
- total: 47,
54
- currency: "BRL",
55
- tags: [],
56
- items: order1Items,
57
- tenantId: "tenant-mock",
58
- createdAt: now,
59
- updatedAt: now
60
- };
61
- const order2Id = nextId("order");
62
- const order2Items = [
63
- makeItem(order2Id, "Cheese Burger", 1, 36, 0, "prod-burger-cheese")
64
- ];
65
- const order2 = {
66
- id: order2Id,
67
- referenceNumber: "ORD-0002",
68
- kind: "takeout",
69
- status: "ready",
70
- source: "pos",
71
- subtotal: 36,
72
- discount: 0,
73
- tax: 0,
74
- total: 36,
75
- currency: "BRL",
76
- tags: [],
77
- items: order2Items,
78
- tenantId: "tenant-mock",
79
- createdAt: now,
80
- updatedAt: now
81
- };
82
- const order3Id = nextId("order");
83
- const order3Items = [
84
- makeItem(order3Id, "Margherita Pizza", 1, 38, 0, "prod-pizza-margherita"),
85
- makeItem(order3Id, "Caesar Salad", 1, 22, 1, "prod-salad-caesar"),
86
- makeItem(order3Id, "Soda 350ml", 1, 12, 2, "prod-soda")
87
- ];
88
- const order3 = {
89
- id: order3Id,
90
- referenceNumber: "ORD-0003",
91
- kind: "delivery",
92
- status: "confirmed",
93
- source: "ifood",
94
- subtotal: 72,
95
- discount: 0,
96
- tax: 0,
97
- total: 72,
98
- currency: "BRL",
99
- tags: [],
100
- items: order3Items,
101
- tenantId: "tenant-mock",
102
- createdAt: now,
103
- updatedAt: now
104
- };
105
- const order4Id = nextId("order");
106
- const order4Items = [
107
- makeItem(order4Id, "BBQ Bacon Burger", 1, 42, 0, "prod-burger-bbq")
108
- ];
109
- const order4 = {
110
- id: order4Id,
111
- referenceNumber: "ORD-0004",
112
- kind: "dine_in",
113
- status: "new",
114
- source: "pos",
115
- tableId: "table-7",
116
- tableName: "Table 7",
117
- subtotal: 42,
118
- discount: 0,
119
- tax: 0,
120
- total: 42,
121
- currency: "BRL",
122
- tags: [],
123
- items: order4Items,
124
- tenantId: "tenant-mock",
125
- createdAt: now,
126
- updatedAt: now
127
- };
128
- const order5Id = nextId("order");
129
- const order5Items = [
130
- makeItem(order5Id, "Classic Burger", 2, 32, 0, "prod-burger-classic"),
131
- makeItem(order5Id, "Fries", 1, 15, 1, "prod-fries"),
132
- makeItem(order5Id, "Onion Rings", 1, 18, 2, "prod-onion-rings"),
133
- makeItem(order5Id, "Milkshake", 1, 23, 3, "prod-milkshake")
134
- ];
135
- const order5 = {
136
- id: order5Id,
137
- referenceNumber: "ORD-0005",
138
- kind: "dine_in",
139
- status: "completed",
140
- source: "pos",
141
- tableId: "table-1",
142
- tableName: "Table 1",
143
- subtotal: 120,
144
- discount: 0,
145
- tax: 0,
146
- total: 120,
147
- currency: "BRL",
148
- tags: [],
149
- items: order5Items,
150
- tenantId: "tenant-mock",
151
- createdAt: now,
152
- updatedAt: now,
153
- completedAt: now
154
- };
155
- return [order1, order2, order3, order4, order5];
156
- }
157
- function recomputeTotals(order) {
158
- order.subtotal = order.items.reduce((sum, i) => sum + i.total, 0);
159
- order.total = order.subtotal - order.discount + order.tax;
160
- }
161
- var refCounter = 5;
162
- function generateReferenceNumber() {
163
- refCounter++;
164
- return "ORD-" + String(refCounter).padStart(4, "0");
165
- }
166
- function toDateStr(d) {
167
- return d.toISOString().slice(0, 10);
168
- }
169
- function createMockOrdersProvider() {
170
- const orders = createSeedOrders();
171
- function findOrder(id) {
172
- return orders.find((o) => o.id === id);
173
- }
174
- function findItem(itemId) {
175
- for (const order of orders) {
176
- const index = order.items.findIndex((i) => i.id === itemId);
177
- if (index !== -1) return { order, item: order.items[index], index };
178
- }
179
- return void 0;
180
- }
181
- const provider = {
182
- // ------------------------------------------------------------------
183
- // getOrders
184
- // ------------------------------------------------------------------
185
- async getOrders(query) {
186
- let result = [...orders];
187
- if (query.kind) {
188
- const kinds = Array.isArray(query.kind) ? query.kind : [query.kind];
189
- result = result.filter((o) => kinds.includes(o.kind));
190
- }
191
- if (query.status) {
192
- const statuses = Array.isArray(query.status) ? query.status : [query.status];
193
- result = result.filter((o) => statuses.includes(o.status));
194
- }
195
- if (query.source) {
196
- result = result.filter((o) => o.source === query.source);
197
- }
198
- if (query.tableId) {
199
- result = result.filter((o) => o.tableId === query.tableId);
200
- }
201
- if (query.assigneeId) {
202
- result = result.filter((o) => o.assigneeId === query.assigneeId);
203
- }
204
- if (query.search) {
205
- const q = query.search.toLowerCase();
206
- result = result.filter(
207
- (o) => o.referenceNumber.toLowerCase().includes(q) || o.partyName?.toLowerCase().includes(q) || o.tableName?.toLowerCase().includes(q) || o.items.some((i) => i.name.toLowerCase().includes(q))
208
- );
209
- }
210
- if (query.dateRange) {
211
- const from = query.dateRange.from;
212
- const to = query.dateRange.to;
213
- result = result.filter((o) => {
214
- const d = o.createdAt.slice(0, 10);
215
- return d >= from && d <= to;
216
- });
217
- }
218
- result.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
219
- const total = result.length;
220
- const page = query.page ?? 1;
221
- const pageSize = query.pageSize ?? 20;
222
- const start = (page - 1) * pageSize;
223
- const data = result.slice(start, start + pageSize);
224
- return { data, total };
225
- },
226
- // ------------------------------------------------------------------
227
- // getOrderById
228
- // ------------------------------------------------------------------
229
- async getOrderById(id) {
230
- return findOrder(id) ?? null;
231
- },
232
- // ------------------------------------------------------------------
233
- // createOrder
234
- // ------------------------------------------------------------------
235
- async createOrder(input) {
236
- const orderId = nextId("order");
237
- const items = input.items.map((item, idx) => ({
238
- id: nextId("item"),
239
- orderId,
240
- productId: item.productId,
241
- name: item.name,
242
- quantity: item.quantity,
243
- unitPrice: item.unitPrice,
244
- discount: item.discount ?? 0,
245
- total: item.quantity * item.unitPrice - (item.discount ?? 0),
246
- sortOrder: idx,
247
- modifiers: item.modifiers,
248
- kitchenNotes: item.kitchenNotes,
249
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
250
- }));
251
- const subtotal = items.reduce((sum, i) => sum + i.total, 0);
252
- const order = {
253
- id: orderId,
254
- referenceNumber: generateReferenceNumber(),
255
- kind: input.kind,
256
- status: "new",
257
- source: input.source ?? "pos",
258
- tableId: input.tableId,
259
- partyId: input.partyId,
260
- assigneeId: input.assigneeId,
261
- notes: input.notes,
262
- estimatedMinutes: input.estimatedMinutes,
263
- subtotal,
264
- discount: 0,
265
- tax: 0,
266
- total: subtotal,
267
- currency: "BRL",
268
- tags: [],
269
- items,
270
- tenantId: "tenant-mock",
271
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
272
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
273
- };
274
- orders.push(order);
275
- return order;
276
- },
277
- // ------------------------------------------------------------------
278
- // updateOrderStatus
279
- // ------------------------------------------------------------------
280
- async updateOrderStatus(input) {
281
- const order = findOrder(input.orderId);
282
- if (!order) throw new Error(`Order not found: ${input.orderId}`);
283
- if (input.status !== "cancelled") {
284
- const flow = STATUS_FLOW[order.kind];
285
- const currentIdx = flow.indexOf(order.status);
286
- const targetIdx = flow.indexOf(input.status);
287
- if (targetIdx < 0 || targetIdx !== currentIdx && targetIdx !== currentIdx + 1) {
288
- const next = getNextStatus(order.kind, order.status);
289
- throw new Error(
290
- `Invalid status transition: ${order.status} \u2192 ${input.status}. ` + (next ? `Expected next status: ${next}` : "Order is already in a terminal state.")
291
- );
292
- }
293
- }
294
- order.status = input.status;
295
- order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
296
- if (input.status === "completed") {
297
- order.completedAt = order.updatedAt;
298
- }
299
- return order;
300
- },
301
- // ------------------------------------------------------------------
302
- // updateOrder
303
- // ------------------------------------------------------------------
304
- async updateOrder(id, data) {
305
- const order = findOrder(id);
306
- if (!order) throw new Error(`Order not found: ${id}`);
307
- const { id: _id, items: _items, tenantId: _t, createdAt: _c, ...safe } = data;
308
- Object.assign(order, safe);
309
- order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
310
- return order;
311
- },
312
- // ------------------------------------------------------------------
313
- // cancelOrder
314
- // ------------------------------------------------------------------
315
- async cancelOrder(id, reason) {
316
- const order = findOrder(id);
317
- if (!order) throw new Error(`Order not found: ${id}`);
318
- order.status = "cancelled";
319
- order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
320
- if (reason) {
321
- order.notes = order.notes ? `${order.notes}
322
- Cancelled: ${reason}` : `Cancelled: ${reason}`;
323
- }
324
- },
325
- // ------------------------------------------------------------------
326
- // addOrderItem
327
- // ------------------------------------------------------------------
328
- async addOrderItem(orderId, item) {
329
- const order = findOrder(orderId);
330
- if (!order) throw new Error(`Order not found: ${orderId}`);
331
- const newItem = {
332
- id: nextId("item"),
333
- orderId,
334
- productId: item.productId,
335
- name: item.name,
336
- quantity: item.quantity,
337
- unitPrice: item.unitPrice,
338
- discount: item.discount ?? 0,
339
- total: item.quantity * item.unitPrice - (item.discount ?? 0),
340
- sortOrder: order.items.length,
341
- modifiers: item.modifiers,
342
- kitchenNotes: item.kitchenNotes,
343
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
344
- };
345
- order.items.push(newItem);
346
- recomputeTotals(order);
347
- order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
348
- return newItem;
349
- },
350
- // ------------------------------------------------------------------
351
- // removeOrderItem
352
- // ------------------------------------------------------------------
353
- async removeOrderItem(itemId) {
354
- const found = findItem(itemId);
355
- if (!found) throw new Error(`Order item not found: ${itemId}`);
356
- found.order.items.splice(found.index, 1);
357
- recomputeTotals(found.order);
358
- found.order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
359
- },
360
- // ------------------------------------------------------------------
361
- // updateOrderItem
362
- // ------------------------------------------------------------------
363
- async updateOrderItem(itemId, data) {
364
- const found = findItem(itemId);
365
- if (!found) throw new Error(`Order item not found: ${itemId}`);
366
- const { id: _id, orderId: _oid, createdAt: _c, ...safe } = data;
367
- Object.assign(found.item, safe);
368
- found.item.total = found.item.quantity * found.item.unitPrice - found.item.discount;
369
- recomputeTotals(found.order);
370
- found.order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
371
- return found.item;
372
- },
373
- // ------------------------------------------------------------------
374
- // getSummary
375
- // ------------------------------------------------------------------
376
- async getSummary(dateRange) {
377
- const today = toDateStr(/* @__PURE__ */ new Date());
378
- const from = dateRange?.from ?? today;
379
- const to = dateRange?.to ?? today;
380
- const inRange = orders.filter((o) => {
381
- const d = o.createdAt.slice(0, 10);
382
- return d >= from && d <= to;
383
- });
384
- const completed = inRange.filter((o) => o.status !== "cancelled");
385
- const revenue = completed.reduce((sum, o) => sum + o.total, 0);
386
- const activeStatuses = /* @__PURE__ */ new Set(["new", "confirmed", "preparing", "ready", "served", "dispatched"]);
387
- const active = orders.filter((o) => activeStatuses.has(o.status));
388
- const ordersByKind = { dine_in: 0, takeout: 0, delivery: 0 };
389
- const ordersByStatus = {};
390
- for (const o of inRange) {
391
- ordersByKind[o.kind] = (ordersByKind[o.kind] || 0) + 1;
392
- ordersByStatus[o.status] = (ordersByStatus[o.status] || 0) + 1;
393
- }
394
- const itemMap = /* @__PURE__ */ new Map();
395
- for (const o of completed) {
396
- for (const item of o.items) {
397
- const existing = itemMap.get(item.name);
398
- if (existing) {
399
- existing.quantity += item.quantity;
400
- existing.revenue += item.total;
401
- } else {
402
- itemMap.set(item.name, { name: item.name, quantity: item.quantity, revenue: item.total });
403
- }
404
- }
405
- }
406
- const topItems = Array.from(itemMap.values()).sort((a, b) => b.quantity - a.quantity).slice(0, 5);
407
- return {
408
- todayOrderCount: inRange.length,
409
- todayRevenue: revenue,
410
- averageTicket: completed.length > 0 ? revenue / completed.length : 0,
411
- activeOrderCount: active.length,
412
- ordersByKind,
413
- ordersByStatus,
414
- topItems
415
- };
416
- }
417
- };
418
- return provider;
419
- }
420
12
  var READ_ONLY = "This provider reads the core order archetype; pass `writes` to let the app that owns the flow create and edit them.";
421
13
  var DEFAULT_CHANNELS = {
422
14
  dine_in: "dine_in",
423
15
  delivery: "delivery",
424
16
  takeout: "balcao"
425
17
  };
18
+ function searchTerm(raw) {
19
+ return (raw ?? "").replace(/[,()%*\\"]/g, " ").trim();
20
+ }
426
21
  function createCoreOrdersProvider(options) {
427
22
  const db = () => {
428
23
  const client = getSupabaseClientOptional();
@@ -446,11 +41,23 @@ function createCoreOrdersProvider(options) {
446
41
  if (query?.status) {
447
42
  q = Array.isArray(query.status) ? q.in("status", query.status) : q.eq("status", query.status);
448
43
  }
44
+ if (query?.channel) q = q.eq("channel", query.channel);
45
+ if (query?.excludeKinds?.length) q = q.not("kind", "in", `(${query.excludeKinds.join(",")})`);
46
+ if (query?.fulfillmentStatus) q = q.eq("fulfillment_status", query.fulfillmentStatus);
449
47
  if (query?.tableId) q = q.eq("metadata->>tableId", query.tableId);
450
48
  if (query?.assigneeId) q = q.eq("assignee_id", query.assigneeId);
451
49
  if (query?.dateRange) {
452
50
  q = q.gte("created_at", query.dateRange.from).lte("created_at", query.dateRange.to);
453
51
  }
52
+ const term = searchTerm(query?.search);
53
+ if (term) {
54
+ const { data: named } = await client.from("people").select("id").ilike("name", `%${term}%`).limit(50);
55
+ const ids2 = (named ?? []).map((p) => p.id);
56
+ q = q.or([
57
+ `reference_number.ilike.%${term}%`,
58
+ ...ids2.length ? [`party_id.in.(${ids2.join(",")})`] : []
59
+ ].join(","));
60
+ }
454
61
  const pageSize = query?.pageSize ?? 100;
455
62
  const from = ((query?.page ?? 1) - 1) * pageSize;
456
63
  q = q.range(from, from + pageSize - 1);
@@ -458,11 +65,17 @@ function createCoreOrdersProvider(options) {
458
65
  if (error) throw new Error(error.message);
459
66
  const rows = data ?? [];
460
67
  const ids = rows.map((r) => r.id);
461
- const [{ data: people }, { data: payments }, { data: items }] = await Promise.all([
68
+ const withItems = query?.withItems !== false;
69
+ const [{ data: people }, { data: payments }, { data: items }, { data: shipments }] = await Promise.all([
462
70
  ids.length ? client.from("people").select("id, name").in("id", [...new Set(rows.flatMap((r) => [r.party_id, r.assignee_id]).filter(Boolean))]) : Promise.resolve({ data: [] }),
463
71
  ids.length ? client.from("v_order_payment_status").select("order_id, status, total_amount, paid_amount").in("order_id", ids) : Promise.resolve({ data: [] }),
464
- ids.length ? client.from("items").select("id, order_id, product_id, name, description, quantity, unit_price, discount, total, sort_order, metadata, created_at").in("order_id", ids).order("sort_order") : Promise.resolve({ data: [] })
72
+ ids.length && withItems ? client.from("items").select("id, order_id, product_id, name, description, quantity, unit_price, discount, total, sort_order, metadata, created_at").in("order_id", ids).order("sort_order") : Promise.resolve({ data: [] }),
73
+ ids.length ? client.from("fulfillments").select("order_id, status").in("order_id", ids).in("kind", ["shipment", "pickup"]) : Promise.resolve({ data: [] })
465
74
  ]);
75
+ const shipmentsByOrder = /* @__PURE__ */ new Map();
76
+ for (const s of shipments ?? []) {
77
+ shipmentsByOrder.set(s.order_id, [...shipmentsByOrder.get(s.order_id) ?? [], s.status]);
78
+ }
466
79
  const nameById = new Map((people ?? []).map((p) => [p.id, p.name]));
467
80
  const payById = new Map((payments ?? []).map((p) => [p.order_id, p]));
468
81
  const itemsByOrder = /* @__PURE__ */ new Map();
@@ -506,7 +119,8 @@ function createCoreOrdersProvider(options) {
506
119
  fulfillmentStatus: r.fulfillment_status ?? null,
507
120
  paymentStatus: pay?.status ?? "none",
508
121
  invoicedAmount: Number(pay?.total_amount) || 0,
509
- paidAmount: Number(pay?.paid_amount) || 0
122
+ paidAmount: Number(pay?.paid_amount) || 0,
123
+ shipmentStatuses: shipmentsByOrder.get(r.id) ?? []
510
124
  },
511
125
  tenantId: r.tenant_id,
512
126
  unitId: r.unit_id ?? void 0,
@@ -727,6 +341,62 @@ function createCoreOrdersProvider(options) {
727
341
  }
728
342
  };
729
343
  }
344
+
345
+ // src/types.ts
346
+ var STATUS_FLOW = {
347
+ dine_in: ["draft", "new", "confirmed", "preparing", "ready", "served", "completed"],
348
+ takeout: ["draft", "new", "confirmed", "preparing", "ready", "picked_up", "completed"],
349
+ delivery: ["draft", "new", "confirmed", "preparing", "ready", "dispatched", "delivered", "completed"]
350
+ };
351
+ var OUT_STATUSES = ["served", "picked_up", "dispatched", "delivered"];
352
+ function getNextStatus(kind, current) {
353
+ const flow = STATUS_FLOW[kind];
354
+ let idx = flow.indexOf(current);
355
+ if (idx === -1 && OUT_STATUSES.includes(current)) {
356
+ idx = flow.findIndex((status) => OUT_STATUSES.includes(status));
357
+ }
358
+ return idx >= 0 && idx < flow.length - 1 ? flow[idx + 1] : null;
359
+ }
360
+ var ADVANCE_VERB = {
361
+ draft: "Salvar rascunho",
362
+ new: "Enviar",
363
+ confirmed: "Confirmar",
364
+ preparing: "Preparar",
365
+ ready: "Marcar pronto",
366
+ served: "Servir",
367
+ picked_up: "Entregar",
368
+ dispatched: "Despachar",
369
+ delivered: "Confirmar entrega",
370
+ completed: "Concluir",
371
+ cancelled: "Cancelar"
372
+ };
373
+ function getPreviousStatus(kind, current) {
374
+ const flow = STATUS_FLOW[kind];
375
+ const idx = flow.indexOf(current);
376
+ return idx > 0 ? flow[idx - 1] : null;
377
+ }
378
+ function mapToKanbanColumn(status) {
379
+ if (["served", "picked_up", "dispatched", "delivered"].includes(status)) return "out";
380
+ if (["new", "confirmed", "preparing", "ready", "completed", "cancelled"].includes(status)) {
381
+ return status;
382
+ }
383
+ return "new";
384
+ }
385
+ var KANBAN_COLUMNS = ["new", "confirmed", "preparing", "ready", "out", "completed"];
386
+ var DEFAULT_STATUS_CONFIGS = [
387
+ { value: "new", label: "New", color: "#6366f1", icon: "PlusCircle" },
388
+ { value: "confirmed", label: "Confirmed", color: "#3b82f6", icon: "CheckCircle" },
389
+ { value: "preparing", label: "Preparing", color: "#f59e0b", icon: "ChefHat" },
390
+ { value: "ready", label: "Ready", color: "#10b981", icon: "Bell" },
391
+ { value: "served", label: "Served", color: "#06b6d4", icon: "Utensils" },
392
+ { value: "picked_up", label: "Picked Up", color: "#06b6d4", icon: "ShoppingBag" },
393
+ { value: "dispatched", label: "Dispatched", color: "#8b5cf6", icon: "Truck" },
394
+ { value: "delivered", label: "Delivered", color: "#06b6d4", icon: "PackageCheck" },
395
+ { value: "completed", label: "Completed", color: "#22c55e", icon: "CircleCheck" },
396
+ { value: "cancelled", label: "Cancelled", color: "#ef4444", icon: "XCircle" }
397
+ ];
398
+
399
+ // src/store.ts
730
400
  var ACTIVE_STATUSES = [
731
401
  "new",
732
402
  "confirmed",
@@ -929,6 +599,214 @@ var ordersRegistries = [
929
599
  }
930
600
  ];
931
601
 
602
+ // src/migrations/index.ts
603
+ var MIGRATION_001_AN_ORDER_ARRIVES_FROM_A_MARKETPLACE = `-- ============================================================================
604
+ -- 001 \u2014 an order arrives from a marketplace
605
+ --
606
+ -- The V2 sink of ADR 0003: the ONE door through which the integration service
607
+ -- hands a canonical delivery order to this product, and the two beside it to
608
+ -- apply a later status and to read the current one. No rule lives in the
609
+ -- service; what an order IS here is decided here, in one transaction.
610
+ --
611
+ -- There is no person behind these calls \u2014 the actor is the service, acting
612
+ -- for the tenant it names, which is why \`p_tenant\` is explicit and no
613
+ -- \`app.has_permission\` is asked (the same reasoning as service_authorize_for).
614
+ -- The functions are therefore closed to every signed-in user and opened only
615
+ -- to the service's own role, \`fayz_integration\`, when that role exists on the
616
+ -- cluster. Creating the role is an operator step; the grant below is guarded.
617
+ --
618
+ -- The order shape is @fayz-ai/contracts' CanonicalOrder v1: money in
619
+ -- centavos, items with options, totals that close. Everything that this
620
+ -- product has no column for travels in orders.metadata.external, so nothing
621
+ -- the marketplace said is lost.
622
+ -- ============================================================================
623
+
624
+ CREATE OR REPLACE FUNCTION public.orders_ingest_external(
625
+ p_tenant uuid,
626
+ p_unit uuid,
627
+ p_order jsonb,
628
+ p_status text
629
+ ) RETURNS uuid
630
+ LANGUAGE plpgsql SECURITY DEFINER
631
+ SET search_path TO ''
632
+ AS $$
633
+ DECLARE
634
+ v_provider text := p_order ->> 'provider';
635
+ v_external text := p_order ->> 'externalId';
636
+ v_ref text;
637
+ v_kind text;
638
+ v_channel text;
639
+ v_order uuid;
640
+ v_item jsonb;
641
+ v_n integer := 0;
642
+ v_qty numeric;
643
+ v_unit numeric;
644
+ v_total numeric;
645
+ v_sub numeric;
646
+ v_disc numeric;
647
+ v_ship numeric;
648
+ BEGIN
649
+ IF p_tenant IS NULL THEN
650
+ RAISE EXCEPTION 'orders_ingest_external: tenant is required' USING ERRCODE = '22023';
651
+ END IF;
652
+ IF v_provider IS NULL OR v_external IS NULL THEN
653
+ RAISE EXCEPTION 'orders_ingest_external: order needs provider and externalId' USING ERRCODE = '22023';
654
+ END IF;
655
+ IF p_unit IS NOT NULL AND NOT EXISTS (
656
+ SELECT 1 FROM public.companies c WHERE c.id = p_unit AND c.tenant_id = p_tenant) THEN
657
+ RAISE EXCEPTION 'orders_ingest_external: unit % is not of tenant %', p_unit, p_tenant USING ERRCODE = '22023';
658
+ END IF;
659
+
660
+ -- Once per (provider, externalId) within the tenant: a replay returns the
661
+ -- order it already made. The key lives in metadata, as order_create's does.
662
+ v_ref := v_provider || ':' || v_external;
663
+ SELECT o.id INTO v_order FROM public.orders o
664
+ WHERE o.tenant_id = p_tenant AND o.metadata ->> 'externalRef' = v_ref
665
+ LIMIT 1;
666
+ IF v_order IS NOT NULL THEN
667
+ RETURN v_order;
668
+ END IF;
669
+
670
+ -- The contract's kinds are this product's kinds; the channel is where the
671
+ -- order came in, in this product's vocabulary.
672
+ v_kind := CASE p_order ->> 'kind'
673
+ WHEN 'delivery' THEN 'delivery'
674
+ WHEN 'takeout' THEN 'takeout'
675
+ WHEN 'dine_in' THEN 'dine_in'
676
+ ELSE 'delivery' END;
677
+ v_channel := CASE v_kind WHEN 'delivery' THEN 'delivery' WHEN 'dine_in' THEN 'dine_in' ELSE 'balcao' END;
678
+
679
+ IF p_status IS NULL OR NOT EXISTS (
680
+ SELECT 1 FROM public.order_kinds k WHERE k.kind = v_kind AND p_status = ANY (k.statuses)) THEN
681
+ RAISE EXCEPTION 'orders_ingest_external: % is not a status of kind %', p_status, v_kind USING ERRCODE = '22023';
682
+ END IF;
683
+
684
+ v_sub := round(coalesce((p_order -> 'totals' ->> 'subtotal')::numeric, 0) / 100, 2);
685
+ v_disc := round(coalesce((p_order -> 'totals' ->> 'discount')::numeric, 0) / 100, 2);
686
+ v_ship := round(coalesce((p_order -> 'totals' ->> 'deliveryFee')::numeric, 0) / 100, 2);
687
+
688
+ INSERT INTO public.orders (tenant_id, unit_id, kind, channel, status, reference_number,
689
+ subtotal, discount, shipping, total, currency, notes, metadata)
690
+ VALUES (p_tenant, p_unit, v_kind, v_channel, p_status,
691
+ nullif(p_order ->> 'displayId', ''),
692
+ v_sub, v_disc, v_ship,
693
+ round(coalesce((p_order -> 'totals' ->> 'total')::numeric, 0) / 100, 2),
694
+ coalesce(p_order -> 'totals' ->> 'currency', 'BRL'),
695
+ nullif(p_order ->> 'notes', ''),
696
+ jsonb_build_object(
697
+ 'externalRef', v_ref,
698
+ 'idempotencyKey', v_ref,
699
+ 'external', jsonb_build_object(
700
+ 'provider', v_provider,
701
+ 'externalId', v_external,
702
+ 'displayId', p_order -> 'displayId',
703
+ 'merchantRef', p_order -> 'merchantRef',
704
+ 'placedAt', p_order -> 'placedAt',
705
+ 'timing', p_order -> 'timing',
706
+ 'customer', p_order -> 'customer',
707
+ 'payment', p_order -> 'payment',
708
+ 'fulfillment', p_order -> 'fulfillment',
709
+ 'totals', p_order -> 'totals',
710
+ 'lastStatusAt', NULL)))
711
+ RETURNING id INTO v_order;
712
+
713
+ FOR v_item IN SELECT * FROM jsonb_array_elements(coalesce(p_order -> 'items', '[]'::jsonb)) LOOP
714
+ v_n := v_n + 1;
715
+ v_qty := coalesce((v_item ->> 'quantity')::numeric, 1);
716
+ v_unit := round(coalesce((v_item ->> 'unitPrice')::numeric, 0) / 100, 2);
717
+ v_total := round(coalesce((v_item ->> 'total')::numeric, 0) / 100, 2);
718
+ INSERT INTO public.items (tenant_id, unit_id, order_id, name, description, quantity,
719
+ unit_price, discount, total, sort_order, metadata)
720
+ VALUES (p_tenant, p_unit, v_order,
721
+ coalesce(nullif(btrim(coalesce(v_item ->> 'name', '')), ''), 'Item'),
722
+ nullif(v_item ->> 'notes', ''),
723
+ v_qty, v_unit, 0, v_total, v_n - 1,
724
+ jsonb_build_object(
725
+ 'externalId', v_item -> 'externalId',
726
+ 'externalCode', v_item -> 'externalCode',
727
+ 'unit', v_item -> 'unit',
728
+ 'options', coalesce(v_item -> 'options', '[]'::jsonb)));
729
+ END LOOP;
730
+
731
+ RETURN v_order;
732
+ END $$;
733
+
734
+ COMMENT ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) IS
735
+ 'The integration service hands a canonical marketplace order to this product, once per (provider, externalId). Actor: the service, for the named tenant.';
736
+
737
+ -- A later status for an order the service ingested. Monotonic on the
738
+ -- service side already; here the only rules are "this order is yours" and
739
+ -- "that status exists for its kind".
740
+ CREATE OR REPLACE FUNCTION public.orders_apply_external_status(
741
+ p_tenant uuid,
742
+ p_order uuid,
743
+ p_status text,
744
+ p_occurred_at timestamptz
745
+ ) RETURNS void
746
+ LANGUAGE plpgsql SECURITY DEFINER
747
+ SET search_path TO ''
748
+ AS $$
749
+ DECLARE
750
+ v_kind text;
751
+ BEGIN
752
+ SELECT o.kind INTO v_kind FROM public.orders o
753
+ WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';
754
+ IF v_kind IS NULL THEN
755
+ RAISE EXCEPTION 'orders_apply_external_status: order % is not an external order of tenant %', p_order, p_tenant
756
+ USING ERRCODE = '22023';
757
+ END IF;
758
+ IF NOT EXISTS (SELECT 1 FROM public.order_kinds k WHERE k.kind = v_kind AND p_status = ANY (k.statuses)) THEN
759
+ RAISE EXCEPTION 'orders_apply_external_status: % is not a status of kind %', p_status, v_kind USING ERRCODE = '22023';
760
+ END IF;
761
+
762
+ UPDATE public.orders
763
+ SET status = p_status,
764
+ completed_at = CASE WHEN p_status IN ('completed', 'delivered') THEN coalesce(p_occurred_at, now()) ELSE completed_at END,
765
+ metadata = jsonb_set(metadata, '{external,lastStatusAt}', to_jsonb(coalesce(p_occurred_at, now())), true),
766
+ updated_at = now()
767
+ WHERE id = p_order;
768
+ END $$;
769
+
770
+ COMMENT ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) IS
771
+ 'A later status from the marketplace for an order the service ingested. Actor: the service, for the named tenant.';
772
+
773
+ CREATE OR REPLACE FUNCTION public.orders_external_status(p_tenant uuid, p_order uuid)
774
+ RETURNS jsonb
775
+ LANGUAGE sql STABLE SECURITY DEFINER
776
+ SET search_path TO ''
777
+ AS $$
778
+ SELECT jsonb_build_object('status', o.status, 'updatedAt', o.updated_at)
779
+ FROM public.orders o
780
+ WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';
781
+ $$;
782
+
783
+ COMMENT ON FUNCTION public.orders_external_status(uuid, uuid) IS
784
+ 'What this product thinks an external order is now. Actor: the service, for the named tenant.';
785
+
786
+ -- Closed to people, open to the service. The pools grant EXECUTE to
787
+ -- \`authenticated\` on every new public function by default; a definer function
788
+ -- that takes the tenant as a parameter must not be reachable by a session.
789
+ REVOKE ALL ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) FROM PUBLIC, anon, authenticated;
790
+ REVOKE ALL ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) FROM PUBLIC, anon, authenticated;
791
+ REVOKE ALL ON FUNCTION public.orders_external_status(uuid, uuid) FROM PUBLIC, anon, authenticated;
792
+ GRANT EXECUTE ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) TO service_role;
793
+ GRANT EXECUTE ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) TO service_role;
794
+ GRANT EXECUTE ON FUNCTION public.orders_external_status(uuid, uuid) TO service_role;
795
+
796
+ DO $$
797
+ BEGIN
798
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN
799
+ GRANT USAGE ON SCHEMA public TO fayz_integration;
800
+ GRANT EXECUTE ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) TO fayz_integration;
801
+ GRANT EXECUTE ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) TO fayz_integration;
802
+ GRANT EXECUTE ON FUNCTION public.orders_external_status(uuid, uuid) TO fayz_integration;
803
+ END IF;
804
+ END $$;
805
+ `;
806
+ var MIGRATIONS = [
807
+ { id: "001_an_order_arrives_from_a_marketplace", sql: MIGRATION_001_AN_ORDER_ARRIVES_FROM_A_MARKETPLACE }
808
+ ];
809
+
932
810
  // src/locales/en.ts
933
811
  var en = {
934
812
  "orders.title": "Orders",
@@ -1540,84 +1418,6 @@ var aiqfomeConnectorDef = {
1540
1418
  disconnect: () => store6.disconnect(),
1541
1419
  disconnectNote: "Desconectar para de trazer pedidos novos. Os pedidos que j\xE1 entraram continuam aqui."
1542
1420
  };
1543
- function toModalInstallment(bill) {
1544
- return {
1545
- id: bill.id,
1546
- amount: Number(bill.amount ?? 0),
1547
- paidAmount: Number(bill.paid_amount ?? 0),
1548
- installmentNumber: bill.installment_number ?? 1,
1549
- direction: bill.direction ?? "credit",
1550
- dueDate: bill.due_date,
1551
- invoiceId: bill.financial_invoice_id ?? bill.invoice_id
1552
- };
1553
- }
1554
- function firstOpenBill(receivable) {
1555
- return receivable?.bills.find(
1556
- (b) => Number(b.paid_amount ?? 0) < Number(b.amount)
1557
- );
1558
- }
1559
- function OrderReceivableActions({ orderId, orderTotal, receivable, reload }) {
1560
- const [busy, setBusy] = React2.useState(false);
1561
- const [paying, setPaying] = React2.useState(null);
1562
- const can = usePermissionOptional();
1563
- const runtime = usePluginRuntimeOptional();
1564
- const hasFinancialRoute = (runtime?.activePlugins ?? []).some(
1565
- (p) => p.routes?.some((r) => r.path === "/financial")
1566
- );
1567
- const canInvoice = can("financial", "create");
1568
- const balance = receivable ? Number(receivable.balance.balance ?? 0) : 0;
1569
- const cancelled = receivable?.balance.status === "cancelled";
1570
- const openBill = firstOpenBill(receivable);
1571
- const canReceive = receivable ? balance > 0 && !cancelled && Boolean(openBill) : canInvoice && Number(orderTotal) > 0;
1572
- async function registrarRecebimento() {
1573
- if (receivable) {
1574
- const bill = firstOpenBill(receivable);
1575
- if (bill) setPaying(toModalInstallment(bill));
1576
- return;
1577
- }
1578
- const db = getSupabaseClientOptional();
1579
- if (!db) return;
1580
- setBusy(true);
1581
- try {
1582
- const { error } = await db.rpc("fn_invoice_from_order", { p_order_id: orderId });
1583
- if (error) throw new Error(error.message ?? "Falha ao faturar o pedido");
1584
- const { data } = await db.from("v_financial_bills").select("*").eq("invoice_id", orderId).order("installment_number", { ascending: true });
1585
- const bill = (data ?? []).find(
1586
- (b) => Number(b.paid_amount ?? 0) < Number(b.amount ?? 0)
1587
- );
1588
- await reload(true);
1589
- if (bill) setPaying(toModalInstallment(bill));
1590
- else toast$1.error("Pedido faturado, mas nenhuma parcela em aberto foi encontrada");
1591
- } catch (err) {
1592
- toast$1.error(describeError(err, "Falha ao faturar o pedido"));
1593
- } finally {
1594
- setBusy(false);
1595
- }
1596
- }
1597
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1598
- canReceive && /* @__PURE__ */ jsx(Button, { onClick: () => void registrarRecebimento(), disabled: busy, "data-testid": "order-register-receipt", children: busy ? "Abrindo\u2026" : "Registrar recebimento" }),
1599
- hasFinancialRoute && /* @__PURE__ */ jsx(
1600
- "button",
1601
- {
1602
- type: "button",
1603
- onClick: () => adminNavigateTo("/financial"),
1604
- className: "rounded-md border px-3 py-1.5 text-sm font-medium transition hover:bg-muted",
1605
- children: "Abrir no Financeiro"
1606
- }
1607
- ),
1608
- paying != null && /* @__PURE__ */ jsx(
1609
- ReceivePaymentModal,
1610
- {
1611
- installment: paying,
1612
- onClose: () => setPaying(null),
1613
- onPaid: () => {
1614
- setPaying(null);
1615
- void reload(true);
1616
- }
1617
- }
1618
- )
1619
- ] });
1620
- }
1621
1421
  var DEBOUNCE_MS = 400;
1622
1422
  var KEYS = {
1623
1423
  enabled: "delivery.enabled",
@@ -1633,14 +1433,14 @@ function message(e) {
1633
1433
  return String(e);
1634
1434
  }
1635
1435
  function useDeliverySettings(defaults = {}) {
1636
- const client = React2__default.useMemo(() => getSupabaseClientOptional(), []);
1436
+ const client = React.useMemo(() => getSupabaseClientOptional(), []);
1637
1437
  const supported = !!client;
1638
- const [stored, setStored] = React2__default.useState({});
1639
- const [loading, setLoading] = React2__default.useState(supported);
1640
- const [saving, setSaving] = React2__default.useState(false);
1641
- const [error, setError] = React2__default.useState(null);
1642
- const [loadError, setLoadError] = React2__default.useState(null);
1643
- React2__default.useEffect(() => {
1438
+ const [stored, setStored] = React.useState({});
1439
+ const [loading, setLoading] = React.useState(supported);
1440
+ const [saving, setSaving] = React.useState(false);
1441
+ const [error, setError] = React.useState(null);
1442
+ const [loadError, setLoadError] = React.useState(null);
1443
+ React.useEffect(() => {
1644
1444
  if (!client) return;
1645
1445
  let cancelled = false;
1646
1446
  setLoading(true);
@@ -1667,12 +1467,12 @@ function useDeliverySettings(defaults = {}) {
1667
1467
  cancelled = true;
1668
1468
  };
1669
1469
  }, [client]);
1670
- const pendingRef = React2__default.useRef({});
1671
- const timerRef = React2__default.useRef(null);
1672
- React2__default.useEffect(() => () => {
1470
+ const pendingRef = React.useRef({});
1471
+ const timerRef = React.useRef(null);
1472
+ React.useEffect(() => () => {
1673
1473
  if (timerRef.current) clearTimeout(timerRef.current);
1674
1474
  }, []);
1675
- const set = React2__default.useCallback((patch) => {
1475
+ const set = React.useCallback((patch) => {
1676
1476
  setStored((current) => ({ ...current, ...patch }));
1677
1477
  if (!client) return;
1678
1478
  setError(null);
@@ -1691,7 +1491,7 @@ function useDeliverySettings(defaults = {}) {
1691
1491
  ).catch((e) => setError(message(e))).finally(() => setSaving(false));
1692
1492
  }, DEBOUNCE_MS);
1693
1493
  }, [client]);
1694
- const value = React2__default.useMemo(() => ({ ...defaults, ...stored }), [defaults, stored]);
1494
+ const value = React.useMemo(() => ({ ...defaults, ...stored }), [defaults, stored]);
1695
1495
  return { value, set, loading, saving, error, loadError, supported };
1696
1496
  }
1697
1497
  var MARKETPLACE_CATEGORIES = ["marketplace"];
@@ -1706,9 +1506,9 @@ function amountText(value) {
1706
1506
  return String(value).replace(".", ",");
1707
1507
  }
1708
1508
  function AmountRow({ label, description, hint, value, onCommit }) {
1709
- const [text, setText] = React2__default.useState(() => amountText(value));
1710
- const [invalid, setInvalid] = React2__default.useState(false);
1711
- React2__default.useEffect(() => {
1509
+ const [text, setText] = React.useState(() => amountText(value));
1510
+ const [invalid, setInvalid] = React.useState(false);
1511
+ React.useEffect(() => {
1712
1512
  setText(amountText(value));
1713
1513
  }, [value]);
1714
1514
  return /* @__PURE__ */ jsx(
@@ -1740,14 +1540,14 @@ function DeliverySettings() {
1740
1540
  const settings = useDeliverySettings({ enabled: true, fee: 0, minOrder: 0, etaMinutes: 45, radiusKm: 0 });
1741
1541
  const { value, set } = settings;
1742
1542
  const { connectors } = useConnectors({ hostPluginId: "orders", includeGrants: false });
1743
- const [adding, setAdding] = React2__default.useState(false);
1744
- const [selected, setSelected] = React2__default.useState(null);
1543
+ const [adding, setAdding] = React.useState(false);
1544
+ const [selected, setSelected] = React.useState(null);
1745
1545
  const panelAvailable = useConnectorsPanelAvailable();
1746
- const openJourney = React2__default.useCallback((connector) => {
1546
+ const openJourney = React.useCallback((connector) => {
1747
1547
  if (panelAvailable) openConnectorsPanel(connector);
1748
1548
  else setSelected(connector);
1749
1549
  }, [panelAvailable]);
1750
- const marketplaces = React2__default.useMemo(
1550
+ const marketplaces = React.useMemo(
1751
1551
  () => (connectors ?? []).filter((c) => c.category && MARKETPLACE_CATEGORIES.includes(c.category)),
1752
1552
  [connectors]
1753
1553
  );
@@ -1925,409 +1725,816 @@ function numberValue(value) {
1925
1725
  function dateKey(value) {
1926
1726
  return value.slice(0, 10);
1927
1727
  }
1928
- function referenceNumber() {
1929
- return `ORD-${Date.now().toString().slice(-6)}`;
1728
+ function referenceNumber() {
1729
+ return `ORD-${Date.now().toString().slice(-6)}`;
1730
+ }
1731
+ function objectValue(value) {
1732
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1733
+ }
1734
+ function canonicalStatus(status) {
1735
+ if (status === "new") return "draft";
1736
+ if (status === "completed" || status === "cancelled") return status;
1737
+ return "confirmed";
1738
+ }
1739
+ function restaurantStatus(rowStatus, metadata) {
1740
+ if (rowStatus === "completed" || rowStatus === "cancelled") return rowStatus;
1741
+ const stored = metadata.restaurantStatus;
1742
+ if (typeof stored === "string" && RESTAURANT_STATUSES.has(stored)) return stored;
1743
+ if (rowStatus === "draft") return "new";
1744
+ if (typeof rowStatus === "string" && RESTAURANT_STATUSES.has(rowStatus)) return rowStatus;
1745
+ return "confirmed";
1746
+ }
1747
+ function ordersMetadata(metadata) {
1748
+ return objectValue(metadata?.[ORDERS_METADATA_KEY]);
1749
+ }
1750
+ function mergeOrdersMetadata(metadata, patch) {
1751
+ const base = { ...metadata ?? {} };
1752
+ const current = ordersMetadata(base);
1753
+ const definedPatch = Object.fromEntries(
1754
+ Object.entries(patch).filter(([, value]) => value !== void 0)
1755
+ );
1756
+ const next = { ...current, ...definedPatch };
1757
+ return { ...base, [ORDERS_METADATA_KEY]: next };
1758
+ }
1759
+ function itemToRow(item, orderId, sortOrder, tenantId, unitId) {
1760
+ const discount = item.discount ?? 0;
1761
+ return {
1762
+ order_id: orderId,
1763
+ ...tenantId ? { tenant_id: tenantId } : {},
1764
+ ...unitId ? { unit_id: unitId } : {},
1765
+ product_id: item.productId ?? null,
1766
+ name: item.name,
1767
+ quantity: item.quantity,
1768
+ unit_price: item.unitPrice,
1769
+ discount,
1770
+ total: item.quantity * item.unitPrice - discount,
1771
+ sort_order: sortOrder,
1772
+ metadata: mergeOrdersMetadata(void 0, {
1773
+ modifiers: item.modifiers ?? null,
1774
+ kitchenNotes: item.kitchenNotes ?? null
1775
+ })
1776
+ };
1777
+ }
1778
+ function rowToItem(row) {
1779
+ const quantity = numberValue(row.quantity);
1780
+ const unitPrice = numberValue(row.unit_price);
1781
+ const discount = numberValue(row.discount);
1782
+ const restaurant = ordersMetadata(row.metadata);
1783
+ return {
1784
+ id: row.id,
1785
+ orderId: row.order_id ?? "",
1786
+ productId: row.product_id ?? void 0,
1787
+ name: row.name ?? "Item",
1788
+ description: row.description ?? void 0,
1789
+ quantity,
1790
+ unitPrice,
1791
+ discount,
1792
+ total: numberValue(row.total) || quantity * unitPrice - discount,
1793
+ sortOrder: row.sort_order ?? 0,
1794
+ modifiers: row.modifiers ?? (typeof restaurant.modifiers === "string" ? restaurant.modifiers : void 0),
1795
+ kitchenNotes: row.kitchen_notes ?? (typeof restaurant.kitchenNotes === "string" ? restaurant.kitchenNotes : void 0),
1796
+ metadata: row.metadata ?? {},
1797
+ createdAt: row.created_at ?? nowIso()
1798
+ };
1799
+ }
1800
+ function rowToOrder(row, items = []) {
1801
+ const subtotal = numberValue(row.subtotal) || items.reduce((sum, item) => sum + item.total, 0);
1802
+ const discount = numberValue(row.discount);
1803
+ const tax = numberValue(row.tax);
1804
+ const restaurant = ordersMetadata(row.metadata);
1805
+ return {
1806
+ id: row.id,
1807
+ referenceNumber: row.reference_number ?? row.id,
1808
+ kind: row.kind ?? "dine_in",
1809
+ status: restaurantStatus(row.status, restaurant),
1810
+ partyId: row.party_id ?? void 0,
1811
+ partyName: row.party_name ?? (typeof restaurant.partyName === "string" ? restaurant.partyName : void 0),
1812
+ assigneeId: row.assignee_id ?? void 0,
1813
+ assigneeName: row.assignee_name ?? (typeof restaurant.assigneeName === "string" ? restaurant.assigneeName : void 0),
1814
+ locationId: row.location_id ?? void 0,
1815
+ subtotal,
1816
+ discount,
1817
+ tax,
1818
+ total: numberValue(row.total) || subtotal - discount + tax,
1819
+ currency: row.currency ?? "BRL",
1820
+ notes: row.notes ?? void 0,
1821
+ tags: row.tags ?? [],
1822
+ source: row.source ?? (typeof restaurant.source === "string" ? restaurant.source : "pos"),
1823
+ tableId: row.table_id ?? (typeof restaurant.tableId === "string" ? restaurant.tableId : void 0),
1824
+ tableName: row.table_name ?? (typeof restaurant.tableName === "string" ? restaurant.tableName : void 0),
1825
+ estimatedMinutes: row.estimated_minutes ?? (typeof restaurant.estimatedMinutes === "number" ? restaurant.estimatedMinutes : void 0),
1826
+ items,
1827
+ metadata: row.metadata ?? {},
1828
+ tenantId: row.tenant_id ?? "runtime-tenant",
1829
+ unitId: row.unit_id ?? void 0,
1830
+ createdAt: row.created_at ?? nowIso(),
1831
+ updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
1832
+ completedAt: row.completed_at ?? void 0
1833
+ };
1834
+ }
1835
+ function orderPayload(input) {
1836
+ const subtotal = input.items.reduce((sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0), 0);
1837
+ return {
1838
+ reference_number: referenceNumber(),
1839
+ kind: input.kind,
1840
+ status: "draft",
1841
+ party_id: input.partyId ?? null,
1842
+ assignee_id: input.assigneeId ?? null,
1843
+ notes: input.notes ?? null,
1844
+ subtotal,
1845
+ discount: 0,
1846
+ tax: 0,
1847
+ total: subtotal,
1848
+ currency: "BRL",
1849
+ tags: [],
1850
+ metadata: mergeOrdersMetadata(void 0, {
1851
+ source: input.source ?? "pos",
1852
+ tableId: input.tableId ?? null,
1853
+ estimatedMinutes: input.estimatedMinutes ?? null,
1854
+ restaurantStatus: "new"
1855
+ })
1856
+ };
1930
1857
  }
1931
- function objectValue(value) {
1932
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1858
+ function updatePayload(data, existingMetadata) {
1859
+ const payload = {};
1860
+ if (data.referenceNumber !== void 0) payload.reference_number = data.referenceNumber;
1861
+ if (data.kind !== void 0) payload.kind = data.kind;
1862
+ if (data.status !== void 0) payload.status = canonicalStatus(data.status);
1863
+ if (data.partyId !== void 0) payload.party_id = data.partyId;
1864
+ if (data.assigneeId !== void 0) payload.assignee_id = data.assigneeId;
1865
+ if (data.locationId !== void 0) payload.location_id = data.locationId;
1866
+ if (data.subtotal !== void 0) payload.subtotal = data.subtotal;
1867
+ if (data.discount !== void 0) payload.discount = data.discount;
1868
+ if (data.tax !== void 0) payload.tax = data.tax;
1869
+ if (data.total !== void 0) payload.total = data.total;
1870
+ if (data.currency !== void 0) payload.currency = data.currency;
1871
+ if (data.notes !== void 0) payload.notes = data.notes;
1872
+ if (data.tags !== void 0) payload.tags = data.tags;
1873
+ const metadataBase = data.metadata !== void 0 ? { ...existingMetadata ?? {}, ...data.metadata } : existingMetadata;
1874
+ const metadataPatch = {
1875
+ partyName: data.partyName,
1876
+ assigneeName: data.assigneeName,
1877
+ source: data.source,
1878
+ tableId: data.tableId,
1879
+ tableName: data.tableName,
1880
+ estimatedMinutes: data.estimatedMinutes,
1881
+ restaurantStatus: data.status
1882
+ };
1883
+ if (data.metadata !== void 0 || Object.values(metadataPatch).some((value) => value !== void 0)) {
1884
+ payload.metadata = mergeOrdersMetadata(metadataBase, metadataPatch);
1885
+ }
1886
+ if (data.completedAt !== void 0) payload.completed_at = data.completedAt;
1887
+ return payload;
1933
1888
  }
1934
- function canonicalStatus(status) {
1935
- if (status === "new") return "draft";
1936
- if (status === "completed" || status === "cancelled") return status;
1937
- return "confirmed";
1889
+ function filtersFromQuery(query) {
1890
+ const filters = [];
1891
+ if (query.kind && !Array.isArray(query.kind)) filters.push({ column: "kind", operator: "eq", value: query.kind });
1892
+ if (query.assigneeId) filters.push({ column: "assignee_id", operator: "eq", value: query.assigneeId });
1893
+ return filters;
1938
1894
  }
1939
- function restaurantStatus(rowStatus, metadata) {
1940
- if (rowStatus === "completed" || rowStatus === "cancelled") return rowStatus;
1941
- const stored = metadata.restaurantStatus;
1942
- if (typeof stored === "string" && RESTAURANT_STATUSES.has(stored)) return stored;
1943
- if (rowStatus === "draft") return "new";
1944
- if (typeof rowStatus === "string" && RESTAURANT_STATUSES.has(rowStatus)) return rowStatus;
1945
- return "confirmed";
1895
+ function matchesClientQuery(order, query) {
1896
+ if (Array.isArray(query.kind) && !query.kind.includes(order.kind)) return false;
1897
+ if (Array.isArray(query.status) && !query.status.includes(order.status)) return false;
1898
+ if (query.status && !Array.isArray(query.status) && order.status !== query.status) return false;
1899
+ if (query.source && order.source !== query.source) return false;
1900
+ if (query.tableId && order.tableId !== query.tableId) return false;
1901
+ if (query.assigneeId && order.assigneeId !== query.assigneeId) return false;
1902
+ if (query.dateRange) {
1903
+ const created = dateKey(order.createdAt);
1904
+ if (created < query.dateRange.from || created > query.dateRange.to) return false;
1905
+ }
1906
+ if (query.search) {
1907
+ const search = query.search.toLowerCase();
1908
+ return order.referenceNumber.toLowerCase().includes(search) || (order.partyName?.toLowerCase().includes(search) ?? false) || (order.tableName?.toLowerCase().includes(search) ?? false) || order.items.some((item) => item.name.toLowerCase().includes(search));
1909
+ }
1910
+ return true;
1946
1911
  }
1947
- function ordersMetadata(metadata) {
1948
- return objectValue(metadata?.[ORDERS_METADATA_KEY]);
1912
+ function paginate(items, page, pageSize) {
1913
+ const currentPage = page ?? 1;
1914
+ const limit = pageSize ?? 20;
1915
+ const start = (currentPage - 1) * limit;
1916
+ return { data: items.slice(start, start + limit), total: items.length };
1949
1917
  }
1950
- function mergeOrdersMetadata(metadata, patch) {
1951
- const base = { ...metadata ?? {} };
1952
- const current = ordersMetadata(base);
1953
- const definedPatch = Object.fromEntries(
1954
- Object.entries(patch).filter(([, value]) => value !== void 0)
1955
- );
1956
- const next = { ...current, ...definedPatch };
1957
- return { ...base, [ORDERS_METADATA_KEY]: next };
1918
+ function summarize(orders, dateRange) {
1919
+ const today = dateKey(nowIso());
1920
+ const from = dateRange?.from ?? today;
1921
+ const to = dateRange?.to ?? today;
1922
+ const inRange = orders.filter((order) => {
1923
+ const created = dateKey(order.createdAt);
1924
+ return created >= from && created <= to;
1925
+ });
1926
+ const billable = inRange.filter((order) => order.status !== "cancelled");
1927
+ const revenue = billable.reduce((sum, order) => sum + order.total, 0);
1928
+ const ordersByKind = { dine_in: 0, takeout: 0, delivery: 0 };
1929
+ const ordersByStatus = {};
1930
+ const top = /* @__PURE__ */ new Map();
1931
+ for (const order of inRange) {
1932
+ ordersByKind[order.kind] = (ordersByKind[order.kind] ?? 0) + 1;
1933
+ ordersByStatus[order.status] = (ordersByStatus[order.status] ?? 0) + 1;
1934
+ if (order.status === "cancelled") continue;
1935
+ for (const item of order.items) {
1936
+ const current = top.get(item.name) ?? { name: item.name, quantity: 0, revenue: 0 };
1937
+ current.quantity += item.quantity;
1938
+ current.revenue += item.total;
1939
+ top.set(item.name, current);
1940
+ }
1941
+ }
1942
+ return {
1943
+ todayOrderCount: inRange.length,
1944
+ todayRevenue: revenue,
1945
+ averageTicket: billable.length > 0 ? revenue / billable.length : 0,
1946
+ activeOrderCount: orders.filter((order) => ACTIVE_STATUSES2.has(order.status)).length,
1947
+ ordersByKind,
1948
+ ordersByStatus,
1949
+ topItems: [...top.values()].sort((a, b) => b.quantity - a.quantity).slice(0, 5)
1950
+ };
1958
1951
  }
1959
- function itemToRow(item, orderId, sortOrder, tenantId, unitId) {
1960
- const discount = item.discount ?? 0;
1952
+ function createFayzOrdersProvider(options = {}) {
1953
+ const client = createFayzClient(options);
1954
+ const ordersTable = options.ordersTable ?? DEFAULT_ORDERS_TABLE;
1955
+ const orderItemsTable = options.orderItemsTable ?? DEFAULT_ORDER_ITEMS_TABLE;
1956
+ const tableOptions = { projectId: options.projectId, schema: options.schema, runtime: options.runtime };
1957
+ function resolveTenant() {
1958
+ const value = typeof options.tenantId === "function" ? options.tenantId() : options.tenantId;
1959
+ return value ?? void 0;
1960
+ }
1961
+ function resolveUnit() {
1962
+ const value = typeof options.unitId === "function" ? options.unitId() : options.unitId;
1963
+ return value ?? void 0;
1964
+ }
1965
+ function withTenant(filters) {
1966
+ const tenantId = resolveTenant();
1967
+ return tenantId ? [{ column: "tenant_id", operator: "eq", value: tenantId }, ...filters] : filters;
1968
+ }
1969
+ async function listItemRows(orderIds) {
1970
+ if (orderIds.length === 0) return [];
1971
+ const rows = [];
1972
+ await Promise.all(orderIds.map(async (orderId) => {
1973
+ const response = await client.data.listRows({
1974
+ ...tableOptions,
1975
+ table: orderItemsTable,
1976
+ filters: [{ column: "order_id", operator: "eq", value: orderId }],
1977
+ sortColumn: "sort_order",
1978
+ sortDirection: "asc",
1979
+ limit: 200
1980
+ });
1981
+ rows.push(...response.rows);
1982
+ }));
1983
+ return rows;
1984
+ }
1985
+ async function hydrate(rows) {
1986
+ const itemRows = await listItemRows(rows.map((row) => row.id));
1987
+ const byOrder = /* @__PURE__ */ new Map();
1988
+ for (const itemRow of itemRows) {
1989
+ const item = rowToItem(itemRow);
1990
+ const list = byOrder.get(item.orderId) ?? [];
1991
+ list.push(item);
1992
+ byOrder.set(item.orderId, list);
1993
+ }
1994
+ return rows.map((row) => rowToOrder(row, byOrder.get(row.id) ?? []));
1995
+ }
1996
+ async function listOrders(query = {}) {
1997
+ const response = await client.data.listRows({
1998
+ ...tableOptions,
1999
+ table: ordersTable,
2000
+ filters: withTenant(filtersFromQuery(query)),
2001
+ sortColumn: "created_at",
2002
+ sortDirection: "desc",
2003
+ limit: 500
2004
+ });
2005
+ return (await hydrate(response.rows)).filter((order) => matchesClientQuery(order, query));
2006
+ }
2007
+ async function getById(id) {
2008
+ const response = await client.data.listRows({
2009
+ ...tableOptions,
2010
+ table: ordersTable,
2011
+ filters: withTenant([{ column: "id", operator: "eq", value: id }]),
2012
+ limit: 1
2013
+ });
2014
+ return (await hydrate(response.rows))[0] ?? null;
2015
+ }
1961
2016
  return {
1962
- order_id: orderId,
1963
- ...tenantId ? { tenant_id: tenantId } : {},
1964
- ...unitId ? { unit_id: unitId } : {},
1965
- product_id: item.productId ?? null,
1966
- name: item.name,
1967
- quantity: item.quantity,
1968
- unit_price: item.unitPrice,
1969
- discount,
1970
- total: item.quantity * item.unitPrice - discount,
1971
- sort_order: sortOrder,
1972
- metadata: mergeOrdersMetadata(void 0, {
1973
- modifiers: item.modifiers ?? null,
1974
- kitchenNotes: item.kitchenNotes ?? null
1975
- })
2017
+ async getOrders(query) {
2018
+ const orders = await listOrders(query);
2019
+ return paginate(orders, query.page, query.pageSize);
2020
+ },
2021
+ async getOrderById(id) {
2022
+ return getById(id);
2023
+ },
2024
+ async createOrder(input) {
2025
+ const tenantId = resolveTenant();
2026
+ const unitId = resolveUnit();
2027
+ const orderRow = await client.data.createRow({
2028
+ ...tableOptions,
2029
+ table: ordersTable,
2030
+ row: {
2031
+ ...orderPayload(input),
2032
+ ...tenantId ? { tenant_id: tenantId } : {},
2033
+ ...unitId ? { unit_id: unitId } : {}
2034
+ }
2035
+ });
2036
+ await Promise.all(input.items.map((item, index) => client.data.createRow({
2037
+ ...tableOptions,
2038
+ table: orderItemsTable,
2039
+ row: itemToRow(item, orderRow.id, index, tenantId, unitId)
2040
+ })));
2041
+ return await getById(orderRow.id) ?? rowToOrder(orderRow);
2042
+ },
2043
+ async updateOrderStatus(input) {
2044
+ const completedAt = input.status === "completed" ? nowIso() : void 0;
2045
+ return this.updateOrder(input.orderId, { status: input.status, completedAt });
2046
+ },
2047
+ async updateOrder(id, data) {
2048
+ const existing = await getById(id);
2049
+ const row = await client.data.updateRow({
2050
+ ...tableOptions,
2051
+ table: ordersTable,
2052
+ primaryKeys: { id },
2053
+ row: updatePayload(data, existing?.metadata)
2054
+ });
2055
+ return await getById(row.id) ?? rowToOrder(row);
2056
+ },
2057
+ async cancelOrder(id, reason) {
2058
+ const order = await getById(id);
2059
+ await this.updateOrder(id, {
2060
+ status: "cancelled",
2061
+ notes: reason ? `${order?.notes ? `${order.notes}
2062
+ ` : ""}Cancelled: ${reason}` : order?.notes
2063
+ });
2064
+ },
2065
+ async addOrderItem(orderId, item) {
2066
+ const order = await getById(orderId);
2067
+ const row = await client.data.createRow({
2068
+ ...tableOptions,
2069
+ table: orderItemsTable,
2070
+ row: itemToRow(item, orderId, order?.items.length ?? 0, order?.tenantId, order?.unitId ?? resolveUnit())
2071
+ });
2072
+ const created = rowToItem(row);
2073
+ const items = [...order?.items ?? [], created];
2074
+ await this.updateOrder(orderId, {
2075
+ subtotal: items.reduce((sum, orderItem) => sum + orderItem.total, 0),
2076
+ total: items.reduce((sum, orderItem) => sum + orderItem.total, 0) - (order?.discount ?? 0) + (order?.tax ?? 0)
2077
+ });
2078
+ return created;
2079
+ },
2080
+ async removeOrderItem(itemId) {
2081
+ const orders = await listOrders();
2082
+ const order = orders.find((item) => item.items.some((orderItem) => orderItem.id === itemId));
2083
+ await client.data.deleteRows({ ...tableOptions, table: orderItemsTable, rows: [{ id: itemId }] });
2084
+ if (order) {
2085
+ const items = order.items.filter((item) => item.id !== itemId);
2086
+ const subtotal = items.reduce((sum, item) => sum + item.total, 0);
2087
+ await this.updateOrder(order.id, { subtotal, total: subtotal - order.discount + order.tax });
2088
+ }
2089
+ },
2090
+ async updateOrderItem(itemId, data) {
2091
+ const existingResult = await client.data.listRows({
2092
+ ...tableOptions,
2093
+ table: orderItemsTable,
2094
+ filters: [{ column: "id", operator: "eq", value: itemId }],
2095
+ limit: 1
2096
+ });
2097
+ const existingMetadata = existingResult.rows[0]?.metadata ?? void 0;
2098
+ const metadataPatch = {
2099
+ modifiers: data.modifiers,
2100
+ kitchenNotes: data.kitchenNotes
2101
+ };
2102
+ const row = await client.data.updateRow({
2103
+ ...tableOptions,
2104
+ table: orderItemsTable,
2105
+ primaryKeys: { id: itemId },
2106
+ row: {
2107
+ product_id: data.productId,
2108
+ name: data.name,
2109
+ description: data.description,
2110
+ quantity: data.quantity,
2111
+ unit_price: data.unitPrice,
2112
+ discount: data.discount,
2113
+ total: data.total,
2114
+ sort_order: data.sortOrder,
2115
+ metadata: data.metadata !== void 0 || Object.values(metadataPatch).some((value) => value !== void 0) ? mergeOrdersMetadata(
2116
+ data.metadata !== void 0 ? { ...existingMetadata ?? {}, ...data.metadata } : existingMetadata,
2117
+ metadataPatch
2118
+ ) : void 0
2119
+ }
2120
+ });
2121
+ return rowToItem(row);
2122
+ },
2123
+ async getSummary(dateRange) {
2124
+ return summarize(await listOrders({ dateRange }), dateRange);
2125
+ }
1976
2126
  };
1977
2127
  }
1978
- function rowToItem(row) {
1979
- const quantity = numberValue(row.quantity);
1980
- const unitPrice = numberValue(row.unit_price);
1981
- const discount = numberValue(row.discount);
1982
- const restaurant = ordersMetadata(row.metadata);
2128
+
2129
+ // src/data/mock.ts
2130
+ var uid = 100;
2131
+ function nextId(prefix) {
2132
+ return prefix + "-" + ++uid;
2133
+ }
2134
+ var now = (/* @__PURE__ */ new Date()).toISOString();
2135
+ function makeItem(orderId, name, quantity, unitPrice, sortOrder, productId) {
2136
+ const id = nextId("item");
1983
2137
  return {
1984
- id: row.id,
1985
- orderId: row.order_id ?? "",
1986
- productId: row.product_id ?? void 0,
1987
- name: row.name ?? "Item",
1988
- description: row.description ?? void 0,
2138
+ id,
2139
+ orderId,
2140
+ productId,
2141
+ name,
1989
2142
  quantity,
1990
2143
  unitPrice,
1991
- discount,
1992
- total: numberValue(row.total) || quantity * unitPrice - discount,
1993
- sortOrder: row.sort_order ?? 0,
1994
- modifiers: row.modifiers ?? (typeof restaurant.modifiers === "string" ? restaurant.modifiers : void 0),
1995
- kitchenNotes: row.kitchen_notes ?? (typeof restaurant.kitchenNotes === "string" ? restaurant.kitchenNotes : void 0),
1996
- metadata: row.metadata ?? {},
1997
- createdAt: row.created_at ?? nowIso()
2144
+ discount: 0,
2145
+ total: quantity * unitPrice,
2146
+ sortOrder,
2147
+ createdAt: now
1998
2148
  };
1999
2149
  }
2000
- function rowToOrder(row, items = []) {
2001
- const subtotal = numberValue(row.subtotal) || items.reduce((sum, item) => sum + item.total, 0);
2002
- const discount = numberValue(row.discount);
2003
- const tax = numberValue(row.tax);
2004
- const restaurant = ordersMetadata(row.metadata);
2005
- return {
2006
- id: row.id,
2007
- referenceNumber: row.reference_number ?? row.id,
2008
- kind: row.kind ?? "dine_in",
2009
- status: restaurantStatus(row.status, restaurant),
2010
- partyId: row.party_id ?? void 0,
2011
- partyName: row.party_name ?? (typeof restaurant.partyName === "string" ? restaurant.partyName : void 0),
2012
- assigneeId: row.assignee_id ?? void 0,
2013
- assigneeName: row.assignee_name ?? (typeof restaurant.assigneeName === "string" ? restaurant.assigneeName : void 0),
2014
- locationId: row.location_id ?? void 0,
2015
- subtotal,
2016
- discount,
2017
- tax,
2018
- total: numberValue(row.total) || subtotal - discount + tax,
2019
- currency: row.currency ?? "BRL",
2020
- notes: row.notes ?? void 0,
2021
- tags: row.tags ?? [],
2022
- source: row.source ?? (typeof restaurant.source === "string" ? restaurant.source : "pos"),
2023
- tableId: row.table_id ?? (typeof restaurant.tableId === "string" ? restaurant.tableId : void 0),
2024
- tableName: row.table_name ?? (typeof restaurant.tableName === "string" ? restaurant.tableName : void 0),
2025
- estimatedMinutes: row.estimated_minutes ?? (typeof restaurant.estimatedMinutes === "number" ? restaurant.estimatedMinutes : void 0),
2026
- items,
2027
- metadata: row.metadata ?? {},
2028
- tenantId: row.tenant_id ?? "runtime-tenant",
2029
- unitId: row.unit_id ?? void 0,
2030
- createdAt: row.created_at ?? nowIso(),
2031
- updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
2032
- completedAt: row.completed_at ?? void 0
2150
+ function createSeedOrders() {
2151
+ const order1Id = nextId("order");
2152
+ const order1Items = [
2153
+ makeItem(order1Id, "Classic Burger", 1, 32, 0, "prod-burger-classic"),
2154
+ makeItem(order1Id, "Fries", 1, 15, 1, "prod-fries")
2155
+ ];
2156
+ const order1 = {
2157
+ id: order1Id,
2158
+ referenceNumber: "ORD-0001",
2159
+ kind: "dine_in",
2160
+ status: "preparing",
2161
+ source: "pos",
2162
+ tableId: "table-3",
2163
+ tableName: "Table 3",
2164
+ subtotal: 47,
2165
+ discount: 0,
2166
+ tax: 0,
2167
+ total: 47,
2168
+ currency: "BRL",
2169
+ tags: [],
2170
+ items: order1Items,
2171
+ tenantId: "tenant-mock",
2172
+ createdAt: now,
2173
+ updatedAt: now
2033
2174
  };
2034
- }
2035
- function orderPayload(input) {
2036
- const subtotal = input.items.reduce((sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0), 0);
2037
- return {
2038
- reference_number: referenceNumber(),
2039
- kind: input.kind,
2040
- status: "draft",
2041
- party_id: input.partyId ?? null,
2042
- assignee_id: input.assigneeId ?? null,
2043
- notes: input.notes ?? null,
2044
- subtotal,
2175
+ const order2Id = nextId("order");
2176
+ const order2Items = [
2177
+ makeItem(order2Id, "Cheese Burger", 1, 36, 0, "prod-burger-cheese")
2178
+ ];
2179
+ const order2 = {
2180
+ id: order2Id,
2181
+ referenceNumber: "ORD-0002",
2182
+ kind: "takeout",
2183
+ status: "ready",
2184
+ source: "pos",
2185
+ subtotal: 36,
2186
+ discount: 0,
2187
+ tax: 0,
2188
+ total: 36,
2189
+ currency: "BRL",
2190
+ tags: [],
2191
+ items: order2Items,
2192
+ tenantId: "tenant-mock",
2193
+ createdAt: now,
2194
+ updatedAt: now
2195
+ };
2196
+ const order3Id = nextId("order");
2197
+ const order3Items = [
2198
+ makeItem(order3Id, "Margherita Pizza", 1, 38, 0, "prod-pizza-margherita"),
2199
+ makeItem(order3Id, "Caesar Salad", 1, 22, 1, "prod-salad-caesar"),
2200
+ makeItem(order3Id, "Soda 350ml", 1, 12, 2, "prod-soda")
2201
+ ];
2202
+ const order3 = {
2203
+ id: order3Id,
2204
+ referenceNumber: "ORD-0003",
2205
+ kind: "delivery",
2206
+ status: "confirmed",
2207
+ source: "ifood",
2208
+ subtotal: 72,
2209
+ discount: 0,
2210
+ tax: 0,
2211
+ total: 72,
2212
+ currency: "BRL",
2213
+ tags: [],
2214
+ items: order3Items,
2215
+ tenantId: "tenant-mock",
2216
+ createdAt: now,
2217
+ updatedAt: now
2218
+ };
2219
+ const order4Id = nextId("order");
2220
+ const order4Items = [
2221
+ makeItem(order4Id, "BBQ Bacon Burger", 1, 42, 0, "prod-burger-bbq")
2222
+ ];
2223
+ const order4 = {
2224
+ id: order4Id,
2225
+ referenceNumber: "ORD-0004",
2226
+ kind: "dine_in",
2227
+ status: "new",
2228
+ source: "pos",
2229
+ tableId: "table-7",
2230
+ tableName: "Table 7",
2231
+ subtotal: 42,
2232
+ discount: 0,
2233
+ tax: 0,
2234
+ total: 42,
2235
+ currency: "BRL",
2236
+ tags: [],
2237
+ items: order4Items,
2238
+ tenantId: "tenant-mock",
2239
+ createdAt: now,
2240
+ updatedAt: now
2241
+ };
2242
+ const order5Id = nextId("order");
2243
+ const order5Items = [
2244
+ makeItem(order5Id, "Classic Burger", 2, 32, 0, "prod-burger-classic"),
2245
+ makeItem(order5Id, "Fries", 1, 15, 1, "prod-fries"),
2246
+ makeItem(order5Id, "Onion Rings", 1, 18, 2, "prod-onion-rings"),
2247
+ makeItem(order5Id, "Milkshake", 1, 23, 3, "prod-milkshake")
2248
+ ];
2249
+ const order5 = {
2250
+ id: order5Id,
2251
+ referenceNumber: "ORD-0005",
2252
+ kind: "dine_in",
2253
+ status: "completed",
2254
+ source: "pos",
2255
+ tableId: "table-1",
2256
+ tableName: "Table 1",
2257
+ subtotal: 120,
2045
2258
  discount: 0,
2046
2259
  tax: 0,
2047
- total: subtotal,
2260
+ total: 120,
2048
2261
  currency: "BRL",
2049
2262
  tags: [],
2050
- metadata: mergeOrdersMetadata(void 0, {
2051
- source: input.source ?? "pos",
2052
- tableId: input.tableId ?? null,
2053
- estimatedMinutes: input.estimatedMinutes ?? null,
2054
- restaurantStatus: "new"
2055
- })
2056
- };
2057
- }
2058
- function updatePayload(data, existingMetadata) {
2059
- const payload = {};
2060
- if (data.referenceNumber !== void 0) payload.reference_number = data.referenceNumber;
2061
- if (data.kind !== void 0) payload.kind = data.kind;
2062
- if (data.status !== void 0) payload.status = canonicalStatus(data.status);
2063
- if (data.partyId !== void 0) payload.party_id = data.partyId;
2064
- if (data.assigneeId !== void 0) payload.assignee_id = data.assigneeId;
2065
- if (data.locationId !== void 0) payload.location_id = data.locationId;
2066
- if (data.subtotal !== void 0) payload.subtotal = data.subtotal;
2067
- if (data.discount !== void 0) payload.discount = data.discount;
2068
- if (data.tax !== void 0) payload.tax = data.tax;
2069
- if (data.total !== void 0) payload.total = data.total;
2070
- if (data.currency !== void 0) payload.currency = data.currency;
2071
- if (data.notes !== void 0) payload.notes = data.notes;
2072
- if (data.tags !== void 0) payload.tags = data.tags;
2073
- const metadataBase = data.metadata !== void 0 ? { ...existingMetadata ?? {}, ...data.metadata } : existingMetadata;
2074
- const metadataPatch = {
2075
- partyName: data.partyName,
2076
- assigneeName: data.assigneeName,
2077
- source: data.source,
2078
- tableId: data.tableId,
2079
- tableName: data.tableName,
2080
- estimatedMinutes: data.estimatedMinutes,
2081
- restaurantStatus: data.status
2263
+ items: order5Items,
2264
+ tenantId: "tenant-mock",
2265
+ createdAt: now,
2266
+ updatedAt: now,
2267
+ completedAt: now
2082
2268
  };
2083
- if (data.metadata !== void 0 || Object.values(metadataPatch).some((value) => value !== void 0)) {
2084
- payload.metadata = mergeOrdersMetadata(metadataBase, metadataPatch);
2085
- }
2086
- if (data.completedAt !== void 0) payload.completed_at = data.completedAt;
2087
- return payload;
2088
- }
2089
- function filtersFromQuery(query) {
2090
- const filters = [];
2091
- if (query.kind && !Array.isArray(query.kind)) filters.push({ column: "kind", operator: "eq", value: query.kind });
2092
- if (query.assigneeId) filters.push({ column: "assignee_id", operator: "eq", value: query.assigneeId });
2093
- return filters;
2269
+ return [order1, order2, order3, order4, order5];
2094
2270
  }
2095
- function matchesClientQuery(order, query) {
2096
- if (Array.isArray(query.kind) && !query.kind.includes(order.kind)) return false;
2097
- if (Array.isArray(query.status) && !query.status.includes(order.status)) return false;
2098
- if (query.status && !Array.isArray(query.status) && order.status !== query.status) return false;
2099
- if (query.source && order.source !== query.source) return false;
2100
- if (query.tableId && order.tableId !== query.tableId) return false;
2101
- if (query.assigneeId && order.assigneeId !== query.assigneeId) return false;
2102
- if (query.dateRange) {
2103
- const created = dateKey(order.createdAt);
2104
- if (created < query.dateRange.from || created > query.dateRange.to) return false;
2105
- }
2106
- if (query.search) {
2107
- const search = query.search.toLowerCase();
2108
- return order.referenceNumber.toLowerCase().includes(search) || (order.partyName?.toLowerCase().includes(search) ?? false) || (order.tableName?.toLowerCase().includes(search) ?? false) || order.items.some((item) => item.name.toLowerCase().includes(search));
2109
- }
2110
- return true;
2271
+ function recomputeTotals(order) {
2272
+ order.subtotal = order.items.reduce((sum, i) => sum + i.total, 0);
2273
+ order.total = order.subtotal - order.discount + order.tax;
2111
2274
  }
2112
- function paginate(items, page, pageSize) {
2113
- const currentPage = page ?? 1;
2114
- const limit = pageSize ?? 20;
2115
- const start = (currentPage - 1) * limit;
2116
- return { data: items.slice(start, start + limit), total: items.length };
2275
+ var refCounter = 5;
2276
+ function generateReferenceNumber() {
2277
+ refCounter++;
2278
+ return "ORD-" + String(refCounter).padStart(4, "0");
2117
2279
  }
2118
- function summarize(orders, dateRange) {
2119
- const today = dateKey(nowIso());
2120
- const from = dateRange?.from ?? today;
2121
- const to = dateRange?.to ?? today;
2122
- const inRange = orders.filter((order) => {
2123
- const created = dateKey(order.createdAt);
2124
- return created >= from && created <= to;
2125
- });
2126
- const billable = inRange.filter((order) => order.status !== "cancelled");
2127
- const revenue = billable.reduce((sum, order) => sum + order.total, 0);
2128
- const ordersByKind = { dine_in: 0, takeout: 0, delivery: 0 };
2129
- const ordersByStatus = {};
2130
- const top = /* @__PURE__ */ new Map();
2131
- for (const order of inRange) {
2132
- ordersByKind[order.kind] = (ordersByKind[order.kind] ?? 0) + 1;
2133
- ordersByStatus[order.status] = (ordersByStatus[order.status] ?? 0) + 1;
2134
- if (order.status === "cancelled") continue;
2135
- for (const item of order.items) {
2136
- const current = top.get(item.name) ?? { name: item.name, quantity: 0, revenue: 0 };
2137
- current.quantity += item.quantity;
2138
- current.revenue += item.total;
2139
- top.set(item.name, current);
2140
- }
2141
- }
2142
- return {
2143
- todayOrderCount: inRange.length,
2144
- todayRevenue: revenue,
2145
- averageTicket: billable.length > 0 ? revenue / billable.length : 0,
2146
- activeOrderCount: orders.filter((order) => ACTIVE_STATUSES2.has(order.status)).length,
2147
- ordersByKind,
2148
- ordersByStatus,
2149
- topItems: [...top.values()].sort((a, b) => b.quantity - a.quantity).slice(0, 5)
2150
- };
2280
+ function toDateStr(d) {
2281
+ return d.toISOString().slice(0, 10);
2151
2282
  }
2152
- function createFayzOrdersProvider(options = {}) {
2153
- const client = createFayzClient(options);
2154
- const ordersTable = options.ordersTable ?? DEFAULT_ORDERS_TABLE;
2155
- const orderItemsTable = options.orderItemsTable ?? DEFAULT_ORDER_ITEMS_TABLE;
2156
- const tableOptions = { projectId: options.projectId, schema: options.schema, runtime: options.runtime };
2157
- function resolveTenant() {
2158
- const value = typeof options.tenantId === "function" ? options.tenantId() : options.tenantId;
2159
- return value ?? void 0;
2160
- }
2161
- function resolveUnit() {
2162
- const value = typeof options.unitId === "function" ? options.unitId() : options.unitId;
2163
- return value ?? void 0;
2164
- }
2165
- function withTenant(filters) {
2166
- const tenantId = resolveTenant();
2167
- return tenantId ? [{ column: "tenant_id", operator: "eq", value: tenantId }, ...filters] : filters;
2168
- }
2169
- async function listItemRows(orderIds) {
2170
- if (orderIds.length === 0) return [];
2171
- const rows = [];
2172
- await Promise.all(orderIds.map(async (orderId) => {
2173
- const response = await client.data.listRows({
2174
- ...tableOptions,
2175
- table: orderItemsTable,
2176
- filters: [{ column: "order_id", operator: "eq", value: orderId }],
2177
- sortColumn: "sort_order",
2178
- sortDirection: "asc",
2179
- limit: 200
2180
- });
2181
- rows.push(...response.rows);
2182
- }));
2183
- return rows;
2283
+ function createMockOrdersProvider() {
2284
+ const orders = createSeedOrders();
2285
+ function findOrder(id) {
2286
+ return orders.find((o) => o.id === id);
2184
2287
  }
2185
- async function hydrate(rows) {
2186
- const itemRows = await listItemRows(rows.map((row) => row.id));
2187
- const byOrder = /* @__PURE__ */ new Map();
2188
- for (const itemRow of itemRows) {
2189
- const item = rowToItem(itemRow);
2190
- const list = byOrder.get(item.orderId) ?? [];
2191
- list.push(item);
2192
- byOrder.set(item.orderId, list);
2288
+ function findItem(itemId) {
2289
+ for (const order of orders) {
2290
+ const index = order.items.findIndex((i) => i.id === itemId);
2291
+ if (index !== -1) return { order, item: order.items[index], index };
2193
2292
  }
2194
- return rows.map((row) => rowToOrder(row, byOrder.get(row.id) ?? []));
2195
- }
2196
- async function listOrders(query = {}) {
2197
- const response = await client.data.listRows({
2198
- ...tableOptions,
2199
- table: ordersTable,
2200
- filters: withTenant(filtersFromQuery(query)),
2201
- sortColumn: "created_at",
2202
- sortDirection: "desc",
2203
- limit: 500
2204
- });
2205
- return (await hydrate(response.rows)).filter((order) => matchesClientQuery(order, query));
2206
- }
2207
- async function getById(id) {
2208
- const response = await client.data.listRows({
2209
- ...tableOptions,
2210
- table: ordersTable,
2211
- filters: withTenant([{ column: "id", operator: "eq", value: id }]),
2212
- limit: 1
2213
- });
2214
- return (await hydrate(response.rows))[0] ?? null;
2293
+ return void 0;
2215
2294
  }
2216
- return {
2295
+ const provider = {
2296
+ // ------------------------------------------------------------------
2297
+ // getOrders
2298
+ // ------------------------------------------------------------------
2217
2299
  async getOrders(query) {
2218
- const orders = await listOrders(query);
2219
- return paginate(orders, query.page, query.pageSize);
2220
- },
2221
- async getOrderById(id) {
2222
- return getById(id);
2223
- },
2224
- async createOrder(input) {
2225
- const tenantId = resolveTenant();
2226
- const unitId = resolveUnit();
2227
- const orderRow = await client.data.createRow({
2228
- ...tableOptions,
2229
- table: ordersTable,
2230
- row: {
2231
- ...orderPayload(input),
2232
- ...tenantId ? { tenant_id: tenantId } : {},
2233
- ...unitId ? { unit_id: unitId } : {}
2234
- }
2235
- });
2236
- await Promise.all(input.items.map((item, index) => client.data.createRow({
2237
- ...tableOptions,
2238
- table: orderItemsTable,
2239
- row: itemToRow(item, orderRow.id, index, tenantId, unitId)
2240
- })));
2241
- return await getById(orderRow.id) ?? rowToOrder(orderRow);
2300
+ let result = [...orders];
2301
+ if (query.kind) {
2302
+ const kinds = Array.isArray(query.kind) ? query.kind : [query.kind];
2303
+ result = result.filter((o) => kinds.includes(o.kind));
2304
+ }
2305
+ if (query.status) {
2306
+ const statuses = Array.isArray(query.status) ? query.status : [query.status];
2307
+ result = result.filter((o) => statuses.includes(o.status));
2308
+ }
2309
+ if (query.source) {
2310
+ result = result.filter((o) => o.source === query.source);
2311
+ }
2312
+ if (query.tableId) {
2313
+ result = result.filter((o) => o.tableId === query.tableId);
2314
+ }
2315
+ if (query.assigneeId) {
2316
+ result = result.filter((o) => o.assigneeId === query.assigneeId);
2317
+ }
2318
+ if (query.search) {
2319
+ const q = query.search.toLowerCase();
2320
+ result = result.filter(
2321
+ (o) => o.referenceNumber.toLowerCase().includes(q) || o.partyName?.toLowerCase().includes(q) || o.tableName?.toLowerCase().includes(q) || o.items.some((i) => i.name.toLowerCase().includes(q))
2322
+ );
2323
+ }
2324
+ if (query.dateRange) {
2325
+ const from = query.dateRange.from;
2326
+ const to = query.dateRange.to;
2327
+ result = result.filter((o) => {
2328
+ const d = o.createdAt.slice(0, 10);
2329
+ return d >= from && d <= to;
2330
+ });
2331
+ }
2332
+ result.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
2333
+ const total = result.length;
2334
+ const page = query.page ?? 1;
2335
+ const pageSize = query.pageSize ?? 20;
2336
+ const start = (page - 1) * pageSize;
2337
+ const data = result.slice(start, start + pageSize);
2338
+ return { data, total };
2339
+ },
2340
+ // ------------------------------------------------------------------
2341
+ // getOrderById
2342
+ // ------------------------------------------------------------------
2343
+ async getOrderById(id) {
2344
+ return findOrder(id) ?? null;
2345
+ },
2346
+ // ------------------------------------------------------------------
2347
+ // createOrder
2348
+ // ------------------------------------------------------------------
2349
+ async createOrder(input) {
2350
+ const orderId = nextId("order");
2351
+ const items = input.items.map((item, idx) => ({
2352
+ id: nextId("item"),
2353
+ orderId,
2354
+ productId: item.productId,
2355
+ name: item.name,
2356
+ quantity: item.quantity,
2357
+ unitPrice: item.unitPrice,
2358
+ discount: item.discount ?? 0,
2359
+ total: item.quantity * item.unitPrice - (item.discount ?? 0),
2360
+ sortOrder: idx,
2361
+ modifiers: item.modifiers,
2362
+ kitchenNotes: item.kitchenNotes,
2363
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2364
+ }));
2365
+ const subtotal = items.reduce((sum, i) => sum + i.total, 0);
2366
+ const order = {
2367
+ id: orderId,
2368
+ referenceNumber: generateReferenceNumber(),
2369
+ kind: input.kind,
2370
+ status: "new",
2371
+ source: input.source ?? "pos",
2372
+ tableId: input.tableId,
2373
+ partyId: input.partyId,
2374
+ assigneeId: input.assigneeId,
2375
+ notes: input.notes,
2376
+ estimatedMinutes: input.estimatedMinutes,
2377
+ subtotal,
2378
+ discount: 0,
2379
+ tax: 0,
2380
+ total: subtotal,
2381
+ currency: "BRL",
2382
+ tags: [],
2383
+ items,
2384
+ tenantId: "tenant-mock",
2385
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2386
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2387
+ };
2388
+ orders.push(order);
2389
+ return order;
2242
2390
  },
2391
+ // ------------------------------------------------------------------
2392
+ // updateOrderStatus
2393
+ // ------------------------------------------------------------------
2243
2394
  async updateOrderStatus(input) {
2244
- const completedAt = input.status === "completed" ? nowIso() : void 0;
2245
- return this.updateOrder(input.orderId, { status: input.status, completedAt });
2395
+ const order = findOrder(input.orderId);
2396
+ if (!order) throw new Error(`Order not found: ${input.orderId}`);
2397
+ if (input.status !== "cancelled") {
2398
+ const flow = STATUS_FLOW[order.kind];
2399
+ const currentIdx = flow.indexOf(order.status);
2400
+ const targetIdx = flow.indexOf(input.status);
2401
+ if (targetIdx < 0 || targetIdx !== currentIdx && targetIdx !== currentIdx + 1) {
2402
+ const next = getNextStatus(order.kind, order.status);
2403
+ throw new Error(
2404
+ `Invalid status transition: ${order.status} \u2192 ${input.status}. ` + (next ? `Expected next status: ${next}` : "Order is already in a terminal state.")
2405
+ );
2406
+ }
2407
+ }
2408
+ order.status = input.status;
2409
+ order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2410
+ if (input.status === "completed") {
2411
+ order.completedAt = order.updatedAt;
2412
+ }
2413
+ return order;
2246
2414
  },
2415
+ // ------------------------------------------------------------------
2416
+ // updateOrder
2417
+ // ------------------------------------------------------------------
2247
2418
  async updateOrder(id, data) {
2248
- const existing = await getById(id);
2249
- const row = await client.data.updateRow({
2250
- ...tableOptions,
2251
- table: ordersTable,
2252
- primaryKeys: { id },
2253
- row: updatePayload(data, existing?.metadata)
2254
- });
2255
- return await getById(row.id) ?? rowToOrder(row);
2419
+ const order = findOrder(id);
2420
+ if (!order) throw new Error(`Order not found: ${id}`);
2421
+ const { id: _id, items: _items, tenantId: _t, createdAt: _c, ...safe } = data;
2422
+ Object.assign(order, safe);
2423
+ order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2424
+ return order;
2256
2425
  },
2426
+ // ------------------------------------------------------------------
2427
+ // cancelOrder
2428
+ // ------------------------------------------------------------------
2257
2429
  async cancelOrder(id, reason) {
2258
- const order = await getById(id);
2259
- await this.updateOrder(id, {
2260
- status: "cancelled",
2261
- notes: reason ? `${order?.notes ? `${order.notes}
2262
- ` : ""}Cancelled: ${reason}` : order?.notes
2263
- });
2430
+ const order = findOrder(id);
2431
+ if (!order) throw new Error(`Order not found: ${id}`);
2432
+ order.status = "cancelled";
2433
+ order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2434
+ if (reason) {
2435
+ order.notes = order.notes ? `${order.notes}
2436
+ Cancelled: ${reason}` : `Cancelled: ${reason}`;
2437
+ }
2264
2438
  },
2439
+ // ------------------------------------------------------------------
2440
+ // addOrderItem
2441
+ // ------------------------------------------------------------------
2265
2442
  async addOrderItem(orderId, item) {
2266
- const order = await getById(orderId);
2267
- const row = await client.data.createRow({
2268
- ...tableOptions,
2269
- table: orderItemsTable,
2270
- row: itemToRow(item, orderId, order?.items.length ?? 0, order?.tenantId, order?.unitId ?? resolveUnit())
2271
- });
2272
- const created = rowToItem(row);
2273
- const items = [...order?.items ?? [], created];
2274
- await this.updateOrder(orderId, {
2275
- subtotal: items.reduce((sum, orderItem) => sum + orderItem.total, 0),
2276
- total: items.reduce((sum, orderItem) => sum + orderItem.total, 0) - (order?.discount ?? 0) + (order?.tax ?? 0)
2277
- });
2278
- return created;
2443
+ const order = findOrder(orderId);
2444
+ if (!order) throw new Error(`Order not found: ${orderId}`);
2445
+ const newItem = {
2446
+ id: nextId("item"),
2447
+ orderId,
2448
+ productId: item.productId,
2449
+ name: item.name,
2450
+ quantity: item.quantity,
2451
+ unitPrice: item.unitPrice,
2452
+ discount: item.discount ?? 0,
2453
+ total: item.quantity * item.unitPrice - (item.discount ?? 0),
2454
+ sortOrder: order.items.length,
2455
+ modifiers: item.modifiers,
2456
+ kitchenNotes: item.kitchenNotes,
2457
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2458
+ };
2459
+ order.items.push(newItem);
2460
+ recomputeTotals(order);
2461
+ order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2462
+ return newItem;
2279
2463
  },
2464
+ // ------------------------------------------------------------------
2465
+ // removeOrderItem
2466
+ // ------------------------------------------------------------------
2280
2467
  async removeOrderItem(itemId) {
2281
- const orders = await listOrders();
2282
- const order = orders.find((item) => item.items.some((orderItem) => orderItem.id === itemId));
2283
- await client.data.deleteRows({ ...tableOptions, table: orderItemsTable, rows: [{ id: itemId }] });
2284
- if (order) {
2285
- const items = order.items.filter((item) => item.id !== itemId);
2286
- const subtotal = items.reduce((sum, item) => sum + item.total, 0);
2287
- await this.updateOrder(order.id, { subtotal, total: subtotal - order.discount + order.tax });
2288
- }
2468
+ const found = findItem(itemId);
2469
+ if (!found) throw new Error(`Order item not found: ${itemId}`);
2470
+ found.order.items.splice(found.index, 1);
2471
+ recomputeTotals(found.order);
2472
+ found.order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2289
2473
  },
2474
+ // ------------------------------------------------------------------
2475
+ // updateOrderItem
2476
+ // ------------------------------------------------------------------
2290
2477
  async updateOrderItem(itemId, data) {
2291
- const existingResult = await client.data.listRows({
2292
- ...tableOptions,
2293
- table: orderItemsTable,
2294
- filters: [{ column: "id", operator: "eq", value: itemId }],
2295
- limit: 1
2296
- });
2297
- const existingMetadata = existingResult.rows[0]?.metadata ?? void 0;
2298
- const metadataPatch = {
2299
- modifiers: data.modifiers,
2300
- kitchenNotes: data.kitchenNotes
2301
- };
2302
- const row = await client.data.updateRow({
2303
- ...tableOptions,
2304
- table: orderItemsTable,
2305
- primaryKeys: { id: itemId },
2306
- row: {
2307
- product_id: data.productId,
2308
- name: data.name,
2309
- description: data.description,
2310
- quantity: data.quantity,
2311
- unit_price: data.unitPrice,
2312
- discount: data.discount,
2313
- total: data.total,
2314
- sort_order: data.sortOrder,
2315
- metadata: data.metadata !== void 0 || Object.values(metadataPatch).some((value) => value !== void 0) ? mergeOrdersMetadata(
2316
- data.metadata !== void 0 ? { ...existingMetadata ?? {}, ...data.metadata } : existingMetadata,
2317
- metadataPatch
2318
- ) : void 0
2319
- }
2320
- });
2321
- return rowToItem(row);
2478
+ const found = findItem(itemId);
2479
+ if (!found) throw new Error(`Order item not found: ${itemId}`);
2480
+ const { id: _id, orderId: _oid, createdAt: _c, ...safe } = data;
2481
+ Object.assign(found.item, safe);
2482
+ found.item.total = found.item.quantity * found.item.unitPrice - found.item.discount;
2483
+ recomputeTotals(found.order);
2484
+ found.order.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2485
+ return found.item;
2322
2486
  },
2487
+ // ------------------------------------------------------------------
2488
+ // getSummary
2489
+ // ------------------------------------------------------------------
2323
2490
  async getSummary(dateRange) {
2324
- return summarize(await listOrders({ dateRange }), dateRange);
2491
+ const today = toDateStr(/* @__PURE__ */ new Date());
2492
+ const from = dateRange?.from ?? today;
2493
+ const to = dateRange?.to ?? today;
2494
+ const inRange = orders.filter((o) => {
2495
+ const d = o.createdAt.slice(0, 10);
2496
+ return d >= from && d <= to;
2497
+ });
2498
+ const completed = inRange.filter((o) => o.status !== "cancelled");
2499
+ const revenue = completed.reduce((sum, o) => sum + o.total, 0);
2500
+ const activeStatuses = /* @__PURE__ */ new Set(["new", "confirmed", "preparing", "ready", "served", "dispatched"]);
2501
+ const active = orders.filter((o) => activeStatuses.has(o.status));
2502
+ const ordersByKind = { dine_in: 0, takeout: 0, delivery: 0 };
2503
+ const ordersByStatus = {};
2504
+ for (const o of inRange) {
2505
+ ordersByKind[o.kind] = (ordersByKind[o.kind] || 0) + 1;
2506
+ ordersByStatus[o.status] = (ordersByStatus[o.status] || 0) + 1;
2507
+ }
2508
+ const itemMap = /* @__PURE__ */ new Map();
2509
+ for (const o of completed) {
2510
+ for (const item of o.items) {
2511
+ const existing = itemMap.get(item.name);
2512
+ if (existing) {
2513
+ existing.quantity += item.quantity;
2514
+ existing.revenue += item.total;
2515
+ } else {
2516
+ itemMap.set(item.name, { name: item.name, quantity: item.quantity, revenue: item.total });
2517
+ }
2518
+ }
2519
+ }
2520
+ const topItems = Array.from(itemMap.values()).sort((a, b) => b.quantity - a.quantity).slice(0, 5);
2521
+ return {
2522
+ todayOrderCount: inRange.length,
2523
+ todayRevenue: revenue,
2524
+ averageTicket: completed.length > 0 ? revenue / completed.length : 0,
2525
+ activeOrderCount: active.length,
2526
+ ordersByKind,
2527
+ ordersByStatus,
2528
+ topItems
2529
+ };
2325
2530
  }
2326
2531
  };
2532
+ return provider;
2327
2533
  }
2328
2534
 
2329
2535
  // src/index.ts
2330
- var OrdersPage = React2__default.lazy(() => import('./OrdersPage-QV2YWARF.js').then((m) => ({ default: m.OrdersPage })));
2536
+ var OrdersPage = React.lazy(() => import('./OrdersPage-5326V62H.js').then((m) => ({ default: m.OrdersPage })));
2537
+ var OrderDetailRoute = React.lazy(() => import('./OrderDetailRoute-LTPDQXQU.js').then((m) => ({ default: m.OrderDetailRoute })));
2331
2538
  var DEFAULT_LABELS = {
2332
2539
  pageTitle: "Orders",
2333
2540
  kanban: "Orders",
@@ -2355,65 +2562,29 @@ function resolveConfig(options) {
2355
2562
  createRoute: options?.createRoute,
2356
2563
  editRoute: options?.editRoute,
2357
2564
  canEditOrder: options?.canEditOrder,
2358
- orderRoute: options?.orderRoute,
2359
- floorView: options?.floorView !== false,
2360
- boardView: options?.boardView !== false,
2361
- deliveryRoute: options?.deliveryRoute,
2362
- detailRail: options?.detailRail ?? "operate"
2565
+ orderRoute: options?.orderRoute
2363
2566
  };
2364
2567
  }
2365
2568
  function createOrdersPlugin(options) {
2366
2569
  const config = resolveConfig(options);
2367
- const provider = options?.dataProvider ?? (options?.variant === "commercial" ? createCoreOrdersProvider() : createMockOrdersProvider());
2570
+ const provider = options?.dataProvider ?? createCoreOrdersProvider();
2368
2571
  const store7 = createOrdersStore(provider, {
2369
2572
  onOrderCompleted: config.onOrderCompleted,
2370
2573
  onOrderCancelled: config.onOrderCancelled
2371
2574
  });
2372
- const screen = (extra) => () => React2__default.createElement(
2373
- React2__default.Suspense,
2575
+ const PageComponent = () => React.createElement(
2576
+ React.Suspense,
2374
2577
  { fallback: null },
2375
- React2__default.createElement(OrdersPage, {
2376
- config: extra ? { ...config, ...extra } : config,
2377
- provider,
2378
- store: store7,
2379
- registries: ordersRegistries,
2380
- variant: options?.variant ?? "restaurant"
2381
- })
2578
+ React.createElement(OrdersPage, { config, provider, store: store7, registries: ordersRegistries })
2382
2579
  );
2383
- const PageComponent = screen();
2384
- const DeliveryRoute = screen({ pinnedView: "delivery" });
2385
- const OrderDetailRoute = ({ id }) => id ? React2__default.createElement(OrderDetailPage, {
2386
- orderId: id,
2387
- backTo: "/orders",
2388
- // The header had no verbs at all — not just no "Editar". The page
2389
- // always had the slot; nobody was filling it.
2390
- actions: (order) => {
2391
- const edit = options?.editRoute;
2392
- if (!edit) return null;
2393
- const allowed = options?.canEditOrder ?? orderIsEditable;
2394
- if (!allowed(order)) return null;
2395
- return React2__default.createElement(PageActions, {
2396
- actions: [{
2397
- id: "edit",
2398
- testId: "order-edit",
2399
- label: "Editar pedido",
2400
- icon: Pencil,
2401
- intent: "primary",
2402
- onSelect: () => adminNavigateTo(edit(order.id))
2403
- }]
2404
- });
2405
- },
2406
- // Os verbos do dinheiro. O seam existia desde a primeira passada do
2407
- // archetype e nunca foi preenchido fora da loja — entao a tela sabia
2408
- // dizer "sem conta a receber" e nao oferecia como criar uma. Ver
2409
- // ./views/OrderReceivableActions.
2410
- receivableActions: (order, ctx) => React2__default.createElement(OrderReceivableActions, {
2411
- orderId: order.id,
2412
- orderTotal: order.total ?? 0,
2413
- receivable: ctx.receivable,
2414
- reload: ctx.reload
2580
+ const DetailComponent = ({ id }) => React.createElement(
2581
+ React.Suspense,
2582
+ { fallback: null },
2583
+ React.createElement(OrderDetailRoute, {
2584
+ id,
2585
+ options: { editRoute: options?.editRoute, canEditOrder: options?.canEditOrder }
2415
2586
  })
2416
- }) : null;
2587
+ );
2417
2588
  return {
2418
2589
  id: "orders",
2419
2590
  // Como esta conta refaz o módulo com as opções DELA. Ver PluginRebuild:
@@ -2430,7 +2601,18 @@ function createOrdersPlugin(options) {
2430
2601
  scope: options?.scope ?? "vertical",
2431
2602
  verticalId: options?.verticalId,
2432
2603
  defaultEnabled: true,
2433
- dependencies: ["menu"],
2604
+ // No `menu`: a sale is not a restaurant's, and a dependency lights up by
2605
+ // drag — every account with Orders would grow a Cardápio line.
2606
+ dependencies: [],
2607
+ // What installing this provisions, byte-for-byte the files under
2608
+ // src/migrations (embed barrel). The spine holds the orders tables; this
2609
+ // list holds the doors the plugin adds on top of them.
2610
+ migrations: MIGRATIONS.map((m) => ({
2611
+ id: `orders-${m.id}`,
2612
+ version: "1.0.0",
2613
+ sql: m.sql,
2614
+ description: m.id
2615
+ })),
2434
2616
  events: [
2435
2617
  {
2436
2618
  name: "orders.order_completed",
@@ -2487,17 +2669,9 @@ function createOrdersPlugin(options) {
2487
2669
  component: PageComponent,
2488
2670
  permission: { feature: "orders", action: "read" }
2489
2671
  },
2490
- ...options?.deliveryRoute ? [{
2491
- path: options.deliveryRoute,
2492
- component: DeliveryRoute,
2493
- permission: { feature: "orders", action: "read" }
2494
- }] : [],
2495
2672
  {
2496
- // The page the rail's "Ver pedido completo" points at. It used to exist
2497
- // only in plugin-shop, so the restaurant's link opened a blank screen —
2498
- // the route was never mounted, not the data missing.
2499
2673
  path: "/orders/:id",
2500
- component: OrderDetailRoute,
2674
+ component: DetailComponent,
2501
2675
  permission: { feature: "orders", action: "read" }
2502
2676
  }
2503
2677
  ],
@@ -2568,7 +2742,7 @@ function createOrdersPlugin(options) {
2568
2742
  label: config.labels.pageTitle,
2569
2743
  icon: "ClipboardList",
2570
2744
  component: (() => {
2571
- const OrdersSettingsTab = () => React2__default.createElement(PluginSettingsPanel, {
2745
+ const OrdersSettingsTab = () => React.createElement(PluginSettingsPanel, {
2572
2746
  title: "Orders Settings",
2573
2747
  registries: ordersRegistries,
2574
2748
  routeBase: "/settings/orders"
@@ -2588,10 +2762,10 @@ function createOrdersPlugin(options) {
2588
2762
  label: "Delivery",
2589
2763
  icon: "Bike",
2590
2764
  component: (() => {
2591
- const DeliverySettingsTab = () => React2__default.createElement(
2592
- React2__default.Suspense,
2765
+ const DeliverySettingsTab = () => React.createElement(
2766
+ React.Suspense,
2593
2767
  { fallback: null },
2594
- React2__default.createElement(DeliverySettings)
2768
+ React.createElement(DeliverySettings)
2595
2769
  );
2596
2770
  DeliverySettingsTab.displayName = "DeliverySettingsTab";
2597
2771
  return DeliverySettingsTab;
@@ -2616,6 +2790,6 @@ function createOrdersPlugin(options) {
2616
2790
  };
2617
2791
  }
2618
2792
 
2619
- export { AIQFOME_CONNECTOR_ID, AIQFOME_SETTINGS, AUTO_ACCEPT_SETTING, IFOOD_CONNECTOR_ID, IFOOD_SETTINGS, KEETA_CONNECTOR_ID, KEETA_SETTINGS, NINETY_NINE_FOOD_CONNECTOR_ID, NINETY_NINE_FOOD_SETTINGS, PREP_TIME_SETTING, RAPPI_CONNECTOR_ID, RAPPI_SETTINGS, UBER_EATS_CONNECTOR_ID, UBER_EATS_SETTINGS, aiqfomeConnectorDef, createCoreOrdersProvider, createFayzOrdersProvider, createOrdersPlugin, ifoodConnectorDef, keetaConnectorDef, marketplacePermissions, nineNineFoodConnectorDef, rappiConnectorDef, uberEatsConnectorDef };
2793
+ export { ADVANCE_VERB, AIQFOME_CONNECTOR_ID, AIQFOME_SETTINGS, AUTO_ACCEPT_SETTING, DEFAULT_STATUS_CONFIGS, IFOOD_CONNECTOR_ID, IFOOD_SETTINGS, KANBAN_COLUMNS, KEETA_CONNECTOR_ID, KEETA_SETTINGS, NINETY_NINE_FOOD_CONNECTOR_ID, NINETY_NINE_FOOD_SETTINGS, PREP_TIME_SETTING, RAPPI_CONNECTOR_ID, RAPPI_SETTINGS, STATUS_FLOW, UBER_EATS_CONNECTOR_ID, UBER_EATS_SETTINGS, aiqfomeConnectorDef, createCoreOrdersProvider, createFayzOrdersProvider, createMockOrdersProvider, createOrdersPlugin, getNextStatus, getPreviousStatus, ifoodConnectorDef, keetaConnectorDef, mapToKanbanColumn, marketplacePermissions, nineNineFoodConnectorDef, rappiConnectorDef, uberEatsConnectorDef };
2620
2794
  //# sourceMappingURL=index.js.map
2621
2795
  //# sourceMappingURL=index.js.map