@fayz-ai/plugin-tables 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/TablesContext.d.ts +35 -0
  4. package/dist/TablesContext.d.ts.map +1 -0
  5. package/dist/TablesPage-5GCFGSX3.cjs +276 -0
  6. package/dist/TablesPage-5GCFGSX3.cjs.map +1 -0
  7. package/dist/TablesPage-IM732NWO.js +274 -0
  8. package/dist/TablesPage-IM732NWO.js.map +1 -0
  9. package/dist/TablesPage.d.ts +13 -0
  10. package/dist/TablesPage.d.ts.map +1 -0
  11. package/dist/data/fayz.d.ts +15 -0
  12. package/dist/data/fayz.d.ts.map +1 -0
  13. package/dist/data/mock.d.ts +3 -0
  14. package/dist/data/mock.d.ts.map +1 -0
  15. package/dist/data/types.d.ts +24 -0
  16. package/dist/data/types.d.ts.map +1 -0
  17. package/dist/index.cjs +887 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.ts +33 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +880 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/locales/en.d.ts +2 -0
  24. package/dist/locales/en.d.ts.map +1 -0
  25. package/dist/locales/index.d.ts +2 -0
  26. package/dist/locales/index.d.ts.map +1 -0
  27. package/dist/locales/pt-BR.d.ts +2 -0
  28. package/dist/locales/pt-BR.d.ts.map +1 -0
  29. package/dist/registries.d.ts +3 -0
  30. package/dist/registries.d.ts.map +1 -0
  31. package/dist/store.d.ts +28 -0
  32. package/dist/store.d.ts.map +1 -0
  33. package/dist/types.d.ts +84 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/views/FloorPlanView.d.ts +3 -0
  36. package/dist/views/FloorPlanView.d.ts.map +1 -0
  37. package/package.json +55 -0
  38. package/src/TablesContext.tsx +41 -0
  39. package/src/TablesPage.tsx +20 -0
  40. package/src/data/fayz.ts +348 -0
  41. package/src/data/mock.ts +322 -0
  42. package/src/data/types.ts +31 -0
  43. package/src/index.ts +179 -0
  44. package/src/locales/en.ts +45 -0
  45. package/src/locales/index.ts +4 -0
  46. package/src/locales/pt-BR.ts +45 -0
  47. package/src/registries.ts +32 -0
  48. package/src/store.ts +178 -0
  49. package/src/types.ts +116 -0
  50. package/src/views/FloorPlanView.tsx +388 -0
package/src/index.ts ADDED
@@ -0,0 +1,179 @@
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/fayz'
178
+ export type { FayzTablesProviderOptions } from './data/fayz'
179
+ export type { ResolvedTablesConfig } from './TablesContext'
@@ -0,0 +1,45 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ import { en } from './en'
2
+ import { ptBR } from './pt-BR'
3
+
4
+ export const tablesLocales: Record<string, Record<string, string>> = { en, 'pt-BR': ptBR }
@@ -0,0 +1,45 @@
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
+ }
@@ -0,0 +1,32 @@
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
+ ]
package/src/store.ts ADDED
@@ -0,0 +1,178 @@
1
+ import { createStore, type StoreApi } from 'zustand/vanilla'
2
+ import { dedup } from '@fayz-ai/saas'
3
+ import { toast } from 'sonner'
4
+ import type { TablesDataProvider } from './data/types'
5
+ import type {
6
+ RestaurantTable, TableSession, Zone,
7
+ CreateTableInput, SeatGuestsInput, UpdateTableStatusInput,
8
+ TableQuery, TablesSummary,
9
+ } from './types'
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Store state
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export interface TablesUIState {
16
+ // Data cache
17
+ tables: RestaurantTable[]
18
+ tablesLoading: boolean
19
+
20
+ zones: Zone[]
21
+ zonesLoading: boolean
22
+
23
+ activeSessions: TableSession[]
24
+ sessionsLoading: boolean
25
+
26
+ sessionHistory: TableSession[]
27
+ historyLoading: boolean
28
+
29
+ summary: TablesSummary | null
30
+ summaryLoading: boolean
31
+
32
+ selectedTableId: string | null
33
+
34
+ // Actions
35
+ fetchTables(query?: TableQuery): Promise<void>
36
+ fetchZones(): Promise<void>
37
+ fetchActiveSessions(): Promise<void>
38
+ fetchSessionHistory(tableId?: string): Promise<void>
39
+ fetchSummary(): Promise<void>
40
+ selectTable(id: string | null): void
41
+ seatGuests(input: SeatGuestsInput): Promise<TableSession>
42
+ closeSession(sessionId: string): Promise<void>
43
+ updateTableStatus(input: UpdateTableStatusInput): Promise<void>
44
+ createTable(input: CreateTableInput): Promise<RestaurantTable>
45
+ }
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Store factory
49
+ // ---------------------------------------------------------------------------
50
+
51
+ export function createTablesStore(provider: TablesDataProvider): StoreApi<TablesUIState> {
52
+ return createStore<TablesUIState>((set, get) => ({
53
+ tables: [],
54
+ tablesLoading: false,
55
+
56
+ zones: [],
57
+ zonesLoading: false,
58
+
59
+ activeSessions: [],
60
+ sessionsLoading: false,
61
+
62
+ sessionHistory: [],
63
+ historyLoading: false,
64
+
65
+ summary: null,
66
+ summaryLoading: false,
67
+
68
+ selectedTableId: null,
69
+
70
+ async fetchTables(query) {
71
+ return dedup('tables:tables:' + JSON.stringify(query), async () => {
72
+ set({ tablesLoading: true })
73
+ const tables = await provider.getTables(query)
74
+ set({ tables, tablesLoading: false })
75
+ })
76
+ },
77
+
78
+ async fetchZones() {
79
+ return dedup('tables:zones', async () => {
80
+ set({ zonesLoading: true })
81
+ const zones = await provider.getZones()
82
+ set({ zones, zonesLoading: false })
83
+ })
84
+ },
85
+
86
+ async fetchActiveSessions() {
87
+ return dedup('tables:activeSessions', async () => {
88
+ set({ sessionsLoading: true })
89
+ const activeSessions = await provider.getActiveSessions()
90
+ set({ activeSessions, sessionsLoading: false })
91
+ })
92
+ },
93
+
94
+ async fetchSessionHistory(tableId) {
95
+ return dedup('tables:history:' + (tableId ?? ''), async () => {
96
+ set({ historyLoading: true })
97
+ const sessionHistory = await provider.getSessionHistory(tableId)
98
+ set({ sessionHistory, historyLoading: false })
99
+ })
100
+ },
101
+
102
+ async fetchSummary() {
103
+ return dedup('tables:summary', async () => {
104
+ set({ summaryLoading: true })
105
+ const summary = await provider.getSummary()
106
+ set({ summary, summaryLoading: false })
107
+ })
108
+ },
109
+
110
+ selectTable(id) {
111
+ set({ selectedTableId: id })
112
+ },
113
+
114
+ async seatGuests(input) {
115
+ try {
116
+ const session = await provider.seatGuests(input)
117
+ const [tables, activeSessions, summary] = await Promise.all([
118
+ provider.getTables(),
119
+ provider.getActiveSessions(),
120
+ provider.getSummary(),
121
+ ])
122
+ set({ tables, activeSessions, summary })
123
+ toast.success('Guests seated')
124
+ return session
125
+ } catch (err: any) {
126
+ toast.error('Failed to seat guests', { description: err?.message })
127
+ throw err
128
+ }
129
+ },
130
+
131
+ async closeSession(sessionId) {
132
+ try {
133
+ await provider.closeSession(sessionId)
134
+ const [tables, activeSessions, summary] = await Promise.all([
135
+ provider.getTables(),
136
+ provider.getActiveSessions(),
137
+ provider.getSummary(),
138
+ ])
139
+ set({ tables, activeSessions, summary })
140
+ toast.success('Session closed')
141
+ } catch (err: any) {
142
+ toast.error('Failed to close session', { description: err?.message })
143
+ throw err
144
+ }
145
+ },
146
+
147
+ async updateTableStatus(input) {
148
+ try {
149
+ await provider.updateTableStatus(input)
150
+ const [tables, summary] = await Promise.all([
151
+ provider.getTables(),
152
+ provider.getSummary(),
153
+ ])
154
+ set({ tables, summary })
155
+ toast.success('Table status updated')
156
+ } catch (err: any) {
157
+ toast.error('Failed to update table status', { description: err?.message })
158
+ throw err
159
+ }
160
+ },
161
+
162
+ async createTable(input) {
163
+ try {
164
+ const table = await provider.createTable(input)
165
+ const [tables, summary] = await Promise.all([
166
+ provider.getTables(),
167
+ provider.getSummary(),
168
+ ])
169
+ set({ tables, summary })
170
+ toast.success('Table created')
171
+ return table
172
+ } catch (err: any) {
173
+ toast.error('Failed to create table', { description: err?.message })
174
+ throw err
175
+ }
176
+ },
177
+ }))
178
+ }
package/src/types.ts ADDED
@@ -0,0 +1,116 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Tables Plugin — Pure TypeScript types
3
+ // ---------------------------------------------------------------------------
4
+
5
+ // ============================================================
6
+ // ENUMS / LITERALS
7
+ // ============================================================
8
+
9
+ export type TableStatus = 'available' | 'occupied' | 'reserved' | 'cleaning'
10
+ export type TableShape = 'square' | 'round' | 'rectangle' | 'bar'
11
+
12
+ // ============================================================
13
+ // CORE ENTITIES
14
+ // ============================================================
15
+
16
+ export interface RestaurantTable {
17
+ id: string
18
+ name: string
19
+ number: number
20
+ seats: number
21
+ status: TableStatus
22
+ zone: string
23
+ zoneName?: string
24
+ shape: TableShape
25
+ gridCol: number
26
+ gridRow: number
27
+ isActive: boolean
28
+ // Current session (denormalized for floor plan)
29
+ currentSessionId?: string
30
+ currentOrderId?: string
31
+ currentGuests?: number
32
+ currentWaiterName?: string
33
+ currentElapsedMinutes?: number
34
+ currentTotal?: number
35
+ metadata?: Record<string, unknown>
36
+ tenantId: string
37
+ createdAt: string
38
+ updatedAt: string
39
+ }
40
+
41
+ export interface TableSession {
42
+ id: string
43
+ tableId: string
44
+ tableName?: string
45
+ orderId?: string
46
+ guests: number
47
+ waiterId?: string
48
+ waiterName?: string
49
+ seatedAt: string
50
+ closedAt?: string
51
+ status: 'active' | 'closed'
52
+ notes?: string
53
+ tenantId: string
54
+ createdAt: string
55
+ }
56
+
57
+ export interface Zone {
58
+ id: string
59
+ name: string
60
+ color?: string
61
+ sortOrder: number
62
+ isActive: boolean
63
+ tenantId: string
64
+ createdAt: string
65
+ }
66
+
67
+ // ============================================================
68
+ // INPUT TYPES
69
+ // ============================================================
70
+
71
+ export interface CreateTableInput {
72
+ name: string
73
+ number: number
74
+ seats: number
75
+ zone: string
76
+ shape?: TableShape
77
+ gridCol?: number
78
+ gridRow?: number
79
+ }
80
+
81
+ export interface SeatGuestsInput {
82
+ tableId: string
83
+ guests: number
84
+ waiterId?: string
85
+ notes?: string
86
+ }
87
+
88
+ export interface UpdateTableStatusInput {
89
+ tableId: string
90
+ status: TableStatus
91
+ }
92
+
93
+ // ============================================================
94
+ // QUERY TYPES
95
+ // ============================================================
96
+
97
+ export interface TableQuery {
98
+ zone?: string
99
+ status?: TableStatus | TableStatus[]
100
+ search?: string
101
+ }
102
+
103
+ // ============================================================
104
+ // AGGREGATION
105
+ // ============================================================
106
+
107
+ export interface TablesSummary {
108
+ totalTables: number
109
+ availableCount: number
110
+ occupiedCount: number
111
+ reservedCount: number
112
+ cleaningCount: number
113
+ totalSeats: number
114
+ occupiedSeats: number
115
+ averageSessionMinutes: number
116
+ }