@sanidesk/site-kit 0.1.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 (35) hide show
  1. package/README.md +206 -0
  2. package/dist/asset.d.ts +27 -0
  3. package/dist/asset.d.ts.map +1 -0
  4. package/dist/components/AddressBlock.vue.d.ts +41 -0
  5. package/dist/components/AddressBlock.vue.d.ts.map +1 -0
  6. package/dist/components/ContactForm.vue.d.ts +36 -0
  7. package/dist/components/ContactForm.vue.d.ts.map +1 -0
  8. package/dist/components/FeaturedItems.vue.d.ts +29 -0
  9. package/dist/components/FeaturedItems.vue.d.ts.map +1 -0
  10. package/dist/components/HoursBlock.vue.d.ts +32 -0
  11. package/dist/components/HoursBlock.vue.d.ts.map +1 -0
  12. package/dist/components/MenuPreview.vue.d.ts +26 -0
  13. package/dist/components/MenuPreview.vue.d.ts.map +1 -0
  14. package/dist/components/PoweredBy.vue.d.ts +21 -0
  15. package/dist/components/PoweredBy.vue.d.ts.map +1 -0
  16. package/dist/components/PublicFloorplanViewer.vue.d.ts +26 -0
  17. package/dist/components/PublicFloorplanViewer.vue.d.ts.map +1 -0
  18. package/dist/components/ReservationForm.vue.d.ts +103 -0
  19. package/dist/components/ReservationForm.vue.d.ts.map +1 -0
  20. package/dist/components/SocialLinks.vue.d.ts +31 -0
  21. package/dist/components/SocialLinks.vue.d.ts.map +1 -0
  22. package/dist/composables/use-contact-form.d.ts +45 -0
  23. package/dist/composables/use-contact-form.d.ts.map +1 -0
  24. package/dist/composables/use-featured-items.d.ts +40 -0
  25. package/dist/composables/use-featured-items.d.ts.map +1 -0
  26. package/dist/composables/use-hours-block.d.ts +78 -0
  27. package/dist/composables/use-hours-block.d.ts.map +1 -0
  28. package/dist/composables/use-menu-preview.d.ts +23 -0
  29. package/dist/composables/use-menu-preview.d.ts.map +1 -0
  30. package/dist/composables/use-reservation-form.d.ts +171 -0
  31. package/dist/composables/use-reservation-form.d.ts.map +1 -0
  32. package/dist/index.d.ts +40 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +1083 -0
  35. package/package.json +53 -0
@@ -0,0 +1,45 @@
1
+ import { type Ref, type ComputedRef } from 'vue';
2
+ /**
3
+ * State for a customer-facing contact form. The composable owns the
4
+ * fields, validation, and POST. Templates render their own markup and
5
+ * bind to `form.*`.
6
+ *
7
+ * Honeypot: the `website` field is silently submitted but real users
8
+ * never fill it. The server treats non-empty `website` as a bot and
9
+ * silently succeeds without sending.
10
+ */
11
+ export interface ContactFormState {
12
+ name: string;
13
+ email: string;
14
+ phone: string;
15
+ subject: string;
16
+ message: string;
17
+ /** Honeypot — keep hidden in CSS. Real users leave it empty. */
18
+ website: string;
19
+ }
20
+ export interface ContactFormErrors {
21
+ name?: string;
22
+ email?: string;
23
+ message?: string;
24
+ }
25
+ export interface UseContactFormOptions {
26
+ slug: string;
27
+ /** Destination address — must be a registered tenant_email_account. */
28
+ toEmail: string;
29
+ apiBase?: string;
30
+ fetcher?: typeof fetch;
31
+ /** Localized error strings — defaults are English. */
32
+ messages?: Partial<Record<'required' | 'invalidEmail', string>>;
33
+ }
34
+ export interface UseContactFormReturn {
35
+ form: ContactFormState;
36
+ errors: Ref<ContactFormErrors>;
37
+ submitting: Ref<boolean>;
38
+ submitted: Ref<boolean>;
39
+ serverError: Ref<string | null>;
40
+ isValid: ComputedRef<boolean>;
41
+ submit: () => Promise<boolean>;
42
+ reset: () => void;
43
+ }
44
+ export declare function useContactForm(opts: UseContactFormOptions): UseContactFormReturn;
45
+ //# sourceMappingURL=use-contact-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-contact-form.d.ts","sourceRoot":"","sources":["../../src/composables/use-contact-form.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAE1E;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/B,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACzB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACxB,WAAW,EAAE,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAWD,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,CAsEhF"}
@@ -0,0 +1,40 @@
1
+ import { type Ref } from 'vue';
2
+ import type { Cents, UUID, CatalogItemType } from '@sanidesk/types';
3
+ /**
4
+ * Lightweight item shape returned by `GET /public/tenants/:slug/items`.
5
+ * Smaller than the full `CatalogItem` — only fields a customer surface
6
+ * actually needs (no kitchen notes, SKUs, weight, etc.).
7
+ */
8
+ export interface PublicCatalogItem {
9
+ id: UUID;
10
+ category_id: UUID;
11
+ name: string;
12
+ description: string;
13
+ type: CatalogItemType;
14
+ price_cents: Cents;
15
+ compare_at_price_cents: Cents | null;
16
+ image_path: string;
17
+ image_url: string;
18
+ tags: string[];
19
+ sort_order: number;
20
+ }
21
+ export interface UseFeaturedItemsOptions {
22
+ /** Tenant slug — usually `tenant.value.slug`. */
23
+ slug: string;
24
+ /** Filter to items whose `tags` array contains this value (e.g. `"signature"`). */
25
+ tag?: string;
26
+ /** Cap the returned list. Server enforces max 100. */
27
+ limit?: number;
28
+ /** API base URL, default `/api`. */
29
+ apiBase?: string;
30
+ /** Override fetch — useful for tests. */
31
+ fetcher?: typeof fetch;
32
+ }
33
+ export interface UseFeaturedItemsReturn {
34
+ items: Ref<PublicCatalogItem[]>;
35
+ loading: Ref<boolean>;
36
+ error: Ref<Error | null>;
37
+ refresh: () => Promise<void>;
38
+ }
39
+ export declare function useFeaturedItems(opts: UseFeaturedItemsOptions): UseFeaturedItemsReturn;
40
+ //# sourceMappingURL=use-featured-items.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-featured-items.d.ts","sourceRoot":"","sources":["../../src/composables/use-featured-items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,EAAE,KAAK,CAAC;IACnB,sBAAsB,EAAE,KAAK,GAAG,IAAI,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,sBAAsB,CAkCtF"}
@@ -0,0 +1,78 @@
1
+ import { type Ref, type ComputedRef } from 'vue';
2
+ import type { WeekSchedule, DateOverride, DaySchedule } from '@sanidesk/types';
3
+ /**
4
+ * Override row enriched server-side: when the date matches a Swiss public
5
+ * holiday for the tenant's canton, `name` carries the localized holiday
6
+ * label (e.g. "Weihnachten"). Otherwise undefined.
7
+ */
8
+ export type DateOverrideWithName = DateOverride & {
9
+ name?: string;
10
+ };
11
+ export interface NextOpenSlot {
12
+ date: string;
13
+ open: string;
14
+ close: string;
15
+ }
16
+ export interface PublicHoursPayload {
17
+ schedule: WeekSchedule;
18
+ upcoming_overrides: DateOverrideWithName[];
19
+ is_open_now: boolean;
20
+ next_open_slot: NextOpenSlot | null;
21
+ timezone: string;
22
+ canton: string;
23
+ }
24
+ export interface WeekDay {
25
+ /** Lowercase key — `"monday"`, `"tuesday"`, … */
26
+ key: string;
27
+ /** Same as `key` for callers that want to provide their own labels. */
28
+ schedule: DaySchedule;
29
+ }
30
+ /**
31
+ * Consecutive days collapsed into one row. `startKey === endKey` for
32
+ * single-day groups. Closed days group too (e.g. Sa+So both closed
33
+ * collapse to one `Sa – So Geschlossen` row).
34
+ *
35
+ * The primitive only emits day keys — the consuming template owns
36
+ * the localised labels (`Montag`, `Lundi`, `Monday`, …) and the
37
+ * separator (`–`, `to`, `bis`).
38
+ */
39
+ export interface WeekGroup {
40
+ /** Lowercase key of the first day in this group. */
41
+ startKey: string;
42
+ /** Lowercase key of the last day in this group; equals `startKey` when the group covers a single day. */
43
+ endKey: string;
44
+ /** Number of days covered (1..7). */
45
+ days: number;
46
+ /** Shared schedule for every day in this group. */
47
+ schedule: DaySchedule;
48
+ }
49
+ export interface UseHoursBlockOptions {
50
+ /** Tenant slug — usually `tenant.value.slug`. */
51
+ slug: string;
52
+ /** API base, default `/api`. */
53
+ apiBase?: string;
54
+ /** Override fetch — useful for tests. */
55
+ fetcher?: typeof fetch;
56
+ }
57
+ export interface UseHoursBlockReturn {
58
+ /**
59
+ * Days in Mo→Su order. Disabled days still appear so templates can
60
+ * render "geschlossen" — caller decides how to label.
61
+ */
62
+ week: ComputedRef<WeekDay[]>;
63
+ /**
64
+ * Compact view of `week` — consecutive days that share the same
65
+ * schedule (same enabled flag + identical periods) are collapsed
66
+ * into a single group. Equivalent to `week` when every day differs.
67
+ */
68
+ weekCompact: ComputedRef<WeekGroup[]>;
69
+ upcomingOverrides: ComputedRef<DateOverrideWithName[]>;
70
+ isOpenNow: ComputedRef<boolean>;
71
+ nextOpenSlot: ComputedRef<NextOpenSlot | null>;
72
+ timezone: ComputedRef<string>;
73
+ loading: Ref<boolean>;
74
+ error: Ref<Error | null>;
75
+ refresh: () => Promise<void>;
76
+ }
77
+ export declare function useHoursBlock(opts: UseHoursBlockOptions): UseHoursBlockReturn;
78
+ //# sourceMappingURL=use-hours-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-hours-block.d.ts","sourceRoot":"","sources":["../../src/composables/use-hours-block.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAc,MAAM,iBAAiB,CAAC;AAE3F;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;IAC3C,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7B;;;;OAIG;IACH,WAAW,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC;IACtC,iBAAiB,EAAE,WAAW,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACvD,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,YAAY,EAAE,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC/C,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAcD,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,mBAAmB,CAyC7E"}
@@ -0,0 +1,23 @@
1
+ import { type Ref } from 'vue';
2
+ import type { MenuFull, MenuItemFull } from '@sanidesk/types';
3
+ export interface UseMenuPreviewOptions {
4
+ /** Tenant slug — usually `tenant.value.slug`. */
5
+ slug: string;
6
+ /** API base URL, default `/api`. The Nuxt dev proxy + prod nginx both serve `/api`. */
7
+ apiBase?: string;
8
+ /** Cap how many items the slot receives (after section/availability filtering). */
9
+ limit?: number;
10
+ /** Override fetch — useful for tests. Defaults to `globalThis.fetch`. */
11
+ fetcher?: typeof fetch;
12
+ }
13
+ export interface UseMenuPreviewReturn {
14
+ items: Ref<MenuItemFull[]>;
15
+ menus: Ref<MenuFull[]>;
16
+ loading: Ref<boolean>;
17
+ error: Ref<Error | null>;
18
+ /** Deep-link to ordering, prefilled with the tenant slug. */
19
+ orderHref: Ref<string>;
20
+ refresh: () => Promise<void>;
21
+ }
22
+ export declare function useMenuPreview(opts: UseMenuPreviewOptions): UseMenuPreviewReturn;
23
+ //# sourceMappingURL=use-menu-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-menu-preview.d.ts","sourceRoot":"","sources":["../../src/composables/use-menu-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IAC3B,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvB,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACzB,6DAA6D;IAC7D,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAwBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,CAkChF"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Three-step reservation form state — Wann / Tisch / Kontakt
3
+ * (per docs/MODULE-RESERVATIONS.md §9.3).
4
+ *
5
+ * Composable owns the data fetches (availability + alternatives + submit),
6
+ * the per-step validation, and the post-submit confirmation flag. Templates
7
+ * render their own markup and bind to `state.*` / `actions.*`.
8
+ *
9
+ * Honeypot: the `website` field on the contact step is silently submitted
10
+ * but real users never fill it. Server treats non-empty `website` as a bot
11
+ * and silently 200s without persisting (R2 implementation).
12
+ */
13
+ import { type Ref, type ComputedRef } from 'vue';
14
+ export type FormStep = 'when' | 'table' | 'contact' | 'confirmed' | 'pending';
15
+ export interface AvailabilitySlot {
16
+ starts_at: string;
17
+ fitting_table_ids: string[];
18
+ }
19
+ export interface AlternativeSlot {
20
+ starts_at: string;
21
+ ends_at: string;
22
+ table_id: string;
23
+ }
24
+ export interface PublicTableRow {
25
+ id: string;
26
+ /** Floorplan this table belongs to — drives the per-plan filter when
27
+ * the tenant has multiple plans (e.g. main room + terrace). */
28
+ floor_plan_id: string | null;
29
+ object_id: string | null;
30
+ label: string;
31
+ seats: number;
32
+ }
33
+ export interface PublicFloorPlanEntry {
34
+ id: string;
35
+ name: string;
36
+ is_default: boolean;
37
+ /** Existing FloorPlanData JSONB shape — kept loose so the host template
38
+ * can pass the same value into its own SVG renderer without coupling
39
+ * this package to the heavier @sanidesk/types object graph. */
40
+ data: unknown;
41
+ }
42
+ export interface PublicFloorplan {
43
+ floor_plans: PublicFloorPlanEntry[];
44
+ tables: PublicTableRow[];
45
+ }
46
+ export interface ReservationFormState {
47
+ step: FormStep;
48
+ /** YYYY-MM-DD */
49
+ date: string;
50
+ /** ISO datetime of the chosen slot, or empty if not yet picked. */
51
+ starts_at: string;
52
+ party_size: number;
53
+ /** 'auto' lets the server pick; 'pick' shows the floorplan picker. */
54
+ tableMode: 'auto' | 'pick';
55
+ picked_table_id: string;
56
+ customer_first_name: string;
57
+ customer_last_name: string;
58
+ customer_phone: string;
59
+ customer_email: string;
60
+ occasion: string;
61
+ notes: string;
62
+ /** Honeypot — kept hidden in CSS by the host template. */
63
+ website: string;
64
+ newsletter_opt_in: boolean;
65
+ }
66
+ export interface ReservationFormErrors {
67
+ date?: string;
68
+ starts_at?: string;
69
+ party_size?: string;
70
+ customer_first_name?: string;
71
+ customer_last_name?: string;
72
+ customer_phone?: string;
73
+ customer_email?: string;
74
+ /** Legacy combined error used when both phone+email are missing.
75
+ * Newer code emits the granular field-level errors above. */
76
+ customer_phone_or_email?: string;
77
+ /** Table-picker step — when tableMode='pick' but no table chosen. */
78
+ picked_table_id?: string;
79
+ }
80
+ export interface SubmitResult {
81
+ id: string;
82
+ status: 'confirmed' | 'pending_manual';
83
+ starts_at: string;
84
+ ends_at: string;
85
+ cancel_token: string;
86
+ }
87
+ export interface UseReservationFormOptions {
88
+ /** Tenant slug — `medusabar` for `medusabar.dev.sanidesk.ch`. */
89
+ slug: string;
90
+ /** API base; defaults to `/api`. */
91
+ apiBase?: string;
92
+ /** Override fetch — useful for tests. */
93
+ fetcher?: typeof fetch;
94
+ /** Localized error strings. Defaults are English. */
95
+ messages?: Partial<Record<'requiredField' | 'invalidEmail' | 'phoneOrEmail' | 'invalidPhone' | 'pastDate' | 'pickTable', string>>;
96
+ /** Pre-fill party size. Defaults to 2. */
97
+ initialPartySize?: number;
98
+ /** Pre-fill date (YYYY-MM-DD). Defaults to today. */
99
+ initialDate?: string;
100
+ /**
101
+ * Pre-fill first_name / last_name / email / phone on step 3. Templates
102
+ * pass last-used values from `useCustomerProfileCache(tenantSlug)` so
103
+ * returning customers see their contact info already filled in. Empty
104
+ * string or undefined leaves the field blank. The host template is
105
+ * responsible for the write-back (call `remember(...)` on the form's
106
+ * `result` watcher) — keeping the composable storage-agnostic.
107
+ */
108
+ initialContact?: {
109
+ first_name?: string;
110
+ last_name?: string;
111
+ email?: string;
112
+ phone?: string;
113
+ };
114
+ /** Locale snapshot saved on the reservation for reminder/confirmation
115
+ * emails. The browser's `navigator.language` is used when omitted. */
116
+ customerLocale?: string;
117
+ }
118
+ /** Public-visible subset of `ReservationsConfig` + tenant-derived limits.
119
+ * Fetched once from `/public/:slug/spec` on mount so the widget can
120
+ * enforce tenant rules client-side (date max, lead time, max party,
121
+ * required field list) without hardcoding values per template. */
122
+ export interface ReservationSpec {
123
+ slot_minutes: number;
124
+ default_duration_minutes: number;
125
+ max_advance_days: number;
126
+ lead_time_minutes: number;
127
+ cancel_cutoff_hours: number;
128
+ /** Largest reservable table — parties beyond this need staff. */
129
+ max_party_size: number;
130
+ required_fields: readonly ('first_name' | 'last_name' | 'phone' | 'email')[];
131
+ }
132
+ export interface UseReservationFormReturn {
133
+ state: ReservationFormState;
134
+ errors: Ref<ReservationFormErrors>;
135
+ serverError: Ref<string | null>;
136
+ loading: Ref<boolean>;
137
+ submitting: Ref<boolean>;
138
+ result: Ref<SubmitResult | null>;
139
+ slots: Ref<AvailabilitySlot[]>;
140
+ alternatives: Ref<AlternativeSlot[]>;
141
+ floorplan: Ref<PublicFloorplan | null>;
142
+ /** Tenant rules — fetched on mount; `null` until first response. */
143
+ spec: Ref<ReservationSpec | null>;
144
+ /** ISO YYYY-MM-DD bounds for the date input derived from `spec`. */
145
+ minDate: ComputedRef<string>;
146
+ maxDate: ComputedRef<string>;
147
+ /** True when party_size exceeds the largest reservable table — the
148
+ * template should swap the slot grid for a "Bitte anrufen" CTA. */
149
+ isPartyOversized: ComputedRef<boolean>;
150
+ /** Currently visible floorplan in step 2's "Selbst wählen" mode.
151
+ * Defaults to the tenant's `is_default` plan, then the first plan. */
152
+ activeFloorPlanId: Ref<string | null>;
153
+ /** The active plan's data + tables — convenience for templates. */
154
+ activeFloorPlan: ComputedRef<PublicFloorPlanEntry | null>;
155
+ activeTables: ComputedRef<PublicTableRow[]>;
156
+ /** Tables that fit the requested party at the chosen `starts_at`.
157
+ * Computed from `slots` — empty when no slot is picked yet. */
158
+ fittingTableIds: ComputedRef<string[]>;
159
+ hasAnyAvailability: ComputedRef<boolean>;
160
+ loadSlots: () => Promise<void>;
161
+ loadAlternatives: () => Promise<void>;
162
+ loadFloorplan: () => Promise<void>;
163
+ setActiveFloorPlan: (planId: string) => void;
164
+ goToStep: (step: FormStep) => void;
165
+ next: () => Promise<boolean>;
166
+ back: () => void;
167
+ submit: () => Promise<boolean>;
168
+ reset: () => void;
169
+ }
170
+ export declare function useReservationForm(opts: UseReservationFormOptions): UseReservationFormReturn;
171
+ //# sourceMappingURL=use-reservation-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-reservation-form.d.ts","sourceRoot":"","sources":["../../src/composables/use-reservation-form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @sanidesk/site-kit — headless primitives for tenant public sites.
3
+ *
4
+ * Each component owns the data fetching, state, and integration glue
5
+ * (API calls, deep-link construction). Markup and styles are owned
6
+ * by the consuming tenant template via scoped slots — no opinionated
7
+ * design system here.
8
+ *
9
+ * See `docs/SITE-KIT.md` for the design rationale.
10
+ */
11
+ export { default as MenuPreview } from './components/MenuPreview.vue';
12
+ export type { MenuPreviewProps, MenuPreviewSlotProps } from './components/MenuPreview.vue';
13
+ export { useMenuPreview } from './composables/use-menu-preview';
14
+ export type { UseMenuPreviewOptions, UseMenuPreviewReturn } from './composables/use-menu-preview';
15
+ export { default as FeaturedItems } from './components/FeaturedItems.vue';
16
+ export type { FeaturedItemsProps, FeaturedItemsSlotProps } from './components/FeaturedItems.vue';
17
+ export { useFeaturedItems } from './composables/use-featured-items';
18
+ export type { UseFeaturedItemsOptions, UseFeaturedItemsReturn, PublicCatalogItem, } from './composables/use-featured-items';
19
+ export { default as AddressBlock } from './components/AddressBlock.vue';
20
+ export type { AddressBlockProps, AddressBlockSlotProps } from './components/AddressBlock.vue';
21
+ export { default as SocialLinks } from './components/SocialLinks.vue';
22
+ export type { SocialLinksProps, SocialLinksSlotProps, SocialLinkEntry } from './components/SocialLinks.vue';
23
+ export { default as HoursBlock } from './components/HoursBlock.vue';
24
+ export type { HoursBlockProps, HoursBlockSlotProps } from './components/HoursBlock.vue';
25
+ export { useHoursBlock } from './composables/use-hours-block';
26
+ export type { UseHoursBlockOptions, UseHoursBlockReturn, PublicHoursPayload, DateOverrideWithName, NextOpenSlot, WeekDay, WeekGroup, } from './composables/use-hours-block';
27
+ export { default as PoweredBy } from './components/PoweredBy.vue';
28
+ export type { PoweredByProps } from './components/PoweredBy.vue';
29
+ export { default as ContactForm } from './components/ContactForm.vue';
30
+ export type { ContactFormProps, ContactFormSlotProps } from './components/ContactForm.vue';
31
+ export { useContactForm } from './composables/use-contact-form';
32
+ export type { UseContactFormOptions, UseContactFormReturn, ContactFormState, ContactFormErrors, } from './composables/use-contact-form';
33
+ export { default as ReservationForm } from './components/ReservationForm.vue';
34
+ export type { ReservationFormProps, ReservationFormSlotProps, } from './components/ReservationForm.vue';
35
+ export { useReservationForm } from './composables/use-reservation-form';
36
+ export type { UseReservationFormOptions, UseReservationFormReturn, ReservationFormState, ReservationFormErrors, FormStep as ReservationFormStep, AvailabilitySlot as ReservationAvailabilitySlot, AlternativeSlot as ReservationAlternativeSlot, PublicFloorplan as ReservationPublicFloorplan, PublicFloorPlanEntry as ReservationPublicFloorPlanEntry, PublicTableRow as ReservationPublicTable, SubmitResult as ReservationSubmitResult, ReservationSpec, } from './composables/use-reservation-form';
37
+ export { default as PublicFloorplanViewer } from './components/PublicFloorplanViewer.vue';
38
+ export type { PublicFloorplanViewerProps, PublicFloorplanViewerEmits, TableState as PublicFloorplanTableState, } from './components/PublicFloorplanViewer.vue';
39
+ export { createAssetFn } from './asset';
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAElG,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE9F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE5G,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,OAAO,EACP,SAAS,GACV,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAClE,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC9E,YAAY,EACV,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,QAAQ,IAAI,mBAAmB,EAC/B,gBAAgB,IAAI,2BAA2B,EAC/C,eAAe,IAAI,0BAA0B,EAC7C,eAAe,IAAI,0BAA0B,EAC7C,oBAAoB,IAAI,+BAA+B,EACvD,cAAc,IAAI,sBAAsB,EACxC,YAAY,IAAI,uBAAuB,EACvC,eAAe,GAChB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC1F,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,UAAU,IAAI,yBAAyB,GACxC,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}