@reblu/site-blocks 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -183,7 +183,7 @@ function Collection({
183
183
  ] })
184
184
  ] });
185
185
  }
186
- var PAGE_BTN_CLASS = "inline-flex h-9 items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
186
+ var PAGE_BTN_CLASS = "inline-flex h-9 cursor-pointer items-center rounded-md border border-border px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
187
187
  function FacetChip({ label, pressed, color, onClick }) {
188
188
  return /* @__PURE__ */ jsx(
189
189
  "button",
@@ -193,7 +193,7 @@ function FacetChip({ label, pressed, color, onClick }) {
193
193
  onClick,
194
194
  style: color ? { "--facet-color": color } : void 0,
195
195
  className: cn(
196
- "inline-flex items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
196
+ "inline-flex cursor-pointer items-center rounded-full border px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
197
197
  pressed ? color ? "border-transparent bg-[var(--facet-color)] text-white" : "border-transparent bg-primary text-primary-foreground" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
198
198
  ),
199
199
  children: label
@@ -278,7 +278,7 @@ function ShareButtons({ url, title, className, labels }) {
278
278
  {
279
279
  type: "button",
280
280
  onClick: handleCopy,
281
- className: cn2(LINK_CLASS, "w-auto gap-2 px-3", copied && "border-primary text-primary"),
281
+ className: cn2(LINK_CLASS, "w-auto cursor-pointer gap-2 px-3", copied && "border-primary text-primary"),
282
282
  "aria-label": t.copy,
283
283
  children: [
284
284
  copied ? /* @__PURE__ */ jsx2(CheckIcon, {}) : /* @__PURE__ */ jsx2(CopyIcon, {}),
@@ -399,7 +399,7 @@ function AddToCartButton({
399
399
  "data-slot": "add-to-cart-button",
400
400
  "data-status": status,
401
401
  className: cn3(
402
- "inline-flex items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
402
+ "inline-flex cursor-pointer items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
403
403
  isDisabled ? "cursor-not-allowed bg-muted text-muted-foreground" : "bg-primary text-primary-foreground hover:bg-primary/90",
404
404
  status === "error" && "bg-destructive text-destructive-foreground",
405
405
  className
@@ -415,7 +415,7 @@ import { cn as cn4 } from "@reblu/site-ui/utils";
415
415
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
416
416
  var DEFAULT_LABELS4 = {
417
417
  cart: "Carrito",
418
- itemsSuffix: (count) => `${count} art\xEDculo${count === 1 ? "" : "s"}`
418
+ itemsSuffix: (count2) => `${count2} art\xEDculo${count2 === 1 ? "" : "s"}`
419
419
  };
420
420
  var BUMP_DURATION_MS = 400;
421
421
  function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }) {
@@ -484,13 +484,404 @@ function CartIcon({ itemCount, href, hydrated, bumpOnAdd, labels, className }) {
484
484
  );
485
485
  }
486
486
 
487
- // src/components/data-request-form.tsx
488
- import { useEffect as useEffect3, useId, useRef as useRef3, useState as useState5 } from "react";
487
+ // src/components/product-carousel.tsx
488
+ import { useCallback as useCallback2, useEffect as useEffect3, useId, useRef as useRef3, useState as useState5 } from "react";
489
+ import { buttonVariants } from "@reblu/site-ui/button-variants";
490
+ import { cn as cn6 } from "@reblu/site-ui/utils";
491
+
492
+ // src/components/_heading.tsx
493
+ import { jsx as jsx5 } from "react/jsx-runtime";
494
+ function Heading({
495
+ level,
496
+ className,
497
+ children
498
+ }) {
499
+ const Tag = `h${level}`;
500
+ return /* @__PURE__ */ jsx5(Tag, { className, children });
501
+ }
502
+
503
+ // src/components/product-card.tsx
489
504
  import { cn as cn5 } from "@reblu/site-ui/utils";
505
+
506
+ // src/lib/format-price.ts
507
+ function formatPrice(cents, currency = "EUR", locale = "es-ES") {
508
+ return new Intl.NumberFormat(locale, {
509
+ style: "currency",
510
+ currency
511
+ }).format(cents / 100);
512
+ }
513
+
514
+ // src/components/product-card.tsx
515
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
516
+ function ProductCard({ product, href, currencyCode = "EUR", locale, className }) {
517
+ const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
518
+ const hasDiscount = product.comparePrice != null && product.comparePrice > product.price;
519
+ return /* @__PURE__ */ jsxs4(
520
+ "article",
521
+ {
522
+ className: cn5(
523
+ "group flex flex-col overflow-hidden rounded-lg border bg-card transition-shadow hover:shadow-lg",
524
+ className
525
+ ),
526
+ "data-slot": "product-card",
527
+ children: [
528
+ /* @__PURE__ */ jsx6("div", { className: "relative aspect-square overflow-hidden bg-muted", children: image ? /* @__PURE__ */ jsx6(
529
+ "img",
530
+ {
531
+ src: image.url,
532
+ alt: image.alt ?? product.name,
533
+ loading: "lazy",
534
+ className: "h-full w-full object-cover transition-transform group-hover:scale-105"
535
+ }
536
+ ) : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center text-4xl text-muted-foreground/30", "aria-hidden": "true", children: "\u{1F6CD}\uFE0F" }) }),
537
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-1 flex-col gap-2 p-4", children: [
538
+ product.category && /* @__PURE__ */ jsx6("span", { className: "inline-flex w-fit items-center rounded-full bg-secondary px-2.5 py-0.5 text-xs font-medium text-secondary-foreground", children: product.category.name }),
539
+ /* @__PURE__ */ jsx6("h3", { className: "text-base font-semibold leading-tight", children: href ? /* @__PURE__ */ jsx6("a", { href, className: "transition-colors hover:text-primary", children: product.name }) : product.name }),
540
+ product.shortDescription && /* @__PURE__ */ jsx6("p", { className: "line-clamp-2 text-sm text-muted-foreground", children: product.shortDescription }),
541
+ /* @__PURE__ */ jsxs4("div", { className: "mt-auto flex items-baseline gap-2 pt-2", children: [
542
+ /* @__PURE__ */ jsx6("span", { className: "text-lg font-bold text-foreground", children: formatPrice(product.price, currencyCode, locale) }),
543
+ hasDiscount && /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground line-through", children: formatPrice(product.comparePrice, currencyCode, locale) })
544
+ ] })
545
+ ] })
546
+ ]
547
+ }
548
+ );
549
+ }
550
+
551
+ // src/components/product-carousel.tsx
552
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
553
+ var DEFAULT_PRODUCT_CAROUSEL_LABELS = {
554
+ region: "Productos relacionados",
555
+ roleDescription: "carrusel",
556
+ previous: "Anterior",
557
+ next: "Siguiente"
558
+ };
559
+ var EDGE_EPSILON = 1;
560
+ function prefersReducedMotion() {
561
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false;
562
+ }
563
+ function ChevronIcon({ direction }) {
564
+ return /* @__PURE__ */ jsx7(
565
+ "svg",
566
+ {
567
+ "aria-hidden": "true",
568
+ viewBox: "0 0 24 24",
569
+ fill: "none",
570
+ stroke: "currentColor",
571
+ strokeWidth: 2,
572
+ strokeLinecap: "round",
573
+ strokeLinejoin: "round",
574
+ className: "rtl:-scale-x-100",
575
+ children: /* @__PURE__ */ jsx7("path", { d: direction === "left" ? "m15 18-6-6 6-6" : "m9 18 6-6-6-6" })
576
+ }
577
+ );
578
+ }
579
+ function ProductCarousel(props) {
580
+ const { products, heading, headingLevel = 2, headingClassName, labels, className, itemClassName } = props;
581
+ const t = { ...DEFAULT_PRODUCT_CAROUSEL_LABELS, ...labels };
582
+ const trackId = useId();
583
+ const headingId = useId();
584
+ const trackRef = useRef3(null);
585
+ const [canPrev, setCanPrev] = useState5(false);
586
+ const [canNext, setCanNext] = useState5(false);
587
+ const update = useCallback2(() => {
588
+ const el = trackRef.current;
589
+ if (!el) return;
590
+ const max = el.scrollWidth - el.clientWidth;
591
+ const pos = Math.abs(el.scrollLeft);
592
+ setCanPrev(pos > EDGE_EPSILON);
593
+ setCanNext(pos < max - EDGE_EPSILON);
594
+ }, []);
595
+ useEffect3(() => {
596
+ const el = trackRef.current;
597
+ if (!el) return;
598
+ update();
599
+ el.addEventListener("scroll", update, { passive: true });
600
+ let observer;
601
+ if (typeof ResizeObserver !== "undefined") {
602
+ observer = new ResizeObserver(update);
603
+ observer.observe(el);
604
+ } else {
605
+ window.addEventListener("resize", update);
606
+ }
607
+ return () => {
608
+ el.removeEventListener("scroll", update);
609
+ if (observer) observer.disconnect();
610
+ else window.removeEventListener("resize", update);
611
+ };
612
+ }, [update, products.length]);
613
+ const scrollByPage = (direction) => {
614
+ const el = trackRef.current;
615
+ if (!el) return;
616
+ const rtl = typeof getComputedStyle === "function" && getComputedStyle(el).direction === "rtl";
617
+ el.scrollBy({
618
+ left: direction * el.clientWidth * (rtl ? -1 : 1),
619
+ behavior: prefersReducedMotion() ? "auto" : "smooth"
620
+ });
621
+ };
622
+ if (products.length === 0) return null;
623
+ const scrollable = canPrev || canNext;
624
+ return /* @__PURE__ */ jsxs5("section", { "data-slot": "product-carousel", className: cn6("flex flex-col gap-4", className), children: [
625
+ (heading || scrollable) && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between gap-4", children: [
626
+ heading ? /* @__PURE__ */ jsx7("div", { id: headingId, children: /* @__PURE__ */ jsx7(Heading, { level: headingLevel, className: headingClassName ?? "text-xl font-semibold", children: heading }) }) : /* @__PURE__ */ jsx7("span", {}),
627
+ scrollable && /* @__PURE__ */ jsxs5("div", { className: "hidden gap-2 sm:flex", "data-slot": "product-carousel-controls", children: [
628
+ /* @__PURE__ */ jsx7(
629
+ "button",
630
+ {
631
+ type: "button",
632
+ className: cn6(buttonVariants({ variant: "outline", size: "icon" }), "rounded-full"),
633
+ "aria-label": t.previous,
634
+ "aria-controls": trackId,
635
+ disabled: !canPrev,
636
+ onClick: () => scrollByPage(-1),
637
+ children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "left" })
638
+ }
639
+ ),
640
+ /* @__PURE__ */ jsx7(
641
+ "button",
642
+ {
643
+ type: "button",
644
+ className: cn6(buttonVariants({ variant: "outline", size: "icon" }), "rounded-full"),
645
+ "aria-label": t.next,
646
+ "aria-controls": trackId,
647
+ disabled: !canNext,
648
+ onClick: () => scrollByPage(1),
649
+ children: /* @__PURE__ */ jsx7(ChevronIcon, { direction: "right" })
650
+ }
651
+ )
652
+ ] })
653
+ ] }),
654
+ /* @__PURE__ */ jsx7(
655
+ "div",
656
+ {
657
+ ref: trackRef,
658
+ id: trackId,
659
+ role: "region",
660
+ "aria-roledescription": t.roleDescription,
661
+ ...heading ? { "aria-labelledby": headingId } : { "aria-label": t.region },
662
+ tabIndex: 0,
663
+ "data-slot": "product-carousel-track",
664
+ className: "snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth rounded-lg pb-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:scroll-auto",
665
+ children: /* @__PURE__ */ jsx7("ul", { role: "list", className: "m-0 flex list-none gap-4 p-0", children: products.map((product, index) => /* @__PURE__ */ jsx7(
666
+ "li",
667
+ {
668
+ className: cn6(
669
+ "shrink-0 snap-start basis-[calc((100%-1rem)/1.5)] sm:basis-[calc((100%-2rem)/3)] lg:basis-[calc((100%-3rem)/4)]",
670
+ itemClassName
671
+ ),
672
+ children: props.renderProduct ? props.renderProduct(product, index) : /* @__PURE__ */ jsx7(
673
+ ProductCard,
674
+ {
675
+ product,
676
+ href: props.productHref?.(product),
677
+ currencyCode: props.currencyCode,
678
+ locale: props.locale,
679
+ className: "h-full"
680
+ }
681
+ )
682
+ },
683
+ product.id
684
+ )) })
685
+ }
686
+ )
687
+ ] });
688
+ }
689
+
690
+ // src/components/course-syllabus.tsx
691
+ import { useId as useId2, useState as useState6 } from "react";
692
+ import { cn as cn8 } from "@reblu/site-ui/utils";
693
+
694
+ // src/lib/slot-class-name.ts
695
+ import { cn as cn7 } from "@reblu/site-ui/utils";
696
+ var MERGE_SAFE_TEXT = /(?:^|:)!?text-(?:left|center|right|justify|start|end|wrap|nowrap|balance|pretty|ellipsis|clip)$/;
697
+ var TEXT_UTILITY = /(?:^|:)!?-?text-/;
698
+ function bypassesMerge(token) {
699
+ return TEXT_UTILITY.test(token) && !MERGE_SAFE_TEXT.test(token);
700
+ }
701
+ function slotClassName(structural, defaults, consumer) {
702
+ if (consumer === void 0 || consumer.trim() === "") return cn7(structural, defaults);
703
+ const tokens = consumer.split(/\s+/).filter(Boolean);
704
+ const verbatim = tokens.filter(bypassesMerge);
705
+ const merged = cn7(structural, tokens.filter((token) => !bypassesMerge(token)));
706
+ return [merged, ...verbatim].filter(Boolean).join(" ");
707
+ }
708
+
709
+ // src/components/course-syllabus.tsx
710
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
711
+ var DEFAULT_COURSE_SYLLABUS_LABELS = {
712
+ heading: "Programa",
713
+ moduleSingular: "m\xF3dulo",
714
+ modulePlural: "m\xF3dulos",
715
+ lessonSingular: "lecci\xF3n",
716
+ lessonPlural: "lecciones",
717
+ freePreview: "Vista previa gratuita",
718
+ hours: "h",
719
+ minutes: "min"
720
+ };
721
+ var SEPARATOR = " \xB7 ";
722
+ function count(n, singular, plural) {
723
+ return `${n} ${n === 1 ? singular : plural}`;
724
+ }
725
+ function formatDuration(seconds, labels) {
726
+ const totalMinutes = Math.max(1, Math.round(seconds / 60));
727
+ const hours = Math.floor(totalMinutes / 60);
728
+ const minutes = totalMinutes % 60;
729
+ if (hours === 0) return `${minutes} ${labels.minutes}`;
730
+ return minutes === 0 ? `${hours} ${labels.hours}` : `${hours} ${labels.hours} ${minutes} ${labels.minutes}`;
731
+ }
732
+ function moduleSeconds(lessons) {
733
+ let total = 0;
734
+ for (const l of lessons) {
735
+ if (typeof l.durationSeconds !== "number" || l.durationSeconds <= 0) return null;
736
+ total += l.durationSeconds;
737
+ }
738
+ return total;
739
+ }
740
+ function CourseSyllabus({
741
+ modules,
742
+ labels: labelOverrides,
743
+ headingLevel = 2,
744
+ headingClassName,
745
+ className
746
+ }) {
747
+ const labels = { ...DEFAULT_COURSE_SYLLABUS_LABELS, ...labelOverrides };
748
+ const baseId = useId2();
749
+ const [open, setOpen] = useState6(() => /* @__PURE__ */ new Set());
750
+ if (modules.length === 0) return null;
751
+ const totalLessons = modules.reduce((sum, m) => sum + (m.lessons?.length ?? 0), 0);
752
+ const summary = [count(modules.length, labels.moduleSingular, labels.modulePlural)];
753
+ if (totalLessons > 0)
754
+ summary.push(count(totalLessons, labels.lessonSingular, labels.lessonPlural));
755
+ const headingId = `${baseId}-heading`;
756
+ const Tag = `h${headingLevel}`;
757
+ function toggle(id) {
758
+ setOpen((prev) => {
759
+ const next = new Set(prev);
760
+ if (next.has(id)) next.delete(id);
761
+ else next.add(id);
762
+ return next;
763
+ });
764
+ }
765
+ return /* @__PURE__ */ jsxs6(
766
+ "section",
767
+ {
768
+ "data-slot": "course-syllabus",
769
+ "aria-labelledby": headingId,
770
+ className: cn8("space-y-4", className),
771
+ children: [
772
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
773
+ /* @__PURE__ */ jsx8(
774
+ Tag,
775
+ {
776
+ id: headingId,
777
+ className: slotClassName("", "text-xl font-semibold", headingClassName),
778
+ children: labels.heading
779
+ }
780
+ ),
781
+ /* @__PURE__ */ jsx8("p", { "data-slot": "course-syllabus-summary", className: "text-sm text-muted-foreground", children: summary.join(SEPARATOR) })
782
+ ] }),
783
+ /* @__PURE__ */ jsx8("ol", { className: "flex list-none flex-col gap-2.5 p-0", children: modules.map((module, index) => {
784
+ const lessons = module.lessons ?? [];
785
+ const number = /* @__PURE__ */ jsx8(
786
+ "span",
787
+ {
788
+ "aria-hidden": "true",
789
+ className: "flex size-7 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-primary",
790
+ children: index + 1
791
+ }
792
+ );
793
+ if (lessons.length === 0) {
794
+ return /* @__PURE__ */ jsxs6(
795
+ "li",
796
+ {
797
+ "data-slot": "course-syllabus-module",
798
+ className: "flex items-center gap-3.5 rounded-lg border border-border bg-card px-4 py-3.5",
799
+ children: [
800
+ number,
801
+ /* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title })
802
+ ]
803
+ },
804
+ module.id
805
+ );
806
+ }
807
+ const isOpen = open.has(module.id);
808
+ const panelId = `${baseId}-module-${index}`;
809
+ const seconds = moduleSeconds(lessons);
810
+ const meta = [count(lessons.length, labels.lessonSingular, labels.lessonPlural)];
811
+ if (seconds !== null) meta.push(formatDuration(seconds, labels));
812
+ return /* @__PURE__ */ jsxs6(
813
+ "li",
814
+ {
815
+ "data-slot": "course-syllabus-module",
816
+ className: "rounded-lg border border-border bg-card",
817
+ children: [
818
+ /* @__PURE__ */ jsxs6(
819
+ "button",
820
+ {
821
+ type: "button",
822
+ "aria-expanded": isOpen,
823
+ "aria-controls": panelId,
824
+ onClick: () => toggle(module.id),
825
+ className: "flex w-full cursor-pointer items-center gap-3.5 rounded-lg px-4 py-3.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
826
+ children: [
827
+ number,
828
+ /* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
829
+ /* @__PURE__ */ jsx8("span", { className: "font-semibold leading-snug", children: module.title }),
830
+ /* @__PURE__ */ jsx8("span", { className: "text-sm text-muted-foreground", children: meta.join(SEPARATOR) })
831
+ ] }),
832
+ /* @__PURE__ */ jsx8(
833
+ "span",
834
+ {
835
+ "aria-hidden": "true",
836
+ className: cn8(
837
+ "shrink-0 text-muted-foreground transition-transform motion-reduce:transition-none",
838
+ isOpen && "rotate-180"
839
+ ),
840
+ children: "\u25BE"
841
+ }
842
+ )
843
+ ]
844
+ }
845
+ ),
846
+ /* @__PURE__ */ jsx8(
847
+ "ol",
848
+ {
849
+ id: panelId,
850
+ hidden: !isOpen,
851
+ "data-slot": "course-syllabus-lessons",
852
+ className: "list-none space-y-2 border-t border-border px-4 py-3 pl-[3.75rem]",
853
+ children: lessons.map((lesson) => /* @__PURE__ */ jsxs6(
854
+ "li",
855
+ {
856
+ "data-slot": "course-syllabus-lesson",
857
+ className: "flex flex-wrap items-baseline gap-x-3 gap-y-1 text-sm",
858
+ children: [
859
+ /* @__PURE__ */ jsx8("span", { "data-slot": "course-syllabus-lesson-title", className: "flex-1", children: lesson.title }),
860
+ lesson.isFreePreview && /* @__PURE__ */ jsx8("span", { className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium", children: labels.freePreview }),
861
+ typeof lesson.durationSeconds === "number" && lesson.durationSeconds > 0 && /* @__PURE__ */ jsx8("span", { className: "text-muted-foreground tabular-nums", children: formatDuration(lesson.durationSeconds, labels) })
862
+ ]
863
+ },
864
+ lesson.id
865
+ ))
866
+ }
867
+ )
868
+ ]
869
+ },
870
+ module.id
871
+ );
872
+ }) })
873
+ ]
874
+ }
875
+ );
876
+ }
877
+
878
+ // src/components/data-request-form.tsx
879
+ import { useEffect as useEffect4, useId as useId3, useRef as useRef4, useState as useState7 } from "react";
880
+ import { cn as cn9 } from "@reblu/site-ui/utils";
490
881
  import {
491
882
  DATA_REQUEST_TYPES
492
883
  } from "@reblu/site-contracts";
493
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
884
+ import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
494
885
  var DATA_REQUEST_FIELD_LIMITS = Object.freeze({ name: 200, email: 320, details: 5e3 });
495
886
  var DEFAULT_DATA_REQUEST_FORM_LABELS = {
496
887
  requestTypeLabel: "Derecho que quieres ejercer",
@@ -516,7 +907,7 @@ var DEFAULT_DATA_REQUEST_FORM_LABELS = {
516
907
  nameRequired: "Indica tu nombre.",
517
908
  emailInvalid: "Introduce un email v\xE1lido.",
518
909
  consentRequired: "Debes aceptar el tratamiento de tus datos para gestionar la solicitud.",
519
- success: (deadline) => /* @__PURE__ */ jsxs4(Fragment, { children: [
910
+ success: (deadline) => /* @__PURE__ */ jsxs7(Fragment, { children: [
520
911
  "Hemos recibido tu solicitud. Te responderemos como m\xE1ximo el ",
521
912
  deadline,
522
913
  "."
@@ -565,18 +956,18 @@ function DataRequestForm({
565
956
  ...labels,
566
957
  requestTypes: { ...DEFAULT_DATA_REQUEST_FORM_LABELS.requestTypes, ...labels?.requestTypes }
567
958
  };
568
- const id = useId();
569
- const [requestType, setRequestType] = useState5("");
570
- const [name, setName] = useState5("");
571
- const [email, setEmail] = useState5("");
572
- const [details, setDetails] = useState5("");
573
- const [consent, setConsent] = useState5(false);
574
- const [errors, setErrors] = useState5({});
575
- const [formError, setFormError] = useState5(null);
576
- const [submitting, setSubmitting] = useState5(false);
577
- const [ack, setAck] = useState5(null);
578
- const statusRef = useRef3(null);
579
- useEffect3(() => {
959
+ const id = useId3();
960
+ const [requestType, setRequestType] = useState7("");
961
+ const [name, setName] = useState7("");
962
+ const [email, setEmail] = useState7("");
963
+ const [details, setDetails] = useState7("");
964
+ const [consent, setConsent] = useState7(false);
965
+ const [errors, setErrors] = useState7({});
966
+ const [formError, setFormError] = useState7(null);
967
+ const [submitting, setSubmitting] = useState7(false);
968
+ const [ack, setAck] = useState7(null);
969
+ const statusRef = useRef4(null);
970
+ useEffect4(() => {
580
971
  if (ack) statusRef.current?.focus();
581
972
  }, [ack]);
582
973
  async function handleSubmit(event) {
@@ -627,21 +1018,21 @@ function DataRequestForm({
627
1018
  dateStyle: "long",
628
1019
  timeStyle: "short"
629
1020
  }).format(new Date(ack.slaDueAt));
630
- return /* @__PURE__ */ jsx5(
1021
+ return /* @__PURE__ */ jsx9(
631
1022
  "div",
632
1023
  {
633
1024
  ref: statusRef,
634
1025
  tabIndex: -1,
635
1026
  role: "status",
636
1027
  "data-slot": "data-request-form-success",
637
- className: cn5("rounded-lg border border-border p-6 text-sm", className),
1028
+ className: cn9("rounded-lg border border-border p-6 text-sm", className),
638
1029
  children: t.success(deadline)
639
1030
  }
640
1031
  );
641
1032
  }
642
1033
  const errorId = (field) => errors[field] ? `${id}-${field}-error` : void 0;
643
- const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx5("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
644
- return /* @__PURE__ */ jsxs4(
1034
+ const fieldError = (field) => errors[field] ? /* @__PURE__ */ jsx9("p", { id: `${id}-${field}-error`, className: "text-xs text-destructive", children: errors[field] }) : null;
1035
+ return /* @__PURE__ */ jsxs7(
645
1036
  "form",
646
1037
  {
647
1038
  noValidate: true,
@@ -649,11 +1040,11 @@ function DataRequestForm({
649
1040
  onFocusCapture: onInteract,
650
1041
  onPointerDown: onInteract,
651
1042
  "data-slot": "data-request-form",
652
- className: cn5("space-y-4", className),
1043
+ className: cn9("space-y-4", className),
653
1044
  children: [
654
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
655
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-type`, className: "text-sm font-medium", children: t.requestTypeLabel }),
656
- /* @__PURE__ */ jsxs4(
1045
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1046
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${id}-type`, className: "text-sm font-medium", children: t.requestTypeLabel }),
1047
+ /* @__PURE__ */ jsxs7(
657
1048
  "select",
658
1049
  {
659
1050
  id: `${id}-type`,
@@ -661,18 +1052,18 @@ function DataRequestForm({
661
1052
  onChange: (e) => setRequestType(e.target.value),
662
1053
  "aria-invalid": errors.requestType ? true : void 0,
663
1054
  "aria-describedby": errorId("requestType"),
664
- className: cn5(FIELD_CLASS, errors.requestType ? "border-destructive" : "border-border"),
1055
+ className: cn9(FIELD_CLASS, errors.requestType ? "border-destructive" : "border-border"),
665
1056
  children: [
666
- /* @__PURE__ */ jsx5("option", { value: "", children: t.requestTypePlaceholder }),
667
- requestTypes.map((type) => /* @__PURE__ */ jsx5("option", { value: type, children: t.requestTypes[type] }, type))
1057
+ /* @__PURE__ */ jsx9("option", { value: "", children: t.requestTypePlaceholder }),
1058
+ requestTypes.map((type) => /* @__PURE__ */ jsx9("option", { value: type, children: t.requestTypes[type] }, type))
668
1059
  ]
669
1060
  }
670
1061
  ),
671
1062
  fieldError("requestType")
672
1063
  ] }),
673
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
674
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
675
- /* @__PURE__ */ jsx5(
1064
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1065
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${id}-name`, className: "text-sm font-medium", children: t.nameLabel }),
1066
+ /* @__PURE__ */ jsx9(
676
1067
  "input",
677
1068
  {
678
1069
  id: `${id}-name`,
@@ -683,14 +1074,14 @@ function DataRequestForm({
683
1074
  onChange: (e) => setName(e.target.value),
684
1075
  "aria-invalid": errors.name ? true : void 0,
685
1076
  "aria-describedby": errorId("name"),
686
- className: cn5(FIELD_CLASS, errors.name ? "border-destructive" : "border-border")
1077
+ className: cn9(FIELD_CLASS, errors.name ? "border-destructive" : "border-border")
687
1078
  }
688
1079
  ),
689
1080
  fieldError("name")
690
1081
  ] }),
691
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
692
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
693
- /* @__PURE__ */ jsx5(
1082
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1083
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${id}-email`, className: "text-sm font-medium", children: t.emailLabel }),
1084
+ /* @__PURE__ */ jsx9(
694
1085
  "input",
695
1086
  {
696
1087
  id: `${id}-email`,
@@ -701,14 +1092,14 @@ function DataRequestForm({
701
1092
  onChange: (e) => setEmail(e.target.value),
702
1093
  "aria-invalid": errors.email ? true : void 0,
703
1094
  "aria-describedby": errorId("email"),
704
- className: cn5(FIELD_CLASS, errors.email ? "border-destructive" : "border-border")
1095
+ className: cn9(FIELD_CLASS, errors.email ? "border-destructive" : "border-border")
705
1096
  }
706
1097
  ),
707
1098
  fieldError("email")
708
1099
  ] }),
709
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
710
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-details`, className: "text-sm font-medium", children: t.detailsLabel }),
711
- /* @__PURE__ */ jsx5(
1100
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1101
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${id}-details`, className: "text-sm font-medium", children: t.detailsLabel }),
1102
+ /* @__PURE__ */ jsx9(
712
1103
  "textarea",
713
1104
  {
714
1105
  id: `${id}-details`,
@@ -717,13 +1108,13 @@ function DataRequestForm({
717
1108
  value: details,
718
1109
  onChange: (e) => setDetails(e.target.value),
719
1110
  placeholder: t.detailsPlaceholder,
720
- className: cn5(FIELD_CLASS, "border-border")
1111
+ className: cn9(FIELD_CLASS, "border-border")
721
1112
  }
722
1113
  )
723
1114
  ] }),
724
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
725
- /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-2", children: [
726
- /* @__PURE__ */ jsx5(
1115
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
1116
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-2", children: [
1117
+ /* @__PURE__ */ jsx9(
727
1118
  "input",
728
1119
  {
729
1120
  id: `${id}-consent`,
@@ -735,17 +1126,17 @@ function DataRequestForm({
735
1126
  className: "mt-1"
736
1127
  }
737
1128
  ),
738
- /* @__PURE__ */ jsx5("label", { htmlFor: `${id}-consent`, className: "text-sm text-foreground/80", children: t.consent })
1129
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${id}-consent`, className: "text-sm text-foreground/80", children: t.consent })
739
1130
  ] }),
740
1131
  fieldError("consent")
741
1132
  ] }),
742
- formError && /* @__PURE__ */ jsx5("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
743
- /* @__PURE__ */ jsx5(
1133
+ formError && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
1134
+ /* @__PURE__ */ jsx9(
744
1135
  "button",
745
1136
  {
746
1137
  type: "submit",
747
1138
  disabled: submitting,
748
- className: "rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:opacity-60",
1139
+ className: "cursor-pointer rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60",
749
1140
  children: submitting ? t.submitting : t.submit
750
1141
  }
751
1142
  )
@@ -757,10 +1148,14 @@ export {
757
1148
  AddToCartButton,
758
1149
  CartIcon,
759
1150
  Collection,
1151
+ CourseSyllabus,
760
1152
  DATA_REQUEST_FIELD_LIMITS,
761
1153
  DATA_REQUEST_TYPES,
1154
+ DEFAULT_COURSE_SYLLABUS_LABELS,
762
1155
  DEFAULT_DATA_REQUEST_FORM_LABELS,
1156
+ DEFAULT_PRODUCT_CAROUSEL_LABELS,
763
1157
  DataRequestForm,
1158
+ ProductCarousel,
764
1159
  ShareButtons,
765
1160
  useCollection
766
1161
  };