@fayz-ai/plugin-tables 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/{TablesPage-IM732NWO.js → TablesPage-QUFTXYLX.js} +4 -4
- package/dist/TablesPage-QUFTXYLX.js.map +1 -0
- package/dist/{TablesContext.d.ts → context.d.ts} +1 -1
- package/dist/context.d.ts.map +1 -0
- package/dist/data/registries.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{TablesPage.d.ts → views/TablesPage.d.ts} +3 -3
- package/dist/views/TablesPage.d.ts.map +1 -0
- package/package.json +6 -8
- package/dist/TablesContext.d.ts.map +0 -1
- package/dist/TablesPage-IM732NWO.js.map +0 -1
- package/dist/TablesPage.d.ts.map +0 -1
- package/dist/registries.d.ts.map +0 -1
- package/src/TablesContext.tsx +0 -41
- package/src/TablesPage.tsx +0 -20
- package/src/data/mock.ts +0 -322
- package/src/data/platform.ts +0 -348
- package/src/data/types.ts +0 -31
- package/src/index.ts +0 -179
- package/src/locales/en.ts +0 -45
- package/src/locales/index.ts +0 -4
- package/src/locales/pt-BR.ts +0 -45
- package/src/registries.ts +0 -32
- package/src/store.ts +0 -178
- package/src/types.ts +0 -116
- package/src/views/FloorPlanView.tsx +0 -388
- /package/dist/{registries.d.ts → data/registries.d.ts} +0 -0
package/src/data/platform.ts
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import { createFayzClient, type FayzClientOptions, type FayzTableFilter } from '@fayz-ai/sdk'
|
|
2
|
-
import type {
|
|
3
|
-
CreateTableInput,
|
|
4
|
-
RestaurantTable,
|
|
5
|
-
SeatGuestsInput,
|
|
6
|
-
TableQuery,
|
|
7
|
-
TableSession,
|
|
8
|
-
TablesSummary,
|
|
9
|
-
TableStatus,
|
|
10
|
-
UpdateTableStatusInput,
|
|
11
|
-
Zone,
|
|
12
|
-
} from '../types'
|
|
13
|
-
import type { TablesDataProvider } from './types'
|
|
14
|
-
|
|
15
|
-
export interface FayzTablesProviderOptions extends FayzClientOptions {
|
|
16
|
-
projectId?: string
|
|
17
|
-
tableName?: string
|
|
18
|
-
schema?: string
|
|
19
|
-
runtime?: boolean
|
|
20
|
-
/**
|
|
21
|
-
* Active tenant id (or a getter) used to scope reads and stamp writes on the
|
|
22
|
-
* restaurant_tables table. When omitted, scoping is left to runtime/RLS.
|
|
23
|
-
*/
|
|
24
|
-
tenantId?: string | (() => string | undefined | null)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface RestaurantTableRow {
|
|
28
|
-
id: string
|
|
29
|
-
tenant_id?: string | null
|
|
30
|
-
name?: string | null
|
|
31
|
-
number?: number | null
|
|
32
|
-
seats?: number | null
|
|
33
|
-
status?: TableStatus | null
|
|
34
|
-
zone?: string | null
|
|
35
|
-
zone_name?: string | null
|
|
36
|
-
shape?: RestaurantTable['shape'] | null
|
|
37
|
-
grid_col?: number | null
|
|
38
|
-
grid_row?: number | null
|
|
39
|
-
is_active?: boolean | null
|
|
40
|
-
current_session_id?: string | null
|
|
41
|
-
current_order_id?: string | null
|
|
42
|
-
current_guests?: number | null
|
|
43
|
-
current_waiter_name?: string | null
|
|
44
|
-
current_elapsed_minutes?: number | null
|
|
45
|
-
current_total?: number | null
|
|
46
|
-
metadata?: Record<string, unknown> | null
|
|
47
|
-
created_at?: string | null
|
|
48
|
-
updated_at?: string | null
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const DEFAULT_TABLE_NAME = 'restaurant_tables'
|
|
52
|
-
|
|
53
|
-
function nowIso(): string {
|
|
54
|
-
return new Date().toISOString()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function rowToTable(row: RestaurantTableRow): RestaurantTable {
|
|
58
|
-
const number = row.number ?? 0
|
|
59
|
-
const zone = row.zone ?? 'indoor'
|
|
60
|
-
return {
|
|
61
|
-
id: row.id,
|
|
62
|
-
name: row.name ?? `Table ${number}`,
|
|
63
|
-
number,
|
|
64
|
-
seats: row.seats ?? 4,
|
|
65
|
-
status: row.status ?? 'available',
|
|
66
|
-
zone,
|
|
67
|
-
zoneName: row.zone_name ?? zone,
|
|
68
|
-
shape: row.shape ?? 'square',
|
|
69
|
-
gridCol: row.grid_col ?? 0,
|
|
70
|
-
gridRow: row.grid_row ?? 0,
|
|
71
|
-
isActive: row.is_active ?? true,
|
|
72
|
-
currentSessionId: row.current_session_id ?? undefined,
|
|
73
|
-
currentOrderId: row.current_order_id ?? undefined,
|
|
74
|
-
currentGuests: row.current_guests ?? undefined,
|
|
75
|
-
currentWaiterName: row.current_waiter_name ?? undefined,
|
|
76
|
-
currentElapsedMinutes: row.current_elapsed_minutes ?? undefined,
|
|
77
|
-
currentTotal: row.current_total ?? undefined,
|
|
78
|
-
metadata: row.metadata ?? {},
|
|
79
|
-
tenantId: row.tenant_id ?? 'runtime-tenant',
|
|
80
|
-
createdAt: row.created_at ?? nowIso(),
|
|
81
|
-
updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function tableCreatePayload(input: CreateTableInput): Record<string, unknown> {
|
|
86
|
-
return {
|
|
87
|
-
name: input.name,
|
|
88
|
-
number: input.number,
|
|
89
|
-
seats: input.seats,
|
|
90
|
-
zone: input.zone,
|
|
91
|
-
shape: input.shape ?? 'square',
|
|
92
|
-
grid_col: input.gridCol ?? 0,
|
|
93
|
-
grid_row: input.gridRow ?? 0,
|
|
94
|
-
status: 'available',
|
|
95
|
-
is_active: true,
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function tableUpdatePayload(data: Partial<RestaurantTable>): Record<string, unknown> {
|
|
100
|
-
const payload: Record<string, unknown> = {}
|
|
101
|
-
if (data.name !== undefined) payload.name = data.name
|
|
102
|
-
if (data.number !== undefined) payload.number = data.number
|
|
103
|
-
if (data.seats !== undefined) payload.seats = data.seats
|
|
104
|
-
if (data.status !== undefined) payload.status = data.status
|
|
105
|
-
if (data.zone !== undefined) payload.zone = data.zone
|
|
106
|
-
if (data.zoneName !== undefined) payload.zone_name = data.zoneName
|
|
107
|
-
if (data.shape !== undefined) payload.shape = data.shape
|
|
108
|
-
if (data.gridCol !== undefined) payload.grid_col = data.gridCol
|
|
109
|
-
if (data.gridRow !== undefined) payload.grid_row = data.gridRow
|
|
110
|
-
if (data.isActive !== undefined) payload.is_active = data.isActive
|
|
111
|
-
if (data.currentSessionId !== undefined) payload.current_session_id = data.currentSessionId
|
|
112
|
-
if (data.currentOrderId !== undefined) payload.current_order_id = data.currentOrderId
|
|
113
|
-
if (data.currentGuests !== undefined) payload.current_guests = data.currentGuests
|
|
114
|
-
if (data.currentWaiterName !== undefined) payload.current_waiter_name = data.currentWaiterName
|
|
115
|
-
if (data.currentElapsedMinutes !== undefined) payload.current_elapsed_minutes = data.currentElapsedMinutes
|
|
116
|
-
if (data.currentTotal !== undefined) payload.current_total = data.currentTotal
|
|
117
|
-
if (data.metadata !== undefined) payload.metadata = data.metadata
|
|
118
|
-
return payload
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function filtersFromQuery(query?: TableQuery): FayzTableFilter[] {
|
|
122
|
-
const filters: FayzTableFilter[] = [{ column: 'is_active', operator: 'eq', value: true }]
|
|
123
|
-
if (!query) return filters
|
|
124
|
-
if (query.zone) filters.push({ column: 'zone', operator: 'eq', value: query.zone })
|
|
125
|
-
if (query.status && !Array.isArray(query.status)) {
|
|
126
|
-
filters.push({ column: 'status', operator: 'eq', value: query.status })
|
|
127
|
-
}
|
|
128
|
-
return filters
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function matchesClientQuery(table: RestaurantTable, query?: TableQuery): boolean {
|
|
132
|
-
if (!query) return true
|
|
133
|
-
if (Array.isArray(query.status) && !query.status.includes(table.status)) return false
|
|
134
|
-
if (query.search) {
|
|
135
|
-
const search = query.search.toLowerCase()
|
|
136
|
-
return table.name.toLowerCase().includes(search)
|
|
137
|
-
|| String(table.number).includes(search)
|
|
138
|
-
|| (table.zoneName?.toLowerCase().includes(search) ?? false)
|
|
139
|
-
}
|
|
140
|
-
return true
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function summarize(tables: RestaurantTable[]): TablesSummary {
|
|
144
|
-
const availableCount = tables.filter((table) => table.status === 'available').length
|
|
145
|
-
const occupiedCount = tables.filter((table) => table.status === 'occupied').length
|
|
146
|
-
const reservedCount = tables.filter((table) => table.status === 'reserved').length
|
|
147
|
-
const cleaningCount = tables.filter((table) => table.status === 'cleaning').length
|
|
148
|
-
const totalSeats = tables.reduce((sum, table) => sum + table.seats, 0)
|
|
149
|
-
const occupiedSeats = tables
|
|
150
|
-
.filter((table) => table.status === 'occupied')
|
|
151
|
-
.reduce((sum, table) => sum + (table.currentGuests ?? table.seats), 0)
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
totalTables: tables.length,
|
|
155
|
-
availableCount,
|
|
156
|
-
occupiedCount,
|
|
157
|
-
reservedCount,
|
|
158
|
-
cleaningCount,
|
|
159
|
-
totalSeats,
|
|
160
|
-
occupiedSeats,
|
|
161
|
-
averageSessionMinutes: 0,
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function createFayzTablesProvider(options: FayzTablesProviderOptions = {}): TablesDataProvider {
|
|
166
|
-
const client = createFayzClient(options)
|
|
167
|
-
const table = options.tableName ?? DEFAULT_TABLE_NAME
|
|
168
|
-
const baseOptions = {
|
|
169
|
-
projectId: options.projectId,
|
|
170
|
-
table,
|
|
171
|
-
schema: options.schema,
|
|
172
|
-
runtime: options.runtime,
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function resolveTenant(): string | undefined {
|
|
176
|
-
const value = typeof options.tenantId === 'function' ? options.tenantId() : options.tenantId
|
|
177
|
-
return value ?? undefined
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function withTenant(filters: FayzTableFilter[]): FayzTableFilter[] {
|
|
181
|
-
const tenantId = resolveTenant()
|
|
182
|
-
return tenantId ? [{ column: 'tenant_id', operator: 'eq', value: tenantId }, ...filters] : filters
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function listTables(query?: TableQuery): Promise<RestaurantTable[]> {
|
|
186
|
-
const response = await client.data.listRows<RestaurantTableRow>({
|
|
187
|
-
...baseOptions,
|
|
188
|
-
filters: withTenant(filtersFromQuery(query)),
|
|
189
|
-
sortColumn: 'number',
|
|
190
|
-
sortDirection: 'asc',
|
|
191
|
-
limit: 500,
|
|
192
|
-
})
|
|
193
|
-
return response.rows.map(rowToTable).filter((item) => matchesClientQuery(item, query))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function getById(id: string): Promise<RestaurantTable | null> {
|
|
197
|
-
const response = await client.data.listRows<RestaurantTableRow>({
|
|
198
|
-
...baseOptions,
|
|
199
|
-
filters: withTenant([{ column: 'id', operator: 'eq', value: id }]),
|
|
200
|
-
limit: 1,
|
|
201
|
-
})
|
|
202
|
-
return response.rows[0] ? rowToTable(response.rows[0]) : null
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
async getTables(query?: TableQuery) {
|
|
207
|
-
return listTables(query)
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
async getTableById(id: string) {
|
|
211
|
-
return getById(id)
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
async createTable(input: CreateTableInput) {
|
|
215
|
-
const tenantId = resolveTenant()
|
|
216
|
-
const row = await client.data.createRow<RestaurantTableRow>({
|
|
217
|
-
...baseOptions,
|
|
218
|
-
row: tenantId ? { ...tableCreatePayload(input), tenant_id: tenantId } : tableCreatePayload(input),
|
|
219
|
-
})
|
|
220
|
-
return rowToTable(row)
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
async updateTable(id: string, data: Partial<RestaurantTable>) {
|
|
224
|
-
const row = await client.data.updateRow<RestaurantTableRow>({
|
|
225
|
-
...baseOptions,
|
|
226
|
-
primaryKeys: { id },
|
|
227
|
-
row: tableUpdatePayload(data),
|
|
228
|
-
})
|
|
229
|
-
return rowToTable(row)
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
async deleteTable(id: string) {
|
|
233
|
-
await client.data.deleteRows({ ...baseOptions, rows: [{ id }] })
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
async updateTableStatus(input: UpdateTableStatusInput) {
|
|
237
|
-
return this.updateTable(input.tableId, { status: input.status })
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
async updateTablePositions(positions: Array<{ id: string; gridCol: number; gridRow: number }>) {
|
|
241
|
-
await Promise.all(positions.map((position) => this.updateTable(position.id, {
|
|
242
|
-
gridCol: position.gridCol,
|
|
243
|
-
gridRow: position.gridRow,
|
|
244
|
-
})))
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
async seatGuests(input: SeatGuestsInput) {
|
|
248
|
-
const table = await this.updateTable(input.tableId, {
|
|
249
|
-
status: 'occupied',
|
|
250
|
-
currentGuests: input.guests,
|
|
251
|
-
currentWaiterName: input.waiterId ? `Waiter ${input.waiterId}` : undefined,
|
|
252
|
-
currentElapsedMinutes: 0,
|
|
253
|
-
currentTotal: 0,
|
|
254
|
-
})
|
|
255
|
-
const session: TableSession = {
|
|
256
|
-
id: table.currentSessionId ?? `runtime-session-${table.id}`,
|
|
257
|
-
tableId: table.id,
|
|
258
|
-
tableName: table.name,
|
|
259
|
-
guests: input.guests,
|
|
260
|
-
waiterId: input.waiterId,
|
|
261
|
-
waiterName: table.currentWaiterName,
|
|
262
|
-
seatedAt: nowIso(),
|
|
263
|
-
status: 'active',
|
|
264
|
-
notes: input.notes,
|
|
265
|
-
tenantId: table.tenantId,
|
|
266
|
-
createdAt: nowIso(),
|
|
267
|
-
}
|
|
268
|
-
return session
|
|
269
|
-
},
|
|
270
|
-
|
|
271
|
-
async closeSession(sessionId: string) {
|
|
272
|
-
const tables = await listTables()
|
|
273
|
-
const table = tables.find((item) => item.currentSessionId === sessionId)
|
|
274
|
-
if (table) await this.updateTable(table.id, {
|
|
275
|
-
status: 'cleaning',
|
|
276
|
-
currentSessionId: undefined,
|
|
277
|
-
currentOrderId: undefined,
|
|
278
|
-
currentGuests: undefined,
|
|
279
|
-
currentWaiterName: undefined,
|
|
280
|
-
currentElapsedMinutes: undefined,
|
|
281
|
-
currentTotal: undefined,
|
|
282
|
-
})
|
|
283
|
-
return {
|
|
284
|
-
id: sessionId,
|
|
285
|
-
tableId: table?.id ?? '',
|
|
286
|
-
tableName: table?.name,
|
|
287
|
-
guests: table?.currentGuests ?? 0,
|
|
288
|
-
seatedAt: nowIso(),
|
|
289
|
-
closedAt: nowIso(),
|
|
290
|
-
status: 'closed',
|
|
291
|
-
tenantId: table?.tenantId ?? 'runtime-tenant',
|
|
292
|
-
createdAt: nowIso(),
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
async getActiveSessions() {
|
|
297
|
-
const tables = await listTables({ status: 'occupied' })
|
|
298
|
-
return tables.map((table) => ({
|
|
299
|
-
id: table.currentSessionId ?? `runtime-session-${table.id}`,
|
|
300
|
-
tableId: table.id,
|
|
301
|
-
tableName: table.name,
|
|
302
|
-
guests: table.currentGuests ?? table.seats,
|
|
303
|
-
waiterName: table.currentWaiterName,
|
|
304
|
-
seatedAt: nowIso(),
|
|
305
|
-
status: 'active' as const,
|
|
306
|
-
tenantId: table.tenantId,
|
|
307
|
-
createdAt: nowIso(),
|
|
308
|
-
}))
|
|
309
|
-
},
|
|
310
|
-
|
|
311
|
-
async getSessionHistory() {
|
|
312
|
-
return []
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
async getZones() {
|
|
316
|
-
const tables = await listTables()
|
|
317
|
-
const zones = new Map<string, Zone>()
|
|
318
|
-
for (const table of tables) {
|
|
319
|
-
if (zones.has(table.zone)) continue
|
|
320
|
-
zones.set(table.zone, {
|
|
321
|
-
id: table.zone,
|
|
322
|
-
name: table.zoneName ?? table.zone,
|
|
323
|
-
sortOrder: zones.size,
|
|
324
|
-
isActive: true,
|
|
325
|
-
tenantId: table.tenantId,
|
|
326
|
-
createdAt: table.createdAt,
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
return [...zones.values()]
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
async createZone() {
|
|
333
|
-
throw new Error('Restaurant zone writes require a dedicated Fayz table/broker contract.')
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
async updateZone() {
|
|
337
|
-
throw new Error('Restaurant zone writes require a dedicated Fayz table/broker contract.')
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
async deleteZone() {
|
|
341
|
-
throw new Error('Restaurant zone writes require a dedicated Fayz table/broker contract.')
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
async getSummary() {
|
|
345
|
-
return summarize(await listTables())
|
|
346
|
-
},
|
|
347
|
-
}
|
|
348
|
-
}
|
package/src/data/types.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RestaurantTable, TableSession, Zone,
|
|
3
|
-
CreateTableInput, SeatGuestsInput, UpdateTableStatusInput,
|
|
4
|
-
TableQuery, TablesSummary,
|
|
5
|
-
} from '../types'
|
|
6
|
-
|
|
7
|
-
export interface TablesDataProvider {
|
|
8
|
-
// Tables
|
|
9
|
-
getTables(query?: TableQuery): Promise<RestaurantTable[]>
|
|
10
|
-
getTableById(id: string): Promise<RestaurantTable | null>
|
|
11
|
-
createTable(input: CreateTableInput): Promise<RestaurantTable>
|
|
12
|
-
updateTable(id: string, data: Partial<RestaurantTable>): Promise<RestaurantTable>
|
|
13
|
-
deleteTable(id: string): Promise<void>
|
|
14
|
-
updateTableStatus(input: UpdateTableStatusInput): Promise<RestaurantTable>
|
|
15
|
-
updateTablePositions(positions: Array<{ id: string; gridCol: number; gridRow: number }>): Promise<void>
|
|
16
|
-
|
|
17
|
-
// Sessions
|
|
18
|
-
seatGuests(input: SeatGuestsInput): Promise<TableSession>
|
|
19
|
-
closeSession(sessionId: string): Promise<TableSession>
|
|
20
|
-
getActiveSessions(): Promise<TableSession[]>
|
|
21
|
-
getSessionHistory(tableId?: string): Promise<TableSession[]>
|
|
22
|
-
|
|
23
|
-
// Zones
|
|
24
|
-
getZones(): Promise<Zone[]>
|
|
25
|
-
createZone(data: Partial<Zone>): Promise<Zone>
|
|
26
|
-
updateZone(id: string, data: Partial<Zone>): Promise<Zone>
|
|
27
|
-
deleteZone(id: string): Promise<void>
|
|
28
|
-
|
|
29
|
-
// Summary
|
|
30
|
-
getSummary(): Promise<TablesSummary>
|
|
31
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
|
|
3
|
-
import type { TablesDataProvider } from './data/types'
|
|
4
|
-
import type { TableSession } from './types'
|
|
5
|
-
import { createMockTablesProvider } from './data/mock'
|
|
6
|
-
import { createTablesStore } from './store'
|
|
7
|
-
import { tablesRegistries } from './registries'
|
|
8
|
-
import { tablesLocales } from './locales'
|
|
9
|
-
import { PluginSettingsPanel } from '@fayz-ai/saas'
|
|
10
|
-
|
|
11
|
-
const TablesPage = React.lazy(() => import('./TablesPage').then((m) => ({ default: m.TablesPage })))
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Labels
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
export interface TablesPluginLabels {
|
|
18
|
-
pageTitle: string
|
|
19
|
-
floorPlan: string
|
|
20
|
-
zones: string
|
|
21
|
-
sessionHistory: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const DEFAULT_LABELS: TablesPluginLabels = {
|
|
25
|
-
pageTitle: 'Tables',
|
|
26
|
-
floorPlan: 'Floor Plan',
|
|
27
|
-
zones: 'Zones',
|
|
28
|
-
sessionHistory: 'Session History',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Options
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
export interface TablesPluginOptions {
|
|
36
|
-
modules?: {
|
|
37
|
-
reservations?: boolean
|
|
38
|
-
sessionHistory?: boolean
|
|
39
|
-
}
|
|
40
|
-
labels?: Partial<TablesPluginLabels>
|
|
41
|
-
defaultZones?: Array<{ name: string; color?: string }>
|
|
42
|
-
navPosition?: number
|
|
43
|
-
navSection?: 'main' | 'secondary' | 'settings'
|
|
44
|
-
scope?: PluginScope
|
|
45
|
-
verticalId?: VerticalId
|
|
46
|
-
dataProvider?: TablesDataProvider
|
|
47
|
-
onTableSeated?: (session: TableSession) => Promise<string | undefined>
|
|
48
|
-
onTableClosed?: (session: TableSession) => Promise<void>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
// Config resolver
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
|
|
55
|
-
function resolveConfig(options?: TablesPluginOptions) {
|
|
56
|
-
return {
|
|
57
|
-
modules: {
|
|
58
|
-
reservations: options?.modules?.reservations === true,
|
|
59
|
-
sessionHistory: options?.modules?.sessionHistory !== false,
|
|
60
|
-
},
|
|
61
|
-
labels: { ...DEFAULT_LABELS, ...options?.labels } as any,
|
|
62
|
-
defaultZones: options?.defaultZones ?? [
|
|
63
|
-
{ name: 'Indoor', color: '#3b82f6' },
|
|
64
|
-
{ name: 'Outdoor', color: '#22c55e' },
|
|
65
|
-
{ name: 'Bar', color: '#f59e0b' },
|
|
66
|
-
],
|
|
67
|
-
onTableSeated: options?.onTableSeated,
|
|
68
|
-
onTableClosed: options?.onTableClosed,
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
// Factory
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
|
|
76
|
-
export function createTablesPlugin(options?: TablesPluginOptions): PluginManifest {
|
|
77
|
-
const config = resolveConfig(options)
|
|
78
|
-
const provider = options?.dataProvider ?? createMockTablesProvider()
|
|
79
|
-
const store = createTablesStore(provider)
|
|
80
|
-
|
|
81
|
-
const PageComponent: React.FC<any> = () =>
|
|
82
|
-
React.createElement(React.Suspense, { fallback: null },
|
|
83
|
-
React.createElement(TablesPage, { config, provider, store, registries: tablesRegistries })
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
id: 'tables',
|
|
88
|
-
name: config.labels.pageTitle,
|
|
89
|
-
icon: 'MapPin',
|
|
90
|
-
version: '1.0.0',
|
|
91
|
-
scope: options?.scope ?? 'vertical',
|
|
92
|
-
verticalId: options?.verticalId,
|
|
93
|
-
defaultEnabled: true,
|
|
94
|
-
dependencies: [],
|
|
95
|
-
navigation: [
|
|
96
|
-
{
|
|
97
|
-
section: options?.navSection ?? 'main',
|
|
98
|
-
position: options?.navPosition ?? 4,
|
|
99
|
-
label: config.labels.pageTitle,
|
|
100
|
-
route: '/tables',
|
|
101
|
-
icon: 'MapPin',
|
|
102
|
-
permission: { feature: 'tables', action: 'read' as const },
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
routes: [
|
|
106
|
-
{
|
|
107
|
-
path: '/tables',
|
|
108
|
-
component: PageComponent,
|
|
109
|
-
permission: { feature: 'tables', action: 'read' as const },
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
widgets: [],
|
|
113
|
-
aiTools: [
|
|
114
|
-
{
|
|
115
|
-
id: 'tables.availability',
|
|
116
|
-
name: 'getTableAvailability',
|
|
117
|
-
description: 'Returns which tables are available, occupied, reserved, or being cleaned.',
|
|
118
|
-
icon: 'MapPin',
|
|
119
|
-
mode: 'read' as const,
|
|
120
|
-
category: 'Tables',
|
|
121
|
-
parameters: {
|
|
122
|
-
type: 'object' as const,
|
|
123
|
-
properties: {
|
|
124
|
-
zone: { type: 'string' as const, description: 'Filter by zone name' },
|
|
125
|
-
status: { type: 'string' as const, enum: ['available', 'occupied', 'reserved', 'cleaning'] },
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
suggestions: [
|
|
129
|
-
{ label: 'Which tables are available right now?' },
|
|
130
|
-
{ label: 'How many tables are occupied?' },
|
|
131
|
-
],
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
id: 'tables.seat-guests',
|
|
135
|
-
name: 'seatGuests',
|
|
136
|
-
description: 'Seats guests at a specific table.',
|
|
137
|
-
icon: 'UserPlus',
|
|
138
|
-
mode: 'persist' as const,
|
|
139
|
-
category: 'Tables',
|
|
140
|
-
parameters: {
|
|
141
|
-
type: 'object' as const,
|
|
142
|
-
properties: {
|
|
143
|
-
tableNumber: { type: 'number' as const, description: 'Table number' },
|
|
144
|
-
guests: { type: 'number' as const, description: 'Number of guests' },
|
|
145
|
-
},
|
|
146
|
-
required: ['tableNumber', 'guests'],
|
|
147
|
-
},
|
|
148
|
-
permission: { feature: 'tables', action: 'edit' as const },
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
registries: tablesRegistries,
|
|
152
|
-
settings: [
|
|
153
|
-
{
|
|
154
|
-
id: 'tables',
|
|
155
|
-
label: config.labels.pageTitle,
|
|
156
|
-
icon: 'MapPin',
|
|
157
|
-
component: (() => {
|
|
158
|
-
const TablesSettingsTab: React.ComponentType<unknown> = () =>
|
|
159
|
-
React.createElement(PluginSettingsPanel, {
|
|
160
|
-
title: 'Tables Settings',
|
|
161
|
-
subtitle: 'Zones and floor plan configuration',
|
|
162
|
-
registries: tablesRegistries,
|
|
163
|
-
routeBase: '/settings/tables',
|
|
164
|
-
})
|
|
165
|
-
TablesSettingsTab.displayName = 'TablesSettingsTab'
|
|
166
|
-
return TablesSettingsTab
|
|
167
|
-
})(),
|
|
168
|
-
order: 21,
|
|
169
|
-
permission: { feature: 'tables', action: 'read' as const },
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
locales: tablesLocales,
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export type { TablesDataProvider } from './data/types'
|
|
177
|
-
export { createFayzTablesProvider } from './data/platform'
|
|
178
|
-
export type { FayzTablesProviderOptions } from './data/platform'
|
|
179
|
-
export type { ResolvedTablesConfig } from './TablesContext'
|
package/src/locales/en.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export const en: Record<string, string> = {
|
|
2
|
-
'tables.title': 'Tables',
|
|
3
|
-
'tables.nav.floorPlan': 'Floor Plan',
|
|
4
|
-
'tables.nav.zones': 'Zones',
|
|
5
|
-
'tables.nav.history': 'Session History',
|
|
6
|
-
'tables.floorPlan.title': 'Floor Plan',
|
|
7
|
-
'tables.floorPlan.subtitle': 'Manage table layout and seating',
|
|
8
|
-
'tables.floorPlan.available': 'Available',
|
|
9
|
-
'tables.floorPlan.occupied': 'Occupied',
|
|
10
|
-
'tables.floorPlan.reserved': 'Reserved',
|
|
11
|
-
'tables.floorPlan.cleaning': 'Cleaning',
|
|
12
|
-
'tables.floorPlan.seats': 'seats',
|
|
13
|
-
'tables.floorPlan.noTables': 'No tables configured',
|
|
14
|
-
'tables.floorPlan.noTablesDesc': 'Add your first table to get started',
|
|
15
|
-
'tables.floorPlan.addTable': 'Add Table',
|
|
16
|
-
'tables.floorPlan.occupiedOf': '{occupied} occupied, {available} available of {total} tables',
|
|
17
|
-
'tables.floorPlan.selectTable': 'Select a table',
|
|
18
|
-
'tables.floorPlan.selectTableDesc': 'Click on any table to see details and actions',
|
|
19
|
-
'tables.floorPlan.min': 'min',
|
|
20
|
-
'tables.floorPlan.zone': 'Zone',
|
|
21
|
-
'tables.floorPlan.order': 'Order',
|
|
22
|
-
'tables.floorPlan.checkIn': 'Check In',
|
|
23
|
-
'tables.detail.currentSession': 'Current Session',
|
|
24
|
-
'tables.detail.guests': 'Guests',
|
|
25
|
-
'tables.detail.waiter': 'Waiter',
|
|
26
|
-
'tables.detail.elapsed': 'Elapsed',
|
|
27
|
-
'tables.detail.total': 'Running Total',
|
|
28
|
-
'tables.detail.viewOrder': 'View Order',
|
|
29
|
-
'tables.detail.seatGuests': 'Seat Guests',
|
|
30
|
-
'tables.detail.closeTable': 'Close Table',
|
|
31
|
-
'tables.detail.markClean': 'Mark Ready',
|
|
32
|
-
'tables.detail.cancel': 'Cancel',
|
|
33
|
-
'tables.seat.title': 'Seat Guests',
|
|
34
|
-
'tables.seat.guestCount': 'Number of guests',
|
|
35
|
-
'tables.seat.waiter': 'Waiter',
|
|
36
|
-
'tables.seat.notes': 'Notes',
|
|
37
|
-
'tables.seat.confirm': 'Seat',
|
|
38
|
-
'tables.history.title': 'Session History',
|
|
39
|
-
'tables.history.table': 'Table',
|
|
40
|
-
'tables.history.guests': 'Guests',
|
|
41
|
-
'tables.history.waiter': 'Waiter',
|
|
42
|
-
'tables.history.duration': 'Duration',
|
|
43
|
-
'tables.history.noSessions': 'No session history yet',
|
|
44
|
-
'tables.settings.title': 'Tables Settings',
|
|
45
|
-
}
|
package/src/locales/index.ts
DELETED
package/src/locales/pt-BR.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export const ptBR: Record<string, string> = {
|
|
2
|
-
'tables.title': 'Mesas',
|
|
3
|
-
'tables.nav.floorPlan': 'Mapa de Mesas',
|
|
4
|
-
'tables.nav.zones': 'Áreas',
|
|
5
|
-
'tables.nav.history': 'Histórico',
|
|
6
|
-
'tables.floorPlan.title': 'Mapa de Mesas',
|
|
7
|
-
'tables.floorPlan.subtitle': 'Gerencie o layout e a ocupação das mesas',
|
|
8
|
-
'tables.floorPlan.available': 'Disponível',
|
|
9
|
-
'tables.floorPlan.occupied': 'Ocupada',
|
|
10
|
-
'tables.floorPlan.reserved': 'Reservada',
|
|
11
|
-
'tables.floorPlan.cleaning': 'Limpeza',
|
|
12
|
-
'tables.floorPlan.seats': 'lugares',
|
|
13
|
-
'tables.floorPlan.noTables': 'Nenhuma mesa configurada',
|
|
14
|
-
'tables.floorPlan.noTablesDesc': 'Adicione a primeira mesa para começar',
|
|
15
|
-
'tables.floorPlan.addTable': 'Adicionar Mesa',
|
|
16
|
-
'tables.floorPlan.occupiedOf': '{occupied} ocupadas, {available} disponíveis de {total} mesas',
|
|
17
|
-
'tables.floorPlan.selectTable': 'Selecione uma mesa',
|
|
18
|
-
'tables.floorPlan.selectTableDesc': 'Clique em uma mesa para ver detalhes e ações',
|
|
19
|
-
'tables.floorPlan.min': 'min',
|
|
20
|
-
'tables.floorPlan.zone': 'Área',
|
|
21
|
-
'tables.floorPlan.order': 'Pedido',
|
|
22
|
-
'tables.floorPlan.checkIn': 'Check-in',
|
|
23
|
-
'tables.detail.currentSession': 'Sessão Atual',
|
|
24
|
-
'tables.detail.guests': 'Pessoas',
|
|
25
|
-
'tables.detail.waiter': 'Garçom',
|
|
26
|
-
'tables.detail.elapsed': 'Tempo',
|
|
27
|
-
'tables.detail.total': 'Total Parcial',
|
|
28
|
-
'tables.detail.viewOrder': 'Ver Pedido',
|
|
29
|
-
'tables.detail.seatGuests': 'Sentar Clientes',
|
|
30
|
-
'tables.detail.closeTable': 'Fechar Mesa',
|
|
31
|
-
'tables.detail.markClean': 'Marcar Pronta',
|
|
32
|
-
'tables.detail.cancel': 'Cancelar',
|
|
33
|
-
'tables.seat.title': 'Sentar Clientes',
|
|
34
|
-
'tables.seat.guestCount': 'Número de pessoas',
|
|
35
|
-
'tables.seat.waiter': 'Garçom',
|
|
36
|
-
'tables.seat.notes': 'Observações',
|
|
37
|
-
'tables.seat.confirm': 'Sentar',
|
|
38
|
-
'tables.history.title': 'Histórico de Sessões',
|
|
39
|
-
'tables.history.table': 'Mesa',
|
|
40
|
-
'tables.history.guests': 'Pessoas',
|
|
41
|
-
'tables.history.waiter': 'Garçom',
|
|
42
|
-
'tables.history.duration': 'Duração',
|
|
43
|
-
'tables.history.noSessions': 'Nenhum histórico de sessões',
|
|
44
|
-
'tables.settings.title': 'Configurações de Mesas',
|
|
45
|
-
}
|
package/src/registries.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { PluginRegistryDef } from '@fayz-ai/core'
|
|
2
|
-
import type { EntityDef } from '@fayz-ai/core'
|
|
3
|
-
|
|
4
|
-
const zoneEntity: EntityDef = {
|
|
5
|
-
name: 'Zone',
|
|
6
|
-
namePlural: 'Zones',
|
|
7
|
-
icon: 'MapPin',
|
|
8
|
-
displayField: 'name',
|
|
9
|
-
defaultSort: 'sortOrder',
|
|
10
|
-
fields: [
|
|
11
|
-
{ key: 'name', label: 'Name', type: 'text', required: true, showInTable: true },
|
|
12
|
-
{ key: 'color', label: 'Color', type: 'text', showInTable: true },
|
|
13
|
-
{ key: 'sortOrder', label: 'Order', type: 'number', showInTable: true, defaultValue: 0 },
|
|
14
|
-
{ key: 'isActive', label: 'Active', type: 'boolean', showInTable: true, defaultValue: true },
|
|
15
|
-
],
|
|
16
|
-
data: { table: 'restaurant_zones', tenantScoped: true },
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const tablesRegistries: PluginRegistryDef[] = [
|
|
20
|
-
{
|
|
21
|
-
id: 'zones',
|
|
22
|
-
entity: zoneEntity,
|
|
23
|
-
icon: 'MapPin',
|
|
24
|
-
description: 'Floor plan zones (indoor, outdoor, bar, etc.)',
|
|
25
|
-
display: 'table',
|
|
26
|
-
seedData: [
|
|
27
|
-
{ id: 'z-indoor', name: 'Indoor', color: '#3b82f6', sortOrder: 0, isActive: true },
|
|
28
|
-
{ id: 'z-outdoor', name: 'Outdoor', color: '#22c55e', sortOrder: 1, isActive: true },
|
|
29
|
-
{ id: 'z-bar', name: 'Bar', color: '#f59e0b', sortOrder: 2, isActive: true },
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
]
|