@fayz-ai/plugin-orders 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/OrdersContext.d.ts +40 -0
  4. package/dist/OrdersContext.d.ts.map +1 -0
  5. package/dist/OrdersPage-2IXARHDR.cjs +157 -0
  6. package/dist/OrdersPage-2IXARHDR.cjs.map +1 -0
  7. package/dist/OrdersPage-LJIEIZYE.js +155 -0
  8. package/dist/OrdersPage-LJIEIZYE.js.map +1 -0
  9. package/dist/OrdersPage.d.ts +13 -0
  10. package/dist/OrdersPage.d.ts.map +1 -0
  11. package/dist/chunk-7LFVFZKZ.js +23 -0
  12. package/dist/chunk-7LFVFZKZ.js.map +1 -0
  13. package/dist/chunk-WBGECNYU.cjs +28 -0
  14. package/dist/chunk-WBGECNYU.cjs.map +1 -0
  15. package/dist/data/fayz.d.ts +18 -0
  16. package/dist/data/fayz.d.ts.map +1 -0
  17. package/dist/data/mock.d.ts +3 -0
  18. package/dist/data/mock.d.ts.map +1 -0
  19. package/dist/data/types.d.ts +17 -0
  20. package/dist/data/types.d.ts.map +1 -0
  21. package/dist/index.cjs +1185 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +42 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +1178 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/locales/en.d.ts +2 -0
  28. package/dist/locales/en.d.ts.map +1 -0
  29. package/dist/locales/index.d.ts +2 -0
  30. package/dist/locales/index.d.ts.map +1 -0
  31. package/dist/locales/pt-BR.d.ts +2 -0
  32. package/dist/locales/pt-BR.d.ts.map +1 -0
  33. package/dist/registries.d.ts +3 -0
  34. package/dist/registries.d.ts.map +1 -0
  35. package/dist/store.d.ts +29 -0
  36. package/dist/store.d.ts.map +1 -0
  37. package/dist/types.d.ts +119 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/dist/views/OrderKanbanView.d.ts +3 -0
  40. package/dist/views/OrderKanbanView.d.ts.map +1 -0
  41. package/package.json +54 -0
  42. package/src/OrdersContext.tsx +47 -0
  43. package/src/OrdersPage.tsx +20 -0
  44. package/src/data/fayz.ts +456 -0
  45. package/src/data/mock.ts +512 -0
  46. package/src/data/types.ts +23 -0
  47. package/src/index.ts +217 -0
  48. package/src/locales/en.ts +53 -0
  49. package/src/locales/index.ts +4 -0
  50. package/src/locales/pt-BR.ts +53 -0
  51. package/src/registries.ts +32 -0
  52. package/src/store.ts +191 -0
  53. package/src/types.ts +190 -0
  54. package/src/views/OrderKanbanView.tsx +240 -0
package/src/index.ts ADDED
@@ -0,0 +1,217 @@
1
+ import React from 'react'
2
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
3
+ import type { EntityLookup } from '@fayz-ai/saas'
4
+ import type { OrdersDataProvider } from './data/types'
5
+ import type { RestaurantOrder } from './types'
6
+ import { createMockOrdersProvider } from './data/mock'
7
+ import { createOrdersStore } from './store'
8
+ import { ordersRegistries } from './registries'
9
+ import { ordersLocales } from './locales'
10
+ import { PluginSettingsPanel } from '@fayz-ai/saas'
11
+
12
+ const OrdersPage = React.lazy(() => import('./OrdersPage').then((m) => ({ default: m.OrdersPage })))
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Labels
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface OrdersPluginLabels {
19
+ pageTitle: string
20
+ kanban: string
21
+ newOrder: string
22
+ orderList: string
23
+ }
24
+
25
+ const DEFAULT_LABELS: OrdersPluginLabels = {
26
+ pageTitle: 'Orders',
27
+ kanban: 'Orders',
28
+ newOrder: 'New Order',
29
+ orderList: 'History',
30
+ }
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Currency + sources
34
+ // ---------------------------------------------------------------------------
35
+
36
+ const DEFAULT_CURRENCY = { code: 'BRL', locale: 'pt-BR', symbol: 'R$' }
37
+
38
+ export interface OrderSourceOption {
39
+ value: string
40
+ label: string
41
+ icon?: string
42
+ }
43
+
44
+ const DEFAULT_ORDER_SOURCES: OrderSourceOption[] = [
45
+ { value: 'dine_in', label: 'Dine-in', icon: '🍽️' },
46
+ { value: 'takeout', label: 'Takeout', icon: '🥡' },
47
+ ]
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Options
51
+ // ---------------------------------------------------------------------------
52
+
53
+ export interface OrdersPluginOptions {
54
+ modules?: {
55
+ delivery?: boolean
56
+ takeout?: boolean
57
+ }
58
+ labels?: Partial<OrdersPluginLabels>
59
+ currency?: { code?: string; locale?: string; symbol?: string }
60
+ navPosition?: number
61
+ navSection?: 'main' | 'secondary'
62
+ scope?: PluginScope
63
+ verticalId?: VerticalId
64
+ dataProvider?: OrdersDataProvider
65
+ menuItemLookup?: EntityLookup
66
+ staffLookup?: EntityLookup
67
+ onOrderCompleted?: (order: RestaurantOrder) => Promise<void>
68
+ orderSources?: OrderSourceOption[]
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Config resolver
73
+ // ---------------------------------------------------------------------------
74
+
75
+ function resolveConfig(options?: OrdersPluginOptions) {
76
+ return {
77
+ modules: {
78
+ delivery: options?.modules?.delivery !== false,
79
+ takeout: options?.modules?.takeout !== false,
80
+ },
81
+ labels: { ...DEFAULT_LABELS, ...options?.labels } as any,
82
+ currency: { ...DEFAULT_CURRENCY, ...options?.currency },
83
+ orderSources: options?.orderSources ?? DEFAULT_ORDER_SOURCES,
84
+ menuItemLookup: options?.menuItemLookup,
85
+ staffLookup: options?.staffLookup,
86
+ onOrderCompleted: options?.onOrderCompleted,
87
+ }
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Factory
92
+ // ---------------------------------------------------------------------------
93
+
94
+ export function createOrdersPlugin(options?: OrdersPluginOptions): PluginManifest {
95
+ const config = resolveConfig(options)
96
+ const provider = options?.dataProvider ?? createMockOrdersProvider()
97
+ const store = createOrdersStore(provider)
98
+
99
+ const PageComponent: React.FC<any> = () =>
100
+ React.createElement(React.Suspense, { fallback: null },
101
+ React.createElement(OrdersPage, { config, provider, store, registries: ordersRegistries })
102
+ )
103
+
104
+ return {
105
+ id: 'orders',
106
+ name: config.labels.pageTitle,
107
+ icon: 'ClipboardList',
108
+ version: '1.0.0',
109
+ scope: options?.scope ?? 'vertical',
110
+ verticalId: options?.verticalId,
111
+ defaultEnabled: true,
112
+ dependencies: ['menu'],
113
+ navigation: [
114
+ {
115
+ section: options?.navSection ?? 'main',
116
+ position: options?.navPosition ?? 2,
117
+ label: config.labels.pageTitle,
118
+ route: '/orders',
119
+ icon: 'ClipboardList',
120
+ permission: { feature: 'orders', action: 'read' as const },
121
+ },
122
+ ],
123
+ routes: [
124
+ {
125
+ path: '/orders',
126
+ component: PageComponent,
127
+ permission: { feature: 'orders', action: 'read' as const },
128
+ },
129
+ ],
130
+ widgets: [],
131
+ aiTools: [
132
+ {
133
+ id: 'orders.today-summary',
134
+ name: 'getOrdersSummary',
135
+ description: 'Returns order count, revenue, and average ticket for today or a given period.',
136
+ icon: 'ClipboardList',
137
+ mode: 'read' as const,
138
+ category: 'Orders',
139
+ parameters: {
140
+ type: 'object' as const,
141
+ properties: {
142
+ period: { type: 'string' as const, description: 'today, week, month' },
143
+ },
144
+ },
145
+ suggestions: [
146
+ { label: 'How many orders today?' },
147
+ { label: "What's the average ticket today?" },
148
+ { label: "What's the most ordered item?" },
149
+ ],
150
+ },
151
+ {
152
+ id: 'orders.active-orders',
153
+ name: 'getActiveOrders',
154
+ description: 'Lists currently active (non-completed) orders.',
155
+ icon: 'Clock',
156
+ mode: 'read' as const,
157
+ category: 'Orders',
158
+ parameters: {
159
+ type: 'object' as const,
160
+ properties: {
161
+ kind: { type: 'string' as const, enum: ['dine_in', 'takeout', 'delivery'] },
162
+ status: { type: 'string' as const, description: 'Filter by status' },
163
+ },
164
+ },
165
+ suggestions: [
166
+ { label: 'What orders are being prepared right now?' },
167
+ { label: 'Any delivery orders waiting?' },
168
+ ],
169
+ },
170
+ {
171
+ id: 'orders.create-order',
172
+ name: 'createOrder',
173
+ description: 'Creates a new restaurant order with items.',
174
+ icon: 'PlusCircle',
175
+ mode: 'persist' as const,
176
+ category: 'Orders',
177
+ parameters: {
178
+ type: 'object' as const,
179
+ properties: {
180
+ kind: { type: 'string' as const, enum: ['dine_in', 'takeout', 'delivery'] },
181
+ tableNumber: { type: 'number' as const, description: 'Table number (for dine-in)' },
182
+ items: { type: 'array' as const, description: 'Item names and quantities' },
183
+ },
184
+ required: ['kind', 'items'],
185
+ },
186
+ permission: { feature: 'orders', action: 'create' as const },
187
+ },
188
+ ],
189
+ registries: ordersRegistries,
190
+ settings: [
191
+ {
192
+ id: 'orders',
193
+ label: config.labels.pageTitle,
194
+ icon: 'ClipboardList',
195
+ component: (() => {
196
+ const OrdersSettingsTab: React.ComponentType<unknown> = () =>
197
+ React.createElement(PluginSettingsPanel, {
198
+ title: 'Orders Settings',
199
+ subtitle: 'Order sources and preferences',
200
+ registries: ordersRegistries,
201
+ routeBase: '/settings/orders',
202
+ })
203
+ OrdersSettingsTab.displayName = 'OrdersSettingsTab'
204
+ return OrdersSettingsTab
205
+ })(),
206
+ order: 22,
207
+ permission: { feature: 'orders', action: 'read' as const },
208
+ },
209
+ ],
210
+ locales: ordersLocales,
211
+ }
212
+ }
213
+
214
+ export type { OrdersDataProvider } from './data/types'
215
+ export { createFayzOrdersProvider } from './data/fayz'
216
+ export type { FayzOrdersProviderOptions } from './data/fayz'
217
+ export type { ResolvedOrdersConfig } from './OrdersContext'
@@ -0,0 +1,53 @@
1
+ export const en: Record<string, string> = {
2
+ 'orders.title': 'Orders',
3
+ 'orders.nav.kanban': 'Orders',
4
+ 'orders.nav.list': 'History',
5
+ 'orders.nav.new': 'New Order',
6
+ 'orders.nav.dashboard': 'Dashboard',
7
+ 'orders.kanban.title': 'Orders',
8
+ 'orders.kanban.subtitle': 'Active orders',
9
+ 'orders.kanban.noOrders': 'No active orders',
10
+ 'orders.kanban.noOrdersDesc': 'Orders will appear here as they come in',
11
+ 'orders.kanban.newOrder': 'New Order',
12
+ 'orders.kanban.advance': 'Advance',
13
+ 'orders.kanban.cancel': 'Cancel Order',
14
+ 'orders.kanban.column.new': 'New',
15
+ 'orders.kanban.column.confirmed': 'Confirmed',
16
+ 'orders.kanban.column.preparing': 'Preparing',
17
+ 'orders.kanban.column.ready': 'Ready',
18
+ 'orders.kanban.column.out': 'Out',
19
+ 'orders.kanban.column.completed': 'Completed',
20
+ 'orders.kanban.column.cancelled': 'Cancelled',
21
+ 'orders.detail.title': 'Order Details',
22
+ 'orders.detail.items': 'Items',
23
+ 'orders.detail.table': 'Table',
24
+ 'orders.detail.customer': 'Customer',
25
+ 'orders.detail.waiter': 'Waiter',
26
+ 'orders.detail.source': 'Source',
27
+ 'orders.detail.subtotal': 'Subtotal',
28
+ 'orders.detail.discount': 'Discount',
29
+ 'orders.detail.tax': 'Tax',
30
+ 'orders.detail.total': 'Total',
31
+ 'orders.detail.notes': 'Notes',
32
+ 'orders.newOrder.title': 'New Order',
33
+ 'orders.newOrder.selectKind': 'Order type',
34
+ 'orders.newOrder.dineIn': 'Dine-in',
35
+ 'orders.newOrder.takeout': 'Takeout',
36
+ 'orders.newOrder.delivery': 'Delivery',
37
+ 'orders.newOrder.selectTable': 'Select table',
38
+ 'orders.newOrder.addItems': 'Add items',
39
+ 'orders.newOrder.searchItems': 'Search menu...',
40
+ 'orders.newOrder.noItems': 'No items added yet',
41
+ 'orders.newOrder.confirm': 'Place Order',
42
+ 'orders.newOrder.cancel': 'Cancel',
43
+ 'orders.list.title': 'Order History',
44
+ 'orders.list.subtitle': 'All orders',
45
+ 'orders.list.search': 'Search orders...',
46
+ 'orders.list.noOrders': 'No orders found',
47
+ 'orders.source.dine_in': 'Dine-in',
48
+ 'orders.source.takeout': 'Takeout',
49
+ 'orders.source.ifood': 'iFood',
50
+ 'orders.source.99food': '99Food',
51
+ 'orders.source.delivery': 'Delivery',
52
+ 'orders.settings.title': 'Orders Settings',
53
+ }
@@ -0,0 +1,4 @@
1
+ import { en } from './en'
2
+ import { ptBR } from './pt-BR'
3
+
4
+ export const ordersLocales: Record<string, Record<string, string>> = { en, 'pt-BR': ptBR }
@@ -0,0 +1,53 @@
1
+ export const ptBR: Record<string, string> = {
2
+ 'orders.title': 'Pedidos',
3
+ 'orders.nav.kanban': 'Pedidos',
4
+ 'orders.nav.list': 'Histórico',
5
+ 'orders.nav.new': 'Novo Pedido',
6
+ 'orders.nav.dashboard': 'Painel',
7
+ 'orders.kanban.title': 'Pedidos',
8
+ 'orders.kanban.subtitle': 'Pedidos ativos',
9
+ 'orders.kanban.noOrders': 'Nenhum pedido ativo',
10
+ 'orders.kanban.noOrdersDesc': 'Os pedidos aparecerão aqui conforme chegarem',
11
+ 'orders.kanban.newOrder': 'Novo Pedido',
12
+ 'orders.kanban.advance': 'Avançar',
13
+ 'orders.kanban.cancel': 'Cancelar Pedido',
14
+ 'orders.kanban.column.new': 'Novo',
15
+ 'orders.kanban.column.confirmed': 'Confirmado',
16
+ 'orders.kanban.column.preparing': 'Preparando',
17
+ 'orders.kanban.column.ready': 'Pronto',
18
+ 'orders.kanban.column.out': 'Saiu',
19
+ 'orders.kanban.column.completed': 'Concluído',
20
+ 'orders.kanban.column.cancelled': 'Cancelado',
21
+ 'orders.detail.title': 'Detalhes do Pedido',
22
+ 'orders.detail.items': 'Itens',
23
+ 'orders.detail.table': 'Mesa',
24
+ 'orders.detail.customer': 'Cliente',
25
+ 'orders.detail.waiter': 'Garçom',
26
+ 'orders.detail.source': 'Origem',
27
+ 'orders.detail.subtotal': 'Subtotal',
28
+ 'orders.detail.discount': 'Desconto',
29
+ 'orders.detail.tax': 'Imposto',
30
+ 'orders.detail.total': 'Total',
31
+ 'orders.detail.notes': 'Observações',
32
+ 'orders.newOrder.title': 'Novo Pedido',
33
+ 'orders.newOrder.selectKind': 'Tipo de pedido',
34
+ 'orders.newOrder.dineIn': 'Mesa',
35
+ 'orders.newOrder.takeout': 'Para Viagem',
36
+ 'orders.newOrder.delivery': 'Delivery',
37
+ 'orders.newOrder.selectTable': 'Selecionar mesa',
38
+ 'orders.newOrder.addItems': 'Adicionar itens',
39
+ 'orders.newOrder.searchItems': 'Buscar no cardápio...',
40
+ 'orders.newOrder.noItems': 'Nenhum item adicionado',
41
+ 'orders.newOrder.confirm': 'Fazer Pedido',
42
+ 'orders.newOrder.cancel': 'Cancelar',
43
+ 'orders.list.title': 'Histórico de Pedidos',
44
+ 'orders.list.subtitle': 'Todos os pedidos',
45
+ 'orders.list.search': 'Buscar pedidos...',
46
+ 'orders.list.noOrders': 'Nenhum pedido encontrado',
47
+ 'orders.source.dine_in': 'Mesa',
48
+ 'orders.source.takeout': 'Para Viagem',
49
+ 'orders.source.ifood': 'iFood',
50
+ 'orders.source.99food': '99Food',
51
+ 'orders.source.delivery': 'Delivery',
52
+ 'orders.settings.title': 'Configurações de Pedidos',
53
+ }
@@ -0,0 +1,32 @@
1
+ import type { PluginRegistryDef } from '@fayz-ai/core'
2
+ import type { EntityDef } from '@fayz-ai/core'
3
+
4
+ const orderSourceEntity: EntityDef = {
5
+ name: 'Order Source',
6
+ namePlural: 'Order Sources',
7
+ icon: 'Globe',
8
+ displayField: 'name',
9
+ defaultSort: 'name',
10
+ fields: [
11
+ { key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
12
+ { key: 'icon', label: 'Icon', type: 'text', showInTable: true },
13
+ { key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
14
+ ],
15
+ data: { table: 'order_sources', tenantScoped: true },
16
+ }
17
+
18
+ export const ordersRegistries: PluginRegistryDef[] = [
19
+ {
20
+ id: 'order-sources',
21
+ entity: orderSourceEntity,
22
+ icon: 'Globe',
23
+ description: 'Order sources and delivery platforms',
24
+ display: 'table',
25
+ seedData: [
26
+ { id: 'os-dine', name: 'Dine-in', icon: '🍽️', isActive: true },
27
+ { id: 'os-takeout', name: 'Takeout', icon: '🥡', isActive: true },
28
+ { id: 'os-ifood', name: 'iFood', icon: '🔴', isActive: true },
29
+ { id: 'os-99food', name: '99Food', icon: '🟡', isActive: true },
30
+ ],
31
+ },
32
+ ]
package/src/store.ts ADDED
@@ -0,0 +1,191 @@
1
+ import { createStore, type StoreApi } from 'zustand/vanilla'
2
+ import { dedup } from '@fayz-ai/saas'
3
+ import { toast } from 'sonner'
4
+ import type { OrdersDataProvider } from './data/types'
5
+ import type {
6
+ RestaurantOrder, OrdersSummary,
7
+ OrderQuery, CreateOrderInput, CreateOrderItemInput,
8
+ UpdateOrderStatusInput,
9
+ } from './types'
10
+ import { getNextStatus } from './types'
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Store state
14
+ // ---------------------------------------------------------------------------
15
+
16
+ export interface OrdersUIState {
17
+ // Data cache
18
+ orders: RestaurantOrder[]
19
+ ordersTotal: number
20
+ ordersLoading: boolean
21
+ orderQuery: OrderQuery
22
+
23
+ activeOrders: RestaurantOrder[]
24
+ activeOrdersLoading: boolean
25
+
26
+ summary: OrdersSummary | null
27
+ summaryLoading: boolean
28
+
29
+ selectedOrderId: string | null
30
+
31
+ // Actions
32
+ fetchOrders(query: OrderQuery): Promise<void>
33
+ fetchActiveOrders(): Promise<void>
34
+ fetchSummary(dateRange?: { from: string; to: string }): Promise<void>
35
+ selectOrder(id: string | null): void
36
+ createOrder(input: CreateOrderInput): Promise<RestaurantOrder>
37
+ advanceOrderStatus(orderId: string): Promise<void>
38
+ updateOrderStatus(input: UpdateOrderStatusInput): Promise<void>
39
+ cancelOrder(id: string, reason?: string): Promise<void>
40
+ addOrderItem(orderId: string, item: CreateOrderItemInput): Promise<void>
41
+ removeOrderItem(itemId: string): Promise<void>
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Store factory
46
+ // ---------------------------------------------------------------------------
47
+
48
+ export function createOrdersStore(provider: OrdersDataProvider): StoreApi<OrdersUIState> {
49
+ return createStore<OrdersUIState>((set, get) => ({
50
+ orders: [],
51
+ ordersTotal: 0,
52
+ ordersLoading: false,
53
+ orderQuery: {},
54
+
55
+ activeOrders: [],
56
+ activeOrdersLoading: false,
57
+
58
+ summary: null,
59
+ summaryLoading: false,
60
+
61
+ selectedOrderId: null,
62
+
63
+ async fetchOrders(query) {
64
+ return dedup('orders:list:' + JSON.stringify(query), async () => {
65
+ set({ ordersLoading: true, orderQuery: query })
66
+ const result = await provider.getOrders(query)
67
+ set({ orders: result.data, ordersTotal: result.total, ordersLoading: false })
68
+ })
69
+ },
70
+
71
+ async fetchActiveOrders() {
72
+ return dedup('orders:active', async () => {
73
+ set({ activeOrdersLoading: true })
74
+ const result = await provider.getOrders({
75
+ status: ['new', 'confirmed', 'preparing', 'ready'],
76
+ })
77
+ set({ activeOrders: result.data, activeOrdersLoading: false })
78
+ })
79
+ },
80
+
81
+ async fetchSummary(dateRange) {
82
+ return dedup('orders:summary', async () => {
83
+ set({ summaryLoading: true })
84
+ const summary = await provider.getSummary(dateRange)
85
+ set({ summary, summaryLoading: false })
86
+ })
87
+ },
88
+
89
+ selectOrder(id) {
90
+ set({ selectedOrderId: id })
91
+ },
92
+
93
+ async createOrder(input) {
94
+ try {
95
+ const order = await provider.createOrder(input)
96
+ const query = get().orderQuery
97
+ const [result, summary] = await Promise.all([
98
+ provider.getOrders(query),
99
+ provider.getSummary(),
100
+ ])
101
+ set({ orders: result.data, ordersTotal: result.total, summary })
102
+ toast.success('Order created')
103
+ return order
104
+ } catch (err: any) {
105
+ toast.error('Failed to create order', { description: err?.message })
106
+ throw err
107
+ }
108
+ },
109
+
110
+ async advanceOrderStatus(orderId) {
111
+ try {
112
+ const { orders, activeOrders } = get()
113
+ const order = orders.find(o => o.id === orderId) ?? activeOrders.find(o => o.id === orderId)
114
+ if (!order) throw new Error('Order not found')
115
+
116
+ const nextStatus = getNextStatus(order.kind, order.status)
117
+ if (!nextStatus) throw new Error('No next status available')
118
+
119
+ await provider.updateOrderStatus({ orderId, status: nextStatus })
120
+
121
+ // Refresh active orders
122
+ const activeResult = await provider.getOrders({
123
+ status: ['new', 'confirmed', 'preparing', 'ready'],
124
+ })
125
+ set({ activeOrders: activeResult.data })
126
+ toast.success(`Order advanced to ${nextStatus.replace('_', ' ')}`)
127
+ } catch (err: any) {
128
+ toast.error('Failed to advance order', { description: err?.message })
129
+ throw err
130
+ }
131
+ },
132
+
133
+ async updateOrderStatus(input) {
134
+ try {
135
+ await provider.updateOrderStatus(input)
136
+ const query = get().orderQuery
137
+ const [result, summary] = await Promise.all([
138
+ provider.getOrders(query),
139
+ provider.getSummary(),
140
+ ])
141
+ set({ orders: result.data, ordersTotal: result.total, summary })
142
+ toast.success('Order status updated')
143
+ } catch (err: any) {
144
+ toast.error('Failed to update status', { description: err?.message })
145
+ throw err
146
+ }
147
+ },
148
+
149
+ async cancelOrder(id, reason) {
150
+ try {
151
+ await provider.cancelOrder(id, reason)
152
+ const query = get().orderQuery
153
+ const [result, summary] = await Promise.all([
154
+ provider.getOrders(query),
155
+ provider.getSummary(),
156
+ ])
157
+ set({ orders: result.data, ordersTotal: result.total, summary })
158
+ toast.success('Order cancelled')
159
+ } catch (err: any) {
160
+ toast.error('Failed to cancel order', { description: err?.message })
161
+ throw err
162
+ }
163
+ },
164
+
165
+ async addOrderItem(orderId, item) {
166
+ try {
167
+ await provider.addOrderItem(orderId, item)
168
+ const query = get().orderQuery
169
+ const result = await provider.getOrders(query)
170
+ set({ orders: result.data, ordersTotal: result.total })
171
+ toast.success('Item added')
172
+ } catch (err: any) {
173
+ toast.error('Failed to add item', { description: err?.message })
174
+ throw err
175
+ }
176
+ },
177
+
178
+ async removeOrderItem(itemId) {
179
+ try {
180
+ await provider.removeOrderItem(itemId)
181
+ const query = get().orderQuery
182
+ const result = await provider.getOrders(query)
183
+ set({ orders: result.data, ordersTotal: result.total })
184
+ toast.success('Item removed')
185
+ } catch (err: any) {
186
+ toast.error('Failed to remove item', { description: err?.message })
187
+ throw err
188
+ }
189
+ },
190
+ }))
191
+ }