@fayz-ai/plugin-menu 0.9.0 → 0.9.1

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/src/data/mock.ts DELETED
@@ -1,289 +0,0 @@
1
- import type { MenuDataProvider } from './types'
2
- import type {
3
- MenuCategory, MenuItem, ModifierGroup,
4
- CreateMenuItemInput, UpdateMenuItemInput, CreateMenuCategoryInput,
5
- MenuItemQuery, PaginatedResult, MenuSummary,
6
- } from '../types'
7
-
8
- let uid = 0
9
- function nextId(prefix: string) { return prefix + '-' + (++uid) }
10
- function now(): string { return new Date().toISOString() }
11
-
12
- function paginate<T>(items: T[], page?: number, pageSize?: number): PaginatedResult<T> {
13
- const p = page ?? 1
14
- const ps = pageSize ?? 50
15
- const start = (p - 1) * ps
16
- return { data: items.slice(start, start + ps), total: items.length }
17
- }
18
-
19
- function seedCategories(): MenuCategory[] {
20
- const t = 'mock-tenant'
21
- const ts = now()
22
- return [
23
- { id: nextId('cat'), name: 'Burgers', icon: '🍔', color: '#f59e0b', sortOrder: 0, isActive: true, itemCount: 3, tenantId: t, createdAt: ts, updatedAt: ts },
24
- { id: nextId('cat'), name: 'Sides', icon: '🍟', color: '#22c55e', sortOrder: 1, isActive: true, itemCount: 3, tenantId: t, createdAt: ts, updatedAt: ts },
25
- { id: nextId('cat'), name: 'Drinks', icon: '🥤', color: '#3b82f6', sortOrder: 2, isActive: true, itemCount: 4, tenantId: t, createdAt: ts, updatedAt: ts },
26
- { id: nextId('cat'), name: 'Desserts', icon: '🍰', color: '#ec4899', sortOrder: 3, isActive: true, itemCount: 2, tenantId: t, createdAt: ts, updatedAt: ts },
27
- { id: nextId('cat'), name: 'Combos', icon: '⭐', color: '#8b5cf6', sortOrder: 4, isActive: true, itemCount: 0, tenantId: t, createdAt: ts, updatedAt: ts },
28
- ]
29
- }
30
-
31
- function seedItems(categories: MenuCategory[]): MenuItem[] {
32
- const t = 'mock-tenant'
33
- const ts = now()
34
- const [burgers, sides, drinks, desserts] = categories.map((c) => c.id)
35
-
36
- const item = (
37
- name: string, price: number, categoryId: string, categoryName: string,
38
- opts: Partial<MenuItem> = {},
39
- ): MenuItem => ({
40
- id: nextId('mi'),
41
- name,
42
- price,
43
- currency: 'BRL',
44
- categoryId,
45
- categoryName,
46
- status: 'available',
47
- isActive: true,
48
- availableForPos: true,
49
- availableForDelivery: true,
50
- sortOrder: 0,
51
- isFeatured: false,
52
- tenantId: t,
53
- createdAt: ts,
54
- updatedAt: ts,
55
- ...opts,
56
- })
57
-
58
- let order = 0
59
- return [
60
- item('Classic Burger', 32, burgers, 'Burgers', { prepTimeMinutes: 12, sortOrder: order++, isFeatured: true }),
61
- item('Cheese Burger', 36, burgers, 'Burgers', { prepTimeMinutes: 12, sortOrder: order++ }),
62
- item('BBQ Bacon', 42, burgers, 'Burgers', { prepTimeMinutes: 15, sortOrder: order++, isFeatured: true }),
63
- item('Fries', 15, sides, 'Sides', { prepTimeMinutes: 8, sortOrder: order++ }),
64
- item('Onion Rings', 18, sides, 'Sides', { prepTimeMinutes: 8, sortOrder: order++ }),
65
- item('Coleslaw', 12, sides, 'Sides', { prepTimeMinutes: 2, sortOrder: order++, availableForDelivery: false }),
66
- item('Soda', 8, drinks, 'Drinks', { prepTimeMinutes: 1, sortOrder: order++ }),
67
- item('Juice', 12, drinks, 'Drinks', { prepTimeMinutes: 3, sortOrder: order++ }),
68
- item('Water', 5, drinks, 'Drinks', { prepTimeMinutes: 0, sortOrder: order++ }),
69
- item('Milkshake', 16, drinks, 'Drinks', { prepTimeMinutes: 5, sortOrder: order++, isFeatured: true }),
70
- item('Brownie', 14, desserts, 'Desserts', { prepTimeMinutes: 1, sortOrder: order++ }),
71
- item('Ice Cream', 12, desserts, 'Desserts', { prepTimeMinutes: 2, sortOrder: order++, availableForDelivery: false }),
72
- ]
73
- }
74
-
75
- function seedModifierGroups(): ModifierGroup[] {
76
- const t = 'mock-tenant'
77
- const ts = now()
78
-
79
- const g1Id = nextId('mg')
80
- const g2Id = nextId('mg')
81
-
82
- return [
83
- {
84
- id: g1Id, name: 'Extra Toppings', minSelections: 0, maxSelections: 3, isRequired: false, sortOrder: 0, tenantId: t, createdAt: ts,
85
- items: [
86
- { id: nextId('mod'), groupId: g1Id, name: 'Extra Cheese', priceAdjustment: 5, isActive: true, sortOrder: 0 },
87
- { id: nextId('mod'), groupId: g1Id, name: 'Bacon', priceAdjustment: 7, isActive: true, sortOrder: 1 },
88
- { id: nextId('mod'), groupId: g1Id, name: 'Jalapeños', priceAdjustment: 4, isActive: true, sortOrder: 2 },
89
- ],
90
- },
91
- {
92
- id: g2Id, name: 'Drink Size', minSelections: 1, maxSelections: 1, isRequired: true, sortOrder: 1, tenantId: t, createdAt: ts,
93
- items: [
94
- { id: nextId('mod'), groupId: g2Id, name: 'Small', priceAdjustment: 0, isActive: true, sortOrder: 0 },
95
- { id: nextId('mod'), groupId: g2Id, name: 'Medium', priceAdjustment: 3, isActive: true, sortOrder: 1 },
96
- { id: nextId('mod'), groupId: g2Id, name: 'Large', priceAdjustment: 5, isActive: true, sortOrder: 2 },
97
- ],
98
- },
99
- ]
100
- }
101
-
102
- export function createMockMenuProvider(): MenuDataProvider {
103
- const tenantId = 'mock-tenant'
104
- const categories = seedCategories()
105
- const items = seedItems(categories)
106
- const modifierGroups = seedModifierGroups()
107
-
108
- return {
109
- async getCategories(): Promise<MenuCategory[]> {
110
- return [...categories].sort((a, b) => a.sortOrder - b.sortOrder)
111
- },
112
-
113
- async getCategoryById(id: string): Promise<MenuCategory | null> {
114
- return categories.find((c) => c.id === id) ?? null
115
- },
116
-
117
- async createCategory(input: CreateMenuCategoryInput): Promise<MenuCategory> {
118
- const cat: MenuCategory = {
119
- id: nextId('cat'),
120
- name: input.name,
121
- parentId: input.parentId,
122
- icon: input.icon,
123
- color: input.color,
124
- sortOrder: input.sortOrder ?? categories.length,
125
- isActive: true,
126
- itemCount: 0,
127
- tenantId,
128
- createdAt: now(),
129
- updatedAt: now(),
130
- }
131
- categories.push(cat)
132
- return cat
133
- },
134
-
135
- async updateCategory(id: string, data: Partial<MenuCategory>): Promise<MenuCategory> {
136
- const cat = categories.find((c) => c.id === id)
137
- if (!cat) throw new Error(`Category ${id} not found`)
138
- Object.assign(cat, data, { updatedAt: now() })
139
- return cat
140
- },
141
-
142
- async deleteCategory(id: string): Promise<void> {
143
- const idx = categories.findIndex((c) => c.id === id)
144
- if (idx >= 0) categories.splice(idx, 1)
145
- },
146
-
147
- async reorderCategories(ids: string[]): Promise<void> {
148
- ids.forEach((id, i) => {
149
- const cat = categories.find((c) => c.id === id)
150
- if (cat) cat.sortOrder = i
151
- })
152
- },
153
-
154
- async getMenuItems(query: MenuItemQuery): Promise<PaginatedResult<MenuItem>> {
155
- let results = [...items]
156
- if (query.categoryId) results = results.filter((i) => i.categoryId === query.categoryId)
157
- if (query.status) {
158
- const statuses = Array.isArray(query.status) ? query.status : [query.status]
159
- results = results.filter((i) => statuses.includes(i.status))
160
- }
161
- if (query.availableForPos !== undefined) results = results.filter((i) => i.availableForPos === query.availableForPos)
162
- if (query.availableForDelivery !== undefined) results = results.filter((i) => i.availableForDelivery === query.availableForDelivery)
163
- if (query.isFeatured !== undefined) results = results.filter((i) => i.isFeatured === query.isFeatured)
164
- if (query.search) {
165
- const s = query.search.toLowerCase()
166
- results = results.filter((i) => i.name.toLowerCase().includes(s) || i.description?.toLowerCase().includes(s))
167
- }
168
- results.sort((a, b) => a.sortOrder - b.sortOrder)
169
- return paginate(results, query.page, query.pageSize)
170
- },
171
-
172
- async getMenuItemById(id: string): Promise<MenuItem | null> {
173
- return items.find((i) => i.id === id) ?? null
174
- },
175
-
176
- async createMenuItem(input: CreateMenuItemInput): Promise<MenuItem> {
177
- const cat = input.categoryId ? categories.find((c) => c.id === input.categoryId) : undefined
178
- const item: MenuItem = {
179
- id: nextId('mi'),
180
- name: input.name,
181
- description: input.description,
182
- price: input.price,
183
- cost: input.cost,
184
- currency: 'BRL',
185
- imageUrl: input.imageUrl,
186
- categoryId: input.categoryId,
187
- categoryName: cat?.name,
188
- status: input.status ?? 'available',
189
- isActive: true,
190
- prepTimeMinutes: input.prepTimeMinutes,
191
- allergens: input.allergens,
192
- dietaryTags: input.dietaryTags,
193
- availableForPos: input.availableForPos ?? true,
194
- availableForDelivery: input.availableForDelivery ?? true,
195
- deliveryPrice: input.deliveryPrice,
196
- sortOrder: items.length,
197
- isFeatured: input.isFeatured ?? false,
198
- modifierGroupIds: input.modifierGroupIds,
199
- tenantId,
200
- createdAt: now(),
201
- updatedAt: now(),
202
- }
203
- items.push(item)
204
- if (cat) cat.itemCount = (cat.itemCount ?? 0) + 1
205
- return item
206
- },
207
-
208
- async updateMenuItem(id: string, data: UpdateMenuItemInput): Promise<MenuItem> {
209
- const item = items.find((i) => i.id === id)
210
- if (!item) throw new Error(`MenuItem ${id} not found`)
211
- Object.assign(item, data, { updatedAt: now() })
212
- return item
213
- },
214
-
215
- async deleteMenuItem(id: string): Promise<void> {
216
- const idx = items.findIndex((i) => i.id === id)
217
- if (idx >= 0) {
218
- const [removed] = items.splice(idx, 1)
219
- if (removed.categoryId) {
220
- const cat = categories.find((c) => c.id === removed.categoryId)
221
- if (cat && cat.itemCount) cat.itemCount--
222
- }
223
- }
224
- },
225
-
226
- async toggleAvailability(id: string, field: 'status' | 'availableForPos' | 'availableForDelivery', value: any): Promise<MenuItem> {
227
- const item = items.find((i) => i.id === id)
228
- if (!item) throw new Error(`MenuItem ${id} not found`)
229
- ;(item as any)[field] = value
230
- item.updatedAt = now()
231
- return item
232
- },
233
-
234
- async reorderItems(categoryId: string, ids: string[]): Promise<void> {
235
- ids.forEach((id, i) => {
236
- const item = items.find((it) => it.id === id && it.categoryId === categoryId)
237
- if (item) item.sortOrder = i
238
- })
239
- },
240
-
241
- async getModifierGroups(): Promise<ModifierGroup[]> {
242
- return [...modifierGroups].sort((a, b) => a.sortOrder - b.sortOrder)
243
- },
244
-
245
- async createModifierGroup(input: Partial<ModifierGroup>): Promise<ModifierGroup> {
246
- const group: ModifierGroup = {
247
- id: nextId('mg'),
248
- name: input.name ?? '',
249
- minSelections: input.minSelections ?? 0,
250
- maxSelections: input.maxSelections ?? 1,
251
- isRequired: input.isRequired ?? false,
252
- sortOrder: input.sortOrder ?? modifierGroups.length,
253
- items: input.items ?? [],
254
- tenantId,
255
- createdAt: now(),
256
- }
257
- modifierGroups.push(group)
258
- return group
259
- },
260
-
261
- async updateModifierGroup(id: string, data: Partial<ModifierGroup>): Promise<ModifierGroup> {
262
- const group = modifierGroups.find((g) => g.id === id)
263
- if (!group) throw new Error(`ModifierGroup ${id} not found`)
264
- Object.assign(group, data)
265
- return group
266
- },
267
-
268
- async deleteModifierGroup(id: string): Promise<void> {
269
- const idx = modifierGroups.findIndex((g) => g.id === id)
270
- if (idx >= 0) modifierGroups.splice(idx, 1)
271
- },
272
-
273
- async getSummary(): Promise<MenuSummary> {
274
- const available = items.filter((i) => i.status === 'available')
275
- const soldOut = items.filter((i) => i.status === 'sold_out')
276
- const hidden = items.filter((i) => i.status === 'hidden')
277
- const prices = items.filter((i) => i.price > 0).map((i) => i.price)
278
- return {
279
- totalItems: items.length,
280
- availableItems: available.length,
281
- soldOutItems: soldOut.length,
282
- hiddenItems: hidden.length,
283
- categoryCount: categories.length,
284
- averagePrice: prices.length ? prices.reduce((a, b) => a + b, 0) / prices.length : 0,
285
- featuredCount: items.filter((i) => i.isFeatured).length,
286
- }
287
- },
288
- }
289
- }