@zorgo/next 1.1.7 → 2.0.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 (74) hide show
  1. package/dist/cli.d.mts +1 -1
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +322 -1
  4. package/dist/cli.js.map +1 -0
  5. package/dist/cli.mjs +322 -1
  6. package/dist/cli.mjs.map +1 -0
  7. package/dist/components.d.mts +1047 -89
  8. package/dist/components.d.ts +1047 -89
  9. package/dist/components.js +4611 -1048
  10. package/dist/components.js.map +1 -1
  11. package/dist/components.mjs +4635 -1149
  12. package/dist/components.mjs.map +1 -1
  13. package/dist/images.d.mts +48 -0
  14. package/dist/images.d.ts +48 -0
  15. package/dist/images.js +72 -0
  16. package/dist/images.js.map +1 -0
  17. package/dist/images.mjs +39 -0
  18. package/dist/images.mjs.map +1 -0
  19. package/dist/index.d.mts +489 -14
  20. package/dist/index.d.ts +489 -14
  21. package/dist/index.js +168 -1
  22. package/dist/index.js.map +1 -0
  23. package/dist/index.mjs +168 -1
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/item-page.d.mts +157 -0
  26. package/dist/item-page.d.ts +157 -0
  27. package/dist/item-page.js +168 -0
  28. package/dist/item-page.js.map +1 -0
  29. package/dist/item-page.mjs +131 -0
  30. package/dist/item-page.mjs.map +1 -0
  31. package/dist/legal.d.mts +31 -0
  32. package/dist/legal.d.ts +31 -0
  33. package/dist/legal.js +322 -0
  34. package/dist/legal.js.map +1 -0
  35. package/dist/legal.mjs +293 -0
  36. package/dist/legal.mjs.map +1 -0
  37. package/dist/seo.d.mts +87 -2
  38. package/dist/seo.d.ts +87 -2
  39. package/dist/seo.js +111 -2
  40. package/dist/seo.js.map +1 -1
  41. package/dist/seo.mjs +106 -1
  42. package/dist/seo.mjs.map +1 -1
  43. package/dist/server.d.mts +27 -1
  44. package/dist/server.d.ts +27 -1
  45. package/dist/server.js +761 -1
  46. package/dist/server.js.map +1 -0
  47. package/dist/server.mjs +722 -1
  48. package/dist/server.mjs.map +1 -0
  49. package/dist/slugs.d.mts +2 -1
  50. package/dist/slugs.d.ts +2 -1
  51. package/dist/slugs.js +2 -0
  52. package/dist/slugs.js.map +1 -1
  53. package/dist/slugs.mjs +1 -0
  54. package/dist/slugs.mjs.map +1 -1
  55. package/dist/tree/README.md +2 -2
  56. package/dist/tree/app/components/ClientItemForm.tsx +7 -0
  57. package/dist/tree/app/components/Header.tsx +3 -0
  58. package/dist/tree/app/components/ModalRegistry.tsx +3 -2
  59. package/dist/tree/app/components/index.ts +2 -1
  60. package/dist/tree/app/error.tsx +12 -3
  61. package/dist/tree/app/global-error.tsx +21 -4
  62. package/dist/tree/app/globals.css +41 -0
  63. package/dist/tree/app/menu/[slug]/page.tsx +16 -48
  64. package/dist/tree/app/not-found.tsx +17 -3
  65. package/dist/tree/app/privacy/page.tsx.gen.ts +28 -9
  66. package/dist/tree/app/terms/page.tsx.gen.ts +26 -10
  67. package/dist/tree/gitignore +7 -1
  68. package/dist/tree/image-loader.ts +4 -0
  69. package/dist/tree/next.config.ts +13 -0
  70. package/dist/tree/open-next.config.ts +7 -0
  71. package/dist/tree/package.json.gen.ts +8 -2
  72. package/package.json +17 -2
  73. package/dist/tree/app/globals.css.gen.ts +0 -123
  74. package/dist/tree/app/menu/[slug]/ItemCustomizationClient.tsx +0 -18
package/dist/index.d.mts CHANGED
@@ -1,9 +1,80 @@
1
1
  import * as _stripe_stripe_js from '@stripe/stripe-js';
2
2
  import { StripeElementsOptions } from '@stripe/stripe-js';
3
- import * as React from 'react';
4
- import React__default, { RefObject } from 'react';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import * as React$1 from 'react';
4
+ import React__default, { RefObject, SVGProps } from 'react';
6
5
  import { z } from 'zod';
6
+ import * as react_jsx_runtime from 'react/jsx-runtime';
7
+
8
+ /**
9
+ * How the section picker draws itself. Closed set — each value needs a renderer,
10
+ * so adding one is a code change (and an `alter type portion_geometry add value`).
11
+ *
12
+ * Section order is fixed per geometry, and consecutive indices must be spatially
13
+ * adjacent so any divisor of the division count partitions cleanly (sections 1–3
14
+ * of 6 are exactly section 1 of 2). Without that, switching a customer from
15
+ * sixths to halves has no defined answer.
16
+ *
17
+ * - `radial` — contiguous arcs from 12 o'clock, counter-clockwise
18
+ * - `grid` — column-major: down the left column, then the next column
19
+ * - `linear` — one end to the other
20
+ * - `count` — equal shares of a countable quantity, no geometry
21
+ */
22
+ declare const PortionGeometrySchema: z.ZodEnum<{
23
+ radial: "radial";
24
+ grid: "grid";
25
+ linear: "linear";
26
+ count: "count";
27
+ }>;
28
+ type PortionGeometry = z.infer<typeof PortionGeometrySchema>;
29
+ /** A row of `portion_schemes` — a franchise-owned, franchise-named menu entity. */
30
+ declare const PortionSchemeSchema: z.ZodObject<{
31
+ id: z.ZodNumber;
32
+ franchise_id: z.ZodNumber;
33
+ geometry: z.ZodEnum<{
34
+ radial: "radial";
35
+ grid: "grid";
36
+ linear: "linear";
37
+ count: "count";
38
+ }>;
39
+ name: z.ZodString;
40
+ min_divisions: z.ZodDefault<z.ZodNumber>;
41
+ max_divisions: z.ZodNumber;
42
+ }, z.core.$strip>;
43
+ type PortionScheme = z.infer<typeof PortionSchemeSchema>;
44
+
45
+ declare const EventSchema: z.ZodObject<{
46
+ id: z.ZodNumber;
47
+ created_at: z.ZodString;
48
+ severity: z.ZodEnum<{
49
+ error: "error";
50
+ debug: "debug";
51
+ info: "info";
52
+ warn: "warn";
53
+ }>;
54
+ event_name: z.ZodString;
55
+ source: z.ZodEnum<{
56
+ pos: "pos";
57
+ kds: "kds";
58
+ kiosk: "kiosk";
59
+ restaurant_site: "restaurant_site";
60
+ developer_dashboard: "developer_dashboard";
61
+ zorgo_site: "zorgo_site";
62
+ backend: "backend";
63
+ }>;
64
+ service: z.ZodNullable<z.ZodString>;
65
+ device_id: z.ZodNullable<z.ZodNumber>;
66
+ session_id: z.ZodNullable<z.ZodString>;
67
+ request_id: z.ZodNullable<z.ZodString>;
68
+ franchise_id: z.ZodNullable<z.ZodNumber>;
69
+ location_id: z.ZodNullable<z.ZodNumber>;
70
+ user_id: z.ZodNullable<z.ZodString>;
71
+ anonymous_id: z.ZodNullable<z.ZodString>;
72
+ customer_id: z.ZodNullable<z.ZodNumber>;
73
+ order_id: z.ZodNullable<z.ZodNumber>;
74
+ payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
75
+ stack_trace: z.ZodNullable<z.ZodString>;
76
+ }, z.core.$strip>;
77
+ type EventRow = z.infer<typeof EventSchema>;
7
78
 
8
79
  type LocationStatus = "active" | "permanently_closed";
9
80
  interface Location {
@@ -11,6 +82,8 @@ interface Location {
11
82
  franchise_id: number;
12
83
  name: string;
13
84
  address?: string;
85
+ /** Public storefront number, as staff typed it. Display only. */
86
+ phone?: string;
14
87
  force_close?: boolean;
15
88
  status?: LocationStatus | null;
16
89
  lat?: number | null;
@@ -44,6 +117,7 @@ interface Category {
44
117
  name: string;
45
118
  is_catering?: boolean;
46
119
  rules: Rule[];
120
+ sort_index?: number | null;
47
121
  active_promo_id?: number;
48
122
  }
49
123
  interface ItemVariant {
@@ -53,19 +127,32 @@ interface ItemVariant {
53
127
  components?: Component[];
54
128
  base_item_id?: number;
55
129
  rules?: Rule[];
130
+ portion_scheme_id?: number | null;
131
+ portion_scheme?: PortionScheme | null;
132
+ sort_index?: number | null;
56
133
  display_price?: number;
57
134
  active_promo_id?: number;
58
135
  }
136
+ interface BaseItemImage {
137
+ image_id: number;
138
+ base_item_id: number;
139
+ alias: string;
140
+ raw_path?: string | null;
141
+ processed_path?: string | null;
142
+ sort_index?: number;
143
+ local_key?: string | null;
144
+ }
59
145
  interface MenuItem {
60
146
  base_item_id: number;
61
147
  category_id: number;
62
148
  franchise_id: number;
63
149
  name: string;
64
150
  per_person?: boolean | null;
65
- image_path?: string | null;
151
+ images?: BaseItemImage[];
66
152
  variants: ItemVariant[];
67
153
  rules: Rule[];
68
154
  description?: string | null;
155
+ sort_index?: number | null;
69
156
  active_promo_id?: number;
70
157
  is_catering?: boolean;
71
158
  }
@@ -100,6 +187,8 @@ interface Rule {
100
187
  min_selection: number;
101
188
  max_selection?: number;
102
189
  included_quantity: number;
190
+ is_portionable?: boolean;
191
+ sort_index?: number | null;
103
192
  items?: RuleItem[];
104
193
  components?: RuleComponent[];
105
194
  }
@@ -110,6 +199,7 @@ interface RuleItem {
110
199
  upcharge_price: number;
111
200
  description?: string | null;
112
201
  behavior?: ScalingBehavior;
202
+ sort_index?: number | null;
113
203
  }
114
204
  interface RuleComponent {
115
205
  component_id: number;
@@ -119,6 +209,7 @@ interface RuleComponent {
119
209
  description?: string | null;
120
210
  behavior?: ScalingBehavior;
121
211
  serving_weight?: number;
212
+ sort_index?: number | null;
122
213
  }
123
214
  interface CustomizationOption {
124
215
  key: string;
@@ -130,14 +221,25 @@ interface CustomizationOption {
130
221
  selected: boolean;
131
222
  disabled: boolean;
132
223
  toggle: () => void;
224
+ /** Section this option currently targets; null when editing the whole item. */
225
+ portion?: string | null;
226
+ /** Section indices this option already covers, for shading the picker. */
227
+ coveredSections?: number[];
228
+ /** Already applied to the whole item, so individual sections are moot. */
229
+ coveredWhole?: boolean;
133
230
  }
134
231
  interface CustomizationRule extends Rule {
135
232
  rule_id: number;
136
233
  max: number;
234
+ /** Weighted, not a row count: two halves of one topping count as 1. */
137
235
  selectedCount: number;
138
236
  satisfied: boolean;
237
+ /** "Choose 1. Select up to 5. 1 included" — the rule's limits in words. */
238
+ subtext: string;
139
239
  message: () => string | null;
140
240
  options: CustomizationOption[];
241
+ /** This rule can be applied per-section under the item's scheme. */
242
+ portionable?: boolean;
141
243
  }
142
244
  interface OrderItem {
143
245
  item_id: number;
@@ -155,6 +257,7 @@ interface OrderItem {
155
257
  components?: OrderComponent[];
156
258
  selections?: RuleSelection[];
157
259
  comments?: string;
260
+ portion_scheme?: PortionScheme | null;
158
261
  status?: "removed" | "modified";
159
262
  applied_promo_id?: number;
160
263
  discount_amount?: number;
@@ -163,6 +266,7 @@ interface OrderComponent extends Component {
163
266
  quantity_servings: number;
164
267
  upcharge?: number;
165
268
  rule_id?: number;
269
+ portion?: string | null;
166
270
  }
167
271
  interface RuleSelection {
168
272
  rule_id: number;
@@ -177,10 +281,13 @@ interface Selection {
177
281
  serving_price?: number;
178
282
  name: string;
179
283
  status?: "removed" | "modified" | "added";
284
+ portion?: string | null;
180
285
  component_type_id?: number;
181
286
  serving_weight?: number;
182
287
  measurement_type?: "weight" | "count";
183
288
  display_unit?: string;
289
+ /** Only populated when type === "item" and the item has its own recipe (item_components). */
290
+ components?: OrderComponent[];
184
291
  }
185
292
  interface Customer {
186
293
  customer_id?: number;
@@ -229,6 +336,10 @@ interface Order {
229
336
  updated_at?: Date | string;
230
337
  table_number?: number;
231
338
  }
339
+ type DetailedOrder = Order & {
340
+ events: EventRow[];
341
+ actorNames?: Record<string, string>;
342
+ };
232
343
  interface Charge {
233
344
  id?: number;
234
345
  order_id?: number;
@@ -301,6 +412,56 @@ interface RecommendationResult {
301
412
  molecular_score?: number;
302
413
  }
303
414
 
415
+ declare const franchiseFeatureConfigs: {
416
+ readonly catering: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
417
+ notification_emails: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
418
+ email: z.ZodEmail;
419
+ location_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
420
+ }, z.core.$strip>>>>;
421
+ minimum_notice_hours: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
422
+ minimum_people_required: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
423
+ minimum_price: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
424
+ does_cater: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
425
+ preferred_time: z.ZodOptional<z.ZodNullable<z.ZodString>>;
426
+ }, z.core.$strip>>;
427
+ readonly delivery: z.ZodObject<{
428
+ does_deliver: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
429
+ delivery_fee_cents: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
430
+ max_delivery_radius_miles: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
431
+ cents_per_mile: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
432
+ }, z.core.$strip>;
433
+ readonly contact: z.ZodObject<{
434
+ forms: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
435
+ key: z.ZodString;
436
+ enabled: z.ZodDefault<z.ZodBoolean>;
437
+ email: z.ZodOptional<z.ZodNullable<z.ZodEmail>>;
438
+ label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
439
+ }, z.core.$strip>>>>;
440
+ }, z.core.$strip>;
441
+ readonly catering_bulk_discount: z.ZodObject<{
442
+ is_active: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
443
+ full_discount_at: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
444
+ maximum_percent_off: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
445
+ }, z.core.$strip>;
446
+ readonly food_truck: z.ZodObject<{
447
+ base_price_per_person_cents: z.ZodNumber;
448
+ default_service_hours: z.ZodNumber;
449
+ additional_hour_rate_cents: z.ZodNumber;
450
+ service_fee_percent: z.ZodNumber;
451
+ sales_tax_percent: z.ZodNumber;
452
+ minimum_booking_fee_cents: z.ZodNumber;
453
+ date_hold_deposit_cents: z.ZodNumber;
454
+ add_ons: z.ZodDefault<z.ZodArray<z.ZodObject<{
455
+ key: z.ZodString;
456
+ label: z.ZodString;
457
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
458
+ price_per_person_cents: z.ZodNumber;
459
+ }, z.core.$strip>>>;
460
+ }, z.core.$strip>;
461
+ };
462
+ type FranchiseFeatureId = keyof typeof franchiseFeatureConfigs;
463
+ type FranchiseFeatureConfig<T extends FranchiseFeatureId> = z.infer<(typeof franchiseFeatureConfigs)[T]>;
464
+
304
465
  interface Menu {
305
466
  items: MenuItem[];
306
467
  categories: Category[];
@@ -308,6 +469,9 @@ interface Menu {
308
469
  rules: Rule[];
309
470
  }
310
471
 
472
+ declare const CAPABILITIES: readonly ["ztech.page.franchises", "ztech.page.locations", "ztech.page.performance", "ztech.page.orders", "ztech.page.configure-location", "ztech.page.franchise-details", "ztech.page.integrations", "ztech.page.menu", "ztech.page.components", "ztech.page.catering", "ztech.page.promotions", "ztech.page.skills", "ztech.page.receipts", "ztech.page.users", "ztech.page.ingredient-inventory", "ztech.page.batch-prep", "ztech.page.devices", "ztech.page.tables", "portal.page.sales", "portal.page.analytics", "portal.page.analytics.overview", "portal.page.analytics.devices", "portal.page.analytics.backend", "portal.page.analytics.website", "portal.page.settings", "orders.view", "orders.refund", "orders.modify", "catering.view", "catering.confirm", "menu.edit", "promotions.edit", "staff.manage", "notifications.manage_self", "notifications.receive"];
473
+ type Capability = (typeof CAPABILITIES)[number];
474
+
311
475
  interface ModalInstance {
312
476
  type: string;
313
477
  props: any;
@@ -363,6 +527,8 @@ declare function useCart(): {
363
527
  };
364
528
  declare function useLocation(options?: {
365
529
  promptLocationSelection?: boolean;
530
+ /** Location id to select for the user when nothing is selected yet. */
531
+ autoSelectLocationId?: number;
366
532
  }): {
367
533
  availableLocations: Location[];
368
534
  currentLocation: Location | null;
@@ -373,8 +539,12 @@ declare function useLocation(options?: {
373
539
  /**
374
540
  * Reactive menu data for the configured franchise.
375
541
  *
376
- * `items` excludes catering categories; use `allItems`,
377
- * `cateringPrimaryItem`, or `cateringAddOns` when building catering flows.
542
+ * `items` excludes catering categories (Category.is_catering — the sole catering
543
+ * signal per ZOR-396); `allItems` includes them.
544
+ *
545
+ * There is no catering-specific accessor here. A website builds its catering flow
546
+ * from the prebuilt `lib/generated/menu.ts` and `lib/generated/featureConfig.ts`,
547
+ * which is build-time-constant data and doesn't belong behind a reactive hook.
378
548
  *
379
549
  * @example
380
550
  * const { items, categories } = useMenu();
@@ -383,7 +553,6 @@ declare function useMenu(): {
383
553
  items: MenuItem[];
384
554
  allItems: MenuItem[];
385
555
  categories: Category[];
386
- getItemImageUrl: (imagePath?: string | null) => string | null;
387
556
  };
388
557
  /**
389
558
  * Seeds a store with prebuilt data (see the app's prebuild script) during the
@@ -432,11 +601,27 @@ declare function useItemCustomization(baseItemId: number, seed?: OrderItem, edit
432
601
  unmetRule: CustomizationRule | null;
433
602
  canSubmit: boolean;
434
603
  submit: () => OrderItem | null;
604
+ portionScheme: {
605
+ id: number;
606
+ franchise_id: number;
607
+ geometry: "radial" | "grid" | "linear" | "count";
608
+ name: string;
609
+ min_divisions: number;
610
+ max_divisions: number;
611
+ } | null;
612
+ placeOption: (ruleId: number, baseKey: string, portion: string | null, max?: number) => void;
613
+ clearOption: (ruleId: number, baseKey: string) => void;
614
+ recutSelections: (next: number) => void;
435
615
  quantity: number;
436
- setQuantity: React.Dispatch<React.SetStateAction<number>>;
616
+ setQuantity: (next: number | ((current: number) => number)) => void;
437
617
  comments: string;
438
- setComments: React.Dispatch<React.SetStateAction<string>>;
439
- setComponentServings: (componentId: number, qty: number) => void;
618
+ setComments: React$1.Dispatch<React$1.SetStateAction<string>>;
619
+ /**
620
+ * Set a component's servings for the whole item, or for one section when
621
+ * `portion` is given. 0 removes it — "no cheese", or "no cheese on the
622
+ * left half".
623
+ */
624
+ setComponentServings: (componentId: number, qty: number, portion?: string | null) => void;
440
625
  price: number;
441
626
  salePrice: number | undefined;
442
627
  isEditMode: boolean;
@@ -453,12 +638,15 @@ declare function usePromotions(): {
453
638
  removePromo: () => void;
454
639
  activePromotions: Promotion[];
455
640
  };
456
- declare function usePayment(): {
641
+ declare function usePayment(options?: {
642
+ appearance?: StripeElementsOptions["appearance"];
643
+ }): {
457
644
  clientSecret: string | null;
458
645
  intentType: "payment_intent" | "setup_intent" | null;
459
646
  stripe: _stripe_stripe_js.Stripe | undefined;
460
647
  elementOptions: StripeElementsOptions | null;
461
648
  total: number | null;
649
+ orderId: number | null;
462
650
  isLoading: boolean;
463
651
  error: string | null;
464
652
  isReady: boolean;
@@ -485,7 +673,11 @@ declare function usePayment(): {
485
673
  amount?: number;
486
674
  paymentIntentId?: string;
487
675
  error?: string;
676
+ isHold: boolean;
677
+ orderId?: number;
678
+ orderToken?: string;
488
679
  };
680
+ mintOrderLinkToken: (orderId: number, email: string) => Promise<string | null>;
489
681
  getReturnUrl: () => string | undefined;
490
682
  };
491
683
  declare function useCateringConfig(): {
@@ -613,6 +805,8 @@ declare const TextFieldSchema: z.ZodObject<{
613
805
  sanitize: z.ZodAny;
614
806
  min: z.ZodOptional<z.ZodString>;
615
807
  max: z.ZodOptional<z.ZodString>;
808
+ multiline: z.ZodDefault<z.ZodBoolean>;
809
+ rows: z.ZodDefault<z.ZodNumber>;
616
810
  }, z.core.$strip>;
617
811
  type TextFieldInput = Partial<z.infer<typeof TextFieldSchema>> & {
618
812
  fieldId: string;
@@ -658,9 +852,9 @@ declare const ClickableSchema: z.ZodObject<{
658
852
  optionSublabelClassName: z.ZodDefault<z.ZodString>;
659
853
  inputClassName: z.ZodDefault<z.ZodString>;
660
854
  }, z.core.$strip>;
661
- type ClickableInput = Partial<z.infer<typeof ClickableSchema>> & {
855
+ type ClickableInput = Omit<Partial<z.infer<typeof ClickableSchema>>, "options"> & {
662
856
  fieldId: string;
663
- options: z.infer<typeof ClickableSchema>["options"];
857
+ options: z.input<typeof ClickableSchema>["options"];
664
858
  };
665
859
 
666
860
  declare function TextField(raw: TextFieldInput): react_jsx_runtime.JSX.Element;
@@ -741,6 +935,8 @@ declare const LocationFieldSchema: z.ZodObject<{
741
935
  sanitize: z.ZodAny;
742
936
  min: z.ZodOptional<z.ZodString>;
743
937
  max: z.ZodOptional<z.ZodString>;
938
+ multiline: z.ZodDefault<z.ZodBoolean>;
939
+ rows: z.ZodDefault<z.ZodNumber>;
744
940
  fieldId: z.ZodDefault<z.ZodString>;
745
941
  label: z.ZodDefault<z.ZodString>;
746
942
  placeholder: z.ZodDefault<z.ZodString>;
@@ -936,6 +1132,8 @@ declare const NameFieldsSchema: z.ZodObject<{
936
1132
  sanitize: z.ZodAny;
937
1133
  min: z.ZodOptional<z.ZodString>;
938
1134
  max: z.ZodOptional<z.ZodString>;
1135
+ multiline: z.ZodDefault<z.ZodBoolean>;
1136
+ rows: z.ZodDefault<z.ZodNumber>;
939
1137
  fieldId: z.ZodDefault<z.ZodString>;
940
1138
  outerClassName: z.ZodDefault<z.ZodString>;
941
1139
  firstNamePlaceholder: z.ZodDefault<z.ZodString>;
@@ -1105,4 +1303,281 @@ declare function useFormValues(): Record<string, unknown>;
1105
1303
 
1106
1304
  declare function useFormValidity(): boolean;
1107
1305
 
1108
- export { type Category, CheckboxField, ClickableField, ConditionalRenderer, type Customer, type CustomizationOption, type CustomizationRule, DateField, DateTime, DateTimeRange, DisclaimerText, DiscreteNumberField, DynamicFormShell, EmailField, FieldError, FieldLabel, GuestCountField, Header, IncrementField, type ItemVariant, type Location, LocationField, type Menu, type MenuItem, NameFields, type Order, type OrderComponent, type OrderItem, PhoneField, Platforms, PerPersonSummary as PricePreview, type Promotion, type RecommendationRequest, type RecommendationResult, type RecommendationStrategy, type Rule, type RuleComponent, type RuleItem, type RuleSelection, SectionLabel, type Selection, SliderField, SubmitButton, TextField, type TimeConstraints, TimeField, setZorgoBoot, setZorgoPathnameHook, useCart, useCateringConfig, useCustomer, useField, useFormStatus, useFormValidity, useFormValues, useHydrateMenu, useHydrateStore, useItemCustomization, useLocation, useMenu, useModal, usePayment, usePromotions, useRecommendations, useRegisterField };
1306
+ type IconComponent = React.FC<SVGProps<SVGSVGElement>>;
1307
+ type StatusColor = "blue" | "green" | "red" | "gray";
1308
+ declare function resolveEventDisplay(eventName: string): {
1309
+ label: string;
1310
+ color: StatusColor;
1311
+ icon?: IconComponent;
1312
+ };
1313
+ interface OrderApproval {
1314
+ status: "requested" | "confirmed" | "rejected";
1315
+ via: "pos" | "email_link" | "dashboard" | null;
1316
+ when: string;
1317
+ actorName: string | null;
1318
+ }
1319
+ /** null means no approval event exists yet — nearly every order today, since the
1320
+ * email-link path never recorded a userId before this ticket. Render that honestly
1321
+ * rather than a blank timeline. */
1322
+ declare function deriveApproval(order: DetailedOrder): OrderApproval | null;
1323
+ interface OrderStatusInfo {
1324
+ label: string;
1325
+ color: StatusColor;
1326
+ }
1327
+ declare function deriveOrderStatus(order: DetailedOrder): OrderStatusInfo;
1328
+ /** Mirrors confirmOrder's own promotable-status check — the button only appears when
1329
+ * clicking it wouldn't just no-op. */
1330
+ declare function isConfirmable(order: DetailedOrder): boolean;
1331
+ interface StatusColorClasses {
1332
+ /** Field behind the status pill or a timeline icon. */
1333
+ bg: string;
1334
+ /** Ink on that field, and on the event's own label. */
1335
+ text: string;
1336
+ /** The bare dot drawn for an event that has no icon of its own. */
1337
+ dot: string;
1338
+ }
1339
+ interface OrderStatusClasses {
1340
+ root: string;
1341
+ statusPill: string;
1342
+ approval: string;
1343
+ approvalEmpty: string;
1344
+ timeline: string;
1345
+ eventIcon: string;
1346
+ eventLabel: string;
1347
+ eventTime: string;
1348
+ connectorLine: string;
1349
+ connectorDelta: string;
1350
+ confirmButton: string;
1351
+ confirmError: string;
1352
+ colors: Record<StatusColor, StatusColorClasses>;
1353
+ }
1354
+ type OrderStatusItemClasses = Partial<Omit<OrderStatusClasses, "colors"> & {
1355
+ colors: Partial<Record<StatusColor, Partial<StatusColorClasses>>>;
1356
+ }>;
1357
+ /** ztech's dark chrome — what this component rendered before it took classes at all,
1358
+ * so a caller that passes none is unchanged. */
1359
+ declare const ORDER_STATUS_DEFAULT_ITEM_CLASSES: OrderStatusClasses;
1360
+ /**
1361
+ * A slot **replaces** its default rather than appending to it: a site theming this is
1362
+ * swapping `text-white` for its own ink, and two conflicting Tailwind classes in one
1363
+ * string resolve by stylesheet order, not by who wrote them last.
1364
+ *
1365
+ * A restaurant site should expect to pass most of them. It reaches this component
1366
+ * through @zorgo/next's index bundle, which its Tailwind never scans (only
1367
+ * dist/components.mjs is @source'd), so a class named *only* in the defaults above
1368
+ * never reaches that site's CSS.
1369
+ */
1370
+ declare function mergeOrderStatusItemClasses(overrides?: OrderStatusItemClasses): OrderStatusClasses;
1371
+ interface OrderStatusProps {
1372
+ order: DetailedOrder;
1373
+ /** A predicate over whichever capabilities the caller already resolved — ztech passes
1374
+ * `useAccess().can`, a token-gated page passes one built from the server's resolved list.
1375
+ * This component never fetches and holds no session of its own. */
1376
+ can: (capability: Capability) => boolean;
1377
+ onConfirm?: () => void | Promise<void>;
1378
+ confirming?: boolean;
1379
+ confirmError?: string | null;
1380
+ /** Per-slot Tailwind classes. Omitted slots keep the dark defaults above. */
1381
+ itemClasses?: OrderStatusItemClasses;
1382
+ }
1383
+ declare function OrderStatus({ order, can, onConfirm, confirming, confirmError, itemClasses }: OrderStatusProps): react_jsx_runtime.JSX.Element;
1384
+
1385
+ interface ItemizedAddOn {
1386
+ name: string;
1387
+ /** The rule/group this selection came from, e.g. "Proteins". */
1388
+ groupName?: string;
1389
+ /** Per-unit upcharge in cents. 0 means included at no extra cost. */
1390
+ upcharge: number;
1391
+ }
1392
+ interface ItemizedLine {
1393
+ name: string;
1394
+ quantity: number;
1395
+ /** One unit priced the way the line is: base + selection upcharges. */
1396
+ unitPrice: number;
1397
+ /** unitPrice * quantity — equals calculateItemPrice(item). */
1398
+ lineTotal: number;
1399
+ /** true = catering per-guest line, false = catering add-on, null/undefined = plain item. */
1400
+ perPerson?: boolean | null;
1401
+ addOns: ItemizedAddOn[];
1402
+ }
1403
+ interface ItemizedOrder {
1404
+ lines: ItemizedLine[];
1405
+ subtotal: number;
1406
+ discount: number;
1407
+ tax: number;
1408
+ serviceFee: number;
1409
+ tip: number;
1410
+ /** What they were charged: charges.total + tip. */
1411
+ total: number;
1412
+ }
1413
+ /**
1414
+ * The receipt: every line with its add-ons, plus the charge breakdown.
1415
+ *
1416
+ * Catering and standard carts are the same shape — a list of items carrying
1417
+ * selections — so there is one loop, and `perPerson` only labels a line for the
1418
+ * UI rather than pricing it differently.
1419
+ *
1420
+ * Totals prefer the order's own settled charges — what was actually billed —
1421
+ * over `calculateCharges`, which reprices against the current menu. A placed
1422
+ * order's money is what Stripe took, not what today's menu would charge; an
1423
+ * un-charged cart (still in checkout) has nothing settled, so it still prices
1424
+ * itself live. Tip is passed in because it lives on the cart, not the order.
1425
+ */
1426
+ declare function itemizeOrder(order: Order, promotion?: Promotion, tip?: number): ItemizedOrder;
1427
+
1428
+ interface OrderItemizedClasses {
1429
+ root: string;
1430
+ line: string;
1431
+ lineName: string;
1432
+ lineSublabel: string;
1433
+ linePrice: string;
1434
+ addOn: string;
1435
+ addOnName: string;
1436
+ addOnPrice: string;
1437
+ totalsRow: string;
1438
+ discountRow: string;
1439
+ totalRow: string;
1440
+ }
1441
+ type OrderItemizedItemClasses = Partial<OrderItemizedClasses>;
1442
+ /** ztech's dark chrome, matching OrderStatus's defaults — a caller that passes none is unchanged. */
1443
+ declare const ORDER_ITEMIZED_DEFAULT_ITEM_CLASSES: OrderItemizedClasses;
1444
+ /**
1445
+ * A slot **replaces** its default rather than appending to it — see OrderStatus's
1446
+ * mergeOrderStatusItemClasses for why (a restaurant site's Tailwind only scans
1447
+ * dist/components.mjs, so a class named only in the defaults never reaches it).
1448
+ */
1449
+ declare function mergeOrderItemizedItemClasses(overrides?: OrderItemizedItemClasses): OrderItemizedClasses;
1450
+ declare function formatCateringLineLabel(line: ItemizedLine): string;
1451
+ declare function formatAddOnPrice(addOn: ItemizedAddOn, perPerson?: boolean | null): string;
1452
+ interface OrderItemizedProps {
1453
+ order: Order;
1454
+ promotion?: Promotion;
1455
+ tip?: number;
1456
+ /** Per-slot Tailwind classes. Omitted slots keep the dark defaults above. */
1457
+ itemClasses?: OrderItemizedItemClasses;
1458
+ }
1459
+ /** The receipt: every line with its add-ons, plus the charge breakdown. Pure render from
1460
+ * itemizeOrder(order) — never fetches, same contract as OrderStatus. */
1461
+ declare function OrderItemized({ order, promotion, tip, itemClasses }: OrderItemizedProps): react_jsx_runtime.JSX.Element;
1462
+
1463
+ declare function parsePortion(portion: string): {
1464
+ index: number;
1465
+ divisions: number;
1466
+ };
1467
+ /** A section's share of the whole. No portion = the whole item = 1. */
1468
+ declare function portionWeight(portion?: string | null): number;
1469
+ /**
1470
+ * Selection keys carry their portion as a suffix: `component:123@1/2`. A bare
1471
+ * key is the whole item, which is what every pre-portion key already is — so
1472
+ * existing state parses unchanged.
1473
+ */
1474
+ declare function portionKey(baseKey: string, portion?: string | null): string;
1475
+ declare function parsePortionKey(key: string): {
1476
+ baseKey: string;
1477
+ portion: string | null;
1478
+ };
1479
+ /**
1480
+ * A section's share of a whole-item upcharge, in cents. Rounds up, so covering
1481
+ * every section can cost a cent or two more than the whole — which is why a
1482
+ * selection covering everything collapses to a single portion-less row instead.
1483
+ */
1484
+ declare function splitUpcharge(cents: number, divisions: number): number;
1485
+ /** Every division count a scheme allows, for the picker's cut selector. */
1486
+ declare function divisionsFor(scheme: Pick<PortionScheme, "min_divisions" | "max_divisions">): number[];
1487
+ /**
1488
+ * The one place a section index becomes words. The picker, the cart line, the
1489
+ * receipt and the KDS ticket all call this on the same scheme, so the index is
1490
+ * the only token the UI and the data share.
1491
+ */
1492
+ declare function portionLabel(portion: string | null | undefined, scheme: Pick<PortionScheme, "geometry">): string;
1493
+ /**
1494
+ * Expand per-section overrides on one keyed entity into one row per section —
1495
+ * how "no cheese on the left half" becomes a 0-serving row for that section and
1496
+ * a normal row for the rest. No overrides, or the same value on every section,
1497
+ * is a single whole-item row (§3 collapse).
1498
+ */
1499
+ declare function portionRows(baseKey: string, values: Record<string, number>, fallback: number): {
1500
+ portion: string | null;
1501
+ value: number;
1502
+ }[];
1503
+ /** Weighted count of a rule's selections: two halves of one topping count as 1. */
1504
+ declare function selectionWeight(keys: string[]): number;
1505
+ /**
1506
+ * Spend `included_quantity` on selection keys in pick order. A topping is a
1507
+ * topping: it takes one included slot no matter how little of the pizza it covers,
1508
+ * so putting pepperoni on one half never leaves half a free slot behind to
1509
+ * discount everything else — and its rows share that one slot, so two halves of
1510
+ * pepperoni consume one included topping, not two. With no portions this is
1511
+ * exactly the old positional rule (`position < included ? 0 : basePrice`).
1512
+ */
1513
+ declare function weightedUpcharges(keys: string[], basePriceOf: (baseKey: string) => number, included: number): Map<string, number>;
1514
+ /**
1515
+ * Re-express a portion under a different cut, for when the customer switches
1516
+ * from halves to quarters mid-order. Returns null when neither count divides the
1517
+ * other (halves → thirds), meaning the selection can't be carried over.
1518
+ */
1519
+ declare function remapPortion(portion: string, nextDivisions: number): string[] | null;
1520
+ /**
1521
+ * Positional section wording, geometry-free — deliberately not `portionLabel`.
1522
+ * Derived words ("Left", "Top left") only exist for a few geometry/division
1523
+ * pairs and need the scheme; a receipt has to say something for every cut.
1524
+ */
1525
+ declare function sectionLabel(portion?: string | null): string;
1526
+ /** A text glyph for a section, or null when unicode has no character for it. */
1527
+ declare function portionGlyph(portion: string | null | undefined, geometry?: PortionGeometry | null): string | null;
1528
+ interface PortionGroup {
1529
+ portion: string | null;
1530
+ label: string;
1531
+ glyph: string | null;
1532
+ /** The item's rules, each carrying only the selections that land on this section. */
1533
+ selections: RuleSelection[];
1534
+ removals: OrderComponent[];
1535
+ /**
1536
+ * Components present on this section. Whole-item components are the base
1537
+ * recipe — implied by the item's name — so only portioned ones land here,
1538
+ * where "pepperoni on the left" is the whole point of the line.
1539
+ */
1540
+ additions: OrderComponent[];
1541
+ }
1542
+ /**
1543
+ * One group per covered section: the whole-item group first, then sections in
1544
+ * index order, rules keeping their original order inside each. An item with no
1545
+ * portions anywhere yields exactly one whole-item group, so a surface can render
1546
+ * this unconditionally and show the heading only when `portion_scheme` is set.
1547
+ *
1548
+ * It groups and nothing else — every selection it is handed comes back out. What
1549
+ * counts as worth printing is the surface's call, not this function's.
1550
+ */
1551
+ declare function groupItemPortions(item: Pick<OrderItem, "selections" | "components" | "portion_scheme">): PortionGroup[];
1552
+ declare function radialSectionPath(index: number, divisions: number, r?: number): string;
1553
+
1554
+ /** Same opinionated TZ as legacy menu checks. */
1555
+ declare const LOCATION_ORDERING_TIMEZONE = "America/New_York";
1556
+ declare const DAYS: readonly ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
1557
+ /** The open/close instants of one calendar day's window, in LOCATION_ORDERING_TIMEZONE. */
1558
+ interface LocationWindow {
1559
+ /** Lowercase weekday the window's hours were read from. */
1560
+ day: (typeof DAYS)[number];
1561
+ open: Date;
1562
+ close: Date;
1563
+ }
1564
+ /**
1565
+ * The open/close instants for the day `dayOffset` days from `at`, read in
1566
+ * LOCATION_ORDERING_TIMEZONE. Null when that weekday has no hours set.
1567
+ *
1568
+ * A close at or before the open is read as closing after midnight, so an
1569
+ * 11pm–2am window is one continuous night rather than a closed day.
1570
+ */
1571
+ declare function locationWindowOn(at: Date, location: Location, dayOffset?: number): LocationWindow | null;
1572
+ /**
1573
+ * True when the location is outside its weekly open window at `at`
1574
+ * (includes `force_close`). Yesterday's window is checked too, so the small
1575
+ * hours of an overnight window still count as open.
1576
+ */
1577
+ declare function isLocationClosedByWeeklySchedule(at: Date, location: Location): boolean;
1578
+
1579
+ type CateringSiteConfig = Omit<FranchiseFeatureConfig<"catering">, "notification_emails">;
1580
+ type CateringBulkDiscount = FranchiseFeatureConfig<"catering_bulk_discount">;
1581
+ type FoodTruckConfigValue = FranchiseFeatureConfig<"food_truck">;
1582
+
1583
+ export { type Capability, type Category, type CateringBulkDiscount, type CateringSiteConfig, CheckboxField, ClickableField, ConditionalRenderer, type Customer, type CustomizationOption, type CustomizationRule, DateField, DateTime, DateTimeRange, type DetailedOrder, DisclaimerText, DiscreteNumberField, DynamicFormShell, EmailField, type EventRow, FieldError, FieldLabel, type FoodTruckConfigValue, type FranchiseFeatureConfig, GuestCountField, Header, IncrementField, type ItemVariant, type ItemizedAddOn, type ItemizedLine, type ItemizedOrder, LOCATION_ORDERING_TIMEZONE, type Location, LocationField, type LocationWindow, type Menu, type MenuItem, NameFields, ORDER_ITEMIZED_DEFAULT_ITEM_CLASSES, ORDER_STATUS_DEFAULT_ITEM_CLASSES, type Order, type OrderApproval, type OrderComponent, type OrderItem, OrderItemized, type OrderItemizedClasses, type OrderItemizedItemClasses, type OrderItemizedProps, OrderStatus, type OrderStatusClasses, type OrderStatusInfo, type OrderStatusItemClasses, type OrderStatusProps, PhoneField, Platforms, type PortionGroup, PerPersonSummary as PricePreview, type Promotion, type RecommendationRequest, type RecommendationResult, type RecommendationStrategy, type Rule, type RuleComponent, type RuleItem, type RuleSelection, SectionLabel, type Selection, SliderField, type StatusColorClasses, SubmitButton, TextField, type TimeConstraints, TimeField, deriveApproval, deriveOrderStatus, divisionsFor, formatAddOnPrice, formatCateringLineLabel, groupItemPortions, isConfirmable, isLocationClosedByWeeklySchedule, itemizeOrder, locationWindowOn, mergeOrderItemizedItemClasses, mergeOrderStatusItemClasses, parsePortion, parsePortionKey, portionGlyph, portionKey, portionLabel, portionRows, portionWeight, radialSectionPath, remapPortion, resolveEventDisplay, sectionLabel, selectionWeight, setZorgoBoot, setZorgoPathnameHook, splitUpcharge, useCart, useCateringConfig, useCustomer, useField, useFormStatus, useFormValidity, useFormValues, useHydrateMenu, useHydrateStore, useItemCustomization, useLocation, useMenu, useModal, usePayment, usePromotions, useRecommendations, useRegisterField, weightedUpcharges };