@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.
- package/.turbo/turbo-build.log +88 -88
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-O5CP6VP6.mjs → chunk-BF5FKUF6.mjs} +104 -63
- package/dist/{chunk-ZMTCMP2G.mjs → chunk-EB626HVW.mjs} +70 -3
- package/dist/chunk-KICT4VQL.mjs +508 -0
- package/dist/chunk-V23CBULF.mjs +432 -0
- package/dist/components/ui/ai-conversations.js +70 -3
- package/dist/components/ui/ai-conversations.mjs +1 -1
- package/dist/components/ui/appointment-calendar-view.js +177 -176
- package/dist/components/ui/appointment-calendar-view.mjs +1 -1
- package/dist/components/ui/bank-statement-generate-dialog.js +209 -107
- package/dist/components/ui/bank-statement-generate-dialog.mjs +2 -1
- package/dist/components/ui/resource-center/index.js +1030 -0
- package/dist/components/ui/resource-center/index.mjs +29 -0
- package/dist/index.js +661 -403
- package/dist/index.mjs +16 -14
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/components/index.tsx +2 -0
- package/src/components/ui/ai-conversations.tsx +157 -23
- package/src/components/ui/appointment-calendar-view.tsx +211 -199
- package/src/components/ui/bank-statement-generate-dialog.tsx +147 -96
- package/src/components/ui/resource-center/index.tsx +35 -0
- package/src/components/ui/resource-center/resource-cards.tsx +218 -0
- package/src/components/ui/resource-center/resource-carousel.tsx +122 -0
- package/src/components/ui/resource-center/resource-center-header.tsx +95 -0
- package/src/components/ui/resource-center/resource-email-editor-dialog.tsx +131 -0
- package/src/components/ui/resource-center/resource-modal.tsx +76 -0
- package/src/components/ui/resource-center/types.ts +81 -0
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +1 -1
- package/dist/chunk-IODGRCQG.mjs +0 -438
- package/dist/chunk-XYWEGBAA.mjs +0 -348
- package/dist/components/ui/resource-center.js +0 -748
- package/dist/components/ui/resource-center.mjs +0 -24
- package/src/components/ui/resource-center.tsx +0 -539
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wealthx/shadcn",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.13",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -949,9 +949,9 @@
|
|
|
949
949
|
"require": "./dist/components/ui/transactions-summary-block.js"
|
|
950
950
|
},
|
|
951
951
|
"./resource-center": {
|
|
952
|
-
"types": "./src/components/ui/resource-center.tsx",
|
|
953
|
-
"import": "./dist/components/ui/resource-center.mjs",
|
|
954
|
-
"require": "./dist/components/ui/resource-center.js"
|
|
952
|
+
"types": "./src/components/ui/resource-center/index.tsx",
|
|
953
|
+
"import": "./dist/components/ui/resource-center/index.mjs",
|
|
954
|
+
"require": "./dist/components/ui/resource-center/index.js"
|
|
955
955
|
}
|
|
956
956
|
}
|
|
957
957
|
}
|
package/src/components/index.tsx
CHANGED
|
@@ -1262,6 +1262,7 @@ export {
|
|
|
1262
1262
|
ResourceCarousel,
|
|
1263
1263
|
ResourceCenterHeader,
|
|
1264
1264
|
ResourceDocumentCard,
|
|
1265
|
+
ResourceEmailEditorDialog,
|
|
1265
1266
|
ResourceEmailTemplateCard,
|
|
1266
1267
|
ResourceModal,
|
|
1267
1268
|
ResourceVideoCard,
|
|
@@ -1271,6 +1272,7 @@ export type {
|
|
|
1271
1272
|
ResourceCenterHeaderProps,
|
|
1272
1273
|
ResourceDocumentCardProps,
|
|
1273
1274
|
ResourceDocumentItem,
|
|
1275
|
+
ResourceEmailEditorDialogProps,
|
|
1274
1276
|
ResourceEmailTemplateCardProps,
|
|
1275
1277
|
ResourceEmailTemplateItem,
|
|
1276
1278
|
ResourceModalAttachment,
|
|
@@ -659,16 +659,31 @@ export function ChatBubble({ message, className }: ChatBubbleProps) {
|
|
|
659
659
|
// ChatComposer
|
|
660
660
|
// ---------------------------------------------------------------------------
|
|
661
661
|
|
|
662
|
+
export interface AiConvEmailPayload {
|
|
663
|
+
content: string;
|
|
664
|
+
to: string;
|
|
665
|
+
cc: string;
|
|
666
|
+
subject: string;
|
|
667
|
+
}
|
|
668
|
+
|
|
662
669
|
export interface ChatComposerProps {
|
|
663
670
|
mode: AiConvMode;
|
|
664
671
|
/** Active reply channel. Defaults to "chat". */
|
|
665
672
|
channel?: AiConvChannel;
|
|
666
673
|
onChannelChange?: (channel: AiConvChannel) => void;
|
|
674
|
+
/**
|
|
675
|
+
* When true, the Email tab is shown in the composer. Defaults to false —
|
|
676
|
+
* consumers must opt in once their tenant's email integration is wired up.
|
|
677
|
+
*/
|
|
678
|
+
isEmailIntegrated?: boolean;
|
|
667
679
|
/** Lead's email address — pre-fills the To field in email compose. */
|
|
668
680
|
contactEmail?: string;
|
|
669
681
|
inputValue?: string;
|
|
670
682
|
onInputChange?: (v: string) => void;
|
|
671
|
-
|
|
683
|
+
/** Fired when the user sends a chat message. */
|
|
684
|
+
onSend?: (content: string) => void;
|
|
685
|
+
/** Fired when the user sends an email. */
|
|
686
|
+
onSendEmail?: (payload: AiConvEmailPayload) => void;
|
|
672
687
|
onTakeOver?: () => void;
|
|
673
688
|
onLetAiHandle?: () => void;
|
|
674
689
|
className?: string;
|
|
@@ -678,16 +693,21 @@ export function ChatComposer({
|
|
|
678
693
|
mode,
|
|
679
694
|
channel: channelProp = "chat",
|
|
680
695
|
onChannelChange,
|
|
696
|
+
isEmailIntegrated = false,
|
|
681
697
|
contactEmail = "",
|
|
682
698
|
inputValue = "",
|
|
683
699
|
onInputChange,
|
|
684
700
|
onSend,
|
|
701
|
+
onSendEmail,
|
|
685
702
|
onTakeOver,
|
|
686
703
|
onLetAiHandle,
|
|
687
704
|
className,
|
|
688
705
|
}: ChatComposerProps) {
|
|
689
|
-
// Semi-controlled: owns channel state for uncontrolled usage, notifies parent on change
|
|
690
|
-
|
|
706
|
+
// Semi-controlled: owns channel state for uncontrolled usage, notifies parent on change.
|
|
707
|
+
// Force chat when email isn't integrated so the panel never lands on a hidden tab.
|
|
708
|
+
const [channel, setChannel] = React.useState<AiConvChannel>(
|
|
709
|
+
isEmailIntegrated ? channelProp : "chat",
|
|
710
|
+
);
|
|
691
711
|
const [emailTo, setEmailTo] = React.useState(contactEmail);
|
|
692
712
|
const [emailCc, setEmailCc] = React.useState("");
|
|
693
713
|
const [showCc, setShowCc] = React.useState(false);
|
|
@@ -754,23 +774,25 @@ export function ChatComposer({
|
|
|
754
774
|
className,
|
|
755
775
|
)}
|
|
756
776
|
>
|
|
757
|
-
|
|
758
|
-
<
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
<
|
|
764
|
-
<
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
<
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
777
|
+
{isEmailIntegrated && (
|
|
778
|
+
<div className="border-b border-border px-3 py-2">
|
|
779
|
+
<Tabs
|
|
780
|
+
value={channel}
|
|
781
|
+
onValueChange={(v) => v && handleChannelChange(v as AiConvChannel)}
|
|
782
|
+
>
|
|
783
|
+
<TabsList variant="default" className="w-full">
|
|
784
|
+
<TabsTrigger value="chat" className="flex-1 gap-1.5">
|
|
785
|
+
<MessageSquare className="size-3.5" />
|
|
786
|
+
Chat
|
|
787
|
+
</TabsTrigger>
|
|
788
|
+
<TabsTrigger value="email" className="flex-1 gap-1.5">
|
|
789
|
+
<Mail className="size-3.5" />
|
|
790
|
+
Email
|
|
791
|
+
</TabsTrigger>
|
|
792
|
+
</TabsList>
|
|
793
|
+
</Tabs>
|
|
794
|
+
</div>
|
|
795
|
+
)}
|
|
774
796
|
|
|
775
797
|
{mode === "ai" ? (
|
|
776
798
|
<div className="flex items-center gap-2 bg-muted/30 px-4 py-2.5 text-[12px] text-muted-foreground">
|
|
@@ -867,7 +889,14 @@ export function ChatComposer({
|
|
|
867
889
|
</div>
|
|
868
890
|
<Button
|
|
869
891
|
size="sm"
|
|
870
|
-
onClick={() =>
|
|
892
|
+
onClick={() =>
|
|
893
|
+
onSendEmail?.({
|
|
894
|
+
content: inputValue,
|
|
895
|
+
to: emailTo,
|
|
896
|
+
cc: emailCc,
|
|
897
|
+
subject: emailSubject,
|
|
898
|
+
})
|
|
899
|
+
}
|
|
871
900
|
disabled={!inputValue.trim() || !emailTo.trim()}
|
|
872
901
|
>
|
|
873
902
|
<Send className="mr-1.5 size-3.5" />
|
|
@@ -920,9 +949,16 @@ export interface ChatThreadProps {
|
|
|
920
949
|
/** Active reply channel — "chat" (default) or "email". */
|
|
921
950
|
channel?: AiConvChannel;
|
|
922
951
|
onChannelChange?: (channel: AiConvChannel) => void;
|
|
952
|
+
/**
|
|
953
|
+
* When true, the Email tab is shown in the composer. Defaults to false.
|
|
954
|
+
*/
|
|
955
|
+
isEmailIntegrated?: boolean;
|
|
923
956
|
inputValue?: string;
|
|
924
957
|
onInputChange?: (v: string) => void;
|
|
925
|
-
|
|
958
|
+
/** Fired when the user sends a chat message. */
|
|
959
|
+
onSend?: (content: string) => void;
|
|
960
|
+
/** Fired when the user sends an email. */
|
|
961
|
+
onSendEmail?: (payload: AiConvEmailPayload) => void;
|
|
926
962
|
onTakeOver?: () => void;
|
|
927
963
|
onLetAiHandle?: () => void;
|
|
928
964
|
onReopen?: () => void;
|
|
@@ -930,6 +966,12 @@ export interface ChatThreadProps {
|
|
|
930
966
|
onUnmarkUrgent?: () => void;
|
|
931
967
|
onArchive?: () => void;
|
|
932
968
|
onAssignToAdvisor?: () => void;
|
|
969
|
+
/** True when older messages can be loaded (e.g. paginated history). */
|
|
970
|
+
hasMoreMessages?: boolean;
|
|
971
|
+
/** True while a `onLoadMoreMessages` request is in-flight. */
|
|
972
|
+
isLoadingMoreMessages?: boolean;
|
|
973
|
+
/** Fired when the consumer should fetch older messages. */
|
|
974
|
+
onLoadMoreMessages?: () => void;
|
|
933
975
|
/** Mobile only — back to conversation list. */
|
|
934
976
|
onBack?: () => void;
|
|
935
977
|
/** Mobile only — show lead info panel. */
|
|
@@ -945,9 +987,11 @@ export function ChatThread({
|
|
|
945
987
|
isAiTyping = false,
|
|
946
988
|
channel,
|
|
947
989
|
onChannelChange,
|
|
990
|
+
isEmailIntegrated,
|
|
948
991
|
inputValue,
|
|
949
992
|
onInputChange,
|
|
950
993
|
onSend,
|
|
994
|
+
onSendEmail,
|
|
951
995
|
onTakeOver,
|
|
952
996
|
onLetAiHandle,
|
|
953
997
|
onReopen,
|
|
@@ -955,6 +999,9 @@ export function ChatThread({
|
|
|
955
999
|
onUnmarkUrgent,
|
|
956
1000
|
onArchive,
|
|
957
1001
|
onAssignToAdvisor,
|
|
1002
|
+
hasMoreMessages,
|
|
1003
|
+
isLoadingMoreMessages,
|
|
1004
|
+
onLoadMoreMessages,
|
|
958
1005
|
onBack,
|
|
959
1006
|
onShowLeadInfo,
|
|
960
1007
|
className,
|
|
@@ -962,6 +1009,61 @@ export function ChatThread({
|
|
|
962
1009
|
const aiIsHandling = mode === "ai";
|
|
963
1010
|
const isClosed = status === "closed";
|
|
964
1011
|
|
|
1012
|
+
const scrollRef = React.useRef<HTMLDivElement>(null);
|
|
1013
|
+
// Captures scrollHeight just before older messages are prepended, so we can
|
|
1014
|
+
// restore the user's visible scroll offset once the new nodes render.
|
|
1015
|
+
const preLoadScrollHeightRef = React.useRef<number | null>(null);
|
|
1016
|
+
|
|
1017
|
+
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
|
1018
|
+
if (!hasMoreMessages || isLoadingMoreMessages || !onLoadMoreMessages) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (e.currentTarget.scrollTop <= 80) {
|
|
1022
|
+
preLoadScrollHeightRef.current = e.currentTarget.scrollHeight;
|
|
1023
|
+
onLoadMoreMessages();
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
// Tracks the last "tail" message id so we can tell an append (new message,
|
|
1028
|
+
// tail changed) apart from a prepend (older history loaded, tail unchanged).
|
|
1029
|
+
const prevLastMessageIdRef = React.useRef<string | undefined>(undefined);
|
|
1030
|
+
const prevContactIdRef = React.useRef<string>(contact.id);
|
|
1031
|
+
|
|
1032
|
+
React.useLayoutEffect(() => {
|
|
1033
|
+
const el = scrollRef.current;
|
|
1034
|
+
if (!el) return;
|
|
1035
|
+
|
|
1036
|
+
// Prepend (older messages just loaded) — restore scroll so the user
|
|
1037
|
+
// stays anchored to the message they were reading.
|
|
1038
|
+
if (preLoadScrollHeightRef.current !== null) {
|
|
1039
|
+
el.scrollTop = el.scrollHeight - preLoadScrollHeightRef.current;
|
|
1040
|
+
preLoadScrollHeightRef.current = null;
|
|
1041
|
+
prevLastMessageIdRef.current = messages[messages.length - 1]?.id;
|
|
1042
|
+
prevContactIdRef.current = contact.id;
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const currentLastId = messages[messages.length - 1]?.id;
|
|
1047
|
+
const contactChanged = prevContactIdRef.current !== contact.id;
|
|
1048
|
+
const tailChanged = prevLastMessageIdRef.current !== currentLastId;
|
|
1049
|
+
|
|
1050
|
+
// Opening a conversation or appending a new message (sent, received,
|
|
1051
|
+
// or system) — pin to the bottom.
|
|
1052
|
+
if (contactChanged || tailChanged) {
|
|
1053
|
+
el.scrollTop = el.scrollHeight;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
prevLastMessageIdRef.current = currentLastId;
|
|
1057
|
+
prevContactIdRef.current = contact.id;
|
|
1058
|
+
}, [contact.id, messages]);
|
|
1059
|
+
|
|
1060
|
+
// Typing indicator adds DOM height — keep the view pinned to bottom.
|
|
1061
|
+
React.useLayoutEffect(() => {
|
|
1062
|
+
if (!isAiTyping) return;
|
|
1063
|
+
const el = scrollRef.current;
|
|
1064
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
1065
|
+
}, [isAiTyping]);
|
|
1066
|
+
|
|
965
1067
|
return (
|
|
966
1068
|
<div className={cn("flex flex-col bg-background", className)}>
|
|
967
1069
|
{/* Header */}
|
|
@@ -1071,9 +1173,16 @@ export function ChatThread({
|
|
|
1071
1173
|
|
|
1072
1174
|
{/* Messages */}
|
|
1073
1175
|
<div
|
|
1176
|
+
ref={scrollRef}
|
|
1177
|
+
onScroll={handleScroll}
|
|
1074
1178
|
className="flex flex-1 flex-col gap-4 overflow-y-auto p-4"
|
|
1075
1179
|
tabIndex={0}
|
|
1076
1180
|
>
|
|
1181
|
+
{isLoadingMoreMessages && (
|
|
1182
|
+
<div className="flex justify-center py-1 text-caption text-muted-foreground">
|
|
1183
|
+
Loading older messages...
|
|
1184
|
+
</div>
|
|
1185
|
+
)}
|
|
1077
1186
|
{messages.length === 0 ? (
|
|
1078
1187
|
<div className="flex flex-1 flex-col items-center justify-center gap-2 text-muted-foreground">
|
|
1079
1188
|
<MessageSquare className="size-8 opacity-30" />
|
|
@@ -1120,10 +1229,12 @@ export function ChatThread({
|
|
|
1120
1229
|
mode={mode}
|
|
1121
1230
|
channel={channel}
|
|
1122
1231
|
onChannelChange={onChannelChange}
|
|
1232
|
+
isEmailIntegrated={isEmailIntegrated}
|
|
1123
1233
|
contactEmail={contact.email}
|
|
1124
1234
|
inputValue={inputValue}
|
|
1125
1235
|
onInputChange={onInputChange}
|
|
1126
1236
|
onSend={onSend}
|
|
1237
|
+
onSendEmail={onSendEmail}
|
|
1127
1238
|
onTakeOver={onTakeOver}
|
|
1128
1239
|
onLetAiHandle={onLetAiHandle}
|
|
1129
1240
|
/>
|
|
@@ -1584,18 +1695,31 @@ export interface ConversationsPageProps {
|
|
|
1584
1695
|
/** Active reply channel — "chat" (default) or "email". */
|
|
1585
1696
|
channel?: AiConvChannel;
|
|
1586
1697
|
onChannelChange?: (channel: AiConvChannel) => void;
|
|
1698
|
+
/**
|
|
1699
|
+
* When true, the Email tab is shown in the composer. Defaults to false.
|
|
1700
|
+
*/
|
|
1701
|
+
isEmailIntegrated?: boolean;
|
|
1587
1702
|
inputValue?: string;
|
|
1588
1703
|
internalNotes?: string;
|
|
1589
1704
|
showLeadPanel?: boolean;
|
|
1590
1705
|
hasMore?: boolean;
|
|
1591
1706
|
isLoadingMore?: boolean;
|
|
1707
|
+
/** True when older messages can be loaded for the active conversation. */
|
|
1708
|
+
hasMoreMessages?: boolean;
|
|
1709
|
+
/** True while a `onLoadMoreMessages` request is in-flight. */
|
|
1710
|
+
isLoadingMoreMessages?: boolean;
|
|
1711
|
+
/** Fired when the consumer should fetch older messages for the active conversation. */
|
|
1712
|
+
onLoadMoreMessages?: () => void;
|
|
1592
1713
|
onSelectConversation?: (id: string) => void;
|
|
1593
1714
|
onRead?: (id: string) => void;
|
|
1594
1715
|
onSearchChange?: (v: string) => void;
|
|
1595
1716
|
onFilterChange?: (f: AiConvFilterTab) => void;
|
|
1596
1717
|
onChannelFilterChange?: (f: AiConvChannelFilter) => void;
|
|
1597
1718
|
onInputChange?: (v: string) => void;
|
|
1598
|
-
|
|
1719
|
+
/** Fired when the user sends a chat message. */
|
|
1720
|
+
onSend?: (content: string) => void;
|
|
1721
|
+
/** Fired when the user sends an email. */
|
|
1722
|
+
onSendEmail?: (payload: AiConvEmailPayload) => void;
|
|
1599
1723
|
onTakeOver?: () => void;
|
|
1600
1724
|
onLetAiHandle?: () => void;
|
|
1601
1725
|
onReopen?: () => void;
|
|
@@ -1637,11 +1761,15 @@ export function ConversationsPage({
|
|
|
1637
1761
|
onChannelFilterChange,
|
|
1638
1762
|
channel,
|
|
1639
1763
|
onChannelChange,
|
|
1764
|
+
isEmailIntegrated,
|
|
1640
1765
|
inputValue,
|
|
1641
1766
|
internalNotes,
|
|
1642
1767
|
showLeadPanel = true,
|
|
1643
1768
|
hasMore,
|
|
1644
1769
|
isLoadingMore,
|
|
1770
|
+
hasMoreMessages,
|
|
1771
|
+
isLoadingMoreMessages,
|
|
1772
|
+
onLoadMoreMessages,
|
|
1645
1773
|
isAiTyping,
|
|
1646
1774
|
notesSaveStatus,
|
|
1647
1775
|
onSelectConversation,
|
|
@@ -1650,6 +1778,7 @@ export function ConversationsPage({
|
|
|
1650
1778
|
onFilterChange,
|
|
1651
1779
|
onInputChange,
|
|
1652
1780
|
onSend,
|
|
1781
|
+
onSendEmail,
|
|
1653
1782
|
onTakeOver,
|
|
1654
1783
|
onLetAiHandle,
|
|
1655
1784
|
onReopen,
|
|
@@ -1723,9 +1852,11 @@ export function ConversationsPage({
|
|
|
1723
1852
|
isAiTyping={isAiTyping}
|
|
1724
1853
|
channel={channel}
|
|
1725
1854
|
onChannelChange={onChannelChange}
|
|
1855
|
+
isEmailIntegrated={isEmailIntegrated}
|
|
1726
1856
|
inputValue={inputValue}
|
|
1727
1857
|
onInputChange={onInputChange}
|
|
1728
1858
|
onSend={onSend}
|
|
1859
|
+
onSendEmail={onSendEmail}
|
|
1729
1860
|
onTakeOver={onTakeOver}
|
|
1730
1861
|
onLetAiHandle={onLetAiHandle}
|
|
1731
1862
|
onReopen={onReopen}
|
|
@@ -1733,6 +1864,9 @@ export function ConversationsPage({
|
|
|
1733
1864
|
onUnmarkUrgent={onUnmarkUrgent}
|
|
1734
1865
|
onArchive={onArchive}
|
|
1735
1866
|
onAssignToAdvisor={onAssignToAdvisor}
|
|
1867
|
+
hasMoreMessages={hasMoreMessages}
|
|
1868
|
+
isLoadingMoreMessages={isLoadingMoreMessages}
|
|
1869
|
+
onLoadMoreMessages={onLoadMoreMessages}
|
|
1736
1870
|
onBack={() => setMobilePanel("list")}
|
|
1737
1871
|
onShowLeadInfo={() => setMobilePanel("lead")}
|
|
1738
1872
|
className={cn(
|