@lindaui/blocks 0.4.2 → 0.5.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.
Files changed (255) hide show
  1. package/README.md +56 -0
  2. package/dist/ai-cost-card-f403ypsg.js +99 -0
  3. package/dist/ai-performance-card-cggm09nc.js +191 -0
  4. package/dist/analytics/ai-cost-card.d.ts +3 -0
  5. package/dist/analytics/ai-cost-card.d.ts.map +1 -0
  6. package/dist/analytics/ai-performance-card.d.ts +3 -0
  7. package/dist/analytics/ai-performance-card.d.ts.map +1 -0
  8. package/dist/analytics/analytics-card.d.ts +3 -0
  9. package/dist/analytics/analytics-card.d.ts.map +1 -0
  10. package/dist/analytics/analytics-skeleton.d.ts +5 -0
  11. package/dist/analytics/analytics-skeleton.d.ts.map +1 -0
  12. package/dist/analytics/entity-health-card.d.ts +3 -0
  13. package/dist/analytics/entity-health-card.d.ts.map +1 -0
  14. package/dist/analytics/status-distribution-chart.d.ts +3 -0
  15. package/dist/analytics/status-distribution-chart.d.ts.map +1 -0
  16. package/dist/analytics/time-range-picker.d.ts +3 -0
  17. package/dist/analytics/time-range-picker.d.ts.map +1 -0
  18. package/dist/analytics/top-entities-table.d.ts +3 -0
  19. package/dist/analytics/top-entities-table.d.ts.map +1 -0
  20. package/dist/analytics/types.d.ts +104 -0
  21. package/dist/analytics/types.d.ts.map +1 -0
  22. package/dist/analytics-card-bcmijpt6.js +38 -0
  23. package/dist/analytics-skeleton-imve37du.js +164 -0
  24. package/dist/analytics.d.ts +11 -0
  25. package/dist/analytics.d.ts.map +1 -0
  26. package/dist/analytics.js +8 -0
  27. package/dist/app-shell/drawer-focus.d.ts +8 -0
  28. package/dist/app-shell/drawer-focus.d.ts.map +1 -0
  29. package/dist/app-shell/footer.d.ts +39 -0
  30. package/dist/app-shell/footer.d.ts.map +1 -0
  31. package/dist/app-shell/logout-dialog.d.ts +6 -0
  32. package/dist/app-shell/logout-dialog.d.ts.map +1 -0
  33. package/dist/app-shell/navigation.d.ts +15 -0
  34. package/dist/app-shell/navigation.d.ts.map +1 -0
  35. package/dist/app-shell/types.d.ts +55 -0
  36. package/dist/app-shell/types.d.ts.map +1 -0
  37. package/dist/app-shell.d.ts +7 -0
  38. package/dist/app-shell.d.ts.map +1 -0
  39. package/dist/app-shell.js +254 -0
  40. package/dist/channel-settings.d.ts +24 -0
  41. package/dist/channel-settings.d.ts.map +1 -0
  42. package/dist/channel-settings.js +60 -0
  43. package/dist/chat-avatar-jdmeyf2o.js +30 -0
  44. package/dist/chat-layout-fwx3x26g.js +25 -0
  45. package/dist/chat.d.ts +22 -0
  46. package/dist/chat.d.ts.map +1 -0
  47. package/dist/chat.js +66 -0
  48. package/dist/command-palette.d.ts +22 -0
  49. package/dist/command-palette.d.ts.map +1 -0
  50. package/dist/command-palette.js +141 -0
  51. package/dist/compat-chat/chat-avatar.d.ts +16 -0
  52. package/dist/compat-chat/chat-avatar.d.ts.map +1 -0
  53. package/dist/compat-chat/chat-header.d.ts +9 -0
  54. package/dist/compat-chat/chat-header.d.ts.map +1 -0
  55. package/dist/compat-chat/chat-layout.d.ts +10 -0
  56. package/dist/compat-chat/chat-layout.d.ts.map +1 -0
  57. package/dist/compat-chat/conversation-list-item.d.ts +10 -0
  58. package/dist/compat-chat/conversation-list-item.d.ts.map +1 -0
  59. package/dist/compat-chat/conversation-list.d.ts +24 -0
  60. package/dist/compat-chat/conversation-list.d.ts.map +1 -0
  61. package/dist/compat-chat/conversation-thread.d.ts +18 -0
  62. package/dist/compat-chat/conversation-thread.d.ts.map +1 -0
  63. package/dist/compat-chat/date-divider.d.ts +5 -0
  64. package/dist/compat-chat/date-divider.d.ts.map +1 -0
  65. package/dist/compat-chat/empty-chat-view.d.ts +6 -0
  66. package/dist/compat-chat/empty-chat-view.d.ts.map +1 -0
  67. package/dist/compat-chat/image-lightbox.d.ts +7 -0
  68. package/dist/compat-chat/image-lightbox.d.ts.map +1 -0
  69. package/dist/compat-chat/message-bubble.d.ts +6 -0
  70. package/dist/compat-chat/message-bubble.d.ts.map +1 -0
  71. package/dist/compat-chat/message-composer.d.ts +11 -0
  72. package/dist/compat-chat/message-composer.d.ts.map +1 -0
  73. package/dist/compat-chat/time-divider.d.ts +5 -0
  74. package/dist/compat-chat/time-divider.d.ts.map +1 -0
  75. package/dist/compat-chat/time-grouping.d.ts +5 -0
  76. package/dist/compat-chat/time-grouping.d.ts.map +1 -0
  77. package/dist/compat-chat/types.d.ts +35 -0
  78. package/dist/compat-chat/types.d.ts.map +1 -0
  79. package/dist/compat-chat/whatsapp-audio-player.d.ts +7 -0
  80. package/dist/compat-chat/whatsapp-audio-player.d.ts.map +1 -0
  81. package/dist/compat.d.ts +9 -0
  82. package/dist/compat.d.ts.map +1 -0
  83. package/dist/compat.js +4 -0
  84. package/dist/connected-panel-lhwy25yk.js +132 -0
  85. package/dist/connection-panel/connected-panel.d.ts +26 -0
  86. package/dist/connection-panel/connected-panel.d.ts.map +1 -0
  87. package/dist/connection-panel/disconnected-panel.d.ts +12 -0
  88. package/dist/connection-panel/disconnected-panel.d.ts.map +1 -0
  89. package/dist/connection-panel/instructions.d.ts +5 -0
  90. package/dist/connection-panel/instructions.d.ts.map +1 -0
  91. package/dist/connection-panel/qr-panel.d.ts +14 -0
  92. package/dist/connection-panel/qr-panel.d.ts.map +1 -0
  93. package/dist/connection-panel/use-qr-refresh.d.ts +18 -0
  94. package/dist/connection-panel/use-qr-refresh.d.ts.map +1 -0
  95. package/dist/connection-panel.d.ts +39 -0
  96. package/dist/connection-panel.d.ts.map +1 -0
  97. package/dist/connection-panel.js +328 -0
  98. package/dist/conversation-list-ekq2dzca.js +133 -0
  99. package/dist/conversation-list-item-fy6t5af2.js +55 -0
  100. package/dist/conversation-thread-c60431oa.js +205 -0
  101. package/dist/entity-card.d.ts +17 -0
  102. package/dist/entity-card.d.ts.map +1 -0
  103. package/dist/entity-card.js +69 -0
  104. package/dist/entity-health-card-ggnmrtrq.js +192 -0
  105. package/dist/entity-switcher.d.ts +33 -0
  106. package/dist/entity-switcher.d.ts.map +1 -0
  107. package/dist/entity-switcher.js +137 -0
  108. package/dist/filter-bar.d.ts +41 -0
  109. package/dist/filter-bar.d.ts.map +1 -0
  110. package/dist/filter-bar.js +155 -0
  111. package/dist/footer-f7r5e42b.js +192 -0
  112. package/dist/image-lightbox-ci3arb6z.js +62 -0
  113. package/dist/kanban/drop-utils.d.ts +18 -0
  114. package/dist/kanban/drop-utils.d.ts.map +1 -0
  115. package/dist/kanban/navigation-buttons.d.ts +9 -0
  116. package/dist/kanban/navigation-buttons.d.ts.map +1 -0
  117. package/dist/kanban/scroll-indicators.d.ts +6 -0
  118. package/dist/kanban/scroll-indicators.d.ts.map +1 -0
  119. package/dist/kanban/use-keyboard-navigation.d.ts +4 -0
  120. package/dist/kanban/use-keyboard-navigation.d.ts.map +1 -0
  121. package/dist/kanban-board.d.ts +31 -0
  122. package/dist/kanban-board.d.ts.map +1 -0
  123. package/dist/kanban-board.js +128 -0
  124. package/dist/kanban-column.d.ts +13 -0
  125. package/dist/kanban-column.d.ts.map +1 -0
  126. package/dist/kanban-column.js +58 -0
  127. package/dist/legacy-kpi-card.d.ts +16 -0
  128. package/dist/legacy-kpi-card.d.ts.map +1 -0
  129. package/dist/legacy-kpi-card.js +4 -0
  130. package/dist/logout-dialog-bbv59tbi.js +93 -0
  131. package/dist/message-bubble-odjvhroq.js +110 -0
  132. package/dist/message-composer-m2n6soo7.js +59 -0
  133. package/dist/navigation-buttons-e77mshh0.js +65 -0
  134. package/dist/navigation-hotcrqoq.js +115 -0
  135. package/dist/save-bar.d.ts +15 -0
  136. package/dist/save-bar.d.ts.map +1 -0
  137. package/dist/save-bar.js +38 -0
  138. package/dist/scroll-indicators-gvxapogd.js +39 -0
  139. package/dist/settings-layout.d.ts +20 -0
  140. package/dist/settings-layout.d.ts.map +1 -0
  141. package/dist/settings-layout.js +56 -0
  142. package/dist/source-selector.d.ts +18 -0
  143. package/dist/source-selector.d.ts.map +1 -0
  144. package/dist/source-selector.js +59 -0
  145. package/dist/status-distribution-chart-oucedzv7.js +87 -0
  146. package/dist/status-filters.d.ts +12 -0
  147. package/dist/status-filters.d.ts.map +1 -0
  148. package/dist/status-filters.js +31 -0
  149. package/dist/stepper.d.ts +15 -0
  150. package/dist/stepper.d.ts.map +1 -0
  151. package/dist/stepper.js +42 -0
  152. package/dist/time-range-picker-mu38hul4.js +123 -0
  153. package/dist/top-entities-table-o7xzpq96.js +78 -0
  154. package/dist/use-keyboard-navigation-he1xmriz.js +52 -0
  155. package/dist/whatsapp-audio-player-nooty6jb.js +277 -0
  156. package/llms.txt +87 -4
  157. package/package.json +191 -46
  158. package/src/analytics/ai-cost-card.tsx +31 -0
  159. package/src/analytics/ai-performance-card.tsx +48 -0
  160. package/src/analytics/analytics-card.tsx +14 -0
  161. package/src/analytics/analytics-skeleton.tsx +15 -0
  162. package/src/analytics/entity-health-card.tsx +50 -0
  163. package/src/analytics/status-distribution-chart.tsx +20 -0
  164. package/src/analytics/time-range-picker.tsx +43 -0
  165. package/src/analytics/top-entities-table.tsx +24 -0
  166. package/src/analytics/types.ts +43 -0
  167. package/src/analytics.ts +10 -0
  168. package/src/app-shell/drawer-focus.ts +42 -0
  169. package/src/app-shell/footer.tsx +160 -0
  170. package/src/app-shell/logout-dialog.tsx +68 -0
  171. package/src/app-shell/navigation.tsx +147 -0
  172. package/src/app-shell/types.ts +57 -0
  173. package/src/app-shell.tsx +166 -0
  174. package/src/area-chart.tsx +85 -0
  175. package/src/audio-player.tsx +74 -0
  176. package/src/auth-layout.tsx +132 -0
  177. package/src/auth-provider.tsx +29 -0
  178. package/src/bar-chart.tsx +78 -0
  179. package/src/channel-settings.tsx +76 -0
  180. package/src/chart-card.tsx +45 -0
  181. package/src/chat-avatar.tsx +57 -0
  182. package/src/chat-date-divider.tsx +18 -0
  183. package/src/chat-header.tsx +24 -0
  184. package/src/chat-layout.tsx +41 -0
  185. package/src/chat-time-divider.tsx +20 -0
  186. package/src/chat-types.ts +32 -0
  187. package/src/chat.ts +21 -0
  188. package/src/command-palette.tsx +110 -0
  189. package/src/compat-chat/chat-avatar.tsx +23 -0
  190. package/src/compat-chat/chat-header.tsx +12 -0
  191. package/src/compat-chat/chat-layout.tsx +14 -0
  192. package/src/compat-chat/conversation-list-item.tsx +27 -0
  193. package/src/compat-chat/conversation-list.tsx +49 -0
  194. package/src/compat-chat/conversation-thread.tsx +92 -0
  195. package/src/compat-chat/date-divider.tsx +7 -0
  196. package/src/compat-chat/empty-chat-view.tsx +9 -0
  197. package/src/compat-chat/image-lightbox.tsx +22 -0
  198. package/src/compat-chat/message-bubble.tsx +28 -0
  199. package/src/compat-chat/message-composer.tsx +26 -0
  200. package/src/compat-chat/time-divider.tsx +5 -0
  201. package/src/compat-chat/time-grouping.ts +19 -0
  202. package/src/compat-chat/types.ts +31 -0
  203. package/src/compat-chat/whatsapp-audio-player.tsx +100 -0
  204. package/src/compat.ts +8 -0
  205. package/src/confirm-dialog.tsx +85 -0
  206. package/src/connection-panel/connected-panel.tsx +106 -0
  207. package/src/connection-panel/disconnected-panel.tsx +48 -0
  208. package/src/connection-panel/instructions.tsx +9 -0
  209. package/src/connection-panel/qr-panel.tsx +62 -0
  210. package/src/connection-panel/use-qr-refresh.ts +52 -0
  211. package/src/connection-panel.tsx +141 -0
  212. package/src/conversation-list-item.tsx +53 -0
  213. package/src/conversation-list.tsx +67 -0
  214. package/src/conversation-thread.tsx +64 -0
  215. package/src/detail-panel.tsx +123 -0
  216. package/src/document-panel.tsx +109 -0
  217. package/src/document-reader.tsx +29 -0
  218. package/src/empty-chat-view.tsx +25 -0
  219. package/src/entity-card.tsx +74 -0
  220. package/src/entity-health-card.tsx +53 -0
  221. package/src/entity-switcher.tsx +126 -0
  222. package/src/fetching-indicator.tsx +16 -0
  223. package/src/field-array-form.tsx +166 -0
  224. package/src/filter-bar.tsx +203 -0
  225. package/src/image-viewer.tsx +129 -0
  226. package/src/kanban/drop-utils.ts +31 -0
  227. package/src/kanban/navigation-buttons.tsx +80 -0
  228. package/src/kanban/scroll-indicators.tsx +47 -0
  229. package/src/kanban/use-keyboard-navigation.ts +43 -0
  230. package/src/kanban-board.tsx +160 -0
  231. package/src/kanban-column.tsx +62 -0
  232. package/src/legacy-kpi-card.tsx +38 -0
  233. package/src/line-chart.tsx +58 -0
  234. package/src/list-item.tsx +63 -0
  235. package/src/list-pane.tsx +143 -0
  236. package/src/login-form.tsx +128 -0
  237. package/src/message-bubble.tsx +70 -0
  238. package/src/message-composer.tsx +52 -0
  239. package/src/pie-chart.tsx +70 -0
  240. package/src/radar-chart.tsx +55 -0
  241. package/src/radial-chart.tsx +103 -0
  242. package/src/recording-overlay.tsx +159 -0
  243. package/src/save-bar.tsx +35 -0
  244. package/src/settings-layout.tsx +79 -0
  245. package/src/source-selector.tsx +71 -0
  246. package/src/split-workspace.tsx +74 -0
  247. package/src/stat-card.tsx +166 -0
  248. package/src/stats-grid.tsx +23 -0
  249. package/src/status-filters.tsx +28 -0
  250. package/src/stepper.tsx +47 -0
  251. package/src/table-pane.tsx +194 -0
  252. package/src/time-range-picker.tsx +89 -0
  253. package/src/workspace-back-button.tsx +32 -0
  254. package/dist/chat-types.test.d.ts +0 -2
  255. package/dist/chat-types.test.d.ts.map +0 -1
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { InputGroup, ScrollShadow, Spinner } from '@lindaui/ui/compound';
4
+ import { Search, X } from 'lucide-react';
5
+ import { useCallback } from 'react';
6
+ import { cn } from '@lindaui/ui/cn';
7
+ import dayjs from 'dayjs';
8
+ import { C as ConversationListItem } from './conversation-list-item-fy6t5af2.js';
9
+
10
+ function TimeDivider({ label, className }) {
11
+ return /*#__PURE__*/ jsx("div", {
12
+ className: cn("sticky top-0 z-10 bg-background/80 px-4 py-2 backdrop-blur-sm", className),
13
+ children: /*#__PURE__*/ jsx("span", {
14
+ className: "font-medium text-[11px] text-muted",
15
+ children: label
16
+ })
17
+ });
18
+ }
19
+
20
+ const TIME_GROUP_ORDER = [
21
+ "Hoy",
22
+ "Ayer",
23
+ "Esta semana",
24
+ "Este mes",
25
+ "Más antiguos"
26
+ ];
27
+ function getTimeGroup(date) {
28
+ const now = dayjs();
29
+ const target = dayjs(date);
30
+ if (target.isSame(now, "day")) return "Hoy";
31
+ if (target.isSame(now.subtract(1, "day"), "day")) return "Ayer";
32
+ if (target.isAfter(now.subtract(7, "day"))) return "Esta semana";
33
+ if (target.isAfter(now.subtract(30, "day"))) return "Este mes";
34
+ return "Más antiguos";
35
+ }
36
+ function groupByTime(items, getDate) {
37
+ const grouped = {};
38
+ for (const group of TIME_GROUP_ORDER)grouped[group] = [];
39
+ items.forEach((item)=>grouped[getTimeGroup(getDate(item))].push(item));
40
+ return grouped;
41
+ }
42
+
43
+ function ConversationList({ items, searchValue, onSearchChange, searchPlaceholder, filters, labels, isLoading = false, emptyMessage, onItemSelect, renderAvatar, renderItems }) {
44
+ const grouped = groupByTime(items, (item)=>item.timestamp ?? new Date());
45
+ const handleSearchChange = useCallback((e)=>onSearchChange(e.target.value), [
46
+ onSearchChange
47
+ ]);
48
+ const handleClearSearch = useCallback(()=>onSearchChange(""), [
49
+ onSearchChange
50
+ ]);
51
+ const handleItemSelect = useCallback((id)=>()=>onItemSelect(id), [
52
+ onItemSelect
53
+ ]);
54
+ const renderItem = (item)=>/*#__PURE__*/ jsx(ConversationListItem, {
55
+ avatar: renderAvatar?.(item),
56
+ emptyPreview: labels.emptyPreview,
57
+ item: item,
58
+ onClick: handleItemSelect(item.id)
59
+ }, item.id);
60
+ return /*#__PURE__*/ jsxs("div", {
61
+ className: "flex h-full flex-col bg-background",
62
+ children: [
63
+ /*#__PURE__*/ jsxs("div", {
64
+ className: "space-y-3 border-border border-b p-4",
65
+ children: [
66
+ /*#__PURE__*/ jsxs(InputGroup, {
67
+ className: "w-full rounded-lg",
68
+ children: [
69
+ /*#__PURE__*/ jsx(InputGroup.Prefix, {
70
+ children: /*#__PURE__*/ jsx(Search, {
71
+ className: "h-4 w-4 text-muted"
72
+ })
73
+ }),
74
+ /*#__PURE__*/ jsx(InputGroup.Input, {
75
+ className: "text-sm",
76
+ onChange: handleSearchChange,
77
+ placeholder: searchPlaceholder,
78
+ value: searchValue
79
+ }),
80
+ searchValue && /*#__PURE__*/ jsx(InputGroup.Suffix, {
81
+ children: /*#__PURE__*/ jsx("button", {
82
+ className: "text-muted hover:text-muted",
83
+ onClick: handleClearSearch,
84
+ type: "button",
85
+ children: /*#__PURE__*/ jsx(X, {
86
+ className: "h-4 w-4"
87
+ })
88
+ })
89
+ })
90
+ ]
91
+ }),
92
+ filters
93
+ ]
94
+ }),
95
+ /*#__PURE__*/ jsxs(ScrollShadow, {
96
+ className: "flex-1 overflow-y-auto",
97
+ children: [
98
+ isLoading && /*#__PURE__*/ jsxs("div", {
99
+ className: "flex items-center justify-center gap-2 py-8",
100
+ children: [
101
+ /*#__PURE__*/ jsx(Spinner, {
102
+ size: "sm"
103
+ }),
104
+ /*#__PURE__*/ jsx("span", {
105
+ className: "text-muted text-sm",
106
+ children: labels.loading
107
+ })
108
+ ]
109
+ }),
110
+ !isLoading && items.length === 0 && /*#__PURE__*/ jsx("div", {
111
+ className: "flex flex-col items-center justify-center px-4 py-12 text-center",
112
+ children: /*#__PURE__*/ jsx("p", {
113
+ className: "text-muted text-sm",
114
+ children: emptyMessage
115
+ })
116
+ }),
117
+ !isLoading && items.length > 0 && (renderItems ? renderItems(items, renderItem) : /*#__PURE__*/ jsx("div", {
118
+ children: TIME_GROUP_ORDER.map((timeGroup)=>grouped[timeGroup].length === 0 ? null : /*#__PURE__*/ jsxs("div", {
119
+ children: [
120
+ /*#__PURE__*/ jsx(TimeDivider, {
121
+ label: labels.periods[timeGroup]
122
+ }),
123
+ grouped[timeGroup].map(renderItem)
124
+ ]
125
+ }, timeGroup))
126
+ }))
127
+ ]
128
+ })
129
+ ]
130
+ });
131
+ }
132
+
133
+ export { ConversationList as C };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { memo } from 'react';
4
+ import { formatRelativeTime, cn } from '@lindaui/ui/compat';
5
+
6
+ const ConversationListItem = /*#__PURE__*/ memo(({ item, onClick, avatar, emptyPreview })=>/*#__PURE__*/ jsx("button", {
7
+ className: cn("w-full cursor-pointer px-4 py-3 transition-colors", "md:hover:bg-surface-secondary", item.isSelected && "bg-blue-50 dark:bg-blue-950/20"),
8
+ onClick: onClick,
9
+ type: "button",
10
+ children: /*#__PURE__*/ jsxs("div", {
11
+ className: "flex items-start gap-3",
12
+ children: [
13
+ avatar,
14
+ /*#__PURE__*/ jsxs("div", {
15
+ className: "min-w-0 flex-1 text-left",
16
+ children: [
17
+ /*#__PURE__*/ jsxs("div", {
18
+ className: "mb-0.5 flex items-center justify-between gap-2",
19
+ children: [
20
+ /*#__PURE__*/ jsx("h4", {
21
+ className: "truncate font-semibold text-foreground",
22
+ children: item.title
23
+ }),
24
+ item.timestamp && /*#__PURE__*/ jsx("span", {
25
+ className: "flex-shrink-0 text-muted text-xs",
26
+ children: formatRelativeTime(item.timestamp)
27
+ })
28
+ ]
29
+ }),
30
+ /*#__PURE__*/ jsxs("div", {
31
+ className: "flex items-center justify-between gap-2",
32
+ children: [
33
+ /*#__PURE__*/ jsx("p", {
34
+ className: "truncate text-muted text-sm",
35
+ children: item.preview ?? emptyPreview
36
+ }),
37
+ /*#__PURE__*/ jsxs("div", {
38
+ className: "flex flex-shrink-0 items-center gap-1.5",
39
+ children: [
40
+ item.badge,
41
+ item.unreadCount ? /*#__PURE__*/ jsx("span", {
42
+ className: "inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 font-semibold text-accent-foreground text-xs",
43
+ children: item.unreadCount
44
+ }) : null
45
+ ]
46
+ })
47
+ ]
48
+ })
49
+ ]
50
+ })
51
+ ]
52
+ })
53
+ }));
54
+
55
+ export { ConversationListItem as C };
@@ -0,0 +1,205 @@
1
+ 'use client';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { ScrollShadow, Spinner } from '@lindaui/ui/compound';
4
+ import { cn } from '@lindaui/ui/cn';
5
+ import dayjs from 'dayjs';
6
+ import { AlertCircle } from 'lucide-react';
7
+ import { useRef, useMemo, useCallback, useLayoutEffect, useEffect } from 'react';
8
+ import { isToday, isYesterday } from '@lindaui/ui/compat';
9
+ import { M as MessageBubble } from './message-bubble-odjvhroq.js';
10
+
11
+ function DateDivider({ date }) {
12
+ return /*#__PURE__*/ jsx("div", {
13
+ className: "flex justify-center py-2",
14
+ children: /*#__PURE__*/ jsx("span", {
15
+ className: "rounded-lg bg-surface-secondary px-3 py-1 text-muted text-xs shadow-sm dark:bg-surface-secondary/50",
16
+ children: isToday(date) ? "Hoy" : isYesterday(date) ? "Ayer" : dayjs(date).format("DD/MM/YYYY")
17
+ })
18
+ });
19
+ }
20
+
21
+ const DEFAULT_BACKGROUND = {
22
+ dark: "/images/chat-bg-dark.png",
23
+ light: "/images/chat-bg-light.png"
24
+ };
25
+ function ConversationThread({ messages, isLoading = false, isError = false, labels, backgroundImage, showPollingFallback = false, renderMessages }) {
26
+ const scrollRef = useRef(null);
27
+ const messageListRef = useRef(null);
28
+ const groupedMessages = useMemo(()=>{
29
+ const groups = [];
30
+ let currentDay = "";
31
+ for (const msg of messages){
32
+ const day = dayjs(msg.timestamp).format("YYYY-MM-DD");
33
+ if (day === currentDay) groups.at(-1)?.messages.push(msg);
34
+ else {
35
+ currentDay = day;
36
+ groups.push({
37
+ date: msg.timestamp,
38
+ messages: [
39
+ msg
40
+ ]
41
+ });
42
+ }
43
+ }
44
+ return groups;
45
+ }, [
46
+ messages
47
+ ]);
48
+ const hasScrolledOnLoad = useRef(false);
49
+ const firstId = messages.at(0)?.id ?? "";
50
+ const prevFirstIdRef = useRef(firstId);
51
+ const scrollToBottom = useCallback((behavior = "smooth")=>{
52
+ const el = scrollRef.current;
53
+ if (!el) return;
54
+ if (behavior === "auto") {
55
+ el.scrollTop = el.scrollHeight;
56
+ return;
57
+ }
58
+ el.scrollTo({
59
+ behavior,
60
+ top: el.scrollHeight
61
+ });
62
+ }, []);
63
+ useLayoutEffect(()=>{
64
+ if (prevFirstIdRef.current !== firstId) {
65
+ prevFirstIdRef.current = firstId;
66
+ hasScrolledOnLoad.current = false;
67
+ }
68
+ if (isLoading || messages.length === 0 || hasScrolledOnLoad.current) return;
69
+ const el = scrollRef.current;
70
+ const listEl = messageListRef.current;
71
+ if (!el || !listEl) return;
72
+ let settleTimeout = null;
73
+ let settled = false;
74
+ const scrollNow = ()=>{
75
+ el.scrollTop = el.scrollHeight;
76
+ };
77
+ const scheduleSettle = ()=>{
78
+ if (settleTimeout) clearTimeout(settleTimeout);
79
+ settleTimeout = setTimeout(()=>{
80
+ settled = true;
81
+ hasScrolledOnLoad.current = true;
82
+ resizeObserver.disconnect();
83
+ mutationObserver.disconnect();
84
+ }, 250);
85
+ };
86
+ scrollNow();
87
+ scheduleSettle();
88
+ const resizeObserver = new ResizeObserver(()=>{
89
+ if (!settled) {
90
+ scrollNow();
91
+ scheduleSettle();
92
+ }
93
+ });
94
+ const mutationObserver = new MutationObserver(()=>{
95
+ if (!settled) {
96
+ scrollNow();
97
+ scheduleSettle();
98
+ }
99
+ });
100
+ resizeObserver.observe(listEl);
101
+ mutationObserver.observe(listEl, {
102
+ childList: true,
103
+ subtree: true
104
+ });
105
+ return ()=>{
106
+ if (settleTimeout) clearTimeout(settleTimeout);
107
+ resizeObserver.disconnect();
108
+ mutationObserver.disconnect();
109
+ };
110
+ }, [
111
+ isLoading,
112
+ messages.length,
113
+ firstId
114
+ ]);
115
+ const messageCount = messages.length;
116
+ const prevMessageCount = useRef(messageCount);
117
+ useEffect(()=>{
118
+ if (messageCount > prevMessageCount.current) requestAnimationFrame(()=>scrollToBottom("smooth"));
119
+ prevMessageCount.current = messageCount;
120
+ }, [
121
+ messageCount,
122
+ scrollToBottom
123
+ ]);
124
+ const bg = backgroundImage === undefined ? DEFAULT_BACKGROUND : backgroundImage;
125
+ const renderMessage = (message)=>/*#__PURE__*/ jsx(MessageBubble, {
126
+ message: message
127
+ }, message.id);
128
+ return /*#__PURE__*/ jsxs("div", {
129
+ className: cn("relative flex h-full flex-col", bg ? "bg-[#efeae2] dark:bg-[#0b141a]" : "bg-background"),
130
+ children: [
131
+ bg && /*#__PURE__*/ jsxs(Fragment, {
132
+ children: [
133
+ /*#__PURE__*/ jsx("div", {
134
+ className: "pointer-events-none absolute inset-0 bg-contain opacity-15 dark:hidden",
135
+ style: {
136
+ backgroundImage: `url('${bg.light}')`
137
+ }
138
+ }),
139
+ /*#__PURE__*/ jsx("div", {
140
+ className: "pointer-events-none absolute inset-0 hidden bg-contain opacity-15 dark:block",
141
+ style: {
142
+ backgroundImage: `url('${bg.dark}')`
143
+ }
144
+ })
145
+ ]
146
+ }),
147
+ showPollingFallback && labels.pollingFallback && /*#__PURE__*/ jsx("div", {
148
+ className: "flex items-center justify-center py-1.5 text-muted text-xs",
149
+ children: labels.pollingFallback
150
+ }),
151
+ /*#__PURE__*/ jsxs(ScrollShadow, {
152
+ className: "relative flex-1 overflow-y-auto px-4 pt-4 pb-20",
153
+ ref: scrollRef,
154
+ children: [
155
+ isLoading && /*#__PURE__*/ jsxs("div", {
156
+ className: "flex items-center justify-center gap-2 py-8",
157
+ children: [
158
+ /*#__PURE__*/ jsx(Spinner, {
159
+ size: "sm"
160
+ }),
161
+ /*#__PURE__*/ jsx("span", {
162
+ className: "text-muted text-sm",
163
+ children: labels.loading
164
+ })
165
+ ]
166
+ }),
167
+ isError && /*#__PURE__*/ jsxs("div", {
168
+ className: "flex items-center justify-center gap-2 py-8 text-danger",
169
+ children: [
170
+ /*#__PURE__*/ jsx(AlertCircle, {
171
+ className: "h-5 w-5"
172
+ }),
173
+ /*#__PURE__*/ jsx("p", {
174
+ className: "text-sm",
175
+ children: labels.errorLoading
176
+ })
177
+ ]
178
+ }),
179
+ !isLoading && messages.length === 0 && /*#__PURE__*/ jsx("div", {
180
+ className: "flex items-center justify-center py-12 text-center",
181
+ children: /*#__PURE__*/ jsx("p", {
182
+ className: "text-muted text-sm",
183
+ children: labels.noMessages
184
+ })
185
+ }),
186
+ !isLoading && messages.length > 0 && /*#__PURE__*/ jsx("div", {
187
+ className: "space-y-3",
188
+ ref: messageListRef,
189
+ children: renderMessages ? renderMessages(messages, renderMessage) : groupedMessages.map((group)=>/*#__PURE__*/ jsxs("div", {
190
+ className: "space-y-3",
191
+ children: [
192
+ /*#__PURE__*/ jsx(DateDivider, {
193
+ date: group.date
194
+ }),
195
+ group.messages.map(renderMessage)
196
+ ]
197
+ }, String(group.date)))
198
+ })
199
+ ]
200
+ })
201
+ ]
202
+ });
203
+ }
204
+
205
+ export { ConversationThread as C, DateDivider as D };
@@ -0,0 +1,17 @@
1
+ import type { UniqueIdentifier } from "@dnd-kit/core";
2
+ import type { ReactNode } from "react";
3
+ export interface EntityCardProps<Entity extends object> {
4
+ id: UniqueIdentifier;
5
+ entity: Entity;
6
+ isDragging?: boolean;
7
+ onActivate?: (entity: Entity) => void;
8
+ cardClassName?: string;
9
+ badges?: ReactNode;
10
+ time?: ReactNode;
11
+ avatar?: ReactNode;
12
+ title: ReactNode;
13
+ subtitle?: ReactNode;
14
+ }
15
+ /** Sortable presentation only: metadata, urgency and navigation belong to the caller. */
16
+ export declare function EntityCard<Entity extends object>({ id, entity, isDragging, onActivate, cardClassName, badges, time, avatar, title, subtitle, }: EntityCardProps<Entity>): import("react").JSX.Element;
17
+ //# sourceMappingURL=entity-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-card.d.ts","sourceRoot":"","sources":["../src/entity-card.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAKtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,eAAe,CAAC,MAAM,SAAS,MAAM;IACpD,EAAE,EAAE,gBAAgB,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,MAAM,SAAS,MAAM,EAAE,EAChD,EAAE,EACF,MAAM,EACN,UAAkB,EAClB,UAAU,EACV,aAAa,EACb,MAAM,EACN,IAAI,EACJ,MAAM,EACN,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,CAAC,MAAM,CAAC,+BAuCzB"}
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useSortable } from '@dnd-kit/sortable';
4
+ import { CSS } from '@dnd-kit/utilities';
5
+ import { Card } from '@lindaui/ui/compound';
6
+ import { cn } from '@lindaui/ui/cn';
7
+
8
+ /** Sortable presentation only: metadata, urgency and navigation belong to the caller. */ function EntityCard({ id, entity, isDragging = false, onActivate, cardClassName, badges, time, avatar, title, subtitle }) {
9
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging: isSortableDragging } = useSortable({
10
+ data: entity,
11
+ id
12
+ });
13
+ const isCurrentlyDragging = isDragging || isSortableDragging;
14
+ return /*#__PURE__*/ jsx("div", {
15
+ ref: setNodeRef,
16
+ style: {
17
+ transform: CSS.Transform.toString(transform),
18
+ transition
19
+ },
20
+ ...attributes,
21
+ ...listeners,
22
+ className: cn("touch-none", isCurrentlyDragging && "scale-105 opacity-50"),
23
+ children: /*#__PURE__*/ jsx(Card, {
24
+ className: cn("cursor-grab border-none bg-surface transition-all hover:bg-surface-secondary active:cursor-grabbing", "h-full min-w-full shadow-sm hover:shadow-md", cardClassName),
25
+ onClick: ()=>{
26
+ if (!isCurrentlyDragging) onActivate?.(entity);
27
+ },
28
+ children: /*#__PURE__*/ jsxs(Card.Content, {
29
+ className: "flex min-h-[96px] min-w-full flex-col justify-between p-3",
30
+ children: [
31
+ /*#__PURE__*/ jsxs("div", {
32
+ className: "mb-2 flex items-start justify-between",
33
+ children: [
34
+ /*#__PURE__*/ jsx("div", {
35
+ className: "flex flex-1 flex-wrap items-center gap-1 overflow-hidden",
36
+ children: badges
37
+ }),
38
+ /*#__PURE__*/ jsx("div", {
39
+ className: "flex items-center gap-1 text-[10px] text-muted",
40
+ children: time
41
+ })
42
+ ]
43
+ }),
44
+ /*#__PURE__*/ jsxs("div", {
45
+ className: "mb-2 flex items-center gap-3",
46
+ children: [
47
+ avatar,
48
+ /*#__PURE__*/ jsxs("div", {
49
+ className: "min-w-0 flex-1",
50
+ children: [
51
+ /*#__PURE__*/ jsx("p", {
52
+ className: "truncate font-semibold text-sm",
53
+ children: title
54
+ }),
55
+ /*#__PURE__*/ jsx("p", {
56
+ className: "truncate text-[10px] text-muted",
57
+ children: subtitle
58
+ })
59
+ ]
60
+ })
61
+ ]
62
+ })
63
+ ]
64
+ })
65
+ })
66
+ });
67
+ }
68
+
69
+ export { EntityCard };
@@ -0,0 +1,192 @@
1
+ 'use client';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { Activity, CheckCircle2 } from 'lucide-react';
4
+ import { memo, useMemo, Fragment as Fragment$1 } from 'react';
5
+ import { Badge, Divider } from '@lindaui/ui/compat';
6
+ import { A as AnalyticsCard } from './analytics-card-bcmijpt6.js';
7
+
8
+ const STATUS_CONFIG = {
9
+ at_risk: {
10
+ badgeColor: "warning",
11
+ bgClass: "bg-warning/8",
12
+ dotClass: "bg-warning",
13
+ labelClass: "text-warning/80",
14
+ textClass: "text-warning"
15
+ },
16
+ critical: {
17
+ badgeColor: "danger",
18
+ bgClass: "bg-danger/8",
19
+ dotClass: "bg-danger",
20
+ labelClass: "text-danger/80",
21
+ textClass: "text-danger"
22
+ },
23
+ healthy: {
24
+ badgeColor: "success",
25
+ bgClass: "bg-success/8",
26
+ dotClass: "bg-success",
27
+ labelClass: "text-success/80",
28
+ textClass: "text-success"
29
+ }
30
+ };
31
+ function ScoreBar({ count, total, colorClass }) {
32
+ const pct = total > 0 ? Math.round(count / total * 100) : 0;
33
+ return /*#__PURE__*/ jsx("div", {
34
+ className: "mt-2 h-1 w-full overflow-hidden rounded-full bg-surface-secondary",
35
+ children: /*#__PURE__*/ jsx("div", {
36
+ className: `h-full rounded-full transition-all duration-500 ${colorClass}`,
37
+ style: {
38
+ width: `${pct}%`
39
+ }
40
+ })
41
+ });
42
+ }
43
+ const EntityHealthCard = /*#__PURE__*/ memo(({ entities, labels, renderEntityLink })=>{
44
+ const total = entities.length;
45
+ const { healthy, atRisk, critical, alertEntities } = useMemo(()=>({
46
+ healthy: entities.filter((e)=>e.status === "healthy").length,
47
+ atRisk: entities.filter((e)=>e.status === "at_risk").length,
48
+ critical: entities.filter((e)=>e.status === "critical").length,
49
+ alertEntities: entities.filter((e)=>e.status !== "healthy")
50
+ }), [
51
+ entities
52
+ ]);
53
+ const renderLink = renderEntityLink ?? ((_entity, children)=>/*#__PURE__*/ jsx("div", {
54
+ children: children
55
+ }));
56
+ const action = critical > 0 ? /*#__PURE__*/ jsx(Badge, {
57
+ color: "danger",
58
+ size: "sm",
59
+ variant: "flat",
60
+ children: labels.criticalBadge(critical)
61
+ }) : undefined;
62
+ return /*#__PURE__*/ jsxs(AnalyticsCard, {
63
+ action: action,
64
+ icon: Activity,
65
+ title: labels.title,
66
+ children: [
67
+ /*#__PURE__*/ jsxs("div", {
68
+ className: "grid grid-cols-3 gap-3",
69
+ children: [
70
+ /*#__PURE__*/ jsxs("div", {
71
+ className: `rounded-xl p-3 ${STATUS_CONFIG.healthy.bgClass}`,
72
+ children: [
73
+ /*#__PURE__*/ jsx("p", {
74
+ className: `font-bold text-2xl tabular-nums ${STATUS_CONFIG.healthy.textClass}`,
75
+ children: healthy
76
+ }),
77
+ /*#__PURE__*/ jsx("p", {
78
+ className: `mt-0.5 text-xs ${STATUS_CONFIG.healthy.labelClass}`,
79
+ children: labels.healthy
80
+ }),
81
+ /*#__PURE__*/ jsx(ScoreBar, {
82
+ colorClass: "bg-success",
83
+ count: healthy,
84
+ total: total
85
+ })
86
+ ]
87
+ }),
88
+ /*#__PURE__*/ jsxs("div", {
89
+ className: `rounded-xl p-3 ${STATUS_CONFIG.at_risk.bgClass}`,
90
+ children: [
91
+ /*#__PURE__*/ jsx("p", {
92
+ className: `font-bold text-2xl tabular-nums ${STATUS_CONFIG.at_risk.textClass}`,
93
+ children: atRisk
94
+ }),
95
+ /*#__PURE__*/ jsx("p", {
96
+ className: `mt-0.5 text-xs ${STATUS_CONFIG.at_risk.labelClass}`,
97
+ children: labels.atRisk
98
+ }),
99
+ /*#__PURE__*/ jsx(ScoreBar, {
100
+ colorClass: "bg-warning",
101
+ count: atRisk,
102
+ total: total
103
+ })
104
+ ]
105
+ }),
106
+ /*#__PURE__*/ jsxs("div", {
107
+ className: `rounded-xl p-3 ${STATUS_CONFIG.critical.bgClass}`,
108
+ children: [
109
+ /*#__PURE__*/ jsx("p", {
110
+ className: `font-bold text-2xl tabular-nums ${STATUS_CONFIG.critical.textClass}`,
111
+ children: critical
112
+ }),
113
+ /*#__PURE__*/ jsx("p", {
114
+ className: `mt-0.5 text-xs ${STATUS_CONFIG.critical.labelClass}`,
115
+ children: labels.critical
116
+ }),
117
+ /*#__PURE__*/ jsx(ScoreBar, {
118
+ colorClass: "bg-danger",
119
+ count: critical,
120
+ total: total
121
+ })
122
+ ]
123
+ })
124
+ ]
125
+ }),
126
+ alertEntities.length === 0 ? /*#__PURE__*/ jsxs("div", {
127
+ className: "mt-4 flex flex-col items-center justify-center gap-2 py-5",
128
+ children: [
129
+ /*#__PURE__*/ jsx(CheckCircle2, {
130
+ className: "h-8 w-8 text-success/60"
131
+ }),
132
+ /*#__PURE__*/ jsx("p", {
133
+ className: "text-center text-muted text-xs",
134
+ children: labels.allHealthy
135
+ })
136
+ ]
137
+ }) : /*#__PURE__*/ jsxs(Fragment, {
138
+ children: [
139
+ /*#__PURE__*/ jsx(Divider, {
140
+ className: "my-4"
141
+ }),
142
+ /*#__PURE__*/ jsx("div", {
143
+ className: "max-h-[200px] space-y-0.5 overflow-y-auto",
144
+ children: alertEntities.map((entity)=>{
145
+ const cfg = STATUS_CONFIG[entity.status];
146
+ return /*#__PURE__*/ jsx(Fragment$1, {
147
+ children: renderLink(entity, /*#__PURE__*/ jsxs("div", {
148
+ className: "flex items-center justify-between rounded-lg px-3 py-2 transition-colors duration-100 hover:bg-surface-secondary",
149
+ children: [
150
+ /*#__PURE__*/ jsxs("div", {
151
+ className: "flex min-w-0 items-center gap-2.5",
152
+ children: [
153
+ /*#__PURE__*/ jsx("span", {
154
+ className: `h-2 w-2 flex-shrink-0 rounded-full ${cfg.dotClass}`
155
+ }),
156
+ /*#__PURE__*/ jsx("span", {
157
+ className: "truncate font-medium text-foreground text-sm",
158
+ children: entity.name
159
+ })
160
+ ]
161
+ }),
162
+ /*#__PURE__*/ jsxs("div", {
163
+ className: "ml-2 flex flex-shrink-0 items-center gap-2",
164
+ children: [
165
+ /*#__PURE__*/ jsx("span", {
166
+ className: "font-mono text-muted text-xs tabular-nums",
167
+ children: labels.scoreLabel(entity.score)
168
+ }),
169
+ /*#__PURE__*/ jsx(Badge, {
170
+ color: cfg.badgeColor,
171
+ size: "sm",
172
+ variant: "flat",
173
+ children: labels.statusLabel(entity.status)
174
+ })
175
+ ]
176
+ })
177
+ ]
178
+ }))
179
+ }, entity.id);
180
+ })
181
+ }),
182
+ /*#__PURE__*/ jsx("p", {
183
+ className: "mt-3 text-muted text-xs",
184
+ children: labels.idleNote
185
+ })
186
+ ]
187
+ })
188
+ ]
189
+ });
190
+ });
191
+
192
+ export { EntityHealthCard as E };