@lifestreamdynamics/booking-widget 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export declare class LsvBooking extends HTMLElement {
2
2
  static observedAttributes: string[];
3
3
  private root;
4
+ private abortController;
4
5
  private step;
5
6
  private slots;
6
7
  private selectedSlot;
@@ -11,6 +12,7 @@ export declare class LsvBooking extends HTMLElement {
11
12
  private errorMsg;
12
13
  constructor();
13
14
  connectedCallback(): void;
15
+ disconnectedCallback(): void;
14
16
  attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
15
17
  private get apiUrl();
16
18
  private get profileSlug();
@@ -37,6 +39,7 @@ export declare class LsvBooking extends HTMLElement {
37
39
  private renderForm;
38
40
  private makeField;
39
41
  private renderSuccess;
42
+ private handleRootClick;
40
43
  private attachEvents;
41
44
  private handleAction;
42
45
  }
@@ -1,6 +1,6 @@
1
1
  var v = Object.defineProperty;
2
- var y = (p, u, e) => u in p ? v(p, u, { enumerable: !0, configurable: !0, writable: !0, value: e }) : p[u] = e;
3
- var d = (p, u, e) => y(p, typeof u != "symbol" ? u + "" : u, e);
2
+ var C = (h, p, e) => p in h ? v(h, p, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[p] = e;
3
+ var d = (h, p, e) => C(h, typeof p != "symbol" ? p + "" : p, e);
4
4
  const k = `
5
5
  :host {
6
6
  display: block;
@@ -393,11 +393,12 @@ const k = `
393
393
  gap: 0.5rem;
394
394
  margin-top: 1rem;
395
395
  }
396
- `, C = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"], x = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
396
+ `, y = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"], x = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
397
397
  class f extends HTMLElement {
398
398
  constructor() {
399
399
  super();
400
400
  d(this, "root");
401
+ d(this, "abortController", null);
401
402
  d(this, "step", "slots");
402
403
  d(this, "slots", []);
403
404
  d(this, "selectedSlot", null);
@@ -406,10 +407,19 @@ class f extends HTMLElement {
406
407
  d(this, "availableTimes", []);
407
408
  d(this, "isLoading", !1);
408
409
  d(this, "errorMsg", "");
410
+ // ── Event handling ────────────────────────────────────────────────────────
411
+ d(this, "handleRootClick", (e) => {
412
+ const r = e.target.closest("[data-action]");
413
+ r && this.handleAction(r.dataset.action ?? "", r);
414
+ });
409
415
  this.root = this.attachShadow({ mode: "open" });
410
416
  }
411
417
  connectedCallback() {
412
- this.render(), this.loadSlots();
418
+ this.abortController = new AbortController(), this.root.addEventListener("click", this.handleRootClick, { signal: this.abortController.signal }), this.render(), this.loadSlots();
419
+ }
420
+ disconnectedCallback() {
421
+ var e;
422
+ (e = this.abortController) == null || e.abort(), this.abortController = null;
413
423
  }
414
424
  attributeChangedCallback(e, t, r) {
415
425
  t !== r && (e === "theme" ? this.render() : this.isConnected && this.reset());
@@ -439,12 +449,13 @@ class f extends HTMLElement {
439
449
  }
440
450
  // ── API calls ─────────────────────────────────────────────────────────────
441
451
  async loadSlots() {
452
+ var e;
442
453
  this.setLoading(!0);
443
454
  try {
444
- const e = await fetch(`${this.baseApiPath}/booking-slots`);
445
- if (!e.ok) throw new Error(`HTTP ${e.status}`);
446
- const t = await e.json();
447
- this.slots = t.slots, this.errorMsg = "";
455
+ const t = await fetch(`${this.baseApiPath}/booking-slots`, { signal: (e = this.abortController) == null ? void 0 : e.signal });
456
+ if (!t.ok) throw new Error(`HTTP ${t.status}`);
457
+ const r = await t.json();
458
+ this.slots = r.slots, this.errorMsg = "";
448
459
  } catch {
449
460
  this.setError("Failed to load booking slots. Please try again.");
450
461
  return;
@@ -452,13 +463,15 @@ class f extends HTMLElement {
452
463
  this.isLoading = !1, this.render();
453
464
  }
454
465
  async loadTimes(e, t) {
466
+ var r;
455
467
  this.setLoading(!0);
456
468
  try {
457
- const r = await fetch(
458
- `${this.baseApiPath}/booking-slots/${e}/availability?date=${t}`
469
+ const s = await fetch(
470
+ `${this.baseApiPath}/booking-slots/${e}/availability?date=${t}`,
471
+ { signal: (r = this.abortController) == null ? void 0 : r.signal }
459
472
  );
460
- if (!r.ok) throw new Error(`HTTP ${r.status}`);
461
- const o = await r.json();
473
+ if (!s.ok) throw new Error(`HTTP ${s.status}`);
474
+ const o = await s.json();
462
475
  this.availableTimes = o.times, this.errorMsg = "";
463
476
  } catch {
464
477
  this.setError("Failed to load available times. Please try again.");
@@ -466,46 +479,48 @@ class f extends HTMLElement {
466
479
  }
467
480
  this.isLoading = !1, this.step = "time", this.render();
468
481
  }
469
- async submitBooking(e, t, r, o) {
482
+ async submitBooking(e, t, r, s) {
483
+ var c;
470
484
  if (!this.selectedSlot || !this.selectedDate || !this.selectedTime) return;
471
485
  this.setLoading(!0);
472
- const s = (/* @__PURE__ */ new Date(`${this.selectedDate}T${this.selectedTime}`)).toISOString(), a = this.selectedSlot.id, n = {
486
+ const o = (/* @__PURE__ */ new Date(`${this.selectedDate}T${this.selectedTime}`)).toISOString(), a = this.selectedSlot.id, n = {
473
487
  guestName: e,
474
488
  guestEmail: t,
475
- startAt: s
489
+ startAt: o
476
490
  };
477
- r && (n.guestPhone = r), o && (n.notes = o);
491
+ r && (n.guestPhone = r), s && (n.notes = s);
478
492
  try {
479
- const c = await fetch(
493
+ const i = await fetch(
480
494
  `${this.baseApiPath}/booking-slots/${a}/book`,
481
495
  {
482
496
  method: "POST",
483
497
  headers: { "Content-Type": "application/json" },
484
- body: JSON.stringify(n)
498
+ body: JSON.stringify(n),
499
+ signal: (c = this.abortController) == null ? void 0 : c.signal
485
500
  }
486
501
  );
487
- if (!c.ok) {
488
- const l = await c.json().catch(() => ({}));
489
- throw new Error(l.error ?? l.message ?? `HTTP ${c.status}`);
502
+ if (!i.ok) {
503
+ const u = await i.json().catch(() => ({}));
504
+ throw new Error(u.error ?? u.message ?? `HTTP ${i.status}`);
490
505
  }
491
- const i = await c.json();
506
+ const l = await i.json();
492
507
  this.isLoading = !1, this.step = "success", this.render(), this.dispatchEvent(new CustomEvent("lsv-booking-submitted", {
493
508
  bubbles: !0,
494
509
  composed: !0,
495
510
  detail: {
496
511
  bookingId: "",
497
- startAt: i.startAt,
512
+ startAt: l.startAt,
498
513
  endAt: "",
499
514
  slotTitle: this.selectedSlot.title
500
515
  }
501
516
  }));
502
- } catch (c) {
503
- const i = c instanceof Error ? c.message : "Booking failed. Please try again.";
517
+ } catch (i) {
518
+ const l = i instanceof Error ? i.message : "Booking failed. Please try again.";
504
519
  this.dispatchEvent(new CustomEvent("lsv-booking-error", {
505
520
  bubbles: !0,
506
521
  composed: !0,
507
- detail: { message: i, step: "form" }
508
- })), this.setError(i);
522
+ detail: { message: l, step: "form" }
523
+ })), this.setError(l);
509
524
  }
510
525
  }
511
526
  // ── Date helpers ──────────────────────────────────────────────────────────
@@ -513,16 +528,14 @@ class f extends HTMLElement {
513
528
  const e = [], t = /* @__PURE__ */ new Date();
514
529
  t.setHours(0, 0, 0, 0);
515
530
  for (let r = 0; r < 14; r++) {
516
- const o = new Date(t);
517
- o.setDate(t.getDate() + r);
518
- const s = o.toISOString().split("T")[0], a = o.toLocaleDateString("en-US", { month: "short", day: "numeric" });
519
- e.push({ date: s, label: a, dayOfWeek: o.getDay() });
531
+ const s = new Date(t.getTime() + r * 864e5), o = s.toISOString().split("T")[0], a = s.toLocaleDateString("en-US", { month: "short", day: "numeric" });
532
+ e.push({ date: o, label: a, dayOfWeek: s.getDay() });
520
533
  }
521
534
  return e;
522
535
  }
523
536
  isDateAllowed(e) {
524
537
  if (!this.selectedSlot) return !1;
525
- const t = C[e];
538
+ const t = y[e];
526
539
  return this.selectedSlot.daysOfWeek.includes(t);
527
540
  }
528
541
  formatTime(e) {
@@ -533,8 +546,8 @@ class f extends HTMLElement {
533
546
  minute: "2-digit",
534
547
  hour12: !0
535
548
  });
536
- const [t, r] = e.split(":").map(Number), o = /* @__PURE__ */ new Date();
537
- return o.setHours(t, r, 0, 0), o.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: !0 });
549
+ const [t, r] = e.split(":").map(Number), s = /* @__PURE__ */ new Date();
550
+ return s.setHours(t, r, 0, 0), s.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: !0 });
538
551
  } catch {
539
552
  return e;
540
553
  }
@@ -576,9 +589,9 @@ class f extends HTMLElement {
576
589
  }
577
590
  renderStepIndicator() {
578
591
  const e = ["slots", "date", "time", "form"], t = e.indexOf(this.step), r = document.createElement("div");
579
- return r.className = "step-indicator", e.forEach((o, s) => {
592
+ return r.className = "step-indicator", e.forEach((s, o) => {
580
593
  const a = document.createElement("div");
581
- a.className = `step-dot${s === t ? " active" : s < t ? " done" : ""}`, r.appendChild(a);
594
+ a.className = `step-dot${o === t ? " active" : o < t ? " done" : ""}`, r.appendChild(a);
582
595
  }), r;
583
596
  }
584
597
  renderLoading() {
@@ -594,30 +607,30 @@ class f extends HTMLElement {
594
607
  t.className = "error-box", t.textContent = this.errorMsg, e.appendChild(t);
595
608
  const r = document.createElement("button");
596
609
  r.className = "btn-retry", r.dataset.action = "retry", r.textContent = "Try again", e.appendChild(r);
597
- const o = document.createElement("div");
598
- return o.appendChild(e), o;
610
+ const s = document.createElement("div");
611
+ return s.appendChild(e), s;
599
612
  }
600
613
  renderSlots() {
601
614
  const e = document.createDocumentFragment(), t = document.createElement("p");
602
615
  if (t.className = "section-title", t.textContent = "Choose a booking type", e.appendChild(t), this.slots.length === 0) {
603
- const o = document.createElement("p");
604
- o.className = "empty-msg", o.textContent = "No booking slots available.", e.appendChild(o);
616
+ const s = document.createElement("p");
617
+ s.className = "empty-msg", s.textContent = "No booking slots available.", e.appendChild(s);
605
618
  } else {
606
- const o = document.createElement("div");
607
- o.className = "slot-list", this.slots.forEach((s) => {
619
+ const s = document.createElement("div");
620
+ s.className = "slot-list", this.slots.forEach((o) => {
608
621
  const a = document.createElement("button");
609
- a.className = "slot-card", a.dataset.action = "select-slot", a.dataset.slotId = s.id;
622
+ a.className = "slot-card", a.dataset.action = "select-slot", a.dataset.slotId = o.id;
610
623
  const n = document.createElement("p");
611
- n.className = "slot-card-title", n.textContent = s.title;
624
+ n.className = "slot-card-title", n.textContent = o.title;
612
625
  const c = document.createElement("p");
613
626
  c.className = "slot-card-meta";
614
- const i = this.formatDayList(s.daysOfWeek);
615
- if (c.textContent = `${s.durationMin} min · ${i} · ${s.startTime}–${s.endTime}`, a.appendChild(n), s.description) {
627
+ const i = this.formatDayList(o.daysOfWeek);
628
+ if (c.textContent = `${o.durationMin} min · ${i} · ${o.startTime}–${o.endTime}`, a.appendChild(n), o.description) {
616
629
  const l = document.createElement("p");
617
- l.className = "slot-card-meta", l.style.marginTop = "0.25rem", l.textContent = s.description, a.appendChild(l);
630
+ l.className = "slot-card-meta", l.style.marginTop = "0.25rem", l.textContent = o.description, a.appendChild(l);
618
631
  }
619
- a.appendChild(c), o.appendChild(a);
620
- }), e.appendChild(o);
632
+ a.appendChild(c), s.appendChild(a);
633
+ }), e.appendChild(s);
621
634
  }
622
635
  const r = document.createElement("div");
623
636
  return r.appendChild(e), r;
@@ -643,20 +656,20 @@ class f extends HTMLElement {
643
656
  t.className = "btn-back", t.dataset.action = "back-to-slots", t.textContent = "← Back", e.appendChild(t);
644
657
  const r = document.createElement("p");
645
658
  r.className = "section-title", r.textContent = "Select a date", e.appendChild(r);
646
- const o = this.getNextDates(), s = document.createElement("div");
647
- s.className = "date-grid", x.forEach((i) => {
659
+ const s = this.getNextDates(), o = document.createElement("div");
660
+ o.className = "date-grid", x.forEach((i) => {
648
661
  const l = document.createElement("div");
649
- l.className = "date-header", l.textContent = i, s.appendChild(l);
662
+ l.className = "date-header", l.textContent = i, o.appendChild(l);
650
663
  });
651
- const a = ((c = o[0]) == null ? void 0 : c.dayOfWeek) ?? 0;
664
+ const a = ((c = s[0]) == null ? void 0 : c.dayOfWeek) ?? 0;
652
665
  for (let i = 0; i < a; i++) {
653
666
  const l = document.createElement("div");
654
- l.className = "date-cell empty", s.appendChild(l);
667
+ l.className = "date-cell empty", o.appendChild(l);
655
668
  }
656
- o.forEach(({ date: i, label: l, dayOfWeek: h }) => {
657
- const m = document.createElement("button"), g = this.isDateAllowed(h);
658
- m.className = `date-cell${g ? "" : " disabled"}${i === this.selectedDate ? " selected" : ""}`, m.textContent = (/* @__PURE__ */ new Date(`${i}T12:00:00`)).getDate().toString(), m.title = l, g ? (m.dataset.action = "select-date", m.dataset.date = i) : m.disabled = !0, s.appendChild(m);
659
- }), e.appendChild(s);
669
+ s.forEach(({ date: i, label: l, dayOfWeek: u }) => {
670
+ const m = document.createElement("button"), g = this.isDateAllowed(u);
671
+ m.className = `date-cell${g ? "" : " disabled"}${i === this.selectedDate ? " selected" : ""}`, m.textContent = (/* @__PURE__ */ new Date(`${i}T12:00:00`)).getDate().toString(), m.title = l, g ? (m.dataset.action = "select-date", m.dataset.date = i) : m.disabled = !0, o.appendChild(m);
672
+ }), e.appendChild(o);
660
673
  const n = document.createElement("div");
661
674
  return n.appendChild(e), n;
662
675
  }
@@ -665,23 +678,23 @@ class f extends HTMLElement {
665
678
  t.className = "btn-back", t.dataset.action = "back-to-date", t.textContent = "← Back", e.appendChild(t);
666
679
  const r = document.createElement("p");
667
680
  if (r.className = "section-title", r.textContent = `Available times · ${this.formatDate(this.selectedDate)}`, e.appendChild(r), this.availableTimes.length === 0) {
668
- const s = document.createElement("p");
669
- s.className = "empty-msg", s.textContent = "No times available on this date. Try another day.", e.appendChild(s);
681
+ const o = document.createElement("p");
682
+ o.className = "empty-msg", o.textContent = "No times available on this date. Try another day.", e.appendChild(o);
670
683
  } else {
671
- const s = document.createElement("div");
672
- s.className = "time-grid", this.availableTimes.forEach((a) => {
684
+ const o = document.createElement("div");
685
+ o.className = "time-grid", this.availableTimes.forEach((a) => {
673
686
  const n = document.createElement("button");
674
- n.className = `time-btn${a === this.selectedTime ? " selected" : ""}`, n.dataset.action = "select-time", n.dataset.time = a, n.textContent = this.formatTime(a), s.appendChild(n);
675
- }), e.appendChild(s);
687
+ n.className = `time-btn${a === this.selectedTime ? " selected" : ""}`, n.dataset.action = "select-time", n.dataset.time = a, n.textContent = this.formatTime(a), o.appendChild(n);
688
+ }), e.appendChild(o);
676
689
  }
677
690
  if (this.selectedTime) {
678
- const s = document.createElement("div");
679
- s.className = "actions";
691
+ const o = document.createElement("div");
692
+ o.className = "actions";
680
693
  const a = document.createElement("button");
681
- a.className = "btn-primary", a.dataset.action = "go-to-form", a.textContent = "Continue", s.appendChild(a), e.appendChild(s);
694
+ a.className = "btn-primary", a.dataset.action = "go-to-form", a.textContent = "Continue", o.appendChild(a), e.appendChild(o);
682
695
  }
683
- const o = document.createElement("div");
684
- return o.appendChild(e), o;
696
+ const s = document.createElement("div");
697
+ return s.appendChild(e), s;
685
698
  }
686
699
  renderForm() {
687
700
  var g, b;
@@ -689,10 +702,10 @@ class f extends HTMLElement {
689
702
  t.className = "btn-back", t.dataset.action = "back-to-time", t.textContent = "← Back", e.appendChild(t);
690
703
  const r = document.createElement("div");
691
704
  r.className = "booking-summary";
692
- const o = document.createElement("strong");
693
- o.textContent = ((g = this.selectedSlot) == null ? void 0 : g.title) ?? "", r.appendChild(o);
694
- const s = document.createElement("span");
695
- s.textContent = `${this.formatDate(this.selectedDate)} at ${this.formatTime(this.selectedTime)}`, r.appendChild(s), e.appendChild(r);
705
+ const s = document.createElement("strong");
706
+ s.textContent = ((g = this.selectedSlot) == null ? void 0 : g.title) ?? "", r.appendChild(s);
707
+ const o = document.createElement("span");
708
+ o.textContent = `${this.formatDate(this.selectedDate)} at ${this.formatTime(this.selectedTime)}`, r.appendChild(o), e.appendChild(r);
696
709
  const a = document.createElement("form");
697
710
  a.id = "lsv-booking-form", a.appendChild(this.makeField("guestName", "Full name", "text", !0)), a.appendChild(this.makeField("guestEmail", "Email address", "email", !0)), (b = this.selectedSlot) != null && b.requirePhone && a.appendChild(this.makeField("guestPhone", "Phone number", "tel", !0));
698
711
  const n = document.createElement("div");
@@ -703,18 +716,18 @@ class f extends HTMLElement {
703
716
  i.className = "form-textarea", i.id = "lsv-notes", i.name = "notes", i.rows = 3, i.placeholder = "Anything you'd like to share…", n.appendChild(c), n.appendChild(i), a.appendChild(n);
704
717
  const l = document.createElement("div");
705
718
  l.className = "actions";
706
- const h = document.createElement("button");
707
- h.type = "submit", h.className = "btn-primary", h.textContent = "Confirm Booking", l.appendChild(h), a.appendChild(l), e.appendChild(a);
719
+ const u = document.createElement("button");
720
+ u.type = "submit", u.className = "btn-primary", u.textContent = "Confirm Booking", l.appendChild(u), a.appendChild(l), e.appendChild(a);
708
721
  const m = document.createElement("div");
709
722
  return m.appendChild(e), m;
710
723
  }
711
- makeField(e, t, r, o) {
712
- const s = document.createElement("div");
713
- s.className = "form-group";
724
+ makeField(e, t, r, s) {
725
+ const o = document.createElement("div");
726
+ o.className = "form-group";
714
727
  const a = document.createElement("label");
715
- a.className = "form-label", a.setAttribute("for", `lsv-${e}`), a.textContent = o ? `${t} *` : t;
728
+ a.className = "form-label", a.setAttribute("for", `lsv-${e}`), a.textContent = s ? `${t} *` : t;
716
729
  const n = document.createElement("input");
717
- return n.className = "form-input", n.type = r, n.id = `lsv-${e}`, n.name = e, n.required = o, n.autocomplete = e === "guestName" ? "name" : e === "guestEmail" ? "email" : e === "guestPhone" ? "tel" : "off", s.appendChild(a), s.appendChild(n), s;
730
+ return n.className = "form-input", n.type = r, n.id = `lsv-${e}`, n.name = e, n.required = s, n.autocomplete = e === "guestName" ? "name" : e === "guestEmail" ? "email" : e === "guestPhone" ? "tel" : "off", o.appendChild(a), o.appendChild(n), o;
718
731
  }
719
732
  renderSuccess() {
720
733
  const e = document.createElement("div");
@@ -723,20 +736,15 @@ class f extends HTMLElement {
723
736
  t.className = "success-icon", t.textContent = "✓";
724
737
  const r = document.createElement("p");
725
738
  r.className = "success-title", r.textContent = "Booking requested!";
726
- const o = document.createElement("p");
727
- return o.className = "success-msg", o.textContent = "Check your email to confirm your booking.", e.appendChild(t), e.appendChild(r), e.appendChild(o), e;
739
+ const s = document.createElement("p");
740
+ return s.className = "success-msg", s.textContent = "Check your email to confirm your booking.", e.appendChild(t), e.appendChild(r), e.appendChild(s), e;
728
741
  }
729
- // ── Event handling ────────────────────────────────────────────────────────
730
742
  attachEvents() {
731
- this.root.addEventListener("click", (t) => {
732
- const o = t.target.closest("[data-action]");
733
- o && this.handleAction(o.dataset.action ?? "", o);
734
- });
735
743
  const e = this.root.querySelector("#lsv-booking-form");
736
744
  e && e.addEventListener("submit", (t) => {
737
745
  t.preventDefault();
738
- const r = new FormData(e), o = (r.get("guestName") ?? "").trim(), s = (r.get("guestEmail") ?? "").trim(), a = (r.get("guestPhone") ?? "").trim(), n = (r.get("notes") ?? "").trim();
739
- !o || !s || this.submitBooking(o, s, a, n);
746
+ const r = new FormData(e), s = (r.get("guestName") ?? "").trim(), o = (r.get("guestEmail") ?? "").trim(), a = (r.get("guestPhone") ?? "").trim(), n = (r.get("notes") ?? "").trim();
747
+ !s || !o || this.submitBooking(s, o, a, n);
740
748
  });
741
749
  }
742
750
  handleAction(e, t) {
@@ -745,9 +753,9 @@ class f extends HTMLElement {
745
753
  this.errorMsg = "", this.step === "slots" ? this.loadSlots() : this.step === "time" && this.selectedSlot ? this.loadTimes(this.selectedSlot.id, this.selectedDate) : this.render();
746
754
  break;
747
755
  case "select-slot": {
748
- const r = t.dataset.slotId, o = this.slots.find((s) => s.id === r);
749
- if (!o) return;
750
- this.selectedSlot = o, this.step = "date", this.render();
756
+ const r = t.dataset.slotId, s = this.slots.find((o) => o.id === r);
757
+ if (!s) return;
758
+ this.selectedSlot = s, this.step = "date", this.render();
751
759
  break;
752
760
  }
753
761
  case "back-to-slots":
@@ -1,4 +1,4 @@
1
- (function(m,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(m=typeof globalThis<"u"?globalThis:m||self,d(m.LsvBooking={}))})(this,(function(m){"use strict";var k=Object.defineProperty;var C=(m,d,h)=>d in m?k(m,d,{enumerable:!0,configurable:!0,writable:!0,value:h}):m[d]=h;var u=(m,d,h)=>C(m,typeof d!="symbol"?d+"":d,h);const d=`
1
+ (function(u,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(u=typeof globalThis<"u"?globalThis:u||self,d(u.LsvBooking={}))})(this,(function(u){"use strict";var y=Object.defineProperty;var k=(u,d,g)=>d in u?y(u,d,{enumerable:!0,configurable:!0,writable:!0,value:g}):u[d]=g;var m=(u,d,g)=>k(u,typeof d!="symbol"?d+"":d,g);const d=`
2
2
  :host {
3
3
  display: block;
4
4
  font-family: system-ui, -apple-system, sans-serif;
@@ -390,4 +390,4 @@
390
390
  gap: 0.5rem;
391
391
  margin-top: 1rem;
392
392
  }
393
- `,h=["sun","mon","tue","wed","thu","fri","sat"],y=["Su","Mo","Tu","We","Th","Fr","Sa"];class f extends HTMLElement{constructor(){super();u(this,"root");u(this,"step","slots");u(this,"slots",[]);u(this,"selectedSlot",null);u(this,"selectedDate","");u(this,"selectedTime","");u(this,"availableTimes",[]);u(this,"isLoading",!1);u(this,"errorMsg","");this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.loadSlots()}attributeChangedCallback(e,t,r){t!==r&&(e==="theme"?this.render():this.isConnected&&this.reset())}get apiUrl(){return(this.getAttribute("api-url")??"").replace(/\/$/,"")}get profileSlug(){return this.getAttribute("profile-slug")??""}get vaultSlug(){return this.getAttribute("vault-slug")??""}get baseApiPath(){return`${this.apiUrl}/api/v1/public/vaults/${this.profileSlug}/${this.vaultSlug}`}reset(){this.step="slots",this.slots=[],this.selectedSlot=null,this.selectedDate="",this.selectedTime="",this.availableTimes=[],this.isLoading=!1,this.errorMsg="",this.render(),this.loadSlots()}setLoading(e){this.isLoading=e,this.render()}setError(e){this.errorMsg=e,this.isLoading=!1,this.render()}async loadSlots(){this.setLoading(!0);try{const e=await fetch(`${this.baseApiPath}/booking-slots`);if(!e.ok)throw new Error(`HTTP ${e.status}`);const t=await e.json();this.slots=t.slots,this.errorMsg=""}catch{this.setError("Failed to load booking slots. Please try again.");return}this.isLoading=!1,this.render()}async loadTimes(e,t){this.setLoading(!0);try{const r=await fetch(`${this.baseApiPath}/booking-slots/${e}/availability?date=${t}`);if(!r.ok)throw new Error(`HTTP ${r.status}`);const o=await r.json();this.availableTimes=o.times,this.errorMsg=""}catch{this.setError("Failed to load available times. Please try again.");return}this.isLoading=!1,this.step="time",this.render()}async submitBooking(e,t,r,o){if(!this.selectedSlot||!this.selectedDate||!this.selectedTime)return;this.setLoading(!0);const s=new Date(`${this.selectedDate}T${this.selectedTime}`).toISOString(),a=this.selectedSlot.id,n={guestName:e,guestEmail:t,startAt:s};r&&(n.guestPhone=r),o&&(n.notes=o);try{const c=await fetch(`${this.baseApiPath}/booking-slots/${a}/book`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!c.ok){const l=await c.json().catch(()=>({}));throw new Error(l.error??l.message??`HTTP ${c.status}`)}const i=await c.json();this.isLoading=!1,this.step="success",this.render(),this.dispatchEvent(new CustomEvent("lsv-booking-submitted",{bubbles:!0,composed:!0,detail:{bookingId:"",startAt:i.startAt,endAt:"",slotTitle:this.selectedSlot.title}}))}catch(c){const i=c instanceof Error?c.message:"Booking failed. Please try again.";this.dispatchEvent(new CustomEvent("lsv-booking-error",{bubbles:!0,composed:!0,detail:{message:i,step:"form"}})),this.setError(i)}}getNextDates(){const e=[],t=new Date;t.setHours(0,0,0,0);for(let r=0;r<14;r++){const o=new Date(t);o.setDate(t.getDate()+r);const s=o.toISOString().split("T")[0],a=o.toLocaleDateString("en-US",{month:"short",day:"numeric"});e.push({date:s,label:a,dayOfWeek:o.getDay()})}return e}isDateAllowed(e){if(!this.selectedSlot)return!1;const t=h[e];return this.selectedSlot.daysOfWeek.includes(t)}formatTime(e){try{if(e.includes("T"))return new Date(e).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});const[t,r]=e.split(":").map(Number),o=new Date;return o.setHours(t,r,0,0),o.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}catch{return e}}formatDate(e){try{return new Date(`${e}T12:00:00`).toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"})}catch{return e}}render(){const e=document.createElement("style");e.textContent=d;const t=document.createElement("div");if(t.className="widget",this.step==="success")t.appendChild(this.renderSuccess());else if(t.appendChild(this.renderStepIndicator()),this.isLoading)t.appendChild(this.renderLoading());else if(this.errorMsg)t.appendChild(this.renderError());else switch(this.step){case"slots":t.appendChild(this.renderSlots());break;case"date":t.appendChild(this.renderDate());break;case"time":t.appendChild(this.renderTime());break;case"form":t.appendChild(this.renderForm());break}this.root.replaceChildren(e,t),this.attachEvents()}renderStepIndicator(){const e=["slots","date","time","form"],t=e.indexOf(this.step),r=document.createElement("div");return r.className="step-indicator",e.forEach((o,s)=>{const a=document.createElement("div");a.className=`step-dot${s===t?" active":s<t?" done":""}`,r.appendChild(a)}),r}renderLoading(){const e=document.createElement("div");e.className="loading";const t=document.createElement("div");t.className="spinner";const r=document.createElement("span");return r.textContent="Loading…",e.appendChild(t),e.appendChild(r),e}renderError(){const e=document.createDocumentFragment(),t=document.createElement("div");t.className="error-box",t.textContent=this.errorMsg,e.appendChild(t);const r=document.createElement("button");r.className="btn-retry",r.dataset.action="retry",r.textContent="Try again",e.appendChild(r);const o=document.createElement("div");return o.appendChild(e),o}renderSlots(){const e=document.createDocumentFragment(),t=document.createElement("p");if(t.className="section-title",t.textContent="Choose a booking type",e.appendChild(t),this.slots.length===0){const o=document.createElement("p");o.className="empty-msg",o.textContent="No booking slots available.",e.appendChild(o)}else{const o=document.createElement("div");o.className="slot-list",this.slots.forEach(s=>{const a=document.createElement("button");a.className="slot-card",a.dataset.action="select-slot",a.dataset.slotId=s.id;const n=document.createElement("p");n.className="slot-card-title",n.textContent=s.title;const c=document.createElement("p");c.className="slot-card-meta";const i=this.formatDayList(s.daysOfWeek);if(c.textContent=`${s.durationMin} min · ${i} · ${s.startTime}–${s.endTime}`,a.appendChild(n),s.description){const l=document.createElement("p");l.className="slot-card-meta",l.style.marginTop="0.25rem",l.textContent=s.description,a.appendChild(l)}a.appendChild(c),o.appendChild(a)}),e.appendChild(o)}const r=document.createElement("div");return r.appendChild(e),r}formatDayList(e){if(e.length===7)return"Every day";if(e.length===5&&!e.includes("sat")&&!e.includes("sun"))return"Weekdays";if(e.length===2&&e.includes("sat")&&e.includes("sun"))return"Weekends";const t={mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat",sun:"Sun"};return e.map(r=>t[r]??r).join(", ")}renderDate(){var c;const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-slots",t.textContent="← Back",e.appendChild(t);const r=document.createElement("p");r.className="section-title",r.textContent="Select a date",e.appendChild(r);const o=this.getNextDates(),s=document.createElement("div");s.className="date-grid",y.forEach(i=>{const l=document.createElement("div");l.className="date-header",l.textContent=i,s.appendChild(l)});const a=((c=o[0])==null?void 0:c.dayOfWeek)??0;for(let i=0;i<a;i++){const l=document.createElement("div");l.className="date-cell empty",s.appendChild(l)}o.forEach(({date:i,label:l,dayOfWeek:g})=>{const p=document.createElement("button"),b=this.isDateAllowed(g);p.className=`date-cell${b?"":" disabled"}${i===this.selectedDate?" selected":""}`,p.textContent=new Date(`${i}T12:00:00`).getDate().toString(),p.title=l,b?(p.dataset.action="select-date",p.dataset.date=i):p.disabled=!0,s.appendChild(p)}),e.appendChild(s);const n=document.createElement("div");return n.appendChild(e),n}renderTime(){const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-date",t.textContent="← Back",e.appendChild(t);const r=document.createElement("p");if(r.className="section-title",r.textContent=`Available times · ${this.formatDate(this.selectedDate)}`,e.appendChild(r),this.availableTimes.length===0){const s=document.createElement("p");s.className="empty-msg",s.textContent="No times available on this date. Try another day.",e.appendChild(s)}else{const s=document.createElement("div");s.className="time-grid",this.availableTimes.forEach(a=>{const n=document.createElement("button");n.className=`time-btn${a===this.selectedTime?" selected":""}`,n.dataset.action="select-time",n.dataset.time=a,n.textContent=this.formatTime(a),s.appendChild(n)}),e.appendChild(s)}if(this.selectedTime){const s=document.createElement("div");s.className="actions";const a=document.createElement("button");a.className="btn-primary",a.dataset.action="go-to-form",a.textContent="Continue",s.appendChild(a),e.appendChild(s)}const o=document.createElement("div");return o.appendChild(e),o}renderForm(){var b,v;const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-time",t.textContent="← Back",e.appendChild(t);const r=document.createElement("div");r.className="booking-summary";const o=document.createElement("strong");o.textContent=((b=this.selectedSlot)==null?void 0:b.title)??"",r.appendChild(o);const s=document.createElement("span");s.textContent=`${this.formatDate(this.selectedDate)} at ${this.formatTime(this.selectedTime)}`,r.appendChild(s),e.appendChild(r);const a=document.createElement("form");a.id="lsv-booking-form",a.appendChild(this.makeField("guestName","Full name","text",!0)),a.appendChild(this.makeField("guestEmail","Email address","email",!0)),(v=this.selectedSlot)!=null&&v.requirePhone&&a.appendChild(this.makeField("guestPhone","Phone number","tel",!0));const n=document.createElement("div");n.className="form-group";const c=document.createElement("label");c.className="form-label",c.setAttribute("for","lsv-notes"),c.textContent="Notes (optional)";const i=document.createElement("textarea");i.className="form-textarea",i.id="lsv-notes",i.name="notes",i.rows=3,i.placeholder="Anything you'd like to share…",n.appendChild(c),n.appendChild(i),a.appendChild(n);const l=document.createElement("div");l.className="actions";const g=document.createElement("button");g.type="submit",g.className="btn-primary",g.textContent="Confirm Booking",l.appendChild(g),a.appendChild(l),e.appendChild(a);const p=document.createElement("div");return p.appendChild(e),p}makeField(e,t,r,o){const s=document.createElement("div");s.className="form-group";const a=document.createElement("label");a.className="form-label",a.setAttribute("for",`lsv-${e}`),a.textContent=o?`${t} *`:t;const n=document.createElement("input");return n.className="form-input",n.type=r,n.id=`lsv-${e}`,n.name=e,n.required=o,n.autocomplete=e==="guestName"?"name":e==="guestEmail"?"email":e==="guestPhone"?"tel":"off",s.appendChild(a),s.appendChild(n),s}renderSuccess(){const e=document.createElement("div");e.className="success-view";const t=document.createElement("div");t.className="success-icon",t.textContent="✓";const r=document.createElement("p");r.className="success-title",r.textContent="Booking requested!";const o=document.createElement("p");return o.className="success-msg",o.textContent="Check your email to confirm your booking.",e.appendChild(t),e.appendChild(r),e.appendChild(o),e}attachEvents(){this.root.addEventListener("click",t=>{const o=t.target.closest("[data-action]");o&&this.handleAction(o.dataset.action??"",o)});const e=this.root.querySelector("#lsv-booking-form");e&&e.addEventListener("submit",t=>{t.preventDefault();const r=new FormData(e),o=(r.get("guestName")??"").trim(),s=(r.get("guestEmail")??"").trim(),a=(r.get("guestPhone")??"").trim(),n=(r.get("notes")??"").trim();!o||!s||this.submitBooking(o,s,a,n)})}handleAction(e,t){switch(e){case"retry":this.errorMsg="",this.step==="slots"?this.loadSlots():this.step==="time"&&this.selectedSlot?this.loadTimes(this.selectedSlot.id,this.selectedDate):this.render();break;case"select-slot":{const r=t.dataset.slotId,o=this.slots.find(s=>s.id===r);if(!o)return;this.selectedSlot=o,this.step="date",this.render();break}case"back-to-slots":this.selectedSlot=null,this.selectedDate="",this.selectedTime="",this.step="slots",this.render();break;case"select-date":{const r=t.dataset.date;if(!r||!this.selectedSlot)return;this.selectedDate=r,this.selectedTime="",this.loadTimes(this.selectedSlot.id,r);break}case"back-to-date":this.selectedTime="",this.step="date",this.render();break;case"select-time":{const r=t.dataset.time;if(!r)return;this.selectedTime=r,this.render();break}case"go-to-form":this.selectedTime&&(this.step="form",this.render());break;case"back-to-time":this.step="time",this.render();break}}}u(f,"observedAttributes",["api-url","profile-slug","vault-slug","theme"]),customElements.get("lsv-booking")||customElements.define("lsv-booking",f),m.LsvBooking=f,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
393
+ `,g=["sun","mon","tue","wed","thu","fri","sat"],C=["Su","Mo","Tu","We","Th","Fr","Sa"];class f extends HTMLElement{constructor(){super();m(this,"root");m(this,"abortController",null);m(this,"step","slots");m(this,"slots",[]);m(this,"selectedSlot",null);m(this,"selectedDate","");m(this,"selectedTime","");m(this,"availableTimes",[]);m(this,"isLoading",!1);m(this,"errorMsg","");m(this,"handleRootClick",e=>{const r=e.target.closest("[data-action]");r&&this.handleAction(r.dataset.action??"",r)});this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.abortController=new AbortController,this.root.addEventListener("click",this.handleRootClick,{signal:this.abortController.signal}),this.render(),this.loadSlots()}disconnectedCallback(){var e;(e=this.abortController)==null||e.abort(),this.abortController=null}attributeChangedCallback(e,t,r){t!==r&&(e==="theme"?this.render():this.isConnected&&this.reset())}get apiUrl(){return(this.getAttribute("api-url")??"").replace(/\/$/,"")}get profileSlug(){return this.getAttribute("profile-slug")??""}get vaultSlug(){return this.getAttribute("vault-slug")??""}get baseApiPath(){return`${this.apiUrl}/api/v1/public/vaults/${this.profileSlug}/${this.vaultSlug}`}reset(){this.step="slots",this.slots=[],this.selectedSlot=null,this.selectedDate="",this.selectedTime="",this.availableTimes=[],this.isLoading=!1,this.errorMsg="",this.render(),this.loadSlots()}setLoading(e){this.isLoading=e,this.render()}setError(e){this.errorMsg=e,this.isLoading=!1,this.render()}async loadSlots(){var e;this.setLoading(!0);try{const t=await fetch(`${this.baseApiPath}/booking-slots`,{signal:(e=this.abortController)==null?void 0:e.signal});if(!t.ok)throw new Error(`HTTP ${t.status}`);const r=await t.json();this.slots=r.slots,this.errorMsg=""}catch{this.setError("Failed to load booking slots. Please try again.");return}this.isLoading=!1,this.render()}async loadTimes(e,t){var r;this.setLoading(!0);try{const s=await fetch(`${this.baseApiPath}/booking-slots/${e}/availability?date=${t}`,{signal:(r=this.abortController)==null?void 0:r.signal});if(!s.ok)throw new Error(`HTTP ${s.status}`);const o=await s.json();this.availableTimes=o.times,this.errorMsg=""}catch{this.setError("Failed to load available times. Please try again.");return}this.isLoading=!1,this.step="time",this.render()}async submitBooking(e,t,r,s){var c;if(!this.selectedSlot||!this.selectedDate||!this.selectedTime)return;this.setLoading(!0);const o=new Date(`${this.selectedDate}T${this.selectedTime}`).toISOString(),a=this.selectedSlot.id,n={guestName:e,guestEmail:t,startAt:o};r&&(n.guestPhone=r),s&&(n.notes=s);try{const i=await fetch(`${this.baseApiPath}/booking-slots/${a}/book`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:(c=this.abortController)==null?void 0:c.signal});if(!i.ok){const h=await i.json().catch(()=>({}));throw new Error(h.error??h.message??`HTTP ${i.status}`)}const l=await i.json();this.isLoading=!1,this.step="success",this.render(),this.dispatchEvent(new CustomEvent("lsv-booking-submitted",{bubbles:!0,composed:!0,detail:{bookingId:"",startAt:l.startAt,endAt:"",slotTitle:this.selectedSlot.title}}))}catch(i){const l=i instanceof Error?i.message:"Booking failed. Please try again.";this.dispatchEvent(new CustomEvent("lsv-booking-error",{bubbles:!0,composed:!0,detail:{message:l,step:"form"}})),this.setError(l)}}getNextDates(){const e=[],t=new Date;t.setHours(0,0,0,0);for(let r=0;r<14;r++){const s=new Date(t.getTime()+r*864e5),o=s.toISOString().split("T")[0],a=s.toLocaleDateString("en-US",{month:"short",day:"numeric"});e.push({date:o,label:a,dayOfWeek:s.getDay()})}return e}isDateAllowed(e){if(!this.selectedSlot)return!1;const t=g[e];return this.selectedSlot.daysOfWeek.includes(t)}formatTime(e){try{if(e.includes("T"))return new Date(e).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});const[t,r]=e.split(":").map(Number),s=new Date;return s.setHours(t,r,0,0),s.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}catch{return e}}formatDate(e){try{return new Date(`${e}T12:00:00`).toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"})}catch{return e}}render(){const e=document.createElement("style");e.textContent=d;const t=document.createElement("div");if(t.className="widget",this.step==="success")t.appendChild(this.renderSuccess());else if(t.appendChild(this.renderStepIndicator()),this.isLoading)t.appendChild(this.renderLoading());else if(this.errorMsg)t.appendChild(this.renderError());else switch(this.step){case"slots":t.appendChild(this.renderSlots());break;case"date":t.appendChild(this.renderDate());break;case"time":t.appendChild(this.renderTime());break;case"form":t.appendChild(this.renderForm());break}this.root.replaceChildren(e,t),this.attachEvents()}renderStepIndicator(){const e=["slots","date","time","form"],t=e.indexOf(this.step),r=document.createElement("div");return r.className="step-indicator",e.forEach((s,o)=>{const a=document.createElement("div");a.className=`step-dot${o===t?" active":o<t?" done":""}`,r.appendChild(a)}),r}renderLoading(){const e=document.createElement("div");e.className="loading";const t=document.createElement("div");t.className="spinner";const r=document.createElement("span");return r.textContent="Loading…",e.appendChild(t),e.appendChild(r),e}renderError(){const e=document.createDocumentFragment(),t=document.createElement("div");t.className="error-box",t.textContent=this.errorMsg,e.appendChild(t);const r=document.createElement("button");r.className="btn-retry",r.dataset.action="retry",r.textContent="Try again",e.appendChild(r);const s=document.createElement("div");return s.appendChild(e),s}renderSlots(){const e=document.createDocumentFragment(),t=document.createElement("p");if(t.className="section-title",t.textContent="Choose a booking type",e.appendChild(t),this.slots.length===0){const s=document.createElement("p");s.className="empty-msg",s.textContent="No booking slots available.",e.appendChild(s)}else{const s=document.createElement("div");s.className="slot-list",this.slots.forEach(o=>{const a=document.createElement("button");a.className="slot-card",a.dataset.action="select-slot",a.dataset.slotId=o.id;const n=document.createElement("p");n.className="slot-card-title",n.textContent=o.title;const c=document.createElement("p");c.className="slot-card-meta";const i=this.formatDayList(o.daysOfWeek);if(c.textContent=`${o.durationMin} min · ${i} · ${o.startTime}–${o.endTime}`,a.appendChild(n),o.description){const l=document.createElement("p");l.className="slot-card-meta",l.style.marginTop="0.25rem",l.textContent=o.description,a.appendChild(l)}a.appendChild(c),s.appendChild(a)}),e.appendChild(s)}const r=document.createElement("div");return r.appendChild(e),r}formatDayList(e){if(e.length===7)return"Every day";if(e.length===5&&!e.includes("sat")&&!e.includes("sun"))return"Weekdays";if(e.length===2&&e.includes("sat")&&e.includes("sun"))return"Weekends";const t={mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat",sun:"Sun"};return e.map(r=>t[r]??r).join(", ")}renderDate(){var c;const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-slots",t.textContent="← Back",e.appendChild(t);const r=document.createElement("p");r.className="section-title",r.textContent="Select a date",e.appendChild(r);const s=this.getNextDates(),o=document.createElement("div");o.className="date-grid",C.forEach(i=>{const l=document.createElement("div");l.className="date-header",l.textContent=i,o.appendChild(l)});const a=((c=s[0])==null?void 0:c.dayOfWeek)??0;for(let i=0;i<a;i++){const l=document.createElement("div");l.className="date-cell empty",o.appendChild(l)}s.forEach(({date:i,label:l,dayOfWeek:h})=>{const p=document.createElement("button"),b=this.isDateAllowed(h);p.className=`date-cell${b?"":" disabled"}${i===this.selectedDate?" selected":""}`,p.textContent=new Date(`${i}T12:00:00`).getDate().toString(),p.title=l,b?(p.dataset.action="select-date",p.dataset.date=i):p.disabled=!0,o.appendChild(p)}),e.appendChild(o);const n=document.createElement("div");return n.appendChild(e),n}renderTime(){const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-date",t.textContent="← Back",e.appendChild(t);const r=document.createElement("p");if(r.className="section-title",r.textContent=`Available times · ${this.formatDate(this.selectedDate)}`,e.appendChild(r),this.availableTimes.length===0){const o=document.createElement("p");o.className="empty-msg",o.textContent="No times available on this date. Try another day.",e.appendChild(o)}else{const o=document.createElement("div");o.className="time-grid",this.availableTimes.forEach(a=>{const n=document.createElement("button");n.className=`time-btn${a===this.selectedTime?" selected":""}`,n.dataset.action="select-time",n.dataset.time=a,n.textContent=this.formatTime(a),o.appendChild(n)}),e.appendChild(o)}if(this.selectedTime){const o=document.createElement("div");o.className="actions";const a=document.createElement("button");a.className="btn-primary",a.dataset.action="go-to-form",a.textContent="Continue",o.appendChild(a),e.appendChild(o)}const s=document.createElement("div");return s.appendChild(e),s}renderForm(){var b,v;const e=document.createDocumentFragment(),t=document.createElement("button");t.className="btn-back",t.dataset.action="back-to-time",t.textContent="← Back",e.appendChild(t);const r=document.createElement("div");r.className="booking-summary";const s=document.createElement("strong");s.textContent=((b=this.selectedSlot)==null?void 0:b.title)??"",r.appendChild(s);const o=document.createElement("span");o.textContent=`${this.formatDate(this.selectedDate)} at ${this.formatTime(this.selectedTime)}`,r.appendChild(o),e.appendChild(r);const a=document.createElement("form");a.id="lsv-booking-form",a.appendChild(this.makeField("guestName","Full name","text",!0)),a.appendChild(this.makeField("guestEmail","Email address","email",!0)),(v=this.selectedSlot)!=null&&v.requirePhone&&a.appendChild(this.makeField("guestPhone","Phone number","tel",!0));const n=document.createElement("div");n.className="form-group";const c=document.createElement("label");c.className="form-label",c.setAttribute("for","lsv-notes"),c.textContent="Notes (optional)";const i=document.createElement("textarea");i.className="form-textarea",i.id="lsv-notes",i.name="notes",i.rows=3,i.placeholder="Anything you'd like to share…",n.appendChild(c),n.appendChild(i),a.appendChild(n);const l=document.createElement("div");l.className="actions";const h=document.createElement("button");h.type="submit",h.className="btn-primary",h.textContent="Confirm Booking",l.appendChild(h),a.appendChild(l),e.appendChild(a);const p=document.createElement("div");return p.appendChild(e),p}makeField(e,t,r,s){const o=document.createElement("div");o.className="form-group";const a=document.createElement("label");a.className="form-label",a.setAttribute("for",`lsv-${e}`),a.textContent=s?`${t} *`:t;const n=document.createElement("input");return n.className="form-input",n.type=r,n.id=`lsv-${e}`,n.name=e,n.required=s,n.autocomplete=e==="guestName"?"name":e==="guestEmail"?"email":e==="guestPhone"?"tel":"off",o.appendChild(a),o.appendChild(n),o}renderSuccess(){const e=document.createElement("div");e.className="success-view";const t=document.createElement("div");t.className="success-icon",t.textContent="✓";const r=document.createElement("p");r.className="success-title",r.textContent="Booking requested!";const s=document.createElement("p");return s.className="success-msg",s.textContent="Check your email to confirm your booking.",e.appendChild(t),e.appendChild(r),e.appendChild(s),e}attachEvents(){const e=this.root.querySelector("#lsv-booking-form");e&&e.addEventListener("submit",t=>{t.preventDefault();const r=new FormData(e),s=(r.get("guestName")??"").trim(),o=(r.get("guestEmail")??"").trim(),a=(r.get("guestPhone")??"").trim(),n=(r.get("notes")??"").trim();!s||!o||this.submitBooking(s,o,a,n)})}handleAction(e,t){switch(e){case"retry":this.errorMsg="",this.step==="slots"?this.loadSlots():this.step==="time"&&this.selectedSlot?this.loadTimes(this.selectedSlot.id,this.selectedDate):this.render();break;case"select-slot":{const r=t.dataset.slotId,s=this.slots.find(o=>o.id===r);if(!s)return;this.selectedSlot=s,this.step="date",this.render();break}case"back-to-slots":this.selectedSlot=null,this.selectedDate="",this.selectedTime="",this.step="slots",this.render();break;case"select-date":{const r=t.dataset.date;if(!r||!this.selectedSlot)return;this.selectedDate=r,this.selectedTime="",this.loadTimes(this.selectedSlot.id,r);break}case"back-to-date":this.selectedTime="",this.step="date",this.render();break;case"select-time":{const r=t.dataset.time;if(!r)return;this.selectedTime=r,this.render();break}case"go-to-form":this.selectedTime&&(this.step="form",this.render());break;case"back-to-time":this.step="time",this.render();break}}}m(f,"observedAttributes",["api-url","profile-slug","vault-slug","theme"]),customElements.get("lsv-booking")||customElements.define("lsv-booking",f),u.LsvBooking=f,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifestreamdynamics/booking-widget",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Embeddable booking widget Web Component for Lifestream Vault",
5
5
  "type": "module",
6
6
  "main": "dist/lsv-booking.umd.cjs",
@@ -44,7 +44,7 @@
44
44
  "prepublishOnly": "npm run build && npm test"
45
45
  },
46
46
  "devDependencies": {
47
- "jsdom": "^28.1.0",
47
+ "jsdom": "^28.0.0",
48
48
  "typescript": "^5.7.3",
49
49
  "vite": "^6.1.0",
50
50
  "vite-plugin-dts": "^4.5.0",