@greatapps/greatchat-ui 0.1.2 → 0.1.3

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
@@ -1537,14 +1537,956 @@ function ChatView({
1537
1537
  /* @__PURE__ */ jsx9(ChatInput, { onSend, disabled: sendDisabled })
1538
1538
  ] });
1539
1539
  }
1540
+
1541
+ // src/components/ui/avatar.tsx
1542
+ import { Avatar as AvatarPrimitive } from "radix-ui";
1543
+ import { jsx as jsx10 } from "react/jsx-runtime";
1544
+ function Avatar({
1545
+ className,
1546
+ ...props
1547
+ }) {
1548
+ return /* @__PURE__ */ jsx10(
1549
+ AvatarPrimitive.Root,
1550
+ {
1551
+ "data-slot": "avatar",
1552
+ className: cn(
1553
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1554
+ className
1555
+ ),
1556
+ ...props
1557
+ }
1558
+ );
1559
+ }
1560
+ function AvatarFallback({
1561
+ className,
1562
+ ...props
1563
+ }) {
1564
+ return /* @__PURE__ */ jsx10(
1565
+ AvatarPrimitive.Fallback,
1566
+ {
1567
+ "data-slot": "avatar-fallback",
1568
+ className: cn(
1569
+ "flex size-full items-center justify-center rounded-full bg-muted",
1570
+ className
1571
+ ),
1572
+ ...props
1573
+ }
1574
+ );
1575
+ }
1576
+
1577
+ // src/components/contact-avatar.tsx
1578
+ import { jsx as jsx11 } from "react/jsx-runtime";
1579
+ var COLORS = [
1580
+ "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300",
1581
+ "bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300",
1582
+ "bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300",
1583
+ "bg-emerald-100 text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300",
1584
+ "bg-cyan-100 text-cyan-700 dark:bg-cyan-900 dark:text-cyan-300",
1585
+ "bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300",
1586
+ "bg-violet-100 text-violet-700 dark:bg-violet-900 dark:text-violet-300",
1587
+ "bg-pink-100 text-pink-700 dark:bg-pink-900 dark:text-pink-300"
1588
+ ];
1589
+ var SIZE_MAP = {
1590
+ sm: "h-8 w-8 text-xs",
1591
+ md: "h-10 w-10 text-xs",
1592
+ lg: "h-16 w-16 text-lg"
1593
+ };
1594
+ function hashCode(str) {
1595
+ let hash = 0;
1596
+ for (let i = 0; i < str.length; i++) {
1597
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
1598
+ }
1599
+ return Math.abs(hash);
1600
+ }
1601
+ function getInitials(name) {
1602
+ const parts = name.trim().split(/\s+/);
1603
+ if (parts.length >= 2) {
1604
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
1605
+ }
1606
+ return (name[0] || "?").toUpperCase();
1607
+ }
1608
+ function ContactAvatar({
1609
+ name,
1610
+ className,
1611
+ size = "md"
1612
+ }) {
1613
+ const displayName = name || "?";
1614
+ const color = COLORS[hashCode(displayName) % COLORS.length];
1615
+ const initials = getInitials(displayName);
1616
+ return /* @__PURE__ */ jsx11(Avatar, { className: cn(SIZE_MAP[size], className), children: /* @__PURE__ */ jsx11(AvatarFallback, { className: cn("font-medium", color), children: initials }) });
1617
+ }
1618
+
1619
+ // src/components/inbox-item.tsx
1620
+ import { useState as useState3 } from "react";
1621
+
1622
+ // src/components/ui/badge.tsx
1623
+ import { cva as cva2 } from "class-variance-authority";
1624
+ import { Slot as Slot2 } from "radix-ui";
1625
+ import { jsx as jsx12 } from "react/jsx-runtime";
1626
+ var badgeVariants = cva2(
1627
+ "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none overflow-hidden",
1628
+ {
1629
+ variants: {
1630
+ variant: {
1631
+ default: "bg-primary text-primary-foreground",
1632
+ secondary: "bg-secondary text-secondary-foreground",
1633
+ destructive: "bg-destructive/10 text-destructive dark:bg-destructive/20",
1634
+ outline: "border-border text-foreground",
1635
+ ghost: "hover:bg-muted hover:text-muted-foreground"
1636
+ }
1637
+ },
1638
+ defaultVariants: {
1639
+ variant: "default"
1640
+ }
1641
+ }
1642
+ );
1643
+ function Badge({
1644
+ className,
1645
+ variant = "default",
1646
+ asChild = false,
1647
+ ...props
1648
+ }) {
1649
+ const Comp = asChild ? Slot2.Root : "span";
1650
+ return /* @__PURE__ */ jsx12(
1651
+ Comp,
1652
+ {
1653
+ "data-slot": "badge",
1654
+ className: cn(badgeVariants({ variant }), className),
1655
+ ...props
1656
+ }
1657
+ );
1658
+ }
1659
+
1660
+ // src/components/ui/tooltip.tsx
1661
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
1662
+ import { jsx as jsx13 } from "react/jsx-runtime";
1663
+ function TooltipProvider({
1664
+ delayDuration = 0,
1665
+ ...props
1666
+ }) {
1667
+ return /* @__PURE__ */ jsx13(
1668
+ TooltipPrimitive.Provider,
1669
+ {
1670
+ "data-slot": "tooltip-provider",
1671
+ delayDuration,
1672
+ ...props
1673
+ }
1674
+ );
1675
+ }
1676
+ function Tooltip({
1677
+ ...props
1678
+ }) {
1679
+ return /* @__PURE__ */ jsx13(TooltipProvider, { children: /* @__PURE__ */ jsx13(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
1680
+ }
1681
+ function TooltipTrigger({
1682
+ ...props
1683
+ }) {
1684
+ return /* @__PURE__ */ jsx13(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
1685
+ }
1686
+ function TooltipContent({
1687
+ className,
1688
+ sideOffset = 4,
1689
+ children,
1690
+ ...props
1691
+ }) {
1692
+ return /* @__PURE__ */ jsx13(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx13(
1693
+ TooltipPrimitive.Content,
1694
+ {
1695
+ "data-slot": "tooltip-content",
1696
+ sideOffset,
1697
+ className: cn(
1698
+ "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
1699
+ className
1700
+ ),
1701
+ ...props,
1702
+ children
1703
+ }
1704
+ ) });
1705
+ }
1706
+
1707
+ // src/components/inbox-item.tsx
1708
+ import { formatDistanceToNow } from "date-fns";
1709
+ import { ptBR as ptBR2 } from "date-fns/locale";
1710
+ import { Trash2 as Trash22 } from "lucide-react";
1711
+ import { Fragment, jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
1712
+ var statusColors = {
1713
+ open: "bg-green-500/10 text-green-600 border-green-200",
1714
+ pending: "bg-yellow-500/10 text-yellow-600 border-yellow-200",
1715
+ resolved: "bg-zinc-500/10 text-zinc-500 border-zinc-200"
1716
+ };
1717
+ var statusLabels = {
1718
+ open: "Aberta",
1719
+ pending: "Pendente",
1720
+ resolved: "Resolvida"
1721
+ };
1722
+ function InboxItem({
1723
+ inbox,
1724
+ isSelected,
1725
+ onClick,
1726
+ onDelete,
1727
+ renderActions
1728
+ }) {
1729
+ const [showDeleteDialog, setShowDeleteDialog] = useState3(false);
1730
+ const timestamp = inbox.last_message_at || inbox.datetime_add;
1731
+ const timeAgo = formatDistanceToNow(new Date(timestamp), {
1732
+ addSuffix: true,
1733
+ locale: ptBR2
1734
+ });
1735
+ const lastMessage = inbox.last_message_content_type === "text" ? inbox.last_message_content : inbox.last_message_content_type ? `[${inbox.last_message_content_type}]` : null;
1736
+ const directionPrefix = inbox.last_message_direction === "outbound" ? "Voc\xEA: " : "";
1737
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
1738
+ /* @__PURE__ */ jsxs7(
1739
+ "div",
1740
+ {
1741
+ className: cn(
1742
+ "flex w-full items-start gap-3 rounded-md p-3 text-left transition-colors hover:bg-accent group relative cursor-pointer",
1743
+ isSelected && "bg-accent"
1744
+ ),
1745
+ onClick,
1746
+ children: [
1747
+ /* @__PURE__ */ jsx14(
1748
+ ContactAvatar,
1749
+ {
1750
+ name: inbox.contact_name,
1751
+ className: "mt-0.5",
1752
+ size: "md"
1753
+ }
1754
+ ),
1755
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 overflow-hidden", children: [
1756
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-2", children: [
1757
+ /* @__PURE__ */ jsx14("span", { className: "truncate font-medium text-sm", children: inbox.contact_name || "Desconhecido" }),
1758
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 shrink-0", children: [
1759
+ /* @__PURE__ */ jsx14("span", { className: "text-xs text-muted-foreground", children: timeAgo }),
1760
+ renderActions?.(inbox),
1761
+ onDelete && /* @__PURE__ */ jsxs7(Tooltip, { children: [
1762
+ /* @__PURE__ */ jsx14(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx14(
1763
+ "button",
1764
+ {
1765
+ className: "h-5 w-5 rounded flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-muted text-destructive",
1766
+ onClick: (e) => {
1767
+ e.stopPropagation();
1768
+ setShowDeleteDialog(true);
1769
+ },
1770
+ children: /* @__PURE__ */ jsx14(Trash22, { className: "h-3.5 w-3.5" })
1771
+ }
1772
+ ) }),
1773
+ /* @__PURE__ */ jsx14(TooltipContent, { children: "Excluir conversa" })
1774
+ ] })
1775
+ ] })
1776
+ ] }),
1777
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-2 mt-0.5", children: [
1778
+ /* @__PURE__ */ jsx14("p", { className: "truncate text-xs text-muted-foreground", children: lastMessage ? `${directionPrefix}${lastMessage}` : "Sem mensagens" }),
1779
+ /* @__PURE__ */ jsx14(
1780
+ Badge,
1781
+ {
1782
+ variant: "outline",
1783
+ className: cn(
1784
+ "shrink-0 text-[10px] px-1.5 py-0",
1785
+ statusColors[inbox.status]
1786
+ ),
1787
+ children: statusLabels[inbox.status] || inbox.status
1788
+ }
1789
+ )
1790
+ ] })
1791
+ ] })
1792
+ ]
1793
+ }
1794
+ ),
1795
+ /* @__PURE__ */ jsx14(AlertDialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: /* @__PURE__ */ jsxs7(AlertDialogContent, { children: [
1796
+ /* @__PURE__ */ jsxs7(AlertDialogHeader, { children: [
1797
+ /* @__PURE__ */ jsx14(AlertDialogTitle, { children: "Excluir conversa?" }),
1798
+ /* @__PURE__ */ jsxs7(AlertDialogDescription, { children: [
1799
+ "A conversa com ",
1800
+ inbox.contact_name || "este contato",
1801
+ " ser\xE1 removida da lista. As mensagens n\xE3o ser\xE3o apagadas do WhatsApp."
1802
+ ] })
1803
+ ] }),
1804
+ /* @__PURE__ */ jsxs7(AlertDialogFooter, { children: [
1805
+ /* @__PURE__ */ jsx14(AlertDialogCancel, { children: "Cancelar" }),
1806
+ /* @__PURE__ */ jsx14(
1807
+ AlertDialogAction,
1808
+ {
1809
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1810
+ onClick: () => onDelete?.(inbox.id),
1811
+ children: "Excluir"
1812
+ }
1813
+ )
1814
+ ] })
1815
+ ] }) })
1816
+ ] });
1817
+ }
1818
+
1819
+ // src/components/inbox-sidebar.tsx
1820
+ import { useState as useState4, useMemo as useMemo3 } from "react";
1821
+
1822
+ // src/components/ui/input.tsx
1823
+ import { jsx as jsx15 } from "react/jsx-runtime";
1824
+ function Input({ className, type, ...props }) {
1825
+ return /* @__PURE__ */ jsx15(
1826
+ "input",
1827
+ {
1828
+ type,
1829
+ "data-slot": "input",
1830
+ className: cn(
1831
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
1832
+ className
1833
+ ),
1834
+ ...props
1835
+ }
1836
+ );
1837
+ }
1838
+
1839
+ // src/components/ui/tabs.tsx
1840
+ import { Tabs as TabsPrimitive } from "radix-ui";
1841
+ import { jsx as jsx16 } from "react/jsx-runtime";
1842
+ function Tabs({
1843
+ className,
1844
+ ...props
1845
+ }) {
1846
+ return /* @__PURE__ */ jsx16(
1847
+ TabsPrimitive.Root,
1848
+ {
1849
+ "data-slot": "tabs",
1850
+ className: cn("flex flex-col gap-2", className),
1851
+ ...props
1852
+ }
1853
+ );
1854
+ }
1855
+ function TabsList({
1856
+ className,
1857
+ ...props
1858
+ }) {
1859
+ return /* @__PURE__ */ jsx16(
1860
+ TabsPrimitive.List,
1861
+ {
1862
+ "data-slot": "tabs-list",
1863
+ className: cn(
1864
+ "inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
1865
+ className
1866
+ ),
1867
+ ...props
1868
+ }
1869
+ );
1870
+ }
1871
+ function TabsTrigger({
1872
+ className,
1873
+ ...props
1874
+ }) {
1875
+ return /* @__PURE__ */ jsx16(
1876
+ TabsPrimitive.Trigger,
1877
+ {
1878
+ "data-slot": "tabs-trigger",
1879
+ className: cn(
1880
+ "inline-flex items-center justify-center gap-1.5 rounded-md px-2.5 py-1 text-sm font-medium whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4",
1881
+ className
1882
+ ),
1883
+ ...props
1884
+ }
1885
+ );
1886
+ }
1887
+
1888
+ // src/components/ui/scroll-area.tsx
1889
+ import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
1890
+ import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
1891
+ function ScrollArea({
1892
+ className,
1893
+ children,
1894
+ ...props
1895
+ }) {
1896
+ return /* @__PURE__ */ jsxs8(
1897
+ ScrollAreaPrimitive.Root,
1898
+ {
1899
+ "data-slot": "scroll-area",
1900
+ className: cn("relative", className),
1901
+ ...props,
1902
+ children: [
1903
+ /* @__PURE__ */ jsx17(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit] [&>div]:!block", children }),
1904
+ /* @__PURE__ */ jsx17(ScrollBar, {}),
1905
+ /* @__PURE__ */ jsx17(ScrollAreaPrimitive.Corner, {})
1906
+ ]
1907
+ }
1908
+ );
1909
+ }
1910
+ function ScrollBar({
1911
+ className,
1912
+ orientation = "vertical",
1913
+ ...props
1914
+ }) {
1915
+ return /* @__PURE__ */ jsx17(
1916
+ ScrollAreaPrimitive.Scrollbar,
1917
+ {
1918
+ "data-slot": "scroll-bar",
1919
+ orientation,
1920
+ className: cn(
1921
+ "flex touch-none p-px transition-colors select-none",
1922
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
1923
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
1924
+ className
1925
+ ),
1926
+ ...props,
1927
+ children: /* @__PURE__ */ jsx17(ScrollAreaPrimitive.Thumb, { className: "relative flex-1 rounded-full bg-border" })
1928
+ }
1929
+ );
1930
+ }
1931
+
1932
+ // src/components/inbox-sidebar.tsx
1933
+ import { Search, Plus, Inbox as InboxIcon } from "lucide-react";
1934
+ import { jsx as jsx18, jsxs as jsxs9 } from "react/jsx-runtime";
1935
+ var STATUS_TABS = [
1936
+ { value: "all", label: "Todas" },
1937
+ { value: "open", label: "Abertas" },
1938
+ { value: "pending", label: "Pendentes" },
1939
+ { value: "resolved", label: "Resolvidas" }
1940
+ ];
1941
+ function InboxSidebar({
1942
+ inboxes,
1943
+ isLoading,
1944
+ selectedInboxId,
1945
+ onSelectInbox,
1946
+ onDeleteInbox,
1947
+ onCreateInbox,
1948
+ filterChannelId,
1949
+ renderHeader,
1950
+ renderFooter
1951
+ }) {
1952
+ const [search, setSearch] = useState4("");
1953
+ const [statusFilter, setStatusFilter] = useState4("all");
1954
+ const filtered = useMemo3(() => {
1955
+ if (!inboxes) return [];
1956
+ let list = inboxes;
1957
+ if (filterChannelId != null) {
1958
+ list = list.filter((i) => i.id_channel === filterChannelId);
1959
+ }
1960
+ if (statusFilter !== "all") {
1961
+ list = list.filter((i) => i.status === statusFilter);
1962
+ }
1963
+ if (search) {
1964
+ const q = search.toLowerCase();
1965
+ list = list.filter(
1966
+ (i) => i.contact_name?.toLowerCase().includes(q) || i.contact_phone?.toLowerCase().includes(q) || i.contact_identifier?.toLowerCase().includes(q) || i.last_message_content?.toLowerCase().includes(q)
1967
+ );
1968
+ }
1969
+ return [...list].sort((a, b) => {
1970
+ const dateA = a.last_message_at || a.datetime_add;
1971
+ const dateB = b.last_message_at || b.datetime_add;
1972
+ return new Date(dateB).getTime() - new Date(dateA).getTime();
1973
+ });
1974
+ }, [inboxes, statusFilter, search, filterChannelId]);
1975
+ return /* @__PURE__ */ jsxs9("div", { className: "flex h-full flex-col", children: [
1976
+ renderHeader,
1977
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 p-3", children: [
1978
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-2", children: [
1979
+ /* @__PURE__ */ jsxs9("div", { className: "relative flex-1", children: [
1980
+ /* @__PURE__ */ jsx18(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
1981
+ /* @__PURE__ */ jsx18(
1982
+ Input,
1983
+ {
1984
+ placeholder: "Buscar...",
1985
+ value: search,
1986
+ onChange: (e) => setSearch(e.target.value),
1987
+ className: "pl-9 h-8 text-sm"
1988
+ }
1989
+ )
1990
+ ] }),
1991
+ onCreateInbox && /* @__PURE__ */ jsx18(
1992
+ Button,
1993
+ {
1994
+ size: "icon",
1995
+ className: "h-8 w-8 shrink-0",
1996
+ onClick: onCreateInbox,
1997
+ children: /* @__PURE__ */ jsx18(Plus, { className: "h-4 w-4" })
1998
+ }
1999
+ )
2000
+ ] }),
2001
+ /* @__PURE__ */ jsx18(Tabs, { value: statusFilter, onValueChange: setStatusFilter, children: /* @__PURE__ */ jsx18(TabsList, { className: "w-full", children: STATUS_TABS.map((tab) => /* @__PURE__ */ jsx18(
2002
+ TabsTrigger,
2003
+ {
2004
+ value: tab.value,
2005
+ className: "flex-1 text-xs",
2006
+ children: tab.label
2007
+ },
2008
+ tab.value
2009
+ )) }) })
2010
+ ] }),
2011
+ /* @__PURE__ */ jsx18(ScrollArea, { className: "flex-1 px-2", children: isLoading ? /* @__PURE__ */ jsx18("div", { className: "space-y-2 p-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-3 p-3", children: [
2012
+ /* @__PURE__ */ jsx18(Skeleton, { className: "h-10 w-10 rounded-full shrink-0" }),
2013
+ /* @__PURE__ */ jsxs9("div", { className: "flex-1 space-y-1.5", children: [
2014
+ /* @__PURE__ */ jsx18(Skeleton, { className: "h-4 w-28" }),
2015
+ /* @__PURE__ */ jsx18(Skeleton, { className: "h-3 w-40" })
2016
+ ] })
2017
+ ] }, i)) }) : filtered.length === 0 ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center gap-2 py-8 text-sm text-muted-foreground", children: [
2018
+ /* @__PURE__ */ jsx18(InboxIcon, { className: "h-8 w-8 opacity-50" }),
2019
+ "Nenhuma conversa encontrada"
2020
+ ] }) : filtered.map((inbox) => /* @__PURE__ */ jsx18(
2021
+ InboxItem,
2022
+ {
2023
+ inbox,
2024
+ isSelected: inbox.id === selectedInboxId,
2025
+ onClick: () => onSelectInbox(inbox),
2026
+ onDelete: onDeleteInbox
2027
+ },
2028
+ inbox.id
2029
+ )) }),
2030
+ renderFooter
2031
+ ] });
2032
+ }
2033
+
2034
+ // src/components/ui/separator.tsx
2035
+ import { Separator as SeparatorPrimitive } from "radix-ui";
2036
+ import { jsx as jsx19 } from "react/jsx-runtime";
2037
+ function Separator({
2038
+ className,
2039
+ orientation = "horizontal",
2040
+ decorative = true,
2041
+ ...props
2042
+ }) {
2043
+ return /* @__PURE__ */ jsx19(
2044
+ SeparatorPrimitive.Root,
2045
+ {
2046
+ "data-slot": "separator",
2047
+ decorative,
2048
+ orientation,
2049
+ className: cn(
2050
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
2051
+ className
2052
+ ),
2053
+ ...props
2054
+ }
2055
+ );
2056
+ }
2057
+
2058
+ // src/components/contact-info-panel.tsx
2059
+ import {
2060
+ X,
2061
+ Phone,
2062
+ Fingerprint,
2063
+ Calendar,
2064
+ CalendarClock,
2065
+ Hash
2066
+ } from "lucide-react";
2067
+ import { format as format2 } from "date-fns";
2068
+ import { ptBR as ptBR3 } from "date-fns/locale";
2069
+ import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
2070
+ function InfoRow({
2071
+ icon: Icon,
2072
+ label,
2073
+ value
2074
+ }) {
2075
+ if (!value) return null;
2076
+ return /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-3 py-2", children: [
2077
+ /* @__PURE__ */ jsx20(Icon, { className: "h-4 w-4 text-muted-foreground mt-0.5 shrink-0" }),
2078
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
2079
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: label }),
2080
+ /* @__PURE__ */ jsx20("p", { className: "text-sm break-all", children: value })
2081
+ ] })
2082
+ ] });
2083
+ }
2084
+ function ContactInfoPanel({
2085
+ contact,
2086
+ isLoading,
2087
+ onClose,
2088
+ className
2089
+ }) {
2090
+ return /* @__PURE__ */ jsxs10(
2091
+ "div",
2092
+ {
2093
+ className: cn(
2094
+ "flex h-full w-[320px] shrink-0 flex-col border-l",
2095
+ className
2096
+ ),
2097
+ children: [
2098
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
2099
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium", children: "Informa\xE7\xF5es do contato" }),
2100
+ onClose && /* @__PURE__ */ jsx20(
2101
+ Button,
2102
+ {
2103
+ variant: "ghost",
2104
+ size: "icon",
2105
+ className: "h-7 w-7",
2106
+ onClick: onClose,
2107
+ children: /* @__PURE__ */ jsx20(X, { className: "h-4 w-4" })
2108
+ }
2109
+ )
2110
+ ] }),
2111
+ /* @__PURE__ */ jsx20("div", { className: "flex-1 overflow-y-auto p-4", children: isLoading ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center gap-3 pt-4", children: [
2112
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-16 w-16 rounded-full" }),
2113
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-5 w-32" }),
2114
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-4 w-24" })
2115
+ ] }) : contact ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
2116
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center gap-2 pb-4", children: [
2117
+ /* @__PURE__ */ jsx20(ContactAvatar, { name: contact.name, size: "lg" }),
2118
+ /* @__PURE__ */ jsx20("h3", { className: "text-base font-medium text-center", children: contact.name || "Desconhecido" })
2119
+ ] }),
2120
+ /* @__PURE__ */ jsx20(Separator, {}),
2121
+ /* @__PURE__ */ jsxs10("div", { className: "mt-3 space-y-1", children: [
2122
+ /* @__PURE__ */ jsx20(InfoRow, { icon: Hash, label: "ID", value: contact.id?.toString() }),
2123
+ /* @__PURE__ */ jsx20(
2124
+ InfoRow,
2125
+ {
2126
+ icon: Phone,
2127
+ label: "Telefone",
2128
+ value: contact.phone_number
2129
+ }
2130
+ ),
2131
+ /* @__PURE__ */ jsx20(
2132
+ InfoRow,
2133
+ {
2134
+ icon: Fingerprint,
2135
+ label: "Identificador",
2136
+ value: contact.identifier
2137
+ }
2138
+ ),
2139
+ /* @__PURE__ */ jsx20(
2140
+ InfoRow,
2141
+ {
2142
+ icon: Calendar,
2143
+ label: "Criado em",
2144
+ value: contact.datetime_add ? format2(
2145
+ new Date(contact.datetime_add),
2146
+ "dd/MM/yyyy '\xE0s' HH:mm",
2147
+ { locale: ptBR3 }
2148
+ ) : null
2149
+ }
2150
+ ),
2151
+ /* @__PURE__ */ jsx20(
2152
+ InfoRow,
2153
+ {
2154
+ icon: CalendarClock,
2155
+ label: "Atualizado em",
2156
+ value: contact.datetime_alt ? format2(
2157
+ new Date(contact.datetime_alt),
2158
+ "dd/MM/yyyy '\xE0s' HH:mm",
2159
+ { locale: ptBR3 }
2160
+ ) : null
2161
+ }
2162
+ )
2163
+ ] })
2164
+ ] }) : null })
2165
+ ]
2166
+ }
2167
+ );
2168
+ }
2169
+
2170
+ // src/components/new-conversation-dialog.tsx
2171
+ import { useState as useState5, useCallback, useEffect as useEffect2 } from "react";
2172
+
2173
+ // src/components/ui/dialog.tsx
2174
+ import { Dialog as DialogPrimitive } from "radix-ui";
2175
+ import { X as X2 } from "lucide-react";
2176
+ import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
2177
+ function Dialog({
2178
+ ...props
2179
+ }) {
2180
+ return /* @__PURE__ */ jsx21(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
2181
+ }
2182
+ function DialogPortal({
2183
+ ...props
2184
+ }) {
2185
+ return /* @__PURE__ */ jsx21(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
2186
+ }
2187
+ function DialogOverlay({
2188
+ className,
2189
+ ...props
2190
+ }) {
2191
+ return /* @__PURE__ */ jsx21(
2192
+ DialogPrimitive.Overlay,
2193
+ {
2194
+ "data-slot": "dialog-overlay",
2195
+ className: cn(
2196
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2197
+ className
2198
+ ),
2199
+ ...props
2200
+ }
2201
+ );
2202
+ }
2203
+ function DialogContent({
2204
+ className,
2205
+ children,
2206
+ ...props
2207
+ }) {
2208
+ return /* @__PURE__ */ jsxs11(DialogPortal, { children: [
2209
+ /* @__PURE__ */ jsx21(DialogOverlay, {}),
2210
+ /* @__PURE__ */ jsxs11(
2211
+ DialogPrimitive.Content,
2212
+ {
2213
+ "data-slot": "dialog-content",
2214
+ className: cn(
2215
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
2216
+ className
2217
+ ),
2218
+ ...props,
2219
+ children: [
2220
+ children,
2221
+ /* @__PURE__ */ jsxs11(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
2222
+ /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
2223
+ /* @__PURE__ */ jsx21("span", { className: "sr-only", children: "Close" })
2224
+ ] })
2225
+ ]
2226
+ }
2227
+ )
2228
+ ] });
2229
+ }
2230
+ function DialogHeader({ className, ...props }) {
2231
+ return /* @__PURE__ */ jsx21(
2232
+ "div",
2233
+ {
2234
+ "data-slot": "dialog-header",
2235
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
2236
+ ...props
2237
+ }
2238
+ );
2239
+ }
2240
+ function DialogFooter({ className, ...props }) {
2241
+ return /* @__PURE__ */ jsx21(
2242
+ "div",
2243
+ {
2244
+ "data-slot": "dialog-footer",
2245
+ className: cn(
2246
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2",
2247
+ className
2248
+ ),
2249
+ ...props
2250
+ }
2251
+ );
2252
+ }
2253
+ function DialogTitle({
2254
+ className,
2255
+ ...props
2256
+ }) {
2257
+ return /* @__PURE__ */ jsx21(
2258
+ DialogPrimitive.Title,
2259
+ {
2260
+ "data-slot": "dialog-title",
2261
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
2262
+ ...props
2263
+ }
2264
+ );
2265
+ }
2266
+ function DialogDescription({
2267
+ className,
2268
+ ...props
2269
+ }) {
2270
+ return /* @__PURE__ */ jsx21(
2271
+ DialogPrimitive.Description,
2272
+ {
2273
+ "data-slot": "dialog-description",
2274
+ className: cn("text-sm text-muted-foreground", className),
2275
+ ...props
2276
+ }
2277
+ );
2278
+ }
2279
+
2280
+ // src/components/ui/command.tsx
2281
+ import { Command as CommandPrimitive } from "cmdk";
2282
+ import { Search as Search2 } from "lucide-react";
2283
+ import { jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
2284
+ function Command({
2285
+ className,
2286
+ ...props
2287
+ }) {
2288
+ return /* @__PURE__ */ jsx22(
2289
+ CommandPrimitive,
2290
+ {
2291
+ "data-slot": "command",
2292
+ className: cn(
2293
+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
2294
+ className
2295
+ ),
2296
+ ...props
2297
+ }
2298
+ );
2299
+ }
2300
+ function CommandInput({
2301
+ className,
2302
+ ...props
2303
+ }) {
2304
+ return /* @__PURE__ */ jsxs12("div", { className: "flex h-9 items-center gap-2 border-b px-3", "data-slot": "command-input-wrapper", children: [
2305
+ /* @__PURE__ */ jsx22(Search2, { className: "size-4 shrink-0 opacity-50" }),
2306
+ /* @__PURE__ */ jsx22(
2307
+ CommandPrimitive.Input,
2308
+ {
2309
+ "data-slot": "command-input",
2310
+ className: cn(
2311
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
2312
+ className
2313
+ ),
2314
+ ...props
2315
+ }
2316
+ )
2317
+ ] });
2318
+ }
2319
+ function CommandList({
2320
+ className,
2321
+ ...props
2322
+ }) {
2323
+ return /* @__PURE__ */ jsx22(
2324
+ CommandPrimitive.List,
2325
+ {
2326
+ "data-slot": "command-list",
2327
+ className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className),
2328
+ ...props
2329
+ }
2330
+ );
2331
+ }
2332
+ function CommandEmpty({
2333
+ ...props
2334
+ }) {
2335
+ return /* @__PURE__ */ jsx22(
2336
+ CommandPrimitive.Empty,
2337
+ {
2338
+ "data-slot": "command-empty",
2339
+ className: "py-6 text-center text-sm",
2340
+ ...props
2341
+ }
2342
+ );
2343
+ }
2344
+ function CommandGroup({
2345
+ className,
2346
+ ...props
2347
+ }) {
2348
+ return /* @__PURE__ */ jsx22(
2349
+ CommandPrimitive.Group,
2350
+ {
2351
+ "data-slot": "command-group",
2352
+ className: cn(
2353
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
2354
+ className
2355
+ ),
2356
+ ...props
2357
+ }
2358
+ );
2359
+ }
2360
+ function CommandItem({
2361
+ className,
2362
+ ...props
2363
+ }) {
2364
+ return /* @__PURE__ */ jsx22(
2365
+ CommandPrimitive.Item,
2366
+ {
2367
+ "data-slot": "command-item",
2368
+ className: cn(
2369
+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
2370
+ className
2371
+ ),
2372
+ ...props
2373
+ }
2374
+ );
2375
+ }
2376
+
2377
+ // src/components/new-conversation-dialog.tsx
2378
+ import { Loader2 } from "lucide-react";
2379
+ import { jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
2380
+ function NewConversationDialog({
2381
+ open,
2382
+ onOpenChange,
2383
+ contacts,
2384
+ channels,
2385
+ existingInboxes,
2386
+ onCreateInbox,
2387
+ isCreating,
2388
+ onCreated
2389
+ }) {
2390
+ const [selectedContact, setSelectedContact] = useState5(null);
2391
+ const [selectedChannelId, setSelectedChannelId] = useState5(
2392
+ null
2393
+ );
2394
+ const effectiveChannelId = selectedChannelId ?? (channels.length === 1 ? channels[0]?.id : null);
2395
+ useEffect2(() => {
2396
+ if (!open) {
2397
+ setSelectedContact(null);
2398
+ setSelectedChannelId(null);
2399
+ }
2400
+ }, [open]);
2401
+ const handleCreate = useCallback(() => {
2402
+ if (!selectedContact || !effectiveChannelId) return;
2403
+ if (existingInboxes) {
2404
+ const existing = existingInboxes.find(
2405
+ (i) => i.id_contact === selectedContact.id && i.id_channel === effectiveChannelId
2406
+ );
2407
+ if (existing) {
2408
+ onCreated?.(existing.id);
2409
+ onOpenChange(false);
2410
+ return;
2411
+ }
2412
+ }
2413
+ onCreateInbox(selectedContact.id, effectiveChannelId);
2414
+ }, [
2415
+ selectedContact,
2416
+ effectiveChannelId,
2417
+ existingInboxes,
2418
+ onCreateInbox,
2419
+ onCreated,
2420
+ onOpenChange
2421
+ ]);
2422
+ return /* @__PURE__ */ jsx23(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs13(DialogContent, { className: "sm:max-w-md p-0 gap-0", children: [
2423
+ /* @__PURE__ */ jsxs13(DialogHeader, { className: "px-4 pt-4 pb-2", children: [
2424
+ /* @__PURE__ */ jsx23(DialogTitle, { children: "Nova conversa" }),
2425
+ /* @__PURE__ */ jsx23(DialogDescription, { children: "Selecione um contato para iniciar uma conversa" })
2426
+ ] }),
2427
+ channels.length > 1 && /* @__PURE__ */ jsx23("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsxs13(
2428
+ Select,
2429
+ {
2430
+ value: selectedChannelId?.toString() ?? "",
2431
+ onValueChange: (v) => setSelectedChannelId(Number(v)),
2432
+ children: [
2433
+ /* @__PURE__ */ jsx23(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx23(SelectValue, { placeholder: "Selecione um canal" }) }),
2434
+ /* @__PURE__ */ jsx23(SelectContent, { children: channels.map((ch) => /* @__PURE__ */ jsx23(SelectItem, { value: ch.id.toString(), children: ch.name }, ch.id)) })
2435
+ ]
2436
+ }
2437
+ ) }),
2438
+ /* @__PURE__ */ jsxs13(Command, { className: "rounded-none border-none shadow-none", children: [
2439
+ /* @__PURE__ */ jsx23("div", { className: "px-2", children: /* @__PURE__ */ jsx23(CommandInput, { placeholder: "Buscar contato..." }) }),
2440
+ /* @__PURE__ */ jsxs13(CommandList, { className: "max-h-64 px-2", children: [
2441
+ /* @__PURE__ */ jsx23(CommandEmpty, { children: "Nenhum contato encontrado" }),
2442
+ /* @__PURE__ */ jsx23(CommandGroup, { children: contacts.map((contact) => /* @__PURE__ */ jsxs13(
2443
+ CommandItem,
2444
+ {
2445
+ value: `${contact.name} ${contact.phone_number || ""}`,
2446
+ onSelect: () => setSelectedContact(contact),
2447
+ "data-checked": selectedContact?.id === contact.id,
2448
+ className: "gap-3",
2449
+ children: [
2450
+ /* @__PURE__ */ jsx23(ContactAvatar, { name: contact.name, size: "sm" }),
2451
+ /* @__PURE__ */ jsxs13("div", { className: "min-w-0 flex-1", children: [
2452
+ /* @__PURE__ */ jsx23("p", { className: "truncate text-sm font-medium", children: contact.name }),
2453
+ /* @__PURE__ */ jsx23("p", { className: "truncate text-xs text-muted-foreground", children: contact.phone_number || contact.identifier || "Sem telefone" })
2454
+ ] })
2455
+ ]
2456
+ },
2457
+ contact.id
2458
+ )) })
2459
+ ] })
2460
+ ] }),
2461
+ /* @__PURE__ */ jsxs13(DialogFooter, { className: "px-4 py-3 border-t", children: [
2462
+ /* @__PURE__ */ jsx23(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancelar" }),
2463
+ /* @__PURE__ */ jsxs13(
2464
+ Button,
2465
+ {
2466
+ disabled: !selectedContact || !effectiveChannelId || isCreating,
2467
+ onClick: handleCreate,
2468
+ children: [
2469
+ isCreating && /* @__PURE__ */ jsx23(Loader2, { className: "h-4 w-4 animate-spin" }),
2470
+ "Iniciar conversa"
2471
+ ]
2472
+ }
2473
+ )
2474
+ ] })
2475
+ ] }) });
2476
+ }
1540
2477
  export {
1541
2478
  ChatInput,
1542
2479
  ChatView,
2480
+ ContactAvatar,
2481
+ ContactInfoPanel,
1543
2482
  DEFAULT_CHANNEL_STATUS_POLLING,
1544
2483
  DEFAULT_INBOX_POLLING,
1545
2484
  DEFAULT_MESSAGES_POLLING,
1546
2485
  DEFAULT_QR_POLLING,
2486
+ InboxItem,
2487
+ InboxSidebar,
1547
2488
  MessageBubble,
2489
+ NewConversationDialog,
1548
2490
  cn,
1549
2491
  createGchatClient,
1550
2492
  formatDateGroup,