@idkwebsites/components 0.1.8 → 0.1.14
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.cjs +494 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +447 -137
- package/dist/index.js.map +1 -1
- package/dist/styles.css +119 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -344,8 +344,62 @@ function ServicesList({
|
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
// src/lib/linkify.tsx
|
|
348
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
349
|
+
var LINK_REGEX = /((https?:\/\/|www\.)[^\s]+|[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}|(?:[a-z0-9-]+\.)+[a-z]{2,}(?:\/[^\s]*)?)/gi;
|
|
350
|
+
function buildHref(raw) {
|
|
351
|
+
if (raw.includes("@") && !raw.startsWith("http")) {
|
|
352
|
+
return `mailto:${raw}`;
|
|
353
|
+
}
|
|
354
|
+
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
355
|
+
return raw;
|
|
356
|
+
}
|
|
357
|
+
if (raw.startsWith("www.")) {
|
|
358
|
+
return `https://${raw}`;
|
|
359
|
+
}
|
|
360
|
+
return `https://${raw}`;
|
|
361
|
+
}
|
|
362
|
+
function trimTrailingPunctuation(value) {
|
|
363
|
+
let text = value;
|
|
364
|
+
let trailing = "";
|
|
365
|
+
while (text && /[),.!?:;]+$/.test(text)) {
|
|
366
|
+
trailing = text.slice(-1) + trailing;
|
|
367
|
+
text = text.slice(0, -1);
|
|
368
|
+
}
|
|
369
|
+
return { text, trailing };
|
|
370
|
+
}
|
|
371
|
+
function renderLinkedText(text) {
|
|
372
|
+
if (!text) return null;
|
|
373
|
+
const matches = Array.from(text.matchAll(LINK_REGEX));
|
|
374
|
+
if (matches.length === 0) return text;
|
|
375
|
+
const nodes = [];
|
|
376
|
+
let lastIndex = 0;
|
|
377
|
+
matches.forEach((match, index) => {
|
|
378
|
+
const matchText = match[0];
|
|
379
|
+
const start = match.index ?? 0;
|
|
380
|
+
if (start > lastIndex) {
|
|
381
|
+
nodes.push(text.slice(lastIndex, start));
|
|
382
|
+
}
|
|
383
|
+
const { text: cleanText, trailing } = trimTrailingPunctuation(matchText);
|
|
384
|
+
if (cleanText) {
|
|
385
|
+
const href = buildHref(cleanText);
|
|
386
|
+
nodes.push(
|
|
387
|
+
/* @__PURE__ */ jsx4("a", { href, target: "_blank", rel: "noopener noreferrer", children: cleanText }, `link-${start}-${index}`)
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
if (trailing) {
|
|
391
|
+
nodes.push(trailing);
|
|
392
|
+
}
|
|
393
|
+
lastIndex = start + matchText.length;
|
|
394
|
+
});
|
|
395
|
+
if (lastIndex < text.length) {
|
|
396
|
+
nodes.push(text.slice(lastIndex));
|
|
397
|
+
}
|
|
398
|
+
return nodes;
|
|
399
|
+
}
|
|
400
|
+
|
|
347
401
|
// src/components/TeamMember.tsx
|
|
348
|
-
import { jsx as
|
|
402
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
349
403
|
function TeamMember({
|
|
350
404
|
member,
|
|
351
405
|
className,
|
|
@@ -356,32 +410,32 @@ function TeamMember({
|
|
|
356
410
|
imagePosition = "left"
|
|
357
411
|
}) {
|
|
358
412
|
const initials = member.name?.split(" ").map((part) => part[0]).slice(0, 2).join("").toUpperCase();
|
|
359
|
-
const avatar = /* @__PURE__ */
|
|
413
|
+
const avatar = /* @__PURE__ */ jsx5("div", { className: "idk-team__avatar", children: member.photo?.url ? /* @__PURE__ */ jsx5("img", { src: member.photo.url, alt: member.name }) : /* @__PURE__ */ jsx5("span", { children: initials }) });
|
|
360
414
|
if (layout === "profile") {
|
|
361
415
|
return /* @__PURE__ */ jsxs2("div", { className: ["idk-team__profile", className].filter(Boolean).join(" "), children: [
|
|
362
416
|
imagePosition === "left" ? avatar : null,
|
|
363
417
|
/* @__PURE__ */ jsxs2("div", { className: "idk-team__info", children: [
|
|
364
|
-
/* @__PURE__ */
|
|
365
|
-
showRole ? /* @__PURE__ */
|
|
366
|
-
showBio && member.bio ? /* @__PURE__ */
|
|
367
|
-
showEmail ? /* @__PURE__ */
|
|
418
|
+
/* @__PURE__ */ jsx5("h3", { className: "idk-card__title", children: member.name }),
|
|
419
|
+
showRole ? /* @__PURE__ */ jsx5("p", { className: "idk-card__description", children: member.role || "Staff" }) : null,
|
|
420
|
+
showBio && member.bio ? /* @__PURE__ */ jsx5("p", { className: "idk-team__bio", children: renderLinkedText(member.bio) }) : null,
|
|
421
|
+
showEmail ? /* @__PURE__ */ jsx5("p", { className: "idk-card__meta", children: member.email }) : null
|
|
368
422
|
] }),
|
|
369
423
|
imagePosition === "right" ? avatar : null
|
|
370
424
|
] });
|
|
371
425
|
}
|
|
372
426
|
return /* @__PURE__ */ jsxs2("div", { className: ["idk-card", className].filter(Boolean).join(" "), children: [
|
|
373
|
-
/* @__PURE__ */
|
|
427
|
+
/* @__PURE__ */ jsx5("div", { className: "idk-team__avatar", children: member.photo?.url ? /* @__PURE__ */ jsx5("img", { src: member.photo.url, alt: member.name }) : /* @__PURE__ */ jsx5("span", { children: initials }) }),
|
|
374
428
|
/* @__PURE__ */ jsxs2("div", { className: "idk-team__info", children: [
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
showRole ? /* @__PURE__ */
|
|
377
|
-
showBio && member.bio ? /* @__PURE__ */
|
|
378
|
-
showEmail ? /* @__PURE__ */
|
|
429
|
+
/* @__PURE__ */ jsx5("h3", { className: "idk-card__title", children: member.name }),
|
|
430
|
+
showRole ? /* @__PURE__ */ jsx5("p", { className: "idk-card__description", children: member.role || "Staff" }) : null,
|
|
431
|
+
showBio && member.bio ? /* @__PURE__ */ jsx5("p", { className: "idk-team__bio", children: renderLinkedText(member.bio) }) : null,
|
|
432
|
+
showEmail ? /* @__PURE__ */ jsx5("p", { className: "idk-card__meta", children: member.email }) : null
|
|
379
433
|
] })
|
|
380
434
|
] });
|
|
381
435
|
}
|
|
382
436
|
|
|
383
437
|
// src/components/TeamGrid.tsx
|
|
384
|
-
import { jsx as
|
|
438
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
385
439
|
function TeamGrid({
|
|
386
440
|
columns = 3,
|
|
387
441
|
className,
|
|
@@ -399,7 +453,7 @@ function TeamGrid({
|
|
|
399
453
|
}) {
|
|
400
454
|
const { data, isLoading, error } = useTeam(query);
|
|
401
455
|
if (isLoading) {
|
|
402
|
-
return /* @__PURE__ */
|
|
456
|
+
return /* @__PURE__ */ jsx6("div", { className: "idk-state", children: loadingMessage });
|
|
403
457
|
}
|
|
404
458
|
let members = data?.team ? [...data.team] : [];
|
|
405
459
|
if (filter) {
|
|
@@ -412,15 +466,15 @@ function TeamGrid({
|
|
|
412
466
|
members = members.slice(0, Math.max(0, limit));
|
|
413
467
|
}
|
|
414
468
|
if (error || members.length === 0) {
|
|
415
|
-
return /* @__PURE__ */
|
|
469
|
+
return /* @__PURE__ */ jsx6("div", { className: "idk-state", children: emptyMessage });
|
|
416
470
|
}
|
|
417
471
|
const gridStyle = { gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))` };
|
|
418
|
-
return /* @__PURE__ */
|
|
472
|
+
return /* @__PURE__ */ jsx6(
|
|
419
473
|
"div",
|
|
420
474
|
{
|
|
421
475
|
className: ["idk-team", className].filter(Boolean).join(" "),
|
|
422
476
|
style: gridStyle,
|
|
423
|
-
children: members.map((member) => /* @__PURE__ */
|
|
477
|
+
children: members.map((member) => /* @__PURE__ */ jsx6(
|
|
424
478
|
TeamMember,
|
|
425
479
|
{
|
|
426
480
|
member,
|
|
@@ -439,7 +493,7 @@ function TeamGrid({
|
|
|
439
493
|
// src/components/ContactForm.tsx
|
|
440
494
|
import { useState as useState2 } from "react";
|
|
441
495
|
import { useMutation as useMutation2 } from "@tanstack/react-query";
|
|
442
|
-
import { jsx as
|
|
496
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
443
497
|
var DEFAULT_FIELDS = ["name", "email", "phone", "message"];
|
|
444
498
|
function ContactForm({
|
|
445
499
|
fields = DEFAULT_FIELDS,
|
|
@@ -487,8 +541,8 @@ function ContactForm({
|
|
|
487
541
|
};
|
|
488
542
|
return /* @__PURE__ */ jsxs3("form", { className: ["idk-form", className].filter(Boolean).join(" "), onSubmit: handleSubmit, children: [
|
|
489
543
|
fields.includes("name") && /* @__PURE__ */ jsxs3("label", { className: "idk-form__field", children: [
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
/* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ jsx7("span", { children: "Name" }),
|
|
545
|
+
/* @__PURE__ */ jsx7(
|
|
492
546
|
"input",
|
|
493
547
|
{
|
|
494
548
|
value: formState.name,
|
|
@@ -498,8 +552,8 @@ function ContactForm({
|
|
|
498
552
|
)
|
|
499
553
|
] }),
|
|
500
554
|
fields.includes("email") && /* @__PURE__ */ jsxs3("label", { className: "idk-form__field", children: [
|
|
501
|
-
/* @__PURE__ */
|
|
502
|
-
/* @__PURE__ */
|
|
555
|
+
/* @__PURE__ */ jsx7("span", { children: "Email" }),
|
|
556
|
+
/* @__PURE__ */ jsx7(
|
|
503
557
|
"input",
|
|
504
558
|
{
|
|
505
559
|
type: "email",
|
|
@@ -510,8 +564,8 @@ function ContactForm({
|
|
|
510
564
|
)
|
|
511
565
|
] }),
|
|
512
566
|
fields.includes("phone") && /* @__PURE__ */ jsxs3("label", { className: "idk-form__field", children: [
|
|
513
|
-
/* @__PURE__ */
|
|
514
|
-
/* @__PURE__ */
|
|
567
|
+
/* @__PURE__ */ jsx7("span", { children: "Phone" }),
|
|
568
|
+
/* @__PURE__ */ jsx7(
|
|
515
569
|
"input",
|
|
516
570
|
{
|
|
517
571
|
value: formState.phone,
|
|
@@ -520,8 +574,8 @@ function ContactForm({
|
|
|
520
574
|
)
|
|
521
575
|
] }),
|
|
522
576
|
fields.includes("subject") && /* @__PURE__ */ jsxs3("label", { className: "idk-form__field", children: [
|
|
523
|
-
/* @__PURE__ */
|
|
524
|
-
/* @__PURE__ */
|
|
577
|
+
/* @__PURE__ */ jsx7("span", { children: "Subject" }),
|
|
578
|
+
/* @__PURE__ */ jsx7(
|
|
525
579
|
"input",
|
|
526
580
|
{
|
|
527
581
|
value: formState.subject,
|
|
@@ -530,8 +584,8 @@ function ContactForm({
|
|
|
530
584
|
)
|
|
531
585
|
] }),
|
|
532
586
|
fields.includes("message") && /* @__PURE__ */ jsxs3("label", { className: "idk-form__field", children: [
|
|
533
|
-
/* @__PURE__ */
|
|
534
|
-
/* @__PURE__ */
|
|
587
|
+
/* @__PURE__ */ jsx7("span", { children: "Message" }),
|
|
588
|
+
/* @__PURE__ */ jsx7(
|
|
535
589
|
"textarea",
|
|
536
590
|
{
|
|
537
591
|
value: formState.message,
|
|
@@ -541,15 +595,27 @@ function ContactForm({
|
|
|
541
595
|
}
|
|
542
596
|
)
|
|
543
597
|
] }),
|
|
544
|
-
/* @__PURE__ */
|
|
545
|
-
mutation.isError ? /* @__PURE__ */
|
|
546
|
-
mutation.isSuccess ? /* @__PURE__ */
|
|
598
|
+
/* @__PURE__ */ jsx7("button", { type: "submit", className: "idk-button", disabled: mutation.isPending, children: mutation.isPending ? "Sending..." : submitLabel }),
|
|
599
|
+
mutation.isError ? /* @__PURE__ */ jsx7("p", { className: "idk-form__error", children: "Something went wrong. Please try again." }) : null,
|
|
600
|
+
mutation.isSuccess ? /* @__PURE__ */ jsx7("p", { className: "idk-form__success", children: "Thanks! We'll be in touch soon." }) : null
|
|
547
601
|
] });
|
|
548
602
|
}
|
|
549
603
|
|
|
550
604
|
// src/components/BookingWidget.tsx
|
|
551
605
|
import { useCallback, useEffect, useMemo as useMemo4, useRef, useState as useState3 } from "react";
|
|
552
|
-
import { jsx as
|
|
606
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
607
|
+
var parseDateOnly = (value) => {
|
|
608
|
+
if (!value) return null;
|
|
609
|
+
const normalized = value.includes("T") ? value.split("T")[0] : value;
|
|
610
|
+
const parsed = /* @__PURE__ */ new Date(`${normalized}T00:00:00`);
|
|
611
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
612
|
+
};
|
|
613
|
+
var addDays = (dateStr, days) => {
|
|
614
|
+
const parsed = parseDateOnly(dateStr);
|
|
615
|
+
if (!parsed) return dateStr;
|
|
616
|
+
parsed.setDate(parsed.getDate() + days);
|
|
617
|
+
return parsed.toISOString().slice(0, 10);
|
|
618
|
+
};
|
|
553
619
|
function BookingWidget({
|
|
554
620
|
className,
|
|
555
621
|
showStaffSelection = true,
|
|
@@ -576,7 +642,16 @@ function BookingWidget({
|
|
|
576
642
|
staffLayout = "grid",
|
|
577
643
|
staffColumns = 2,
|
|
578
644
|
showAnyStaffOption = true,
|
|
579
|
-
|
|
645
|
+
staffPageSize = 8,
|
|
646
|
+
showStaffPagination,
|
|
647
|
+
timeLayout = "split",
|
|
648
|
+
availabilityDays: availabilityDaysProp = 7,
|
|
649
|
+
enableDatePaging = true,
|
|
650
|
+
datePickerMode = "list",
|
|
651
|
+
calendarVariant = "default",
|
|
652
|
+
maxAdvanceMonths = 3,
|
|
653
|
+
disabledWeekdays = [],
|
|
654
|
+
showFullyBookedLabel = true
|
|
580
655
|
}) {
|
|
581
656
|
const resolvedServicesQuery = useMemo4(
|
|
582
657
|
() => services ? { ...servicesQuery, enabled: false } : servicesQuery,
|
|
@@ -595,6 +670,17 @@ function BookingWidget({
|
|
|
595
670
|
const [serviceSearch, setServiceSearch] = useState3("");
|
|
596
671
|
const [categoryFilter, setCategoryFilter] = useState3("all");
|
|
597
672
|
const [servicePage, setServicePage] = useState3(1);
|
|
673
|
+
const [staffPage, setStaffPage] = useState3(1);
|
|
674
|
+
const [availabilityDays, setAvailabilityDays] = useState3(
|
|
675
|
+
Math.max(1, availabilityDaysProp)
|
|
676
|
+
);
|
|
677
|
+
const [availabilityStartDate, setAvailabilityStartDate] = useState3(
|
|
678
|
+
() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
679
|
+
);
|
|
680
|
+
const [calendarMonth, setCalendarMonth] = useState3(() => {
|
|
681
|
+
const now = /* @__PURE__ */ new Date();
|
|
682
|
+
return new Date(now.getFullYear(), now.getMonth(), 1);
|
|
683
|
+
});
|
|
598
684
|
const [bookingError, setBookingError] = useState3(null);
|
|
599
685
|
const [details, setDetails] = useState3({
|
|
600
686
|
name: "",
|
|
@@ -626,6 +712,9 @@ function BookingWidget({
|
|
|
626
712
|
useEffect(() => {
|
|
627
713
|
setServicePage(1);
|
|
628
714
|
}, [serviceSearch, categoryFilter]);
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
setStaffPage(1);
|
|
717
|
+
}, [selectedService?.id, teamData, teamFilter]);
|
|
629
718
|
const pagedServices = useMemo4(() => {
|
|
630
719
|
const pageSize = Math.max(1, servicePageSize);
|
|
631
720
|
return filteredServices.slice(0, pageSize * servicePage);
|
|
@@ -641,10 +730,21 @@ function BookingWidget({
|
|
|
641
730
|
const teamList = teamData?.team || [];
|
|
642
731
|
return teamFilter ? teamList.filter(teamFilter) : teamList;
|
|
643
732
|
}, [selectedService, teamData, teamFilter]);
|
|
733
|
+
const pagedStaff = useMemo4(() => {
|
|
734
|
+
const pageSize = Math.max(1, staffPageSize);
|
|
735
|
+
return staffOptions.slice(0, pageSize * staffPage);
|
|
736
|
+
}, [staffOptions, staffPage, staffPageSize]);
|
|
737
|
+
const hasMoreStaff = staffOptions.length > pagedStaff.length;
|
|
738
|
+
const shouldPaginateStaff = showStaffPagination ?? hasMoreStaff;
|
|
739
|
+
const availabilityEndDate = useMemo4(
|
|
740
|
+
() => addDays(availabilityStartDate, Math.max(1, availabilityDays) - 1),
|
|
741
|
+
[availabilityStartDate, availabilityDays]
|
|
742
|
+
);
|
|
644
743
|
const availability = useAvailability({
|
|
645
744
|
serviceId: selectedService?.id || "",
|
|
646
745
|
staffId: selectedStaff?.id,
|
|
647
|
-
|
|
746
|
+
startDate: availabilityStartDate,
|
|
747
|
+
endDate: availabilityEndDate,
|
|
648
748
|
enabled: Boolean(selectedService)
|
|
649
749
|
});
|
|
650
750
|
const timeZone = availability.data?.timeZone || "America/Chicago";
|
|
@@ -714,18 +814,6 @@ function BookingWidget({
|
|
|
714
814
|
setSelectedEndTime(null);
|
|
715
815
|
setBookingError(null);
|
|
716
816
|
};
|
|
717
|
-
useEffect(() => {
|
|
718
|
-
if (step !== "time") return;
|
|
719
|
-
if (!availability.data?.dates?.length) return;
|
|
720
|
-
if (selectedDate) return;
|
|
721
|
-
setSelectedDate(availability.data.dates[0].date);
|
|
722
|
-
}, [step, availability.data, selectedDate]);
|
|
723
|
-
const parseDateOnly = (value) => {
|
|
724
|
-
if (!value) return null;
|
|
725
|
-
const normalized = value.includes("T") ? value.split("T")[0] : value;
|
|
726
|
-
const parsed = /* @__PURE__ */ new Date(`${normalized}T00:00:00`);
|
|
727
|
-
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
728
|
-
};
|
|
729
817
|
const parseDateTime = (value, fallbackDate) => {
|
|
730
818
|
if (!value) return null;
|
|
731
819
|
const direct = new Date(value);
|
|
@@ -771,6 +859,118 @@ function BookingWidget({
|
|
|
771
859
|
const remaining = minutes % 60;
|
|
772
860
|
return remaining === 0 ? `${hours} hr${hours > 1 ? "s" : ""}` : `${hours} hr ${remaining} min`;
|
|
773
861
|
};
|
|
862
|
+
const todayString = useMemo4(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), []);
|
|
863
|
+
const todayDate = useMemo4(() => {
|
|
864
|
+
const now = /* @__PURE__ */ new Date();
|
|
865
|
+
now.setHours(0, 0, 0, 0);
|
|
866
|
+
return now;
|
|
867
|
+
}, []);
|
|
868
|
+
const disabledWeekdaysSet = useMemo4(() => new Set(disabledWeekdays), [disabledWeekdays]);
|
|
869
|
+
const getMonthStart = useCallback((date) => {
|
|
870
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
871
|
+
}, []);
|
|
872
|
+
const getMonthEnd = useCallback((date) => {
|
|
873
|
+
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
874
|
+
}, []);
|
|
875
|
+
const getMonthDays = useCallback(
|
|
876
|
+
(date) => {
|
|
877
|
+
const start = getMonthStart(date);
|
|
878
|
+
const end = getMonthEnd(date);
|
|
879
|
+
const days = [];
|
|
880
|
+
const startDay = start.getDay();
|
|
881
|
+
for (let i = startDay; i > 0; i -= 1) {
|
|
882
|
+
const d = new Date(start);
|
|
883
|
+
d.setDate(start.getDate() - i);
|
|
884
|
+
days.push({ date: d.toISOString().slice(0, 10), isCurrentMonth: false });
|
|
885
|
+
}
|
|
886
|
+
for (let day = 1; day <= end.getDate(); day += 1) {
|
|
887
|
+
const d = new Date(date.getFullYear(), date.getMonth(), day);
|
|
888
|
+
days.push({ date: d.toISOString().slice(0, 10), isCurrentMonth: true });
|
|
889
|
+
}
|
|
890
|
+
const remainder = 7 - (days.length % 7 || 7);
|
|
891
|
+
for (let i = 1; i <= remainder; i += 1) {
|
|
892
|
+
const d = new Date(end);
|
|
893
|
+
d.setDate(end.getDate() + i);
|
|
894
|
+
days.push({ date: d.toISOString().slice(0, 10), isCurrentMonth: false });
|
|
895
|
+
}
|
|
896
|
+
return days;
|
|
897
|
+
},
|
|
898
|
+
[getMonthEnd, getMonthStart]
|
|
899
|
+
);
|
|
900
|
+
const maxAdvanceDate = useMemo4(() => {
|
|
901
|
+
if (!maxAdvanceMonths || maxAdvanceMonths <= 0) return null;
|
|
902
|
+
const maxMonth = new Date(todayDate.getFullYear(), todayDate.getMonth() + maxAdvanceMonths - 1, 1);
|
|
903
|
+
return getMonthEnd(maxMonth);
|
|
904
|
+
}, [getMonthEnd, maxAdvanceMonths, todayDate]);
|
|
905
|
+
const maxAdvanceMonthStart = useMemo4(() => {
|
|
906
|
+
if (!maxAdvanceMonths || maxAdvanceMonths <= 0) return null;
|
|
907
|
+
return new Date(todayDate.getFullYear(), todayDate.getMonth() + maxAdvanceMonths - 1, 1);
|
|
908
|
+
}, [maxAdvanceMonths, todayDate]);
|
|
909
|
+
const handlePrevDates = () => {
|
|
910
|
+
if (availabilityStartDate <= todayString) return;
|
|
911
|
+
setAvailabilityStartDate((prev) => addDays(prev, -availabilityDays));
|
|
912
|
+
setSelectedDate(null);
|
|
913
|
+
setSelectedTime(null);
|
|
914
|
+
setSelectedEndTime(null);
|
|
915
|
+
setBookingError(null);
|
|
916
|
+
};
|
|
917
|
+
const handleNextDates = () => {
|
|
918
|
+
if (maxAdvanceDate) {
|
|
919
|
+
const nextStart = parseDateOnly(addDays(availabilityStartDate, availabilityDays));
|
|
920
|
+
if (nextStart && nextStart > maxAdvanceDate) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
setAvailabilityStartDate((prev) => addDays(prev, availabilityDays));
|
|
925
|
+
setSelectedDate(null);
|
|
926
|
+
setSelectedTime(null);
|
|
927
|
+
setSelectedEndTime(null);
|
|
928
|
+
setBookingError(null);
|
|
929
|
+
};
|
|
930
|
+
const handlePrevMonth = () => {
|
|
931
|
+
setCalendarMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1));
|
|
932
|
+
setSelectedDate(null);
|
|
933
|
+
setSelectedTime(null);
|
|
934
|
+
setSelectedEndTime(null);
|
|
935
|
+
setBookingError(null);
|
|
936
|
+
};
|
|
937
|
+
const handleNextMonth = () => {
|
|
938
|
+
if (maxAdvanceMonthStart) {
|
|
939
|
+
const nextMonth = new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() + 1, 1);
|
|
940
|
+
if (nextMonth > maxAdvanceMonthStart) return;
|
|
941
|
+
}
|
|
942
|
+
setCalendarMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1));
|
|
943
|
+
setSelectedDate(null);
|
|
944
|
+
setSelectedTime(null);
|
|
945
|
+
setSelectedEndTime(null);
|
|
946
|
+
setBookingError(null);
|
|
947
|
+
};
|
|
948
|
+
useEffect(() => {
|
|
949
|
+
if (step !== "time") return;
|
|
950
|
+
if (!availability.data?.dates?.length) return;
|
|
951
|
+
const matches = selectedDate ? availability.data.dates.some((entry) => entry.date === selectedDate) : false;
|
|
952
|
+
if (matches) return;
|
|
953
|
+
setSelectedDate(availability.data.dates[0].date);
|
|
954
|
+
}, [step, availability.data, selectedDate]);
|
|
955
|
+
useEffect(() => {
|
|
956
|
+
if (!selectedService) return;
|
|
957
|
+
if (datePickerMode === "calendar") {
|
|
958
|
+
const now = /* @__PURE__ */ new Date();
|
|
959
|
+
setCalendarMonth(new Date(now.getFullYear(), now.getMonth(), 1));
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
setAvailabilityStartDate((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
963
|
+
setAvailabilityDays(Math.max(1, availabilityDaysProp));
|
|
964
|
+
}, [selectedService?.id, selectedStaff?.id, availabilityDaysProp, datePickerMode]);
|
|
965
|
+
useEffect(() => {
|
|
966
|
+
if (datePickerMode !== "calendar") return;
|
|
967
|
+
const start = getMonthStart(calendarMonth);
|
|
968
|
+
const end = getMonthEnd(calendarMonth);
|
|
969
|
+
const startString = start.toISOString().slice(0, 10);
|
|
970
|
+
const daysInMonth = end.getDate();
|
|
971
|
+
setAvailabilityStartDate(startString);
|
|
972
|
+
setAvailabilityDays(daysInMonth);
|
|
973
|
+
}, [calendarMonth, datePickerMode, getMonthEnd, getMonthStart]);
|
|
774
974
|
const handleBack = () => {
|
|
775
975
|
if (step === "service") return;
|
|
776
976
|
if (step === "staff") {
|
|
@@ -840,10 +1040,10 @@ function BookingWidget({
|
|
|
840
1040
|
};
|
|
841
1041
|
const isServicesLoading = services ? false : servicesLoading;
|
|
842
1042
|
if (isServicesLoading) {
|
|
843
|
-
return /* @__PURE__ */
|
|
1043
|
+
return /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "Loading booking options..." });
|
|
844
1044
|
}
|
|
845
1045
|
if (!servicesList.length) {
|
|
846
|
-
return /* @__PURE__ */
|
|
1046
|
+
return /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "No services available." });
|
|
847
1047
|
}
|
|
848
1048
|
return /* @__PURE__ */ jsxs4(
|
|
849
1049
|
"div",
|
|
@@ -853,10 +1053,10 @@ function BookingWidget({
|
|
|
853
1053
|
children: [
|
|
854
1054
|
/* @__PURE__ */ jsxs4("div", { className: "idk-booking__header", children: [
|
|
855
1055
|
/* @__PURE__ */ jsxs4("div", { className: "idk-booking__title-wrap", children: [
|
|
856
|
-
/* @__PURE__ */
|
|
857
|
-
subtitle ? /* @__PURE__ */
|
|
1056
|
+
/* @__PURE__ */ jsx8("h3", { className: "idk-booking__title", children: title }),
|
|
1057
|
+
subtitle ? /* @__PURE__ */ jsx8("p", { className: "idk-booking__subtitle", children: subtitle }) : null
|
|
858
1058
|
] }),
|
|
859
|
-
step !== "service" && step !== "done" ? /* @__PURE__ */
|
|
1059
|
+
step !== "service" && step !== "done" ? /* @__PURE__ */ jsx8("button", { type: "button", className: "idk-link", onClick: handleBack, children: "Back" }) : null
|
|
860
1060
|
] }),
|
|
861
1061
|
step !== "done" ? /* @__PURE__ */ jsxs4("div", { className: "idk-booking__progress", children: [
|
|
862
1062
|
/* @__PURE__ */ jsxs4("span", { children: [
|
|
@@ -865,7 +1065,7 @@ function BookingWidget({
|
|
|
865
1065
|
" of ",
|
|
866
1066
|
totalSteps
|
|
867
1067
|
] }),
|
|
868
|
-
/* @__PURE__ */
|
|
1068
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-booking__bar", children: /* @__PURE__ */ jsx8(
|
|
869
1069
|
"div",
|
|
870
1070
|
{
|
|
871
1071
|
className: "idk-booking__bar-fill",
|
|
@@ -874,9 +1074,9 @@ function BookingWidget({
|
|
|
874
1074
|
) })
|
|
875
1075
|
] }) : null,
|
|
876
1076
|
step === "service" && /* @__PURE__ */ jsxs4("div", { className: "idk-booking__step", children: [
|
|
877
|
-
/* @__PURE__ */
|
|
1077
|
+
/* @__PURE__ */ jsx8("h4", { children: "Select a service" }),
|
|
878
1078
|
enableServiceSearch || enableCategoryFilter ? /* @__PURE__ */ jsxs4("div", { className: "idk-booking__filters", children: [
|
|
879
|
-
enableServiceSearch ? /* @__PURE__ */
|
|
1079
|
+
enableServiceSearch ? /* @__PURE__ */ jsx8(
|
|
880
1080
|
"input",
|
|
881
1081
|
{
|
|
882
1082
|
type: "search",
|
|
@@ -891,13 +1091,13 @@ function BookingWidget({
|
|
|
891
1091
|
value: categoryFilter,
|
|
892
1092
|
onChange: (event) => setCategoryFilter(event.target.value),
|
|
893
1093
|
children: [
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
categories.map((category) => /* @__PURE__ */
|
|
1094
|
+
/* @__PURE__ */ jsx8("option", { value: "all", children: categoryLabel }),
|
|
1095
|
+
categories.map((category) => /* @__PURE__ */ jsx8("option", { value: category, children: category }, category))
|
|
896
1096
|
]
|
|
897
1097
|
}
|
|
898
1098
|
) : null
|
|
899
1099
|
] }) : null,
|
|
900
|
-
filteredServices.length === 0 ? /* @__PURE__ */
|
|
1100
|
+
filteredServices.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "No services match your filters." }) : /* @__PURE__ */ jsx8(
|
|
901
1101
|
"div",
|
|
902
1102
|
{
|
|
903
1103
|
className: [
|
|
@@ -929,19 +1129,19 @@ function BookingWidget({
|
|
|
929
1129
|
},
|
|
930
1130
|
children: [
|
|
931
1131
|
/* @__PURE__ */ jsxs4("div", { className: "idk-card__header", children: [
|
|
932
|
-
/* @__PURE__ */
|
|
1132
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-card__title", children: service.name }),
|
|
933
1133
|
/* @__PURE__ */ jsxs4("span", { className: "idk-card__aside", children: [
|
|
934
|
-
isSelected ? /* @__PURE__ */
|
|
1134
|
+
isSelected ? /* @__PURE__ */ jsx8("span", { className: "idk-card__check", children: "\u2713" }) : null,
|
|
935
1135
|
typeof service.price === "number" && service.price > 0 ? /* @__PURE__ */ jsxs4("span", { className: "idk-card__price", children: [
|
|
936
1136
|
"$",
|
|
937
1137
|
(service.price / 100).toFixed(2)
|
|
938
1138
|
] }) : null
|
|
939
1139
|
] })
|
|
940
1140
|
] }),
|
|
941
|
-
service.description ? /* @__PURE__ */
|
|
1141
|
+
service.description ? /* @__PURE__ */ jsx8("div", { className: "idk-card__description", children: service.description }) : null,
|
|
942
1142
|
/* @__PURE__ */ jsxs4("div", { className: "idk-card__meta", children: [
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
service.category ? /* @__PURE__ */
|
|
1143
|
+
/* @__PURE__ */ jsx8("span", { children: formatDuration(service.duration) }),
|
|
1144
|
+
service.category ? /* @__PURE__ */ jsx8("span", { className: "idk-pill", children: service.category }) : null
|
|
945
1145
|
] })
|
|
946
1146
|
]
|
|
947
1147
|
},
|
|
@@ -951,7 +1151,7 @@ function BookingWidget({
|
|
|
951
1151
|
}
|
|
952
1152
|
),
|
|
953
1153
|
/* @__PURE__ */ jsxs4("div", { className: "idk-booking__actions", children: [
|
|
954
|
-
shouldPaginate ? /* @__PURE__ */
|
|
1154
|
+
shouldPaginate ? /* @__PURE__ */ jsx8(
|
|
955
1155
|
"button",
|
|
956
1156
|
{
|
|
957
1157
|
type: "button",
|
|
@@ -961,12 +1161,12 @@ function BookingWidget({
|
|
|
961
1161
|
children: hasMoreServices ? "Show more services" : "All services shown"
|
|
962
1162
|
}
|
|
963
1163
|
) : null,
|
|
964
|
-
selectedService && !autoAdvanceOnSelect ? /* @__PURE__ */
|
|
1164
|
+
selectedService && !autoAdvanceOnSelect ? /* @__PURE__ */ jsx8("button", { type: "button", className: "idk-button", onClick: handleServiceContinue, children: "Continue" }) : null
|
|
965
1165
|
] })
|
|
966
1166
|
] }),
|
|
967
1167
|
step === "staff" && /* @__PURE__ */ jsxs4("div", { className: "idk-booking__step", children: [
|
|
968
|
-
/* @__PURE__ */
|
|
969
|
-
staffOptions.length === 0 ? /* @__PURE__ */
|
|
1168
|
+
/* @__PURE__ */ jsx8("h4", { children: "Select a team member" }),
|
|
1169
|
+
staffOptions.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "No team members available." }) : /* @__PURE__ */ jsxs4(
|
|
970
1170
|
"div",
|
|
971
1171
|
{
|
|
972
1172
|
className: [
|
|
@@ -992,15 +1192,15 @@ function BookingWidget({
|
|
|
992
1192
|
},
|
|
993
1193
|
children: [
|
|
994
1194
|
/* @__PURE__ */ jsxs4("div", { className: "idk-card__header", children: [
|
|
995
|
-
/* @__PURE__ */
|
|
996
|
-
/* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-card__title", children: "Any Available" }),
|
|
1196
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-card__badge", children: "Recommended" })
|
|
997
1197
|
] }),
|
|
998
|
-
/* @__PURE__ */
|
|
1198
|
+
/* @__PURE__ */ jsx8("p", { className: "idk-card__description", children: "We'll match you with the first available stylist." })
|
|
999
1199
|
]
|
|
1000
1200
|
},
|
|
1001
1201
|
"any-staff"
|
|
1002
1202
|
) : null,
|
|
1003
|
-
|
|
1203
|
+
pagedStaff.map((staff) => /* @__PURE__ */ jsx8(
|
|
1004
1204
|
"button",
|
|
1005
1205
|
{
|
|
1006
1206
|
type: "button",
|
|
@@ -1017,11 +1217,11 @@ function BookingWidget({
|
|
|
1017
1217
|
}
|
|
1018
1218
|
},
|
|
1019
1219
|
children: /* @__PURE__ */ jsxs4("div", { className: "idk-team__card", children: [
|
|
1020
|
-
/* @__PURE__ */
|
|
1220
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-team__avatar", children: staff.photo?.url ? /* @__PURE__ */ jsx8("img", { src: staff.photo.url, alt: staff.name }) : /* @__PURE__ */ jsx8("span", { children: staff.name.split(" ").map((part) => part[0]).slice(0, 2).join("").toUpperCase() }) }),
|
|
1021
1221
|
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1022
|
-
/* @__PURE__ */
|
|
1023
|
-
/* @__PURE__ */
|
|
1024
|
-
staff.bio ? /* @__PURE__ */
|
|
1222
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-card__title", children: staff.name }),
|
|
1223
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-card__meta", children: staff.role || "Staff" }),
|
|
1224
|
+
staff.bio ? /* @__PURE__ */ jsx8("p", { className: "idk-card__description", children: renderLinkedText(staff.bio) }) : null
|
|
1025
1225
|
] })
|
|
1026
1226
|
] })
|
|
1027
1227
|
},
|
|
@@ -1031,7 +1231,17 @@ function BookingWidget({
|
|
|
1031
1231
|
}
|
|
1032
1232
|
),
|
|
1033
1233
|
/* @__PURE__ */ jsxs4("div", { className: "idk-booking__actions", children: [
|
|
1034
|
-
|
|
1234
|
+
shouldPaginateStaff ? /* @__PURE__ */ jsx8(
|
|
1235
|
+
"button",
|
|
1236
|
+
{
|
|
1237
|
+
type: "button",
|
|
1238
|
+
className: "idk-button idk-button--ghost",
|
|
1239
|
+
onClick: () => setStaffPage((prev) => hasMoreStaff ? prev + 1 : 1),
|
|
1240
|
+
disabled: !hasMoreStaff && staffPage === 1,
|
|
1241
|
+
children: hasMoreStaff ? "Show more staff" : staffPage > 1 ? "Show fewer staff" : "All staff shown"
|
|
1242
|
+
}
|
|
1243
|
+
) : null,
|
|
1244
|
+
canSkipStaff ? /* @__PURE__ */ jsx8(
|
|
1035
1245
|
"button",
|
|
1036
1246
|
{
|
|
1037
1247
|
type: "button",
|
|
@@ -1044,7 +1254,7 @@ function BookingWidget({
|
|
|
1044
1254
|
children: "Continue without selecting"
|
|
1045
1255
|
}
|
|
1046
1256
|
) : null,
|
|
1047
|
-
!autoAdvanceOnSelect ? /* @__PURE__ */
|
|
1257
|
+
!autoAdvanceOnSelect ? /* @__PURE__ */ jsx8(
|
|
1048
1258
|
"button",
|
|
1049
1259
|
{
|
|
1050
1260
|
type: "button",
|
|
@@ -1057,36 +1267,136 @@ function BookingWidget({
|
|
|
1057
1267
|
] })
|
|
1058
1268
|
] }),
|
|
1059
1269
|
step === "time" && /* @__PURE__ */ jsxs4("div", { className: "idk-booking__step", children: [
|
|
1060
|
-
/* @__PURE__ */
|
|
1061
|
-
availability.isLoading ? /* @__PURE__ */
|
|
1062
|
-
/* @__PURE__ */
|
|
1063
|
-
"
|
|
1270
|
+
/* @__PURE__ */ jsx8("h4", { children: "Select a time" }),
|
|
1271
|
+
availability.isLoading ? /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "Loading availability..." }) : availability.data ? /* @__PURE__ */ jsxs4("div", { className: ["idk-availability", timeLayout === "split" ? "idk-availability--split" : ""].join(" "), children: [
|
|
1272
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-availability__dates", children: datePickerMode === "calendar" ? /* @__PURE__ */ jsxs4(
|
|
1273
|
+
"div",
|
|
1064
1274
|
{
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1275
|
+
className: [
|
|
1276
|
+
"idk-calendar",
|
|
1277
|
+
calendarVariant === "compact" ? "idk-calendar--compact" : ""
|
|
1278
|
+
].filter(Boolean).join(" "),
|
|
1279
|
+
children: [
|
|
1280
|
+
/* @__PURE__ */ jsxs4("div", { className: "idk-calendar__header", children: [
|
|
1281
|
+
/* @__PURE__ */ jsx8(
|
|
1282
|
+
"button",
|
|
1283
|
+
{
|
|
1284
|
+
type: "button",
|
|
1285
|
+
className: "idk-button idk-button--ghost",
|
|
1286
|
+
onClick: handlePrevMonth,
|
|
1287
|
+
disabled: calendarMonth <= getMonthStart(todayDate),
|
|
1288
|
+
children: "Previous"
|
|
1289
|
+
}
|
|
1290
|
+
),
|
|
1291
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-calendar__title", children: new Intl.DateTimeFormat("en-US", {
|
|
1292
|
+
month: "long",
|
|
1293
|
+
year: "numeric"
|
|
1294
|
+
}).format(calendarMonth) }),
|
|
1295
|
+
/* @__PURE__ */ jsx8(
|
|
1296
|
+
"button",
|
|
1297
|
+
{
|
|
1298
|
+
type: "button",
|
|
1299
|
+
className: "idk-button idk-button--ghost",
|
|
1300
|
+
onClick: handleNextMonth,
|
|
1301
|
+
disabled: Boolean(maxAdvanceMonthStart && calendarMonth >= maxAdvanceMonthStart),
|
|
1302
|
+
children: "Next"
|
|
1303
|
+
}
|
|
1304
|
+
)
|
|
1305
|
+
] }),
|
|
1306
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-calendar__weekdays", children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((label) => /* @__PURE__ */ jsx8("span", { children: label }, label)) }),
|
|
1307
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-calendar__grid", children: getMonthDays(calendarMonth).map((day) => {
|
|
1308
|
+
const entry = availability.data?.dates?.find(
|
|
1309
|
+
(item) => item.date === day.date
|
|
1310
|
+
);
|
|
1311
|
+
const hasAvailable = entry?.slots?.some((slot) => slot.available) ?? false;
|
|
1312
|
+
const dateValue = parseDateOnly(day.date);
|
|
1313
|
+
const isPast = dateValue ? dateValue < todayDate : false;
|
|
1314
|
+
const isBeyondMax = maxAdvanceDate ? dateValue ? dateValue > maxAdvanceDate : false : false;
|
|
1315
|
+
const weekday = dateValue ? dateValue.getDay() : null;
|
|
1316
|
+
const isDisabledWeekday = weekday !== null ? disabledWeekdaysSet.has(weekday) : false;
|
|
1317
|
+
const isBlocked = Boolean(entry) && !hasAvailable;
|
|
1318
|
+
const isDisabled = !entry || isPast || isBeyondMax || isBlocked || isDisabledWeekday;
|
|
1319
|
+
const isActive = selectedDate === day.date;
|
|
1320
|
+
return /* @__PURE__ */ jsxs4(
|
|
1321
|
+
"button",
|
|
1322
|
+
{
|
|
1323
|
+
type: "button",
|
|
1324
|
+
className: [
|
|
1325
|
+
"idk-calendar__day",
|
|
1326
|
+
day.isCurrentMonth ? "" : "is-muted",
|
|
1327
|
+
hasAvailable ? "is-available" : "",
|
|
1328
|
+
isBlocked ? "is-blocked" : "",
|
|
1329
|
+
isDisabledWeekday ? "is-disabled" : "",
|
|
1330
|
+
isActive ? "is-active" : ""
|
|
1331
|
+
].filter(Boolean).join(" "),
|
|
1332
|
+
disabled: isDisabled,
|
|
1333
|
+
onClick: () => {
|
|
1334
|
+
setSelectedDate(day.date);
|
|
1335
|
+
setSelectedTime(null);
|
|
1336
|
+
setSelectedEndTime(null);
|
|
1337
|
+
setBookingError(null);
|
|
1338
|
+
},
|
|
1339
|
+
children: [
|
|
1340
|
+
/* @__PURE__ */ jsx8("span", { children: day.date.split("-")[2] }),
|
|
1341
|
+
showFullyBookedLabel && isBlocked && day.isCurrentMonth ? /* @__PURE__ */ jsx8("span", { className: "idk-calendar__label", children: calendarVariant === "compact" ? "Full" : "Fully booked" }) : null
|
|
1342
|
+
]
|
|
1343
|
+
},
|
|
1344
|
+
day.date
|
|
1345
|
+
);
|
|
1346
|
+
}) })
|
|
1347
|
+
]
|
|
1348
|
+
}
|
|
1349
|
+
) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1350
|
+
enableDatePaging ? /* @__PURE__ */ jsxs4("div", { className: "idk-availability__nav", children: [
|
|
1351
|
+
/* @__PURE__ */ jsx8(
|
|
1352
|
+
"button",
|
|
1353
|
+
{
|
|
1354
|
+
type: "button",
|
|
1355
|
+
className: "idk-button idk-button--ghost",
|
|
1356
|
+
onClick: handlePrevDates,
|
|
1357
|
+
disabled: availabilityStartDate <= todayString,
|
|
1358
|
+
children: "Previous"
|
|
1359
|
+
}
|
|
1360
|
+
),
|
|
1361
|
+
/* @__PURE__ */ jsx8(
|
|
1362
|
+
"button",
|
|
1363
|
+
{
|
|
1364
|
+
type: "button",
|
|
1365
|
+
className: "idk-button idk-button--ghost",
|
|
1366
|
+
onClick: handleNextDates,
|
|
1367
|
+
children: "Next"
|
|
1368
|
+
}
|
|
1369
|
+
)
|
|
1370
|
+
] }) : null,
|
|
1371
|
+
availability.data.dates.map((entry) => /* @__PURE__ */ jsx8(
|
|
1372
|
+
"button",
|
|
1373
|
+
{
|
|
1374
|
+
type: "button",
|
|
1375
|
+
className: entry.date === selectedDate ? "is-active" : void 0,
|
|
1376
|
+
onClick: () => {
|
|
1377
|
+
setSelectedDate(entry.date);
|
|
1378
|
+
setSelectedTime(null);
|
|
1379
|
+
setSelectedEndTime(null);
|
|
1380
|
+
setBookingError(null);
|
|
1381
|
+
},
|
|
1382
|
+
children: /* @__PURE__ */ jsx8("span", { className: "idk-date-chip__day", children: formatDateLabel(entry.date) })
|
|
1072
1383
|
},
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
)) }),
|
|
1384
|
+
entry.date
|
|
1385
|
+
))
|
|
1386
|
+
] }) }),
|
|
1077
1387
|
/* @__PURE__ */ jsxs4("div", { className: "idk-availability__panel", children: [
|
|
1078
|
-
/* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
/* @__PURE__ */
|
|
1388
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-availability__panel-header", children: /* @__PURE__ */ jsxs4("div", { children: [
|
|
1389
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-availability__panel-title", children: selectedDate ? formatDateHeading(selectedDate) : availability.data.dates[0]?.date ? formatDateHeading(availability.data.dates[0].date) : "Select a date" }),
|
|
1390
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-availability__panel-subtitle", children: timeZoneLabel })
|
|
1081
1391
|
] }) }),
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
const activeEntry = availability.data.dates.find((entry) => entry.date === selectedDate)
|
|
1084
|
-
if (!activeEntry) return /* @__PURE__ */
|
|
1392
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-availability__slots", children: (() => {
|
|
1393
|
+
const activeEntry = selectedDate ? availability.data.dates.find((entry) => entry.date === selectedDate) : availability.data.dates[0];
|
|
1394
|
+
if (!activeEntry) return /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "Select a date to see times." });
|
|
1085
1395
|
const slots = (activeEntry.slots || []).filter((slot) => slot.available);
|
|
1086
1396
|
if (slots.length === 0) {
|
|
1087
|
-
return /* @__PURE__ */
|
|
1397
|
+
return /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "No available times for this date." });
|
|
1088
1398
|
}
|
|
1089
|
-
return slots.map((slot) => /* @__PURE__ */
|
|
1399
|
+
return slots.map((slot, index) => /* @__PURE__ */ jsx8(
|
|
1090
1400
|
"button",
|
|
1091
1401
|
{
|
|
1092
1402
|
type: "button",
|
|
@@ -1104,12 +1414,12 @@ function BookingWidget({
|
|
|
1104
1414
|
},
|
|
1105
1415
|
children: formatTimeLabel(slot.time)
|
|
1106
1416
|
},
|
|
1107
|
-
`${slot.time}-${slot.endTime}`
|
|
1417
|
+
`${activeEntry.date}-${slot.time}-${slot.endTime}-${index}`
|
|
1108
1418
|
));
|
|
1109
1419
|
})() })
|
|
1110
1420
|
] })
|
|
1111
|
-
] }) : /* @__PURE__ */
|
|
1112
|
-
!autoAdvanceOnSelect ? /* @__PURE__ */
|
|
1421
|
+
] }) : /* @__PURE__ */ jsx8("div", { className: "idk-state", children: "No availability found." }),
|
|
1422
|
+
!autoAdvanceOnSelect ? /* @__PURE__ */ jsx8("div", { className: "idk-booking__actions", children: /* @__PURE__ */ jsx8(
|
|
1113
1423
|
"button",
|
|
1114
1424
|
{
|
|
1115
1425
|
type: "button",
|
|
@@ -1121,19 +1431,19 @@ function BookingWidget({
|
|
|
1121
1431
|
) }) : null
|
|
1122
1432
|
] }),
|
|
1123
1433
|
step === "details" && /* @__PURE__ */ jsxs4("form", { className: "idk-form", onSubmit: handleSubmit, children: [
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
bookingError ? /* @__PURE__ */
|
|
1434
|
+
/* @__PURE__ */ jsx8("h4", { children: "Your details" }),
|
|
1435
|
+
bookingError ? /* @__PURE__ */ jsx8("div", { className: "idk-booking__error", children: bookingError }) : null,
|
|
1126
1436
|
selectedService && selectedTime ? /* @__PURE__ */ jsxs4("div", { className: "idk-booking__summary", children: [
|
|
1127
1437
|
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1128
|
-
/* @__PURE__ */
|
|
1129
|
-
/* @__PURE__ */
|
|
1438
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-booking__summary-label", children: "Service" }),
|
|
1439
|
+
/* @__PURE__ */ jsx8("p", { children: selectedService.name })
|
|
1130
1440
|
] }),
|
|
1131
1441
|
selectedStaff ? /* @__PURE__ */ jsxs4("div", { children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-booking__summary-label", children: "Staff" }),
|
|
1443
|
+
/* @__PURE__ */ jsx8("p", { children: selectedStaff.name })
|
|
1134
1444
|
] }) : null,
|
|
1135
1445
|
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1136
|
-
/* @__PURE__ */
|
|
1446
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-booking__summary-label", children: "When" }),
|
|
1137
1447
|
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1138
1448
|
formatDateHeading(selectedDate || ""),
|
|
1139
1449
|
" \xB7 ",
|
|
@@ -1141,7 +1451,7 @@ function BookingWidget({
|
|
|
1141
1451
|
] })
|
|
1142
1452
|
] }),
|
|
1143
1453
|
selectedService.price ? /* @__PURE__ */ jsxs4("div", { children: [
|
|
1144
|
-
/* @__PURE__ */
|
|
1454
|
+
/* @__PURE__ */ jsx8("span", { className: "idk-booking__summary-label", children: "Price" }),
|
|
1145
1455
|
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1146
1456
|
"$",
|
|
1147
1457
|
(selectedService.price / 100).toFixed(2)
|
|
@@ -1149,8 +1459,8 @@ function BookingWidget({
|
|
|
1149
1459
|
] }) : null
|
|
1150
1460
|
] }) : null,
|
|
1151
1461
|
/* @__PURE__ */ jsxs4("label", { className: "idk-form__field", children: [
|
|
1152
|
-
/* @__PURE__ */
|
|
1153
|
-
/* @__PURE__ */
|
|
1462
|
+
/* @__PURE__ */ jsx8("span", { children: "Name" }),
|
|
1463
|
+
/* @__PURE__ */ jsx8(
|
|
1154
1464
|
"input",
|
|
1155
1465
|
{
|
|
1156
1466
|
value: details.name,
|
|
@@ -1160,8 +1470,8 @@ function BookingWidget({
|
|
|
1160
1470
|
)
|
|
1161
1471
|
] }),
|
|
1162
1472
|
/* @__PURE__ */ jsxs4("label", { className: "idk-form__field", children: [
|
|
1163
|
-
/* @__PURE__ */
|
|
1164
|
-
/* @__PURE__ */
|
|
1473
|
+
/* @__PURE__ */ jsx8("span", { children: "Email" }),
|
|
1474
|
+
/* @__PURE__ */ jsx8(
|
|
1165
1475
|
"input",
|
|
1166
1476
|
{
|
|
1167
1477
|
type: "email",
|
|
@@ -1172,8 +1482,8 @@ function BookingWidget({
|
|
|
1172
1482
|
)
|
|
1173
1483
|
] }),
|
|
1174
1484
|
/* @__PURE__ */ jsxs4("label", { className: "idk-form__field", children: [
|
|
1175
|
-
/* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1485
|
+
/* @__PURE__ */ jsx8("span", { children: "Phone" }),
|
|
1486
|
+
/* @__PURE__ */ jsx8(
|
|
1177
1487
|
"input",
|
|
1178
1488
|
{
|
|
1179
1489
|
value: details.phone,
|
|
@@ -1182,8 +1492,8 @@ function BookingWidget({
|
|
|
1182
1492
|
)
|
|
1183
1493
|
] }),
|
|
1184
1494
|
/* @__PURE__ */ jsxs4("label", { className: "idk-form__field", children: [
|
|
1185
|
-
/* @__PURE__ */
|
|
1186
|
-
/* @__PURE__ */
|
|
1495
|
+
/* @__PURE__ */ jsx8("span", { children: "Notes" }),
|
|
1496
|
+
/* @__PURE__ */ jsx8(
|
|
1187
1497
|
"textarea",
|
|
1188
1498
|
{
|
|
1189
1499
|
rows: 4,
|
|
@@ -1192,12 +1502,12 @@ function BookingWidget({
|
|
|
1192
1502
|
}
|
|
1193
1503
|
)
|
|
1194
1504
|
] }),
|
|
1195
|
-
/* @__PURE__ */
|
|
1505
|
+
/* @__PURE__ */ jsx8("button", { type: "submit", className: "idk-button", disabled: createBooking.isPending, children: createBooking.isPending ? "Booking..." : "Confirm booking" })
|
|
1196
1506
|
] }),
|
|
1197
1507
|
step === "done" && /* @__PURE__ */ jsxs4("div", { className: "idk-booking__done", children: [
|
|
1198
|
-
/* @__PURE__ */
|
|
1199
|
-
/* @__PURE__ */
|
|
1200
|
-
/* @__PURE__ */
|
|
1508
|
+
/* @__PURE__ */ jsx8("div", { className: "idk-booking__done-title", children: "Booking confirmed!" }),
|
|
1509
|
+
/* @__PURE__ */ jsx8("p", { className: "idk-booking__done-text", children: "We'll send a confirmation email shortly with your appointment details." }),
|
|
1510
|
+
/* @__PURE__ */ jsx8(
|
|
1201
1511
|
"button",
|
|
1202
1512
|
{
|
|
1203
1513
|
type: "button",
|
|
@@ -1220,7 +1530,7 @@ function BookingWidget({
|
|
|
1220
1530
|
|
|
1221
1531
|
// src/components/AvailabilityPicker.tsx
|
|
1222
1532
|
import { useState as useState4 } from "react";
|
|
1223
|
-
import { jsx as
|
|
1533
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1224
1534
|
function AvailabilityPicker({
|
|
1225
1535
|
serviceId,
|
|
1226
1536
|
staffId,
|
|
@@ -1238,15 +1548,15 @@ function AvailabilityPicker({
|
|
|
1238
1548
|
days
|
|
1239
1549
|
});
|
|
1240
1550
|
if (isLoading) {
|
|
1241
|
-
return /* @__PURE__ */
|
|
1551
|
+
return /* @__PURE__ */ jsx9("div", { className: "idk-state", children: "Loading availability..." });
|
|
1242
1552
|
}
|
|
1243
1553
|
if (!data?.dates?.length) {
|
|
1244
|
-
return /* @__PURE__ */
|
|
1554
|
+
return /* @__PURE__ */ jsx9("div", { className: "idk-state", children: "No availability found." });
|
|
1245
1555
|
}
|
|
1246
1556
|
const activeDate = selectedDate || data.dates[0]?.date;
|
|
1247
1557
|
const activeSlots = data.dates.find((entry) => entry.date === activeDate)?.slots || [];
|
|
1248
1558
|
return /* @__PURE__ */ jsxs5("div", { className: ["idk-availability", className].filter(Boolean).join(" "), children: [
|
|
1249
|
-
/* @__PURE__ */
|
|
1559
|
+
/* @__PURE__ */ jsx9("div", { className: "idk-availability__dates", children: data.dates.map((entry) => /* @__PURE__ */ jsx9(
|
|
1250
1560
|
"button",
|
|
1251
1561
|
{
|
|
1252
1562
|
type: "button",
|
|
@@ -1259,7 +1569,7 @@ function AvailabilityPicker({
|
|
|
1259
1569
|
},
|
|
1260
1570
|
entry.date
|
|
1261
1571
|
)) }),
|
|
1262
|
-
/* @__PURE__ */
|
|
1572
|
+
/* @__PURE__ */ jsx9("div", { className: "idk-availability__slots", children: activeSlots.filter((slot) => slot.available).length === 0 ? /* @__PURE__ */ jsx9("div", { className: "idk-state", children: "No slots available." }) : activeSlots.filter((slot) => slot.available).map((slot) => /* @__PURE__ */ jsx9(
|
|
1263
1573
|
"button",
|
|
1264
1574
|
{
|
|
1265
1575
|
type: "button",
|
|
@@ -1282,21 +1592,21 @@ function AvailabilityPicker({
|
|
|
1282
1592
|
}
|
|
1283
1593
|
|
|
1284
1594
|
// src/components/ServicePicker.tsx
|
|
1285
|
-
import { jsx as
|
|
1595
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1286
1596
|
function ServicePicker({
|
|
1287
1597
|
services,
|
|
1288
1598
|
selectedId,
|
|
1289
1599
|
onSelect,
|
|
1290
1600
|
className
|
|
1291
1601
|
}) {
|
|
1292
|
-
return /* @__PURE__ */
|
|
1602
|
+
return /* @__PURE__ */ jsx10("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: services.map((service) => /* @__PURE__ */ jsxs6(
|
|
1293
1603
|
"button",
|
|
1294
1604
|
{
|
|
1295
1605
|
type: "button",
|
|
1296
1606
|
className: selectedId === service.id ? "is-active" : void 0,
|
|
1297
1607
|
onClick: () => onSelect?.(service),
|
|
1298
1608
|
children: [
|
|
1299
|
-
/* @__PURE__ */
|
|
1609
|
+
/* @__PURE__ */ jsx10("span", { children: service.name }),
|
|
1300
1610
|
/* @__PURE__ */ jsxs6("small", { children: [
|
|
1301
1611
|
service.duration,
|
|
1302
1612
|
" min"
|
|
@@ -1308,22 +1618,22 @@ function ServicePicker({
|
|
|
1308
1618
|
}
|
|
1309
1619
|
|
|
1310
1620
|
// src/components/StaffPicker.tsx
|
|
1311
|
-
import { jsx as
|
|
1621
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1312
1622
|
function StaffPicker({
|
|
1313
1623
|
staff,
|
|
1314
1624
|
selectedId,
|
|
1315
1625
|
onSelect,
|
|
1316
1626
|
className
|
|
1317
1627
|
}) {
|
|
1318
|
-
return /* @__PURE__ */
|
|
1628
|
+
return /* @__PURE__ */ jsx11("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: staff.map((member) => /* @__PURE__ */ jsxs7(
|
|
1319
1629
|
"button",
|
|
1320
1630
|
{
|
|
1321
1631
|
type: "button",
|
|
1322
1632
|
className: selectedId === member.id ? "is-active" : void 0,
|
|
1323
1633
|
onClick: () => onSelect?.(member),
|
|
1324
1634
|
children: [
|
|
1325
|
-
/* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1635
|
+
/* @__PURE__ */ jsx11("span", { children: member.name }),
|
|
1636
|
+
/* @__PURE__ */ jsx11("small", { children: member.role || "Staff" })
|
|
1327
1637
|
]
|
|
1328
1638
|
},
|
|
1329
1639
|
member.id
|
|
@@ -1331,14 +1641,14 @@ function StaffPicker({
|
|
|
1331
1641
|
}
|
|
1332
1642
|
|
|
1333
1643
|
// src/components/DatePicker.tsx
|
|
1334
|
-
import { jsx as
|
|
1644
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1335
1645
|
function DatePicker({
|
|
1336
1646
|
dates,
|
|
1337
1647
|
selectedDate,
|
|
1338
1648
|
onSelect,
|
|
1339
1649
|
className
|
|
1340
1650
|
}) {
|
|
1341
|
-
return /* @__PURE__ */
|
|
1651
|
+
return /* @__PURE__ */ jsx12("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: dates.map((date) => /* @__PURE__ */ jsx12(
|
|
1342
1652
|
"button",
|
|
1343
1653
|
{
|
|
1344
1654
|
type: "button",
|
|
@@ -1351,14 +1661,14 @@ function DatePicker({
|
|
|
1351
1661
|
}
|
|
1352
1662
|
|
|
1353
1663
|
// src/components/TimePicker.tsx
|
|
1354
|
-
import { jsx as
|
|
1664
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1355
1665
|
function TimePicker({
|
|
1356
1666
|
slots,
|
|
1357
1667
|
selectedTime,
|
|
1358
1668
|
onSelect,
|
|
1359
1669
|
className
|
|
1360
1670
|
}) {
|
|
1361
|
-
return /* @__PURE__ */
|
|
1671
|
+
return /* @__PURE__ */ jsx13("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: slots.map((slot) => /* @__PURE__ */ jsx13(
|
|
1362
1672
|
"button",
|
|
1363
1673
|
{
|
|
1364
1674
|
type: "button",
|