@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
package/dist/index.js ADDED
@@ -0,0 +1,1083 @@
1
+ import { ref as S, onMounted as Z, defineComponent as C, renderSlot as N, unref as u, computed as g, useId as Te, openBlock as E, createElementBlock as T, normalizeStyle as X, toDisplayString as K, createCommentVNode as J, createElementVNode as I, reactive as ve, watch as Q, Fragment as L, renderList as U, normalizeClass as Be } from "vue";
2
+ const Ae = () => (/* @__PURE__ */ new Date()).getDay();
3
+ function Ie(e, a) {
4
+ return !e.weekdays || e.weekdays.length === 0 ? !0 : e.weekdays.includes(a);
5
+ }
6
+ function Fe(e, a) {
7
+ const l = Ae(), r = [];
8
+ for (const i of e)
9
+ for (const d of i.items)
10
+ if (d.item_id && Ie(d, l) && (r.push(d), a != null && r.length >= a))
11
+ return r;
12
+ return r;
13
+ }
14
+ function je(e) {
15
+ const a = S([]), l = S([]), r = S(!0), i = S(null), d = S(`/order?slug=${encodeURIComponent(e.slug)}`), b = e.apiBase ?? "/api", o = e.fetcher ?? globalThis.fetch.bind(globalThis);
16
+ async function t() {
17
+ r.value = !0, i.value = null;
18
+ try {
19
+ const f = await o(`${b}/menu/public/${encodeURIComponent(e.slug)}`);
20
+ if (!f.ok) throw new Error(`Menu fetch failed (${f.status})`);
21
+ const _ = await f.json(), v = Array.isArray(_) ? _ : _.data ?? [];
22
+ l.value = v, a.value = Fe(v, e.limit);
23
+ } catch (f) {
24
+ i.value = f instanceof Error ? f : new Error(String(f)), l.value = [], a.value = [];
25
+ } finally {
26
+ r.value = !1;
27
+ }
28
+ }
29
+ return Z(() => {
30
+ t();
31
+ }), { items: a, menus: l, loading: r, error: i, orderHref: d, refresh: t };
32
+ }
33
+ const vt = /* @__PURE__ */ C({
34
+ __name: "MenuPreview",
35
+ props: {
36
+ slug: {},
37
+ apiBase: {},
38
+ limit: {}
39
+ },
40
+ setup(e) {
41
+ const a = e, { items: l, menus: r, loading: i, error: d, orderHref: b, refresh: o } = je({
42
+ slug: a.slug,
43
+ apiBase: a.apiBase,
44
+ limit: a.limit
45
+ });
46
+ return (t, f) => N(t.$slots, "default", {
47
+ items: u(l),
48
+ menus: u(r),
49
+ loading: u(i),
50
+ error: u(d),
51
+ orderHref: u(b),
52
+ refresh: u(o)
53
+ });
54
+ }
55
+ });
56
+ function De(e) {
57
+ const a = S([]), l = S(!0), r = S(null), i = e.apiBase ?? "/api", d = e.fetcher ?? globalThis.fetch.bind(globalThis);
58
+ async function b() {
59
+ l.value = !0, r.value = null;
60
+ try {
61
+ const o = new URLSearchParams();
62
+ e.tag && o.set("tag", e.tag), e.limit != null && o.set("limit", String(e.limit));
63
+ const t = o.toString(), f = `${i}/public/tenants/${encodeURIComponent(e.slug)}/items${t ? "?" + t : ""}`, _ = await d(f);
64
+ if (!_.ok) throw new Error(`Items fetch failed (${_.status})`);
65
+ const v = await _.json();
66
+ a.value = Array.isArray(v) ? v : v.data ?? [];
67
+ } catch (o) {
68
+ r.value = o instanceof Error ? o : new Error(String(o)), a.value = [];
69
+ } finally {
70
+ l.value = !1;
71
+ }
72
+ }
73
+ return Z(() => {
74
+ b();
75
+ }), { items: a, loading: l, error: r, refresh: b };
76
+ }
77
+ const gt = /* @__PURE__ */ C({
78
+ __name: "FeaturedItems",
79
+ props: {
80
+ slug: {},
81
+ tag: {},
82
+ limit: {},
83
+ apiBase: {}
84
+ },
85
+ setup(e) {
86
+ const a = e, { items: l, loading: r, error: i, refresh: d } = De({
87
+ slug: a.slug,
88
+ tag: a.tag,
89
+ limit: a.limit,
90
+ apiBase: a.apiBase
91
+ });
92
+ return (b, o) => N(b.$slots, "default", {
93
+ items: u(l),
94
+ loading: u(r),
95
+ error: u(i),
96
+ refresh: u(d)
97
+ });
98
+ }
99
+ }), bt = /* @__PURE__ */ C({
100
+ __name: "AddressBlock",
101
+ props: {
102
+ info: {}
103
+ },
104
+ setup(e) {
105
+ const a = e, l = g(() => {
106
+ var _, v, B;
107
+ const o = a.info;
108
+ if (!o) return [];
109
+ const t = ((_ = o.street) == null ? void 0 : _.trim()) ?? "", f = [(v = o.postal_code) == null ? void 0 : v.trim(), (B = o.city) == null ? void 0 : B.trim()].filter((m) => m && m.length > 0).join(" ");
110
+ return [t, f].filter((m) => m.length > 0);
111
+ }), r = g(() => {
112
+ const o = l.value;
113
+ return o.length ? o.join(", ") : null;
114
+ }), i = g(() => {
115
+ const o = r.value;
116
+ return o ? `https://maps.google.com/maps?q=${encodeURIComponent(o)}&output=embed` : null;
117
+ }), d = g(() => {
118
+ var t, f;
119
+ const o = ((f = (t = a.info) == null ? void 0 : t.phone) == null ? void 0 : f.replace(/\s+/g, "")) ?? null;
120
+ return o ? `tel:${o}` : null;
121
+ }), b = g(() => {
122
+ var t;
123
+ const o = ((t = a.info) == null ? void 0 : t.email) ?? null;
124
+ return o ? `mailto:${o}` : null;
125
+ });
126
+ return (o, t) => {
127
+ var f, _, v, B, m, w, c;
128
+ return N(o.$slots, "default", {
129
+ legalName: ((f = e.info) == null ? void 0 : f.legal_name) ?? null,
130
+ street: ((_ = e.info) == null ? void 0 : _.street) ?? null,
131
+ postalCode: ((v = e.info) == null ? void 0 : v.postal_code) ?? null,
132
+ city: ((B = e.info) == null ? void 0 : B.city) ?? null,
133
+ country: ((m = e.info) == null ? void 0 : m.country) ?? "CH",
134
+ fullAddress: r.value,
135
+ addressLines: l.value,
136
+ phone: ((w = e.info) == null ? void 0 : w.phone) ?? null,
137
+ email: ((c = e.info) == null ? void 0 : c.email) ?? null,
138
+ mapsUrl: i.value,
139
+ telHref: d.value,
140
+ mailHref: b.value
141
+ });
142
+ };
143
+ }
144
+ }), wt = /* @__PURE__ */ C({
145
+ __name: "SocialLinks",
146
+ props: {
147
+ links: {},
148
+ platformOrder: {}
149
+ },
150
+ setup(e) {
151
+ const a = [
152
+ "instagram",
153
+ "facebook",
154
+ "whatsapp",
155
+ "tiktok",
156
+ "youtube",
157
+ "x",
158
+ "twitter",
159
+ "linkedin",
160
+ "website"
161
+ ], l = e, r = g(() => {
162
+ const i = l.links ?? {}, d = Object.entries(i).filter(([, t]) => typeof t == "string" && t.trim().length > 0);
163
+ if (d.length === 0) return [];
164
+ const b = l.platformOrder ?? a, o = new Map(b.map((t, f) => [t, f]));
165
+ return [...d].sort(([t], [f]) => {
166
+ const _ = o.get(t) ?? Number.MAX_SAFE_INTEGER, v = o.get(f) ?? Number.MAX_SAFE_INTEGER;
167
+ return _ - v;
168
+ }).map(([t, f]) => ({ platform: t, url: f }));
169
+ });
170
+ return (i, d) => N(i.$slots, "default", {
171
+ entries: r.value,
172
+ hasLinks: r.value.length > 0
173
+ });
174
+ }
175
+ }), Oe = [
176
+ "monday",
177
+ "tuesday",
178
+ "wednesday",
179
+ "thursday",
180
+ "friday",
181
+ "saturday",
182
+ "sunday"
183
+ ], Ce = { enabled: !1, periods: [] };
184
+ function Me(e) {
185
+ const a = S(null), l = S(!0), r = S(null), i = e.apiBase ?? "/api", d = e.fetcher ?? globalThis.fetch.bind(globalThis);
186
+ async function b() {
187
+ l.value = !0, r.value = null;
188
+ try {
189
+ const m = await d(`${i}/public/tenants/${encodeURIComponent(e.slug)}/hours`);
190
+ if (!m.ok) throw new Error(`Hours fetch failed (${m.status})`);
191
+ const w = await m.json();
192
+ a.value = "data" in w && w.data ? w.data : w;
193
+ } catch (m) {
194
+ r.value = m instanceof Error ? m : new Error(String(m)), a.value = null;
195
+ } finally {
196
+ l.value = !1;
197
+ }
198
+ }
199
+ const o = g(() => {
200
+ var w;
201
+ const m = ((w = a.value) == null ? void 0 : w.schedule) ?? {};
202
+ return Oe.map((c) => ({ key: c, schedule: m[c] ?? Ce }));
203
+ }), t = g(() => Pe(o.value)), f = g(() => {
204
+ var m;
205
+ return ((m = a.value) == null ? void 0 : m.upcoming_overrides) ?? [];
206
+ }), _ = g(() => {
207
+ var m;
208
+ return ((m = a.value) == null ? void 0 : m.is_open_now) ?? !1;
209
+ }), v = g(() => {
210
+ var m;
211
+ return ((m = a.value) == null ? void 0 : m.next_open_slot) ?? null;
212
+ }), B = g(() => {
213
+ var m;
214
+ return ((m = a.value) == null ? void 0 : m.timezone) ?? "Europe/Zurich";
215
+ });
216
+ return Z(() => {
217
+ b();
218
+ }), { week: o, weekCompact: t, upcomingOverrides: f, isOpenNow: _, nextOpenSlot: v, timezone: B, loading: l, error: r, refresh: b };
219
+ }
220
+ function he(e) {
221
+ return e.map((a) => `${a.open}-${a.close}`).join("|");
222
+ }
223
+ function ze(e, a) {
224
+ return e.enabled !== a.enabled ? !1 : e.enabled ? he(e.periods) === he(a.periods) : !0;
225
+ }
226
+ function Pe(e) {
227
+ const a = [];
228
+ let l = 0;
229
+ for (; l < e.length; ) {
230
+ const r = e[l];
231
+ let i = l;
232
+ for (; i + 1 < e.length && ze(e[i + 1].schedule, r.schedule); )
233
+ i++;
234
+ a.push({
235
+ startKey: r.key,
236
+ endKey: e[i].key,
237
+ days: i - l + 1,
238
+ schedule: r.schedule
239
+ }), l = i + 1;
240
+ }
241
+ return a;
242
+ }
243
+ const kt = /* @__PURE__ */ C({
244
+ __name: "HoursBlock",
245
+ props: {
246
+ slug: {},
247
+ apiBase: {}
248
+ },
249
+ setup(e) {
250
+ const a = e, {
251
+ week: l,
252
+ weekCompact: r,
253
+ upcomingOverrides: i,
254
+ isOpenNow: d,
255
+ nextOpenSlot: b,
256
+ timezone: o,
257
+ loading: t,
258
+ error: f,
259
+ refresh: _
260
+ } = Me({ slug: a.slug, apiBase: a.apiBase });
261
+ return (v, B) => N(v.$slots, "default", {
262
+ week: u(l),
263
+ weekCompact: u(r),
264
+ upcomingOverrides: u(i),
265
+ isOpenNow: u(d),
266
+ nextOpenSlot: u(b),
267
+ timezone: u(o),
268
+ loading: u(t),
269
+ error: u(f),
270
+ refresh: u(_)
271
+ });
272
+ }
273
+ }), Re = ["href"], Le = { key: 0 }, Ue = ["fill"], Ne = ["fill"], _e = '"DM Sans", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif', xt = /* @__PURE__ */ C({
274
+ __name: "PoweredBy",
275
+ props: {
276
+ variant: { default: "wordmark" },
277
+ size: { default: "md" },
278
+ theme: { default: "light" },
279
+ label: { default: "powered by" },
280
+ hideLabel: { type: Boolean, default: !1 },
281
+ href: { default: "https://sanidesk.ch" }
282
+ },
283
+ setup(e) {
284
+ const a = e, l = {
285
+ sm: { icon: 14, text: "0.78rem", label: "0.62rem" },
286
+ md: { icon: 18, text: "0.95rem", label: "0.68rem" },
287
+ lg: { icon: 24, text: "1.2rem", label: "0.75rem" }
288
+ }, r = g(() => l[a.size]), i = g(() => a.theme === "dark" ? "#ffffff" : "#1a1a1a"), d = g(() => ({
289
+ display: "inline-flex",
290
+ alignItems: "center",
291
+ gap: "0.245rem",
292
+ fontFamily: _e,
293
+ fontSize: r.value.label,
294
+ letterSpacing: "0.02em",
295
+ textTransform: "lowercase",
296
+ textDecoration: "none",
297
+ opacity: "0.9",
298
+ transition: "opacity 0.2s ease",
299
+ color: i.value,
300
+ whiteSpace: "nowrap",
301
+ flexShrink: 0
302
+ })), b = {
303
+ display: "inline-flex",
304
+ alignItems: "center",
305
+ gap: "0.245rem"
306
+ }, o = g(() => ({
307
+ display: "block",
308
+ width: `${r.value.icon}px`,
309
+ height: `${r.value.icon}px`,
310
+ flexShrink: 0
311
+ })), t = g(() => ({
312
+ fontSize: r.value.text,
313
+ fontWeight: 700,
314
+ letterSpacing: "-0.01em",
315
+ textTransform: "none",
316
+ fontFamily: _e,
317
+ lineHeight: 1,
318
+ display: "inline-flex",
319
+ alignItems: "baseline"
320
+ })), f = g(() => ({
321
+ color: i.value
322
+ })), _ = {
323
+ background: "linear-gradient(90deg, #FF8C42 0%, #FF6B6B 50%, #D65D7A 100%)",
324
+ backgroundClip: "text",
325
+ WebkitBackgroundClip: "text",
326
+ color: "transparent",
327
+ WebkitTextFillColor: "transparent"
328
+ }, v = `sd-pb-grad-${Te()}`;
329
+ return (B, m) => (E(), T("a", {
330
+ style: X(d.value),
331
+ href: a.href,
332
+ target: "_blank",
333
+ rel: "noopener nofollow",
334
+ "aria-label": "Powered by SaniDesk",
335
+ onMouseenter: m[0] || (m[0] = (w) => w.currentTarget.style.opacity = "1"),
336
+ onMouseleave: m[1] || (m[1] = (w) => w.currentTarget.style.opacity = "0.9")
337
+ }, [
338
+ a.hideLabel ? J("", !0) : (E(), T("span", Le, K(a.label), 1)),
339
+ I("span", { style: b }, [
340
+ (E(), T("svg", {
341
+ style: X(o.value),
342
+ viewBox: "267.3 246 95.4 121.8",
343
+ "aria-hidden": "true",
344
+ focusable: "false"
345
+ }, [
346
+ I("defs", null, [
347
+ I("linearGradient", {
348
+ id: v,
349
+ gradientUnits: "userSpaceOnUse",
350
+ x1: "267.3",
351
+ y1: "306.9",
352
+ x2: "362.7",
353
+ y2: "306.9"
354
+ }, [...m[2] || (m[2] = [
355
+ I("stop", {
356
+ offset: "0%",
357
+ "stop-color": "#FF8C42"
358
+ }, null, -1),
359
+ I("stop", {
360
+ offset: "50%",
361
+ "stop-color": "#FF6B6B"
362
+ }, null, -1),
363
+ I("stop", {
364
+ offset: "100%",
365
+ "stop-color": "#D65D7A"
366
+ }, null, -1)
367
+ ])])
368
+ ]),
369
+ I("path", {
370
+ fill: `url(#${v})`,
371
+ d: "M282.1,311l1,1.5l1.1,1.5c7.1,9.4,17.9,15.1,29.7,15.6l1.8,0l0.8,0c5.5-0.6,8.7-6.3,6.3-11.3l-0.4-0.7l-0.5-0.6l-0.5-0.6l-0.6-0.6l-0.6-0.5l-0.7-0.4l-0.7-0.3c-2.3-1.1-4.7-0.4-7.3-1l-1.4-0.3l-1.4-0.4c-3.6-1.1-6.9-3-9.6-5.7l-1-1l-0.9-1.1l-0.9-1.1c-5.3-7.4-5.9-17.2-1.5-25.2l0.7-1.2l0.8-1.2l0.9-1.1l0.9-1.1l1-1c2.3-2.3,5.2-4.1,8.2-5.2l1.3-0.4l1.4-0.4l1.4-0.3l1.4-0.2l1.4-0.1l1.4,0l0.8-0.1l0.8-0.1l0.8-0.2l0.8-0.3l0.7-0.4l0.7-0.4l0.6-0.5c4.4-3.8,3.3-10.9-2-13.2l-0.8-0.3l-0.8-0.2l-0.8-0.1l-0.8,0l-1.8,0L312,251l-1.8,0.2c-27.5,4-42.4,33.9-29,58.3L282.1,311z"
372
+ }, null, 8, Ue),
373
+ I("path", {
374
+ fill: `url(#${v})`,
375
+ d: "M314.8,282.4l-0.8,0.1l-0.8,0.2c-4.6,1.4-6.9,6.7-4.7,11l0.4,0.7l0.4,0.7l0.5,0.6c1.3,1.4,3.1,2.3,4.9,2.4l0.8,0.1l1.4,0c3.2,0.1,6.6,1.1,9.4,2.5l1.3,0.7l1.2,0.8l1.1,0.8l1.1,0.9l1.1,0.9c6.2,6.1,8.7,14.9,6.4,23.3l-0.4,1.3l-0.5,1.3c-3.3,8-10.6,13.6-19.3,14.6l-1.4,0.1l-1.4,0l-0.8,0.1l-0.8,0.1l-0.8,0.2l-0.8,0.3c-4.8,2.1-6.3,8-3.1,12.1l0.5,0.6l0.6,0.6l0.6,0.5c2,1.4,3.9,1.5,6.3,1.4l1.8-0.1l1.8-0.2c4.3-0.6,8.2-1.8,12.1-3.8l1.6-0.8l1.6-0.9l1.5-1l1.5-1.1l1.4-1.1c8.5-7.1,13.6-17.4,14.1-28.5l0.1-1.8v-23.7c0.1-6.9-4.5-13.2-11.2-15.1l-1.1-0.3l-1.1-0.2l-1.1-0.1l-1.1,0h-23.6L314.8,282.4z"
376
+ }, null, 8, Ne)
377
+ ], 4)),
378
+ a.variant === "wordmark" ? (E(), T("span", {
379
+ key: 0,
380
+ style: X(t.value)
381
+ }, [
382
+ I("span", {
383
+ style: X(f.value)
384
+ }, "Sani", 4),
385
+ I("span", { style: _ }, "Desk")
386
+ ], 4)) : J("", !0)
387
+ ])
388
+ ], 44, Re));
389
+ }
390
+ }), ee = {
391
+ name: "",
392
+ email: "",
393
+ phone: "",
394
+ subject: "",
395
+ message: "",
396
+ website: ""
397
+ };
398
+ function Ve(e) {
399
+ var w, c;
400
+ const a = e.apiBase ?? "/api", l = e.fetcher ?? globalThis.fetch.bind(globalThis), r = ((w = e.messages) == null ? void 0 : w.required) ?? "Required", i = ((c = e.messages) == null ? void 0 : c.invalidEmail) ?? "Invalid email address", d = ve({ ...ee }), b = S({}), o = S(!1), t = S(!1), f = S(null);
401
+ function _() {
402
+ const p = {};
403
+ return d.name.trim() || (p.name = r), d.email.trim() ? /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(d.email.trim()) || (p.email = i) : p.email = r, d.message.trim() || (p.message = r), p;
404
+ }
405
+ const v = g(() => Object.keys(_()).length === 0);
406
+ function B() {
407
+ Object.assign(d, ee), b.value = {}, t.value = !1, f.value = null;
408
+ }
409
+ async function m() {
410
+ var s;
411
+ const p = _();
412
+ if (b.value = p, Object.keys(p).length > 0) return !1;
413
+ o.value = !0, f.value = null;
414
+ try {
415
+ const k = await l(
416
+ `${a}/public/tenants/${encodeURIComponent(e.slug)}/contact`,
417
+ {
418
+ method: "POST",
419
+ headers: { "Content-Type": "application/json" },
420
+ body: JSON.stringify({
421
+ to: e.toEmail,
422
+ name: d.name.trim(),
423
+ email: d.email.trim(),
424
+ phone: d.phone.trim() || void 0,
425
+ subject: d.subject.trim() || void 0,
426
+ message: d.message.trim(),
427
+ website: d.website
428
+ })
429
+ }
430
+ );
431
+ if (!k.ok && k.status !== 204) {
432
+ const j = await k.json().catch(() => null);
433
+ throw new Error(((s = j == null ? void 0 : j.error) == null ? void 0 : s.message) ?? `Send failed (${k.status})`);
434
+ }
435
+ return t.value = !0, Object.assign(d, ee), !0;
436
+ } catch (k) {
437
+ return f.value = k instanceof Error ? k.message : String(k), !1;
438
+ } finally {
439
+ o.value = !1;
440
+ }
441
+ }
442
+ return { form: d, errors: b, submitting: o, submitted: t, serverError: f, isValid: v, submit: m, reset: B };
443
+ }
444
+ const St = /* @__PURE__ */ C({
445
+ __name: "ContactForm",
446
+ props: {
447
+ slug: {},
448
+ toEmail: {},
449
+ apiBase: {},
450
+ messages: {}
451
+ },
452
+ setup(e) {
453
+ const a = e, {
454
+ form: l,
455
+ errors: r,
456
+ submitting: i,
457
+ submitted: d,
458
+ serverError: b,
459
+ isValid: o,
460
+ submit: t,
461
+ reset: f
462
+ } = Ve({
463
+ slug: a.slug,
464
+ toEmail: a.toEmail,
465
+ apiBase: a.apiBase,
466
+ messages: a.messages
467
+ });
468
+ return (_, v) => N(_.$slots, "default", {
469
+ form: u(l),
470
+ errors: u(r),
471
+ submitting: u(i),
472
+ submitted: u(d),
473
+ serverError: u(b),
474
+ isValid: u(o),
475
+ submit: u(t),
476
+ reset: u(f)
477
+ });
478
+ }
479
+ });
480
+ function te() {
481
+ const e = /* @__PURE__ */ new Date();
482
+ return [
483
+ e.getFullYear(),
484
+ String(e.getMonth() + 1).padStart(2, "0"),
485
+ String(e.getDate()).padStart(2, "0")
486
+ ].join("-");
487
+ }
488
+ const ye = {
489
+ step: "when",
490
+ starts_at: "",
491
+ tableMode: "auto",
492
+ picked_table_id: "",
493
+ customer_first_name: "",
494
+ customer_last_name: "",
495
+ customer_phone: "",
496
+ customer_email: "",
497
+ occasion: "",
498
+ notes: "",
499
+ website: "",
500
+ newsletter_opt_in: !1
501
+ };
502
+ function He(e) {
503
+ var ne, le, re, ie, oe, ce, ue, de, fe, me;
504
+ const a = e.apiBase ?? "/api", l = e.fetcher ?? globalThis.fetch.bind(globalThis), r = ((ne = e.messages) == null ? void 0 : ne.requiredField) ?? "Required", i = ((le = e.messages) == null ? void 0 : le.invalidEmail) ?? "Invalid email address";
505
+ (re = e.messages) == null || re.phoneOrEmail;
506
+ const d = ((ie = e.messages) == null ? void 0 : ie.invalidPhone) ?? "Invalid phone number", b = ((oe = e.messages) == null ? void 0 : oe.pastDate) ?? "Booking cannot be in the past", o = ((ce = e.messages) == null ? void 0 : ce.pickTable) ?? "Please pick a table or switch to auto-select", t = ve({
507
+ ...ye,
508
+ date: e.initialDate ?? te(),
509
+ party_size: e.initialPartySize ?? 2,
510
+ customer_first_name: ((ue = e.initialContact) == null ? void 0 : ue.first_name) ?? "",
511
+ customer_last_name: ((de = e.initialContact) == null ? void 0 : de.last_name) ?? "",
512
+ customer_email: ((fe = e.initialContact) == null ? void 0 : fe.email) ?? "",
513
+ customer_phone: ((me = e.initialContact) == null ? void 0 : me.phone) ?? ""
514
+ }), f = S({}), _ = S(null), v = S(!1), B = S(!1), m = S(null), w = S([]), c = S([]), p = S(null), s = S(null), k = S(null);
515
+ function j(n) {
516
+ const y = new Date(Date.now() + n * 24 * 3600 * 1e3);
517
+ return [
518
+ y.getFullYear(),
519
+ String(y.getMonth() + 1).padStart(2, "0"),
520
+ String(y.getDate()).padStart(2, "0")
521
+ ].join("-");
522
+ }
523
+ const M = g(() => te()), W = g(() => {
524
+ var y;
525
+ const n = ((y = k.value) == null ? void 0 : y.max_advance_days) ?? 120;
526
+ return j(n);
527
+ }), V = g(() => {
528
+ var y;
529
+ const n = ((y = k.value) == null ? void 0 : y.max_party_size) ?? 0;
530
+ return n > 0 && t.party_size > n;
531
+ }), R = g(() => {
532
+ var x;
533
+ const n = ((x = p.value) == null ? void 0 : x.floor_plans) ?? [];
534
+ if (n.length === 0) return null;
535
+ const y = s.value;
536
+ return n.find(($) => $.id === y) ?? n[0] ?? null;
537
+ }), H = g(() => {
538
+ var x;
539
+ const n = R.value, y = ((x = p.value) == null ? void 0 : x.tables) ?? [];
540
+ return n ? y.filter(
541
+ ($) => $.floor_plan_id == null || $.floor_plan_id === n.id
542
+ ) : y;
543
+ }), h = g(() => {
544
+ if (!t.starts_at) return [];
545
+ const n = w.value.find((y) => y.starts_at === t.starts_at);
546
+ return (n == null ? void 0 : n.fitting_table_ids) ?? [];
547
+ }), F = g(() => w.value.length > 0);
548
+ function z(n) {
549
+ var x;
550
+ (((x = p.value) == null ? void 0 : x.floor_plans) ?? []).some(($) => $.id === n) && s.value !== n && (s.value = n, t.picked_table_id && (H.value.some(
551
+ (O) => O.id === t.picked_table_id
552
+ ) || (t.picked_table_id = "")));
553
+ }
554
+ function A(n) {
555
+ return `${a}/public/reservations/${encodeURIComponent(e.slug)}${n}`;
556
+ }
557
+ async function q() {
558
+ if (!(!t.date || !t.party_size)) {
559
+ v.value = !0, _.value = null;
560
+ try {
561
+ const n = await l(
562
+ A(`/availability?date=${encodeURIComponent(t.date)}&party=${t.party_size}`)
563
+ );
564
+ if (!n.ok) throw new Error(`Slot fetch failed (${n.status})`);
565
+ const y = await n.json();
566
+ w.value = y.data, t.starts_at && !w.value.some((x) => x.starts_at === t.starts_at) && (t.starts_at = "");
567
+ } catch (n) {
568
+ _.value = n instanceof Error ? n.message : String(n);
569
+ } finally {
570
+ v.value = !1;
571
+ }
572
+ }
573
+ }
574
+ async function D() {
575
+ if (!(!t.starts_at || !t.party_size))
576
+ try {
577
+ const n = await l(
578
+ A(`/alternatives?starts_at=${encodeURIComponent(t.starts_at)}&party=${t.party_size}`)
579
+ );
580
+ if (!n.ok) throw new Error(`Alternatives fetch failed (${n.status})`);
581
+ const y = await n.json();
582
+ c.value = y.data;
583
+ } catch {
584
+ c.value = [];
585
+ }
586
+ }
587
+ async function Y() {
588
+ try {
589
+ const n = await l(A("/spec"));
590
+ if (!n.ok) return;
591
+ const y = await n.json();
592
+ k.value = y.data;
593
+ } catch {
594
+ }
595
+ }
596
+ async function ge() {
597
+ try {
598
+ const n = await l(A("/floorplan"));
599
+ if (!n.ok) throw new Error(`Floorplan fetch failed (${n.status})`);
600
+ const y = await n.json();
601
+ p.value = y.data;
602
+ const x = y.data.floor_plans ?? [], $ = x.find((O) => O.is_default) ?? x[0];
603
+ s.value = ($ == null ? void 0 : $.id) ?? null;
604
+ } catch {
605
+ p.value = null, s.value = null;
606
+ }
607
+ }
608
+ function be() {
609
+ var y;
610
+ const n = {};
611
+ if (t.date || (n.date = r), t.starts_at || (n.starts_at = r), (!t.party_size || t.party_size < 1) && (n.party_size = r), t.starts_at) {
612
+ const x = new Date(t.starts_at).getTime();
613
+ if (x < Date.now() - 6e4)
614
+ n.starts_at = b;
615
+ else if ((y = k.value) != null && y.lead_time_minutes) {
616
+ const $ = Date.now() + k.value.lead_time_minutes * 6e4;
617
+ x < $ && (n.starts_at = b);
618
+ }
619
+ }
620
+ return n;
621
+ }
622
+ function we() {
623
+ const n = {};
624
+ return t.tableMode === "pick" && !t.picked_table_id && (n.picked_table_id = o), n;
625
+ }
626
+ function ke() {
627
+ var pe;
628
+ const n = {}, y = t.customer_first_name.trim(), x = t.customer_last_name.trim(), $ = t.customer_phone.trim(), O = t.customer_email.trim(), P = new Set(((pe = k.value) == null ? void 0 : pe.required_fields) ?? ["first_name", "phone"]);
629
+ return P.has("first_name") && !y && (n.customer_first_name = r), P.has("last_name") && !x && (n.customer_last_name = r), P.has("phone") ? $ ? se($) || (n.customer_phone = d) : n.customer_phone = r : $ && !se($) && (n.customer_phone = d), P.has("email") && !O ? n.customer_email = r : O && !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(O) && (n.customer_email = i), n;
630
+ }
631
+ function se(n) {
632
+ if (!/^[+\d\s\-()]+$/.test(n)) return !1;
633
+ const y = n.replace(/[^\d]/g, "");
634
+ return y.length >= 7 && y.length <= 15;
635
+ }
636
+ function xe(n) {
637
+ t.step = n;
638
+ }
639
+ async function Se() {
640
+ if (t.step === "when") {
641
+ const n = be();
642
+ return f.value = n, Object.keys(n).length > 0 ? !1 : (t.step = "table", !0);
643
+ }
644
+ if (t.step === "table") {
645
+ const n = we();
646
+ return f.value = n, Object.keys(n).length > 0 ? !1 : (t.step = "contact", !0);
647
+ }
648
+ return t.step === "contact" ? ae() : !1;
649
+ }
650
+ function $e() {
651
+ t.step === "table" ? t.step = "when" : t.step === "contact" && (t.step = "table");
652
+ }
653
+ async function ae() {
654
+ var y;
655
+ const n = ke();
656
+ if (f.value = n, Object.keys(n).length > 0) return !1;
657
+ B.value = !0, _.value = null;
658
+ try {
659
+ const x = [];
660
+ t.tableMode === "pick" && t.picked_table_id && x.push(t.picked_table_id);
661
+ const $ = await l(A(""), {
662
+ method: "POST",
663
+ headers: { "Content-Type": "application/json" },
664
+ body: JSON.stringify({
665
+ starts_at: t.starts_at,
666
+ party_size: t.party_size,
667
+ table_ids: x.length > 0 ? x : void 0,
668
+ customer_first_name: t.customer_first_name.trim() || void 0,
669
+ customer_last_name: t.customer_last_name.trim() || void 0,
670
+ customer_phone: t.customer_phone.trim() || void 0,
671
+ customer_email: t.customer_email.trim() || void 0,
672
+ notes: t.notes.trim() || void 0,
673
+ occasion: t.occasion.trim() || void 0,
674
+ customer_locale: e.customerLocale ?? (typeof navigator < "u" ? navigator.language : void 0),
675
+ newsletter_opt_in: t.newsletter_opt_in,
676
+ // Honeypot — server reads `company_url` per R2 contract.
677
+ company_url: t.website
678
+ })
679
+ });
680
+ if (!$.ok) {
681
+ const P = await $.json().catch(() => null);
682
+ throw new Error(((y = P == null ? void 0 : P.error) == null ? void 0 : y.message) ?? `Submit failed (${$.status})`);
683
+ }
684
+ const O = await $.json();
685
+ return m.value = O.data, t.step = O.data.status === "confirmed" ? "confirmed" : "pending", !0;
686
+ } catch (x) {
687
+ return _.value = x instanceof Error ? x.message : String(x), !1;
688
+ } finally {
689
+ B.value = !1;
690
+ }
691
+ }
692
+ function Ee() {
693
+ Object.assign(t, ye, {
694
+ date: e.initialDate ?? te(),
695
+ party_size: e.initialPartySize ?? 2
696
+ }), f.value = {}, _.value = null, m.value = null, w.value = [], c.value = [];
697
+ }
698
+ return Y(), {
699
+ state: t,
700
+ errors: f,
701
+ serverError: _,
702
+ loading: v,
703
+ submitting: B,
704
+ result: m,
705
+ slots: w,
706
+ alternatives: c,
707
+ floorplan: p,
708
+ activeFloorPlanId: s,
709
+ activeFloorPlan: R,
710
+ activeTables: H,
711
+ fittingTableIds: h,
712
+ hasAnyAvailability: F,
713
+ spec: k,
714
+ minDate: M,
715
+ maxDate: W,
716
+ isPartyOversized: V,
717
+ loadSlots: q,
718
+ loadAlternatives: D,
719
+ loadFloorplan: ge,
720
+ setActiveFloorPlan: z,
721
+ goToStep: xe,
722
+ next: Se,
723
+ back: $e,
724
+ submit: ae,
725
+ reset: Ee
726
+ };
727
+ }
728
+ const $t = /* @__PURE__ */ C({
729
+ __name: "ReservationForm",
730
+ props: {
731
+ slug: {},
732
+ apiBase: {},
733
+ initialPartySize: {},
734
+ initialDate: {},
735
+ initialContact: {},
736
+ customerLocale: {},
737
+ messages: {}
738
+ },
739
+ emits: ["submitted"],
740
+ setup(e, { emit: a }) {
741
+ const l = e, r = a, {
742
+ state: i,
743
+ errors: d,
744
+ serverError: b,
745
+ loading: o,
746
+ submitting: t,
747
+ result: f,
748
+ slots: _,
749
+ alternatives: v,
750
+ floorplan: B,
751
+ activeFloorPlanId: m,
752
+ activeFloorPlan: w,
753
+ activeTables: c,
754
+ fittingTableIds: p,
755
+ hasAnyAvailability: s,
756
+ spec: k,
757
+ minDate: j,
758
+ maxDate: M,
759
+ isPartyOversized: W,
760
+ loadSlots: V,
761
+ loadFloorplan: R,
762
+ setActiveFloorPlan: H,
763
+ goToStep: h,
764
+ next: F,
765
+ back: z,
766
+ submit: A,
767
+ reset: q
768
+ } = He({
769
+ slug: l.slug,
770
+ apiBase: l.apiBase,
771
+ initialPartySize: l.initialPartySize,
772
+ initialDate: l.initialDate,
773
+ initialContact: l.initialContact,
774
+ customerLocale: l.customerLocale,
775
+ messages: l.messages
776
+ });
777
+ return Q(f, (D, Y) => {
778
+ D && !Y && r("submitted", {
779
+ first_name: i.customer_first_name,
780
+ last_name: i.customer_last_name,
781
+ email: i.customer_email,
782
+ phone: i.customer_phone
783
+ });
784
+ }), Q(
785
+ () => [i.date, i.party_size],
786
+ () => {
787
+ V();
788
+ }
789
+ ), Q(
790
+ () => [i.step, i.tableMode],
791
+ ([D, Y]) => {
792
+ D === "table" && Y === "pick" && !B.value && R();
793
+ }
794
+ ), Z(() => {
795
+ V();
796
+ }), (D, Y) => N(D.$slots, "default", {
797
+ state: u(i),
798
+ errors: u(d),
799
+ serverError: u(b),
800
+ loading: u(o),
801
+ submitting: u(t),
802
+ result: u(f),
803
+ slots: u(_),
804
+ alternatives: u(v),
805
+ floorplan: u(B),
806
+ activeFloorPlanId: u(m),
807
+ activeFloorPlan: u(w),
808
+ activeTables: u(c),
809
+ fittingTableIds: u(p),
810
+ hasAnyAvailability: u(s),
811
+ spec: u(k),
812
+ minDate: u(j),
813
+ maxDate: u(M),
814
+ isPartyOversized: u(W),
815
+ goToStep: u(h),
816
+ next: u(F),
817
+ back: u(z),
818
+ submit: u(A),
819
+ reset: u(q),
820
+ loadFloorplan: u(R),
821
+ setActiveFloorPlan: u(H)
822
+ });
823
+ }
824
+ }), qe = ["viewBox"], Ye = { class: "pf-zones" }, We = ["x", "y", "width", "height"], Ke = ["x", "y"], Xe = { class: "pf-walls" }, Ge = ["x1", "y1", "x2", "y2", "stroke-width"], Je = { class: "pf-fixtures" }, Ze = ["transform"], Qe = ["x", "y", "width", "height"], et = ["x", "y"], tt = { class: "pf-labels" }, st = ["x", "y", "font-size"], at = { class: "pf-tables" }, nt = ["transform", "onClick"], lt = ["x", "y", "width", "height"], rt = ["rx", "ry"], it = ["r"], ot = ["cx", "cy"], ct = {
825
+ class: "pf-table-label",
826
+ "text-anchor": "middle",
827
+ y: "0",
828
+ "dominant-baseline": "central"
829
+ }, G = 30, ut = 25, Et = /* @__PURE__ */ C({
830
+ __name: "PublicFloorplanViewer",
831
+ props: {
832
+ data: {},
833
+ tables: {},
834
+ pickedTableId: {},
835
+ fittingTableIds: {},
836
+ tappable: { type: Boolean, default: !0 }
837
+ },
838
+ emits: ["select"],
839
+ setup(e, { emit: a }) {
840
+ const l = e, r = a, i = g(() => {
841
+ const c = l.data;
842
+ return !c || typeof c != "object" || typeof c.width != "number" || typeof c.height != "number" || !Array.isArray(c.objects) ? null : c;
843
+ }), d = g(() => {
844
+ const c = i.value;
845
+ if (!c) return "0 0 100 100";
846
+ let p = 1 / 0, s = 1 / 0, k = -1 / 0, j = -1 / 0;
847
+ const M = (h, F, z, A) => {
848
+ h < p && (p = h), F < s && (s = F), z > k && (k = z), A > j && (j = A);
849
+ };
850
+ for (const h of c.objects)
851
+ if (h.type === "wall") {
852
+ const F = h.thickness ?? 4;
853
+ M(
854
+ Math.min(h.x1, h.x2) - F / 2,
855
+ Math.min(h.y1, h.y2) - F / 2,
856
+ Math.max(h.x1, h.x2) + F / 2,
857
+ Math.max(h.y1, h.y2) + F / 2
858
+ );
859
+ } else if (h.type === "fixture" || h.type === "zone")
860
+ M(h.x, h.y, h.x + (h.width ?? 0), h.y + (h.height ?? 0));
861
+ else if (h.type === "label") {
862
+ const F = h.fontSize ?? 14;
863
+ M(h.x, h.y - F, h.x + F * 2, h.y);
864
+ } else if (h.type === "table") {
865
+ const F = h.shape === "circle" ? h.radius ?? 50 : (h.width ?? 80) / 2, z = h.shape === "circle" ? h.radius ?? 50 : (h.height ?? 80) / 2;
866
+ let A = Math.hypot(F, z);
867
+ for (const q of h.seats ?? []) {
868
+ const D = Math.hypot(q.x, q.y) + ut;
869
+ D > A && (A = D);
870
+ }
871
+ M(h.x - A, h.y - A, h.x + A, h.y + A);
872
+ }
873
+ if (!isFinite(p))
874
+ return `0 0 ${c.width} ${c.height}`;
875
+ const W = p - G, V = s - G, R = k - p + G * 2, H = j - s + G * 2;
876
+ return `${W} ${V} ${R} ${H}`;
877
+ }), b = g(
878
+ () => {
879
+ var c;
880
+ return (((c = i.value) == null ? void 0 : c.objects) ?? []).filter((p) => p.type === "table");
881
+ }
882
+ ), o = g(
883
+ () => {
884
+ var c;
885
+ return (((c = i.value) == null ? void 0 : c.objects) ?? []).filter((p) => p.type === "wall");
886
+ }
887
+ ), t = g(
888
+ () => {
889
+ var c;
890
+ return (((c = i.value) == null ? void 0 : c.objects) ?? []).filter((p) => p.type === "fixture");
891
+ }
892
+ ), f = g(
893
+ () => {
894
+ var c;
895
+ return (((c = i.value) == null ? void 0 : c.objects) ?? []).filter((p) => p.type === "label");
896
+ }
897
+ ), _ = g(
898
+ () => {
899
+ var c;
900
+ return (((c = i.value) == null ? void 0 : c.objects) ?? []).filter((p) => p.type === "zone");
901
+ }
902
+ ), v = g(() => {
903
+ const c = /* @__PURE__ */ new Map();
904
+ for (const p of l.tables)
905
+ p.object_id && c.set(p.object_id, p);
906
+ return c;
907
+ });
908
+ function B(c) {
909
+ const p = v.value.get(c);
910
+ return p ? p.id === l.pickedTableId ? "selected" : l.fittingTableIds.includes(p.id) ? "available" : "unavailable" : "unavailable";
911
+ }
912
+ function m(c) {
913
+ if (!l.tappable) return;
914
+ const p = B(c);
915
+ if (p === "unavailable") return;
916
+ const s = v.value.get(c);
917
+ s && r("select", p === "selected" ? "" : s.id);
918
+ }
919
+ function w(c) {
920
+ return `pf-table pf-table--${B(c)}`;
921
+ }
922
+ return (c, p) => i.value ? (E(), T("svg", {
923
+ key: 0,
924
+ class: "pf-root",
925
+ viewBox: d.value,
926
+ preserveAspectRatio: "xMidYMid meet",
927
+ role: "img",
928
+ "aria-label": "Floorplan"
929
+ }, [
930
+ I("g", Ye, [
931
+ (E(!0), T(L, null, U(_.value, (s) => (E(), T("rect", {
932
+ key: s.id,
933
+ class: "pf-zone",
934
+ x: s.x,
935
+ y: s.y,
936
+ width: s.width,
937
+ height: s.height,
938
+ rx: "8"
939
+ }, null, 8, We))), 128)),
940
+ (E(!0), T(L, null, U(_.value, (s) => (E(), T("text", {
941
+ key: `${s.id}-label`,
942
+ class: "pf-zone-label",
943
+ x: s.x + 10,
944
+ y: s.y + 20
945
+ }, K(s.name), 9, Ke))), 128))
946
+ ]),
947
+ I("g", Xe, [
948
+ (E(!0), T(L, null, U(o.value, (s) => (E(), T("line", {
949
+ key: s.id,
950
+ class: "pf-wall",
951
+ x1: s.x1,
952
+ y1: s.y1,
953
+ x2: s.x2,
954
+ y2: s.y2,
955
+ "stroke-width": s.thickness,
956
+ "stroke-linecap": "round"
957
+ }, null, 8, Ge))), 128))
958
+ ]),
959
+ I("g", Je, [
960
+ (E(!0), T(L, null, U(t.value, (s) => (E(), T("g", {
961
+ key: s.id,
962
+ transform: s.rotation ? `rotate(${s.rotation} ${s.x + s.width / 2} ${s.y + s.height / 2})` : void 0
963
+ }, [
964
+ I("rect", {
965
+ class: "pf-fixture",
966
+ x: s.x,
967
+ y: s.y,
968
+ width: s.width,
969
+ height: s.height,
970
+ rx: "4"
971
+ }, null, 8, Qe),
972
+ s.label ? (E(), T("text", {
973
+ key: 0,
974
+ class: "pf-fixture-label",
975
+ x: s.x + s.width / 2,
976
+ y: s.y + s.height / 2 + 4,
977
+ "text-anchor": "middle"
978
+ }, K(s.label), 9, et)) : J("", !0)
979
+ ], 8, Ze))), 128))
980
+ ]),
981
+ I("g", tt, [
982
+ (E(!0), T(L, null, U(f.value, (s) => (E(), T("text", {
983
+ key: s.id,
984
+ class: "pf-label",
985
+ x: s.x,
986
+ y: s.y,
987
+ "font-size": s.fontSize
988
+ }, K(s.text), 9, st))), 128))
989
+ ]),
990
+ I("g", at, [
991
+ (E(!0), T(L, null, U(b.value, (s) => (E(), T("g", {
992
+ key: s.id,
993
+ class: Be(w(s.id)),
994
+ transform: `translate(${s.x}, ${s.y})${s.rotation ? ` rotate(${s.rotation})` : ""}`,
995
+ onClick: (k) => m(s.id)
996
+ }, [
997
+ s.shape === "rect" ? (E(), T("rect", {
998
+ key: 0,
999
+ class: "pf-table-shape",
1000
+ x: -(s.width ?? 80) / 2,
1001
+ y: -(s.height ?? 80) / 2,
1002
+ width: s.width ?? 80,
1003
+ height: s.height ?? 80,
1004
+ rx: "6"
1005
+ }, null, 8, lt)) : s.shape === "oval" ? (E(), T("ellipse", {
1006
+ key: 1,
1007
+ class: "pf-table-shape",
1008
+ rx: (s.width ?? 80) / 2,
1009
+ ry: (s.height ?? 80) / 2
1010
+ }, null, 8, rt)) : (E(), T("circle", {
1011
+ key: 2,
1012
+ class: "pf-table-shape",
1013
+ r: s.radius ?? 50
1014
+ }, null, 8, it)),
1015
+ (E(!0), T(L, null, U(s.seats ?? [], (k, j) => (E(), T("circle", {
1016
+ key: `${s.id}-seat-${j}`,
1017
+ class: "pf-seat",
1018
+ cx: k.x,
1019
+ cy: k.y,
1020
+ r: "25"
1021
+ }, null, 8, ot))), 128)),
1022
+ I("text", ct, K(s.label), 1)
1023
+ ], 10, nt))), 128))
1024
+ ])
1025
+ ], 8, qe)) : J("", !0);
1026
+ }
1027
+ }), dt = { BASE_URL: "/", DEV: !1, MODE: "production", PROD: !0, SSR: !1 }, ft = "https://cdn.sanidesk.ch", mt = /^[a-z][a-z0-9+.-]*:/i;
1028
+ function pt() {
1029
+ let e = {};
1030
+ try {
1031
+ e = dt ?? {};
1032
+ } catch {
1033
+ }
1034
+ const a = e.VITE_TEMPLATE_VERSIONS;
1035
+ let l;
1036
+ if (a)
1037
+ try {
1038
+ const r = JSON.parse(a);
1039
+ r && typeof r == "object" && !Array.isArray(r) && (l = r);
1040
+ } catch {
1041
+ }
1042
+ return {
1043
+ local: e.VITE_TEMPLATE_LOCAL === "true",
1044
+ versions: l,
1045
+ cdnBase: e.VITE_CDN_BASE || void 0
1046
+ };
1047
+ }
1048
+ function ht(e) {
1049
+ return e.replace(/^\/+/, "");
1050
+ }
1051
+ function _t(e, a, l) {
1052
+ var d;
1053
+ if (typeof a != "string" || a.length === 0)
1054
+ throw new Error("[site-kit] asset(): path must be a non-empty string");
1055
+ if (mt.test(a)) return a;
1056
+ const r = ht(a);
1057
+ if (l.local) return `/assets/${r}`;
1058
+ const i = (d = l.versions) == null ? void 0 : d[e];
1059
+ return i ? `${l.cdnBase || ft}/templates/${e}/${i}/assets/${r}` : `/templates/${e}/assets/${r}`;
1060
+ }
1061
+ function Tt(e, a) {
1062
+ if (!e || typeof e != "string")
1063
+ throw new Error(`[site-kit] createAssetFn: slug must be a non-empty string (got ${typeof e})`);
1064
+ const l = a ?? pt();
1065
+ return (r) => _t(e, r, l);
1066
+ }
1067
+ export {
1068
+ bt as AddressBlock,
1069
+ St as ContactForm,
1070
+ gt as FeaturedItems,
1071
+ kt as HoursBlock,
1072
+ vt as MenuPreview,
1073
+ xt as PoweredBy,
1074
+ Et as PublicFloorplanViewer,
1075
+ $t as ReservationForm,
1076
+ wt as SocialLinks,
1077
+ Tt as createAssetFn,
1078
+ Ve as useContactForm,
1079
+ De as useFeaturedItems,
1080
+ Me as useHoursBlock,
1081
+ je as useMenuPreview,
1082
+ He as useReservationForm
1083
+ };