@wealthx/shadcn 1.5.11 → 1.5.13

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 (36) hide show
  1. package/.turbo/turbo-build.log +88 -88
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-O5CP6VP6.mjs → chunk-BF5FKUF6.mjs} +104 -63
  4. package/dist/{chunk-ZMTCMP2G.mjs → chunk-EB626HVW.mjs} +70 -3
  5. package/dist/chunk-KICT4VQL.mjs +508 -0
  6. package/dist/chunk-V23CBULF.mjs +432 -0
  7. package/dist/components/ui/ai-conversations.js +70 -3
  8. package/dist/components/ui/ai-conversations.mjs +1 -1
  9. package/dist/components/ui/appointment-calendar-view.js +177 -176
  10. package/dist/components/ui/appointment-calendar-view.mjs +1 -1
  11. package/dist/components/ui/bank-statement-generate-dialog.js +209 -107
  12. package/dist/components/ui/bank-statement-generate-dialog.mjs +2 -1
  13. package/dist/components/ui/resource-center/index.js +1030 -0
  14. package/dist/components/ui/resource-center/index.mjs +29 -0
  15. package/dist/index.js +661 -403
  16. package/dist/index.mjs +16 -14
  17. package/dist/styles.css +1 -1
  18. package/package.json +4 -4
  19. package/src/components/index.tsx +2 -0
  20. package/src/components/ui/ai-conversations.tsx +157 -23
  21. package/src/components/ui/appointment-calendar-view.tsx +211 -199
  22. package/src/components/ui/bank-statement-generate-dialog.tsx +147 -96
  23. package/src/components/ui/resource-center/index.tsx +35 -0
  24. package/src/components/ui/resource-center/resource-cards.tsx +218 -0
  25. package/src/components/ui/resource-center/resource-carousel.tsx +122 -0
  26. package/src/components/ui/resource-center/resource-center-header.tsx +95 -0
  27. package/src/components/ui/resource-center/resource-email-editor-dialog.tsx +131 -0
  28. package/src/components/ui/resource-center/resource-modal.tsx +76 -0
  29. package/src/components/ui/resource-center/types.ts +81 -0
  30. package/src/styles/styles-css.ts +1 -1
  31. package/tsup.config.ts +1 -1
  32. package/dist/chunk-IODGRCQG.mjs +0 -438
  33. package/dist/chunk-XYWEGBAA.mjs +0 -348
  34. package/dist/components/ui/resource-center.js +0 -748
  35. package/dist/components/ui/resource-center.mjs +0 -24
  36. package/src/components/ui/resource-center.tsx +0 -539
@@ -0,0 +1,432 @@
1
+ import {
2
+ Tabs,
3
+ TabsContent,
4
+ TabsList,
5
+ TabsTrigger
6
+ } from "./chunk-WE4YKBDE.mjs";
7
+ import {
8
+ Separator
9
+ } from "./chunk-2GIYVERS.mjs";
10
+ import {
11
+ Avatar,
12
+ AvatarFallback
13
+ } from "./chunk-H6NQTIF4.mjs";
14
+ import {
15
+ Badge
16
+ } from "./chunk-X6RC5UWB.mjs";
17
+ import {
18
+ formatWeekdayShort
19
+ } from "./chunk-LHWJQNLG.mjs";
20
+ import {
21
+ Button
22
+ } from "./chunk-NOOEKOWY.mjs";
23
+
24
+ // src/components/ui/appointment-calendar-view.tsx
25
+ import { ChevronLeft, ChevronRight } from "lucide-react";
26
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
27
+ var STATUS = {
28
+ pending: {
29
+ card: "border-l-warning bg-warning/[0.06]",
30
+ badge: "warning",
31
+ label: "Pending"
32
+ },
33
+ confirmed: {
34
+ card: "border-l-success bg-success/[0.06]",
35
+ badge: "success",
36
+ label: "Confirmed"
37
+ },
38
+ cancelled: {
39
+ card: "border-l-destructive bg-destructive/[0.06]",
40
+ badge: "destructive",
41
+ label: "Cancelled"
42
+ },
43
+ rescheduled: {
44
+ card: "border-l-info bg-info/[0.06]",
45
+ badge: "info",
46
+ label: "Rescheduled"
47
+ }
48
+ };
49
+ function formatHour(h) {
50
+ if (h === 12) return "12 PM";
51
+ return h < 12 ? `${h} AM` : `${h - 12} PM`;
52
+ }
53
+ function getHourFromTime(time) {
54
+ const match = time.match(/^(\d+):/);
55
+ if (!match) return 0;
56
+ const h = parseInt(match[1], 10);
57
+ return time.includes("PM") && h !== 12 ? h + 12 : h;
58
+ }
59
+ var DEFAULT_HOURS = [9, 10, 11, 12, 13, 14, 15, 16, 17];
60
+ function TimeGutter({ hour }) {
61
+ return /* @__PURE__ */ jsx("div", { className: "flex w-16 shrink-0 items-start justify-end border-r border-border pr-3 pt-2", children: /* @__PURE__ */ jsx("span", { className: "text-caption text-muted-foreground", children: formatHour(hour) }) });
62
+ }
63
+ function DayCard({
64
+ apt,
65
+ onSelect
66
+ }) {
67
+ return /* @__PURE__ */ jsxs(
68
+ Button,
69
+ {
70
+ type: "button",
71
+ variant: "ghost",
72
+ onClick: () => onSelect == null ? void 0 : onSelect(apt),
73
+ className: `h-auto w-full justify-start gap-3 border-l-2 px-3 py-3 text-left hover:opacity-80 ${STATUS[apt.status].card}`,
74
+ children: [
75
+ /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "text-caption", children: apt.clientAvatarInitials }) }),
76
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
77
+ /* @__PURE__ */ jsx("span", { className: "text-body-small font-semibold", children: apt.clientName }),
78
+ /* @__PURE__ */ jsxs("span", { className: "text-caption text-muted-foreground", children: [
79
+ apt.timeStart,
80
+ " \u2013 ",
81
+ apt.timeEnd
82
+ ] })
83
+ ] }),
84
+ /* @__PURE__ */ jsx(Badge, { variant: STATUS[apt.status].badge, children: STATUS[apt.status].label })
85
+ ]
86
+ }
87
+ );
88
+ }
89
+ function AptChip({
90
+ apt,
91
+ onSelect,
92
+ className
93
+ }) {
94
+ return /* @__PURE__ */ jsx(
95
+ Button,
96
+ {
97
+ type: "button",
98
+ variant: "ghost",
99
+ onClick: () => onSelect == null ? void 0 : onSelect(apt),
100
+ className: `h-auto w-full justify-start truncate border-l-2 text-left hover:opacity-80 ${STATUS[apt.status].card} ${className != null ? className : ""}`,
101
+ children: /* @__PURE__ */ jsxs("span", { className: "truncate text-caption font-medium", children: [
102
+ apt.timeStart,
103
+ " ",
104
+ apt.clientName
105
+ ] })
106
+ }
107
+ );
108
+ }
109
+ function DayView({
110
+ appointments,
111
+ date,
112
+ today,
113
+ hours,
114
+ onSelectAppointment
115
+ }) {
116
+ const d = /* @__PURE__ */ new Date(date + "T00:00:00");
117
+ const isToday = date === today;
118
+ const dayApts = appointments.filter((a) => a.date === date);
119
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
120
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 border-b border-border", children: [
121
+ /* @__PURE__ */ jsx("div", { className: "w-16 shrink-0 border-r border-border" }),
122
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center gap-1 py-4", children: [
123
+ /* @__PURE__ */ jsx(
124
+ "span",
125
+ {
126
+ className: `text-caption tracking-wide ${isToday ? "font-semibold text-primary" : "text-muted-foreground"}`,
127
+ children: formatWeekdayShort(d)
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsx(
131
+ "span",
132
+ {
133
+ className: `flex h-10 w-10 items-center justify-center text-xl font-semibold ${isToday ? "text-primary" : ""}`,
134
+ children: d.getDate()
135
+ }
136
+ )
137
+ ] })
138
+ ] }),
139
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col", children: hours.map((hour) => {
140
+ const aptsAtHour = dayApts.filter(
141
+ (a) => getHourFromTime(a.timeStart) === hour
142
+ );
143
+ return /* @__PURE__ */ jsxs(
144
+ "div",
145
+ {
146
+ className: "flex flex-1 border-b border-border/40 last:border-b-0",
147
+ children: [
148
+ /* @__PURE__ */ jsx(TimeGutter, { hour }),
149
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-2 p-2", children: aptsAtHour.map((apt) => /* @__PURE__ */ jsx(
150
+ DayCard,
151
+ {
152
+ apt,
153
+ onSelect: onSelectAppointment
154
+ },
155
+ apt.id
156
+ )) })
157
+ ]
158
+ },
159
+ hour
160
+ );
161
+ }) })
162
+ ] });
163
+ }
164
+ function WeekView({
165
+ appointments,
166
+ weekDays,
167
+ today,
168
+ hours,
169
+ onSelectAppointment
170
+ }) {
171
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-x-auto", children: [
172
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 border-b border-border", children: [
173
+ /* @__PURE__ */ jsx("div", { className: "w-16 shrink-0 border-r border-border" }),
174
+ weekDays.map((day) => /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ className: "flex flex-1 flex-col items-center border-r border-border py-3 last:border-r-0",
178
+ children: [
179
+ /* @__PURE__ */ jsx(
180
+ "span",
181
+ {
182
+ className: `text-caption ${day.date === today ? "font-semibold text-primary" : "text-muted-foreground"}`,
183
+ children: day.label
184
+ }
185
+ ),
186
+ /* @__PURE__ */ jsx(
187
+ "span",
188
+ {
189
+ className: `text-body-small font-semibold ${day.date === today ? "text-primary" : ""}`,
190
+ children: day.short
191
+ }
192
+ )
193
+ ]
194
+ },
195
+ day.date
196
+ ))
197
+ ] }),
198
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col", children: hours.map((hour) => /* @__PURE__ */ jsxs(
199
+ "div",
200
+ {
201
+ className: "flex flex-1 border-b border-border/40 last:border-b-0",
202
+ children: [
203
+ /* @__PURE__ */ jsx(TimeGutter, { hour }),
204
+ weekDays.map((day) => {
205
+ const aptsAtCell = appointments.filter(
206
+ (a) => a.date === day.date && getHourFromTime(a.timeStart) === hour
207
+ );
208
+ return /* @__PURE__ */ jsx(
209
+ "div",
210
+ {
211
+ className: "flex flex-1 flex-col overflow-hidden border-r border-border/40 p-1 last:border-r-0",
212
+ children: aptsAtCell.map((apt) => /* @__PURE__ */ jsx(
213
+ AptChip,
214
+ {
215
+ apt,
216
+ onSelect: onSelectAppointment,
217
+ className: "px-2 py-1.5"
218
+ },
219
+ apt.id
220
+ ))
221
+ },
222
+ day.date
223
+ );
224
+ })
225
+ ]
226
+ },
227
+ hour
228
+ )) })
229
+ ] });
230
+ }
231
+ var MONTH_DAY_HEADERS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
232
+ function generateMonthGrid(year, month) {
233
+ const firstDay = new Date(year, month, 1);
234
+ const lastDay = new Date(year, month + 1, 0);
235
+ const startDow = firstDay.getDay();
236
+ const grid = [];
237
+ let week = Array.from({ length: startDow }, () => null);
238
+ for (let day = 1; day <= lastDay.getDate(); day++) {
239
+ week.push(new Date(year, month, day));
240
+ if (week.length === 7) {
241
+ grid.push(week);
242
+ week = [];
243
+ }
244
+ }
245
+ if (week.length > 0) {
246
+ while (week.length < 7) week.push(null);
247
+ grid.push(week);
248
+ }
249
+ return grid;
250
+ }
251
+ function MonthView({
252
+ appointments,
253
+ viewDate,
254
+ today,
255
+ onSelectAppointment
256
+ }) {
257
+ const vd = /* @__PURE__ */ new Date(viewDate + "T00:00:00");
258
+ const grid = generateMonthGrid(vd.getFullYear(), vd.getMonth());
259
+ const toIso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
260
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
261
+ /* @__PURE__ */ jsx("div", { className: "grid shrink-0 grid-cols-7 border-b border-border", children: MONTH_DAY_HEADERS.map((d) => /* @__PURE__ */ jsx(
262
+ "div",
263
+ {
264
+ className: "flex items-center justify-center border-r border-border/40 py-2 last:border-r-0",
265
+ children: /* @__PURE__ */ jsx("span", { className: "text-caption font-medium text-muted-foreground", children: d })
266
+ },
267
+ d
268
+ )) }),
269
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col", children: grid.map((week, wi) => /* @__PURE__ */ jsx(
270
+ "div",
271
+ {
272
+ className: "flex flex-1 border-b border-border last:border-b-0",
273
+ children: week.map((day, di) => {
274
+ const iso = day ? toIso(day) : null;
275
+ const dayApts = iso ? appointments.filter((a) => a.date === iso) : [];
276
+ const isToday = iso === today;
277
+ return /* @__PURE__ */ jsx(
278
+ "div",
279
+ {
280
+ className: `min-w-0 flex-1 border-r border-border/40 p-1.5 last:border-r-0 ${!day ? "bg-muted/20" : ""}`,
281
+ children: day && /* @__PURE__ */ jsxs(Fragment, { children: [
282
+ /* @__PURE__ */ jsx(
283
+ "div",
284
+ {
285
+ className: `mb-1 flex h-6 w-6 items-center justify-center text-xs font-semibold ${isToday ? "text-primary" : "text-foreground"}`,
286
+ children: day.getDate()
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
290
+ dayApts.slice(0, 2).map((apt) => /* @__PURE__ */ jsx(
291
+ AptChip,
292
+ {
293
+ apt,
294
+ onSelect: onSelectAppointment,
295
+ className: "px-1.5 py-1"
296
+ },
297
+ apt.id
298
+ )),
299
+ dayApts.length > 2 && /* @__PURE__ */ jsxs("p", { className: "px-1 text-caption text-muted-foreground", children: [
300
+ "+",
301
+ dayApts.length - 2,
302
+ " more"
303
+ ] })
304
+ ] })
305
+ ] })
306
+ },
307
+ di
308
+ );
309
+ })
310
+ },
311
+ wi
312
+ )) })
313
+ ] });
314
+ }
315
+ function AppointmentCalendarView({
316
+ appointments,
317
+ today,
318
+ periodLabel,
319
+ weekDays,
320
+ hours = DEFAULT_HOURS,
321
+ viewDate,
322
+ dayViewDate,
323
+ defaultView = "week",
324
+ onPrev,
325
+ onNext,
326
+ onToday,
327
+ onViewChange,
328
+ onSelectAppointment,
329
+ toolbarActions
330
+ }) {
331
+ var _a;
332
+ const effectiveViewDate = (_a = viewDate != null ? viewDate : dayViewDate) != null ? _a : today;
333
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col min-h-0 border border-border bg-card", children: [
334
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3", children: [
335
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
336
+ /* @__PURE__ */ jsx(
337
+ Button,
338
+ {
339
+ variant: "outline",
340
+ size: "sm",
341
+ className: "gap-1",
342
+ onClick: onPrev,
343
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" })
344
+ }
345
+ ),
346
+ /* @__PURE__ */ jsx("p", { className: "text-label-medium", children: periodLabel }),
347
+ /* @__PURE__ */ jsx(
348
+ Button,
349
+ {
350
+ variant: "outline",
351
+ size: "sm",
352
+ className: "gap-1",
353
+ onClick: onNext,
354
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })
355
+ }
356
+ ),
357
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onToday, children: "Today" })
358
+ ] }),
359
+ toolbarActions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: toolbarActions })
360
+ ] }),
361
+ /* @__PURE__ */ jsx(Separator, {}),
362
+ /* @__PURE__ */ jsxs(
363
+ Tabs,
364
+ {
365
+ defaultValue: defaultView,
366
+ onValueChange: (v) => onViewChange == null ? void 0 : onViewChange(v),
367
+ className: "flex flex-col flex-1 min-h-0",
368
+ children: [
369
+ /* @__PURE__ */ jsx("div", { className: "px-4 pt-3", children: /* @__PURE__ */ jsxs(TabsList, { children: [
370
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "day", children: "Day" }),
371
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "week", children: "Week" }),
372
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "month", children: "Month" })
373
+ ] }) }),
374
+ /* @__PURE__ */ jsx(
375
+ TabsContent,
376
+ {
377
+ value: "day",
378
+ className: "mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto",
379
+ children: /* @__PURE__ */ jsx(
380
+ DayView,
381
+ {
382
+ appointments,
383
+ date: effectiveViewDate,
384
+ today,
385
+ hours,
386
+ onSelectAppointment
387
+ }
388
+ )
389
+ }
390
+ ),
391
+ /* @__PURE__ */ jsx(
392
+ TabsContent,
393
+ {
394
+ value: "week",
395
+ className: "mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto",
396
+ children: /* @__PURE__ */ jsx(
397
+ WeekView,
398
+ {
399
+ appointments,
400
+ weekDays,
401
+ today,
402
+ hours,
403
+ onSelectAppointment
404
+ }
405
+ )
406
+ }
407
+ ),
408
+ /* @__PURE__ */ jsx(
409
+ TabsContent,
410
+ {
411
+ value: "month",
412
+ className: "mt-0 min-h-0 flex-1 flex flex-col overflow-y-auto",
413
+ children: /* @__PURE__ */ jsx(
414
+ MonthView,
415
+ {
416
+ appointments,
417
+ viewDate: effectiveViewDate,
418
+ today,
419
+ onSelectAppointment
420
+ }
421
+ )
422
+ }
423
+ )
424
+ ]
425
+ }
426
+ )
427
+ ] });
428
+ }
429
+
430
+ export {
431
+ AppointmentCalendarView
432
+ };
@@ -1244,15 +1244,19 @@ function ChatComposer({
1244
1244
  mode,
1245
1245
  channel: channelProp = "chat",
1246
1246
  onChannelChange,
1247
+ isEmailIntegrated = false,
1247
1248
  contactEmail = "",
1248
1249
  inputValue = "",
1249
1250
  onInputChange,
1250
1251
  onSend,
1252
+ onSendEmail,
1251
1253
  onTakeOver,
1252
1254
  onLetAiHandle,
1253
1255
  className
1254
1256
  }) {
1255
- const [channel, setChannel] = import_react3.default.useState(channelProp);
1257
+ const [channel, setChannel] = import_react3.default.useState(
1258
+ isEmailIntegrated ? channelProp : "chat"
1259
+ );
1256
1260
  const [emailTo, setEmailTo] = import_react3.default.useState(contactEmail);
1257
1261
  const [emailCc, setEmailCc] = import_react3.default.useState("");
1258
1262
  const [showCc, setShowCc] = import_react3.default.useState(false);
@@ -1302,7 +1306,7 @@ function ChatComposer({
1302
1306
  className
1303
1307
  ),
1304
1308
  children: [
1305
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1309
+ isEmailIntegrated && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1306
1310
  Tabs,
1307
1311
  {
1308
1312
  value: channel,
@@ -1437,7 +1441,12 @@ function ChatComposer({
1437
1441
  Button,
1438
1442
  {
1439
1443
  size: "sm",
1440
- onClick: () => onSend == null ? void 0 : onSend(inputValue),
1444
+ onClick: () => onSendEmail == null ? void 0 : onSendEmail({
1445
+ content: inputValue,
1446
+ to: emailTo,
1447
+ cc: emailCc,
1448
+ subject: emailSubject
1449
+ }),
1441
1450
  disabled: !inputValue.trim() || !emailTo.trim(),
1442
1451
  children: [
1443
1452
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Send, { className: "mr-1.5 size-3.5" }),
@@ -1492,9 +1501,11 @@ function ChatThread({
1492
1501
  isAiTyping = false,
1493
1502
  channel,
1494
1503
  onChannelChange,
1504
+ isEmailIntegrated,
1495
1505
  inputValue,
1496
1506
  onInputChange,
1497
1507
  onSend,
1508
+ onSendEmail,
1498
1509
  onTakeOver,
1499
1510
  onLetAiHandle,
1500
1511
  onReopen,
@@ -1502,12 +1513,53 @@ function ChatThread({
1502
1513
  onUnmarkUrgent,
1503
1514
  onArchive,
1504
1515
  onAssignToAdvisor,
1516
+ hasMoreMessages,
1517
+ isLoadingMoreMessages,
1518
+ onLoadMoreMessages,
1505
1519
  onBack,
1506
1520
  onShowLeadInfo,
1507
1521
  className
1508
1522
  }) {
1509
1523
  const aiIsHandling = mode === "ai";
1510
1524
  const isClosed = status === "closed";
1525
+ const scrollRef = import_react3.default.useRef(null);
1526
+ const preLoadScrollHeightRef = import_react3.default.useRef(null);
1527
+ const handleScroll = (e) => {
1528
+ if (!hasMoreMessages || isLoadingMoreMessages || !onLoadMoreMessages) {
1529
+ return;
1530
+ }
1531
+ if (e.currentTarget.scrollTop <= 80) {
1532
+ preLoadScrollHeightRef.current = e.currentTarget.scrollHeight;
1533
+ onLoadMoreMessages();
1534
+ }
1535
+ };
1536
+ const prevLastMessageIdRef = import_react3.default.useRef(void 0);
1537
+ const prevContactIdRef = import_react3.default.useRef(contact.id);
1538
+ import_react3.default.useLayoutEffect(() => {
1539
+ var _a, _b;
1540
+ const el = scrollRef.current;
1541
+ if (!el) return;
1542
+ if (preLoadScrollHeightRef.current !== null) {
1543
+ el.scrollTop = el.scrollHeight - preLoadScrollHeightRef.current;
1544
+ preLoadScrollHeightRef.current = null;
1545
+ prevLastMessageIdRef.current = (_a = messages[messages.length - 1]) == null ? void 0 : _a.id;
1546
+ prevContactIdRef.current = contact.id;
1547
+ return;
1548
+ }
1549
+ const currentLastId = (_b = messages[messages.length - 1]) == null ? void 0 : _b.id;
1550
+ const contactChanged = prevContactIdRef.current !== contact.id;
1551
+ const tailChanged = prevLastMessageIdRef.current !== currentLastId;
1552
+ if (contactChanged || tailChanged) {
1553
+ el.scrollTop = el.scrollHeight;
1554
+ }
1555
+ prevLastMessageIdRef.current = currentLastId;
1556
+ prevContactIdRef.current = contact.id;
1557
+ }, [contact.id, messages]);
1558
+ import_react3.default.useLayoutEffect(() => {
1559
+ if (!isAiTyping) return;
1560
+ const el = scrollRef.current;
1561
+ if (el) el.scrollTop = el.scrollHeight;
1562
+ }, [isAiTyping]);
1511
1563
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("flex flex-col bg-background", className), children: [
1512
1564
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1513
1565
  "div",
@@ -1597,9 +1649,12 @@ function ChatThread({
1597
1649
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1598
1650
  "div",
1599
1651
  {
1652
+ ref: scrollRef,
1653
+ onScroll: handleScroll,
1600
1654
  className: "flex flex-1 flex-col gap-4 overflow-y-auto p-4",
1601
1655
  tabIndex: 0,
1602
1656
  children: [
1657
+ isLoadingMoreMessages && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex justify-center py-1 text-caption text-muted-foreground", children: "Loading older messages..." }),
1603
1658
  messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 text-muted-foreground", children: [
1604
1659
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.MessageSquare, { className: "size-8 opacity-30" }),
1605
1660
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm", children: "No messages yet" })
@@ -1637,10 +1692,12 @@ function ChatThread({
1637
1692
  mode,
1638
1693
  channel,
1639
1694
  onChannelChange,
1695
+ isEmailIntegrated,
1640
1696
  contactEmail: contact.email,
1641
1697
  inputValue,
1642
1698
  onInputChange,
1643
1699
  onSend,
1700
+ onSendEmail,
1644
1701
  onTakeOver,
1645
1702
  onLetAiHandle
1646
1703
  }
@@ -1991,11 +2048,15 @@ function ConversationsPage({
1991
2048
  onChannelFilterChange,
1992
2049
  channel,
1993
2050
  onChannelChange,
2051
+ isEmailIntegrated,
1994
2052
  inputValue,
1995
2053
  internalNotes,
1996
2054
  showLeadPanel = true,
1997
2055
  hasMore,
1998
2056
  isLoadingMore,
2057
+ hasMoreMessages,
2058
+ isLoadingMoreMessages,
2059
+ onLoadMoreMessages,
1999
2060
  isAiTyping,
2000
2061
  notesSaveStatus,
2001
2062
  onSelectConversation,
@@ -2004,6 +2065,7 @@ function ConversationsPage({
2004
2065
  onFilterChange,
2005
2066
  onInputChange,
2006
2067
  onSend,
2068
+ onSendEmail,
2007
2069
  onTakeOver,
2008
2070
  onLetAiHandle,
2009
2071
  onReopen,
@@ -2072,9 +2134,11 @@ function ConversationsPage({
2072
2134
  isAiTyping,
2073
2135
  channel,
2074
2136
  onChannelChange,
2137
+ isEmailIntegrated,
2075
2138
  inputValue,
2076
2139
  onInputChange,
2077
2140
  onSend,
2141
+ onSendEmail,
2078
2142
  onTakeOver,
2079
2143
  onLetAiHandle,
2080
2144
  onReopen,
@@ -2082,6 +2146,9 @@ function ConversationsPage({
2082
2146
  onUnmarkUrgent,
2083
2147
  onArchive,
2084
2148
  onAssignToAdvisor,
2149
+ hasMoreMessages,
2150
+ isLoadingMoreMessages,
2151
+ onLoadMoreMessages,
2085
2152
  onBack: () => setMobilePanel("list"),
2086
2153
  onShowLeadInfo: () => setMobilePanel("lead"),
2087
2154
  className: cn(
@@ -9,7 +9,7 @@ import {
9
9
  ConversationStatusChip,
10
10
  ConversationsPage,
11
11
  LeadInfoPanel
12
- } from "../../chunk-ZMTCMP2G.mjs";
12
+ } from "../../chunk-EB626HVW.mjs";
13
13
  import "../../chunk-3S6KVFF5.mjs";
14
14
  import "../../chunk-WE4YKBDE.mjs";
15
15
  import "../../chunk-H5DTKPJ2.mjs";