@sesionario/blocks 1.13.1 → 1.15.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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import * as React7 from 'react';
3
3
  import { createContext, useContext, useState, useRef, useEffect, useLayoutEffect, useCallback, useId, useMemo } from 'react';
4
- import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, User, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, Settings, PictureInPicture2, Minimize, Maximize, BadgeCheck, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, XIcon } from 'lucide-react';
4
+ import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, BadgeCheck, Settings, User, Activity, LayoutDashboard, Users, LogOut, LogIn, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, PictureInPicture2, Minimize, Maximize, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, ChevronRightIcon, XIcon, CircleIcon } from 'lucide-react';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { cva } from 'class-variance-authority';
@@ -926,6 +926,38 @@ function DropdownMenuItem({
926
926
  }
927
927
  );
928
928
  }
929
+ function DropdownMenuRadioGroup({
930
+ ...props
931
+ }) {
932
+ return /* @__PURE__ */ jsx(
933
+ DropdownMenuPrimitive.RadioGroup,
934
+ {
935
+ "data-slot": "dropdown-menu-radio-group",
936
+ ...props
937
+ }
938
+ );
939
+ }
940
+ function DropdownMenuRadioItem({
941
+ className,
942
+ children,
943
+ ...props
944
+ }) {
945
+ return /* @__PURE__ */ jsxs(
946
+ DropdownMenuPrimitive.RadioItem,
947
+ {
948
+ "data-slot": "dropdown-menu-radio-item",
949
+ className: cn(
950
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[highlighted]:[&_svg:not([class*='text-'])]:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:[&_svg:not([class*='text-'])]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
951
+ className
952
+ ),
953
+ ...props,
954
+ children: [
955
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(CircleIcon, { className: "size-2 fill-current" }) }) }),
956
+ children
957
+ ]
958
+ }
959
+ );
960
+ }
929
961
  function DropdownMenuLabel({
930
962
  className,
931
963
  inset,
@@ -973,6 +1005,50 @@ function DropdownMenuShortcut({
973
1005
  }
974
1006
  );
975
1007
  }
1008
+ function DropdownMenuSub({
1009
+ ...props
1010
+ }) {
1011
+ return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Sub, { "data-slot": "dropdown-menu-sub", ...props });
1012
+ }
1013
+ function DropdownMenuSubTrigger({
1014
+ className,
1015
+ inset,
1016
+ children,
1017
+ ...props
1018
+ }) {
1019
+ return /* @__PURE__ */ jsxs(
1020
+ DropdownMenuPrimitive.SubTrigger,
1021
+ {
1022
+ "data-slot": "dropdown-menu-sub-trigger",
1023
+ "data-inset": inset,
1024
+ className: cn(
1025
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-[inset]:pl-8 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[highlighted]:[&_svg:not([class*='text-'])]:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:[&_svg:not([class*='text-'])]:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:[&_svg:not([class*='text-'])]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1026
+ className
1027
+ ),
1028
+ ...props,
1029
+ children: [
1030
+ children,
1031
+ /* @__PURE__ */ jsx(ChevronRightIcon, { className: "ml-auto size-4" })
1032
+ ]
1033
+ }
1034
+ );
1035
+ }
1036
+ function DropdownMenuSubContent({
1037
+ className,
1038
+ ...props
1039
+ }) {
1040
+ return /* @__PURE__ */ jsx(
1041
+ DropdownMenuPrimitive.SubContent,
1042
+ {
1043
+ "data-slot": "dropdown-menu-sub-content",
1044
+ className: cn(
1045
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
1046
+ className
1047
+ ),
1048
+ ...props
1049
+ }
1050
+ );
1051
+ }
976
1052
  var defaultLabels = {
977
1053
  theme: "Theme",
978
1054
  system: "System",
@@ -1465,7 +1541,7 @@ function LanguageSwitcher({
1465
1541
  const sizes = sizeClasses2[size];
1466
1542
  const shapeClass = shapeClasses2[shape];
1467
1543
  const reduceMotion = useReducedMotion();
1468
- const defaultLabels5 = {
1544
+ const defaultLabels6 = {
1469
1545
  language: labels.language ?? "Language"
1470
1546
  };
1471
1547
  if (variant === "mini") {
@@ -1475,7 +1551,7 @@ function LanguageSwitcher({
1475
1551
  {
1476
1552
  variant: "ghost",
1477
1553
  size: "icon",
1478
- "aria-label": defaultLabels5.language,
1554
+ "aria-label": defaultLabels6.language,
1479
1555
  className: cn(sizes.buttonMini, shapeClass, className),
1480
1556
  children: /* @__PURE__ */ jsx(Languages, { className: sizes.iconMini })
1481
1557
  }
@@ -1566,6 +1642,378 @@ function LanguageSwitcher({
1566
1642
  }
1567
1643
  );
1568
1644
  }
1645
+ function PlatformNavbar({
1646
+ logo,
1647
+ leading,
1648
+ center,
1649
+ right,
1650
+ className,
1651
+ innerClassName
1652
+ }) {
1653
+ return /* @__PURE__ */ jsx(
1654
+ "header",
1655
+ {
1656
+ className: cn(
1657
+ "sticky top-0 z-30 shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",
1658
+ className
1659
+ ),
1660
+ "data-testid": "platform-navbar",
1661
+ children: /* @__PURE__ */ jsxs(
1662
+ "div",
1663
+ {
1664
+ className: cn(
1665
+ "flex h-14 min-h-14 items-center gap-2 px-4 md:px-6",
1666
+ innerClassName
1667
+ ),
1668
+ children: [
1669
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: logo }),
1670
+ leading ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: leading }) : null,
1671
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center overflow-x-auto", children: center }),
1672
+ right ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: right }) : null
1673
+ ]
1674
+ }
1675
+ )
1676
+ }
1677
+ );
1678
+ }
1679
+ var LABELS = {
1680
+ verified: "M\xE9dico verificado",
1681
+ pending_review: "Verificaci\xF3n en revisi\xF3n"
1682
+ };
1683
+ var sizeClasses3 = {
1684
+ sm: {
1685
+ icon: "size-3.5",
1686
+ text: "text-xs",
1687
+ gap: "gap-1",
1688
+ padding: "px-1.5 py-0.5"
1689
+ },
1690
+ md: {
1691
+ icon: "size-4",
1692
+ text: "text-sm",
1693
+ gap: "gap-1.5",
1694
+ padding: "px-2 py-0.5"
1695
+ }
1696
+ };
1697
+ function VerifiedProfessionalBadge({
1698
+ status,
1699
+ size = "md",
1700
+ showLabel = false,
1701
+ className
1702
+ }) {
1703
+ if (status === "unverified" || status === "pending_review" && !showLabel) {
1704
+ return null;
1705
+ }
1706
+ const label = LABELS[status];
1707
+ const sizes = sizeClasses3[size];
1708
+ const Icon = status === "verified" ? BadgeCheck : Clock;
1709
+ const colorClass = status === "verified" ? "text-primary" : "text-muted-foreground";
1710
+ if (!showLabel) {
1711
+ return /* @__PURE__ */ jsx(
1712
+ Icon,
1713
+ {
1714
+ className: cn(sizes.icon, colorClass, className),
1715
+ "aria-label": label,
1716
+ children: /* @__PURE__ */ jsx("title", { children: label })
1717
+ }
1718
+ );
1719
+ }
1720
+ return /* @__PURE__ */ jsxs(
1721
+ "span",
1722
+ {
1723
+ title: label,
1724
+ "aria-label": label,
1725
+ className: cn(
1726
+ "inline-flex w-fit items-center rounded-full font-medium",
1727
+ sizes.gap,
1728
+ sizes.text,
1729
+ sizes.padding,
1730
+ status === "verified" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground",
1731
+ className
1732
+ ),
1733
+ children: [
1734
+ /* @__PURE__ */ jsx(Icon, { className: sizes.icon, "aria-hidden": "true" }),
1735
+ /* @__PURE__ */ jsx("span", { children: label })
1736
+ ]
1737
+ }
1738
+ );
1739
+ }
1740
+ var defaultLabels3 = {
1741
+ accountMenu: "Account menu",
1742
+ settingsMenu: "Settings",
1743
+ personalProfile: "Personal profile",
1744
+ activity: "Activity",
1745
+ professionalProfile: "Professional profile",
1746
+ mainApp: "Main app",
1747
+ orgManagement: "Organization management",
1748
+ backoffice: "Backoffice",
1749
+ memberOrgs: "My organizations",
1750
+ managementOrgs: "Management team",
1751
+ language: "Language",
1752
+ theme: "Theme",
1753
+ themeLight: "Light",
1754
+ themeDark: "Dark",
1755
+ themeSystem: "System",
1756
+ signIn: "Sign in",
1757
+ signOut: "Sign out",
1758
+ signingOut: "Signing out\u2026",
1759
+ accountSettings: "Account settings"
1760
+ };
1761
+ function MenuAvatar({
1762
+ name,
1763
+ image,
1764
+ sizeClass
1765
+ }) {
1766
+ const initials = getInitials(name);
1767
+ return /* @__PURE__ */ jsx(
1768
+ "span",
1769
+ {
1770
+ className: cn(
1771
+ "relative flex shrink-0 overflow-hidden rounded-full",
1772
+ sizeClass
1773
+ ),
1774
+ children: image ? /* @__PURE__ */ jsx(
1775
+ "img",
1776
+ {
1777
+ src: image,
1778
+ alt: "",
1779
+ className: "aspect-square size-full object-cover"
1780
+ }
1781
+ ) : /* @__PURE__ */ jsx(
1782
+ "span",
1783
+ {
1784
+ className: "flex size-full items-center justify-center text-xs font-semibold",
1785
+ style: avatarFallbackStyle(name),
1786
+ children: initials
1787
+ }
1788
+ )
1789
+ }
1790
+ );
1791
+ }
1792
+ function LanguageThemeSubs({
1793
+ languages,
1794
+ currentLanguage,
1795
+ onLanguageChange,
1796
+ labels
1797
+ }) {
1798
+ const { theme, setTheme } = useTheme();
1799
+ const themeValue = theme ?? "system";
1800
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1801
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1802
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "gap-2", children: [
1803
+ /* @__PURE__ */ jsx(Languages, { className: "size-4" }),
1804
+ labels.language
1805
+ ] }),
1806
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { className: "min-w-40", children: /* @__PURE__ */ jsx(
1807
+ DropdownMenuRadioGroup,
1808
+ {
1809
+ value: currentLanguage,
1810
+ onValueChange: onLanguageChange,
1811
+ children: languages.map((lang) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: lang.key, children: lang.nativeName }, lang.key))
1812
+ }
1813
+ ) })
1814
+ ] }),
1815
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1816
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "gap-2", children: [
1817
+ themeValue === "light" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : themeValue === "dark" ? /* @__PURE__ */ jsx(Moon, { className: "size-4" }) : /* @__PURE__ */ jsx(Monitor, { className: "size-4" }),
1818
+ labels.theme
1819
+ ] }),
1820
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { className: "min-w-40", children: /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, { value: themeValue, onValueChange: setTheme, children: [
1821
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: "system", children: labels.themeSystem }),
1822
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: "light", children: labels.themeLight }),
1823
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: "dark", children: labels.themeDark })
1824
+ ] }) })
1825
+ ] })
1826
+ ] });
1827
+ }
1828
+ function PlatformAccountMenu({
1829
+ variant = "platform",
1830
+ isPending = false,
1831
+ user = null,
1832
+ isAdmin = false,
1833
+ verificationStatus = "unverified",
1834
+ urls,
1835
+ memberOrgs = [],
1836
+ manageableOrgs = [],
1837
+ languages = defaultLanguages,
1838
+ currentLanguage,
1839
+ onLanguageChange,
1840
+ labels: labelsProp,
1841
+ onSignOut,
1842
+ extraItems,
1843
+ className
1844
+ }) {
1845
+ const labels = { ...defaultLabels3, ...labelsProp };
1846
+ const [signingOut, setSigningOut] = useState(false);
1847
+ const loginHref = `${urls.authAppUrl}${urls.loginPath ?? "/login"}`;
1848
+ const accountHref = `${urls.authAppUrl}${urls.accountPath ?? "/account"}`;
1849
+ const activityHref = `${urls.authAppUrl}${urls.activityPath ?? "/account/activity"}`;
1850
+ const profileHref = `${urls.appUrl}${urls.professionalProfilePath ?? "/profile"}`;
1851
+ async function handleSignOut() {
1852
+ if (!onSignOut) return;
1853
+ setSigningOut(true);
1854
+ try {
1855
+ await onSignOut();
1856
+ } finally {
1857
+ setSigningOut(false);
1858
+ }
1859
+ }
1860
+ if (isPending) {
1861
+ return /* @__PURE__ */ jsx(
1862
+ "div",
1863
+ {
1864
+ className: cn(
1865
+ "size-9 shrink-0 animate-pulse rounded-full bg-muted",
1866
+ className
1867
+ ),
1868
+ "aria-hidden": true
1869
+ }
1870
+ );
1871
+ }
1872
+ const trigger = user ? /* @__PURE__ */ jsx(
1873
+ Button,
1874
+ {
1875
+ variant: "ghost",
1876
+ size: "icon",
1877
+ className: cn("size-9 rounded-full", className),
1878
+ "aria-label": labels.accountMenu,
1879
+ children: /* @__PURE__ */ jsx(MenuAvatar, { name: user.name, image: user.image, sizeClass: "size-8" })
1880
+ }
1881
+ ) : /* @__PURE__ */ jsx(
1882
+ Button,
1883
+ {
1884
+ variant: "ghost",
1885
+ size: "icon",
1886
+ className: cn("size-9", className),
1887
+ "aria-label": labels.settingsMenu,
1888
+ children: /* @__PURE__ */ jsx(Settings, { className: "size-4" })
1889
+ }
1890
+ );
1891
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1892
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger }),
1893
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "min-w-60 rounded-lg", children: user ? /* @__PURE__ */ jsxs(Fragment, { children: [
1894
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex items-center gap-3 p-2 font-normal", children: [
1895
+ /* @__PURE__ */ jsx(
1896
+ MenuAvatar,
1897
+ {
1898
+ name: user.name,
1899
+ image: user.image,
1900
+ sizeClass: "size-9"
1901
+ }
1902
+ ),
1903
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
1904
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5 truncate text-sm font-medium leading-tight", children: [
1905
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: user.name }),
1906
+ variant === "platform" ? /* @__PURE__ */ jsx(
1907
+ VerifiedProfessionalBadge,
1908
+ {
1909
+ status: verificationStatus,
1910
+ className: "shrink-0"
1911
+ }
1912
+ ) : null
1913
+ ] }),
1914
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground leading-tight", children: user.email })
1915
+ ] })
1916
+ ] }),
1917
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1918
+ variant === "platform" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1919
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: accountHref, children: [
1920
+ /* @__PURE__ */ jsx(User, { className: "size-4" }),
1921
+ labels.personalProfile
1922
+ ] }) }),
1923
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: activityHref, children: [
1924
+ /* @__PURE__ */ jsx(Activity, { className: "size-4" }),
1925
+ labels.activity
1926
+ ] }) }),
1927
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: profileHref, children: [
1928
+ /* @__PURE__ */ jsx(User, { className: "size-4" }),
1929
+ labels.professionalProfile
1930
+ ] }) }),
1931
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: urls.appUrl, children: [
1932
+ /* @__PURE__ */ jsx(LayoutDashboard, { className: "size-4" }),
1933
+ labels.mainApp
1934
+ ] }) }),
1935
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: urls.adminUrl, children: [
1936
+ /* @__PURE__ */ jsx(Building2, { className: "size-4" }),
1937
+ labels.orgManagement
1938
+ ] }) }),
1939
+ isAdmin && urls.backofficeUrl ? /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: urls.backofficeUrl, children: [
1940
+ /* @__PURE__ */ jsx(LayoutDashboard, { className: "size-4" }),
1941
+ labels.backoffice
1942
+ ] }) }) : null,
1943
+ memberOrgs.length > 0 ? /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1944
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "gap-2", children: [
1945
+ /* @__PURE__ */ jsx(Users, { className: "size-4" }),
1946
+ labels.memberOrgs
1947
+ ] }),
1948
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { className: "min-w-48 max-h-64 overflow-y-auto", children: memberOrgs.map((org) => /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: `${urls.appUrl}/o/${org.slug}`, children: org.name }) }, org.id)) })
1949
+ ] }) : null,
1950
+ manageableOrgs.length > 0 ? /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
1951
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { className: "gap-2", children: [
1952
+ /* @__PURE__ */ jsx(Building2, { className: "size-4" }),
1953
+ labels.managementOrgs
1954
+ ] }),
1955
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { className: "min-w-48 max-h-64 overflow-y-auto", children: manageableOrgs.map((org) => /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx(
1956
+ "a",
1957
+ {
1958
+ href: `${urls.adminUrl}/api/auth/org/select?id=${encodeURIComponent(org.id)}`,
1959
+ children: org.name
1960
+ }
1961
+ ) }, org.id)) })
1962
+ ] }) : null,
1963
+ extraItems,
1964
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
1965
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1966
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: accountHref, children: [
1967
+ /* @__PURE__ */ jsx(Settings, { className: "size-4" }),
1968
+ labels.accountSettings
1969
+ ] }) }),
1970
+ extraItems,
1971
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
1972
+ ] }),
1973
+ /* @__PURE__ */ jsx(
1974
+ LanguageThemeSubs,
1975
+ {
1976
+ languages,
1977
+ currentLanguage,
1978
+ onLanguageChange,
1979
+ labels
1980
+ }
1981
+ ),
1982
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1983
+ /* @__PURE__ */ jsxs(
1984
+ DropdownMenuItem,
1985
+ {
1986
+ variant: "destructive",
1987
+ className: "gap-2",
1988
+ disabled: signingOut || !onSignOut,
1989
+ onSelect: (event) => {
1990
+ event.preventDefault();
1991
+ void handleSignOut();
1992
+ },
1993
+ children: [
1994
+ /* @__PURE__ */ jsx(LogOut, { className: "size-4" }),
1995
+ signingOut ? labels.signingOut : labels.signOut
1996
+ ]
1997
+ }
1998
+ )
1999
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2000
+ /* @__PURE__ */ jsx(
2001
+ LanguageThemeSubs,
2002
+ {
2003
+ languages,
2004
+ currentLanguage,
2005
+ onLanguageChange,
2006
+ labels
2007
+ }
2008
+ ),
2009
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
2010
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, className: "gap-2", children: /* @__PURE__ */ jsxs("a", { href: loginHref, children: [
2011
+ /* @__PURE__ */ jsx(LogIn, { className: "size-4" }),
2012
+ labels.signIn
2013
+ ] }) })
2014
+ ] }) })
2015
+ ] });
2016
+ }
1569
2017
  function Slider({
1570
2018
  className,
1571
2019
  defaultValue,
@@ -4884,7 +5332,7 @@ function SearchablePickerList({
4884
5332
  ] })
4885
5333
  ] });
4886
5334
  }
4887
- var defaultLabels3 = {
5335
+ var defaultLabels4 = {
4888
5336
  placeholder: "Seleccionar\u2026",
4889
5337
  searchPlaceholder: "Buscar\u2026",
4890
5338
  empty: "No se encontraron resultados",
@@ -4957,7 +5405,7 @@ function SearchablePicker(props) {
4957
5405
  "aria-label": ariaLabel
4958
5406
  } = props;
4959
5407
  const mode = props.mode ?? "single";
4960
- const labels = { ...defaultLabels3, ...userLabels };
5408
+ const labels = { ...defaultLabels4, ...userLabels };
4961
5409
  const resolvedEmptyLabel = emptyLabel ?? labels.emptyOption;
4962
5410
  const isMobile = useMediaQuery2("(max-width: 767px)");
4963
5411
  const generatedId = useId();
@@ -5297,7 +5745,7 @@ function SearchablePicker(props) {
5297
5745
  clearButton
5298
5746
  ] });
5299
5747
  }
5300
- var defaultLabels4 = {
5748
+ var defaultLabels5 = {
5301
5749
  placeholder: "Buscar en toda la plataforma\u2026",
5302
5750
  dialogTitle: "Buscar",
5303
5751
  dialogDescription: "Busca funcionalidades, eventos, documentos y m\xE1s",
@@ -5331,7 +5779,7 @@ function SearchCommand({
5331
5779
  debounceMs = 200,
5332
5780
  className
5333
5781
  }) {
5334
- const labels = { ...defaultLabels4, ...userLabels };
5782
+ const labels = { ...defaultLabels5, ...userLabels };
5335
5783
  const [query, setQuery] = useState("");
5336
5784
  const [hits, setHits] = useState([]);
5337
5785
  const [isLoading, setIsLoading] = useState(false);
@@ -5773,67 +6221,6 @@ function MexicoMap({ selected, onToggle, className }) {
5773
6221
  }
5774
6222
  );
5775
6223
  }
5776
- var LABELS = {
5777
- verified: "M\xE9dico verificado",
5778
- pending_review: "Verificaci\xF3n en revisi\xF3n"
5779
- };
5780
- var sizeClasses3 = {
5781
- sm: {
5782
- icon: "size-3.5",
5783
- text: "text-xs",
5784
- gap: "gap-1",
5785
- padding: "px-1.5 py-0.5"
5786
- },
5787
- md: {
5788
- icon: "size-4",
5789
- text: "text-sm",
5790
- gap: "gap-1.5",
5791
- padding: "px-2 py-0.5"
5792
- }
5793
- };
5794
- function VerifiedProfessionalBadge({
5795
- status,
5796
- size = "md",
5797
- showLabel = false,
5798
- className
5799
- }) {
5800
- if (status === "unverified" || status === "pending_review" && !showLabel) {
5801
- return null;
5802
- }
5803
- const label = LABELS[status];
5804
- const sizes = sizeClasses3[size];
5805
- const Icon = status === "verified" ? BadgeCheck : Clock;
5806
- const colorClass = status === "verified" ? "text-primary" : "text-muted-foreground";
5807
- if (!showLabel) {
5808
- return /* @__PURE__ */ jsx(
5809
- Icon,
5810
- {
5811
- className: cn(sizes.icon, colorClass, className),
5812
- "aria-label": label,
5813
- children: /* @__PURE__ */ jsx("title", { children: label })
5814
- }
5815
- );
5816
- }
5817
- return /* @__PURE__ */ jsxs(
5818
- "span",
5819
- {
5820
- title: label,
5821
- "aria-label": label,
5822
- className: cn(
5823
- "inline-flex w-fit items-center rounded-full font-medium",
5824
- sizes.gap,
5825
- sizes.text,
5826
- sizes.padding,
5827
- status === "verified" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground",
5828
- className
5829
- ),
5830
- children: [
5831
- /* @__PURE__ */ jsx(Icon, { className: sizes.icon, "aria-hidden": "true" }),
5832
- /* @__PURE__ */ jsx("span", { children: label })
5833
- ]
5834
- }
5835
- );
5836
- }
5837
6224
  function MessageGroup({ className, ...props }) {
5838
6225
  return /* @__PURE__ */ jsx(
5839
6226
  "div",
@@ -6954,6 +7341,162 @@ function createSupportChatClient(basePath = "/api/chat-svc") {
6954
7341
  }
6955
7342
  var DEFAULT_RECONNECT_MS = 2e3;
6956
7343
  var MAX_RECONNECT_MS = 3e4;
7344
+ var HEARTBEAT_MS = 3e4;
7345
+ var registry = /* @__PURE__ */ new Map();
7346
+ function channelKeyFromUrl(url) {
7347
+ const q = url.indexOf("?");
7348
+ return q === -1 ? url : url.slice(0, q);
7349
+ }
7350
+ function stopHeartbeat(shared) {
7351
+ if (shared.heartbeatTimer) {
7352
+ clearInterval(shared.heartbeatTimer);
7353
+ shared.heartbeatTimer = null;
7354
+ }
7355
+ }
7356
+ function startHeartbeat(shared, gen) {
7357
+ stopHeartbeat(shared);
7358
+ shared.heartbeatTimer = setInterval(() => {
7359
+ if (gen !== shared.gen) return;
7360
+ if (shared.ws?.readyState === WebSocket.OPEN) {
7361
+ try {
7362
+ shared.ws.send("ping");
7363
+ } catch {
7364
+ }
7365
+ }
7366
+ }, HEARTBEAT_MS);
7367
+ }
7368
+ function clearReconnect(shared) {
7369
+ if (shared.reconnectTimer) {
7370
+ clearTimeout(shared.reconnectTimer);
7371
+ shared.reconnectTimer = null;
7372
+ }
7373
+ }
7374
+ function openSocket(shared, url) {
7375
+ const gen = ++shared.gen;
7376
+ clearReconnect(shared);
7377
+ if (shared.ws) {
7378
+ try {
7379
+ shared.ws.close();
7380
+ } catch {
7381
+ }
7382
+ }
7383
+ const ws = new WebSocket(url);
7384
+ shared.ws = ws;
7385
+ ws.onopen = () => {
7386
+ if (gen !== shared.gen) return;
7387
+ shared.attempt = 0;
7388
+ startHeartbeat(shared, gen);
7389
+ };
7390
+ ws.onmessage = (msg) => {
7391
+ if (gen !== shared.gen) return;
7392
+ let data;
7393
+ try {
7394
+ data = JSON.parse(String(msg.data));
7395
+ } catch {
7396
+ return;
7397
+ }
7398
+ if (typeof data?.type !== "string") return;
7399
+ if (data.type === "pong") return;
7400
+ for (const listener of shared.listeners) {
7401
+ listener(data);
7402
+ }
7403
+ };
7404
+ ws.onclose = () => {
7405
+ if (gen !== shared.gen) return;
7406
+ shared.ws = null;
7407
+ stopHeartbeat(shared);
7408
+ scheduleReconnect(shared);
7409
+ };
7410
+ ws.onerror = () => {
7411
+ };
7412
+ }
7413
+ function scheduleReconnect(shared) {
7414
+ if (shared.listeners.size === 0) return;
7415
+ clearReconnect(shared);
7416
+ const delay = Math.min(
7417
+ shared.reconnectDelayMs * 2 ** shared.attempt,
7418
+ MAX_RECONNECT_MS
7419
+ );
7420
+ shared.attempt += 1;
7421
+ shared.reconnectTimer = setTimeout(() => {
7422
+ shared.reconnectTimer = null;
7423
+ void connectShared(shared);
7424
+ }, delay);
7425
+ }
7426
+ async function connectShared(shared) {
7427
+ if (shared.connecting || shared.listeners.size === 0) return;
7428
+ shared.connecting = true;
7429
+ let url = null;
7430
+ try {
7431
+ url = await shared.getWsUrl();
7432
+ } finally {
7433
+ shared.connecting = false;
7434
+ }
7435
+ if (shared.listeners.size === 0) return;
7436
+ if (!url) return;
7437
+ openSocket(shared, url);
7438
+ }
7439
+ function destroyShared(shared) {
7440
+ shared.gen += 1;
7441
+ clearReconnect(shared);
7442
+ stopHeartbeat(shared);
7443
+ if (shared.ws) {
7444
+ try {
7445
+ shared.ws.close();
7446
+ } catch {
7447
+ }
7448
+ shared.ws = null;
7449
+ }
7450
+ }
7451
+ function joinChannel(key, url, getWsUrl, reconnectDelayMs, listener) {
7452
+ ensureVisibilityHook();
7453
+ let shared = registry.get(key);
7454
+ if (!shared) {
7455
+ shared = {
7456
+ key,
7457
+ listeners: /* @__PURE__ */ new Set(),
7458
+ getWsUrl,
7459
+ reconnectDelayMs,
7460
+ ws: null,
7461
+ attempt: 0,
7462
+ reconnectTimer: null,
7463
+ heartbeatTimer: null,
7464
+ connecting: false,
7465
+ gen: 0
7466
+ };
7467
+ registry.set(key, shared);
7468
+ shared.listeners.add(listener);
7469
+ openSocket(shared, url);
7470
+ return;
7471
+ }
7472
+ shared.getWsUrl = getWsUrl;
7473
+ shared.listeners.add(listener);
7474
+ if (!shared.ws && !shared.connecting && !shared.reconnectTimer) {
7475
+ openSocket(shared, url);
7476
+ }
7477
+ }
7478
+ function leaveChannel(key, listener) {
7479
+ const shared = registry.get(key);
7480
+ if (!shared) return;
7481
+ shared.listeners.delete(listener);
7482
+ if (shared.listeners.size === 0) {
7483
+ destroyShared(shared);
7484
+ registry.delete(key);
7485
+ }
7486
+ }
7487
+ var visibilityHooked = false;
7488
+ function ensureVisibilityHook() {
7489
+ if (visibilityHooked || typeof document === "undefined") return;
7490
+ visibilityHooked = true;
7491
+ document.addEventListener("visibilitychange", () => {
7492
+ if (document.visibilityState !== "visible") return;
7493
+ for (const shared of registry.values()) {
7494
+ if (!shared.ws && !shared.reconnectTimer && !shared.connecting) {
7495
+ void connectShared(shared);
7496
+ }
7497
+ }
7498
+ });
7499
+ }
6957
7500
  function useRealtimeChannel({
6958
7501
  getWsUrl,
6959
7502
  onEvent,
@@ -6967,91 +7510,30 @@ function useRealtimeChannel({
6967
7510
  useEffect(() => {
6968
7511
  if (!enabled) return;
6969
7512
  let cancelled = false;
6970
- let socket = null;
6971
- let reconnectTimer = null;
6972
- let attempt = 0;
6973
- const connectGen = { current: 0 };
6974
- const clearReconnect = () => {
6975
- if (reconnectTimer) {
6976
- clearTimeout(reconnectTimer);
6977
- reconnectTimer = null;
6978
- }
6979
- };
6980
- const scheduleReconnect = () => {
6981
- if (cancelled) return;
6982
- clearReconnect();
6983
- const delay = Math.min(reconnectDelayMs * 2 ** attempt, MAX_RECONNECT_MS);
6984
- attempt += 1;
6985
- reconnectTimer = setTimeout(() => {
6986
- void connect();
6987
- }, delay);
6988
- };
6989
- const connect = async () => {
6990
- const gen = ++connectGen.current;
6991
- clearReconnect();
6992
- const url = await getWsUrlRef.current();
6993
- if (cancelled || gen !== connectGen.current) return;
6994
- if (!url) {
6995
- scheduleReconnect();
6996
- return;
6997
- }
6998
- if (socket) {
6999
- try {
7000
- socket.close();
7001
- } catch {
7002
- }
7003
- socket = null;
7004
- }
7005
- const ws = new WebSocket(url);
7006
- socket = ws;
7007
- ws.onopen = () => {
7008
- if (cancelled || gen !== connectGen.current) return;
7009
- attempt = 0;
7010
- };
7011
- ws.onmessage = (msg) => {
7012
- if (cancelled || gen !== connectGen.current) return;
7013
- let data;
7014
- try {
7015
- data = JSON.parse(String(msg.data));
7016
- } catch {
7017
- return;
7018
- }
7019
- if (typeof data?.type !== "string") return;
7020
- if (data.type === "pong") return;
7021
- onEventRef.current(data);
7022
- };
7023
- ws.onclose = () => {
7024
- if (cancelled || gen !== connectGen.current) return;
7025
- socket = null;
7026
- scheduleReconnect();
7027
- };
7028
- ws.onerror = () => {
7029
- };
7513
+ let joinedKey = null;
7514
+ const listener = (event) => onEventRef.current(event);
7515
+ const resolver = () => getWsUrlRef.current();
7516
+ const join = async () => {
7517
+ if (cancelled || joinedKey) return;
7518
+ const url = await resolver();
7519
+ if (cancelled || !url) return;
7520
+ joinedKey = channelKeyFromUrl(url);
7521
+ joinChannel(joinedKey, url, resolver, reconnectDelayMs, listener);
7030
7522
  };
7031
7523
  const onVisibility = () => {
7032
7524
  if (document.visibilityState !== "visible" || cancelled) return;
7033
- if (!socket || socket.readyState === WebSocket.CLOSED) {
7034
- void connect();
7035
- }
7525
+ if (!joinedKey) void join();
7036
7526
  };
7037
- void connect();
7527
+ void join();
7038
7528
  document.addEventListener("visibilitychange", onVisibility);
7039
7529
  return () => {
7040
7530
  cancelled = true;
7041
- connectGen.current += 1;
7042
- clearReconnect();
7043
7531
  document.removeEventListener("visibilitychange", onVisibility);
7044
- if (socket) {
7045
- try {
7046
- socket.close();
7047
- } catch {
7048
- }
7049
- socket = null;
7050
- }
7532
+ if (joinedKey) leaveChannel(joinedKey, listener);
7051
7533
  };
7052
7534
  }, [enabled, reconnectDelayMs]);
7053
7535
  }
7054
7536
 
7055
- export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describeAvResync, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useRealtimeChannel, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
7537
+ export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, PlatformAccountMenu, PlatformNavbar, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describeAvResync, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useRealtimeChannel, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
7056
7538
  //# sourceMappingURL=index.js.map
7057
7539
  //# sourceMappingURL=index.js.map