@wealthx/shadcn 1.5.9 → 1.5.11
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 +157 -157
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-AANINK2B.mjs → chunk-2KNQZG5S.mjs} +1 -1
- package/dist/chunk-3KLJ4XRE.mjs +375 -0
- package/dist/{chunk-6U4NQGVM.mjs → chunk-4X4MGYHE.mjs} +2 -2
- package/dist/{chunk-CEEVYRQA.mjs → chunk-67DGIPQ4.mjs} +1 -1
- package/dist/{chunk-7UIL5UN3.mjs → chunk-7II6QRCZ.mjs} +1 -1
- package/dist/{chunk-W5QJ57PU.mjs → chunk-7LN5OGC2.mjs} +1 -1
- package/dist/{chunk-ZXEUBBHJ.mjs → chunk-7TMPOZDE.mjs} +1 -1
- package/dist/{chunk-AHSCWXYJ.mjs → chunk-AJUAJC5O.mjs} +1 -1
- package/dist/{chunk-3CGM3QXQ.mjs → chunk-AKWN5ZQG.mjs} +2 -2
- package/dist/{chunk-FRT3S72S.mjs → chunk-CQ7HKBEX.mjs} +1 -1
- package/dist/{chunk-E2BNCA6L.mjs → chunk-EHQL64B7.mjs} +1 -1
- package/dist/{chunk-2WCIORP7.mjs → chunk-EXI64H46.mjs} +1 -1
- package/dist/{chunk-BBXSNDS3.mjs → chunk-FQYFPHDO.mjs} +1 -1
- package/dist/{chunk-3VZ6CYY2.mjs → chunk-GAXNO4JB.mjs} +1 -1
- package/dist/{chunk-3WGFIFP6.mjs → chunk-I4P7RXAE.mjs} +1 -1
- package/dist/{chunk-TAFL4WDY.mjs → chunk-IODGRCQG.mjs} +17 -10
- package/dist/{chunk-GS47ZSSA.mjs → chunk-J7KQON2N.mjs} +20 -5
- package/dist/{chunk-IQGKOT7A.mjs → chunk-K35TFQUB.mjs} +1 -1
- package/dist/{chunk-4DO3WM7V.mjs → chunk-K4VWSDJJ.mjs} +1 -1
- package/dist/{chunk-KWD6GANL.mjs → chunk-MPA2HV5U.mjs} +1 -1
- package/dist/{chunk-5LZZYODG.mjs → chunk-QHAMVWDG.mjs} +19 -1
- package/dist/{chunk-XUCDPAVI.mjs → chunk-R6U246E4.mjs} +2 -2
- package/dist/{chunk-VCDGLN25.mjs → chunk-S6AYZJYO.mjs} +47 -21
- package/dist/{chunk-WL6WVV47.mjs → chunk-X6RC5UWB.mjs} +1 -1
- package/dist/{chunk-4BHDDLWK.mjs → chunk-XAS6KBIG.mjs} +2 -2
- package/dist/{chunk-VWZS32ZQ.mjs → chunk-XYWEGBAA.mjs} +1 -1
- package/dist/{chunk-54MTIKNC.mjs → chunk-YV7XF32X.mjs} +49 -24
- package/dist/{chunk-E5EDZQ5J.mjs → chunk-ZA44WICP.mjs} +1 -1
- package/dist/{chunk-54TRNCID.mjs → chunk-ZMTCMP2G.mjs} +8 -8
- package/dist/components/ui/advisor-card.js +144 -55
- package/dist/components/ui/advisor-card.mjs +5 -2
- package/dist/components/ui/agent-evaluation-toast.js +1 -1
- package/dist/components/ui/agent-evaluation-toast.mjs +2 -2
- package/dist/components/ui/ai-assistant-drawer.js +1 -1
- package/dist/components/ui/ai-assistant-drawer.mjs +2 -2
- package/dist/components/ui/ai-builder.js +1 -1
- package/dist/components/ui/ai-builder.mjs +2 -2
- package/dist/components/ui/ai-conversations.js +1 -1
- package/dist/components/ui/ai-conversations.mjs +3 -3
- package/dist/components/ui/appointment-action-dialogs.js +1 -1
- package/dist/components/ui/appointment-action-dialogs.mjs +3 -3
- package/dist/components/ui/appointment-book-dialog.js +19 -4
- package/dist/components/ui/appointment-book-dialog.mjs +3 -3
- package/dist/components/ui/appointment-calendar-view.js +17 -10
- package/dist/components/ui/appointment-calendar-view.mjs +2 -2
- package/dist/components/ui/appointment-detail-sheet.js +1 -1
- package/dist/components/ui/appointment-detail-sheet.mjs +4 -4
- package/dist/components/ui/appointment-gmail-connect.js +1 -1
- package/dist/components/ui/appointment-gmail-connect.mjs +2 -2
- package/dist/components/ui/appointment-time-slot-picker.js +1 -1
- package/dist/components/ui/appointment-time-slot-picker.mjs +2 -2
- package/dist/components/ui/appointment-upcoming-card.js +1 -1
- package/dist/components/ui/appointment-upcoming-card.mjs +3 -3
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/badge.mjs +1 -1
- package/dist/components/ui/chat-widget-primitives.js +1 -1
- package/dist/components/ui/chat-widget-primitives.mjs +2 -2
- package/dist/components/ui/chat-widget.js +1 -1
- package/dist/components/ui/chat-widget.mjs +3 -3
- package/dist/components/ui/chip.js +1 -1
- package/dist/components/ui/chip.mjs +2 -2
- package/dist/components/ui/contact-alert-dialog/index.js +19 -1
- package/dist/components/ui/contact-alert-dialog/index.mjs +1 -1
- package/dist/components/ui/dashboard-transactions-table.js +1 -1
- package/dist/components/ui/dashboard-transactions-table.mjs +2 -2
- package/dist/components/ui/financial-cards.js +1 -1
- package/dist/components/ui/financial-cards.mjs +2 -2
- package/dist/components/ui/financial-sections.js +1 -1
- package/dist/components/ui/financial-sections.mjs +3 -3
- package/dist/components/ui/income-summary-component.js +1 -1
- package/dist/components/ui/income-summary-component.mjs +1 -1
- package/dist/components/ui/integration-card.js +1 -1
- package/dist/components/ui/integration-card.mjs +2 -2
- package/dist/components/ui/kanban-column.js +46 -23
- package/dist/components/ui/kanban-column.mjs +4 -4
- package/dist/components/ui/loan-applicant-information.js +1 -1
- package/dist/components/ui/loan-applicant-information.mjs +1 -1
- package/dist/components/ui/loan-application-badge.js +1 -1
- package/dist/components/ui/loan-application-badge.mjs +2 -2
- package/dist/components/ui/opportunity-card.js +46 -23
- package/dist/components/ui/opportunity-card.mjs +3 -3
- package/dist/components/ui/opportunity-summary-tab.js +1 -1
- package/dist/components/ui/opportunity-summary-tab.mjs +3 -3
- package/dist/components/ui/pipeline-board.js +46 -23
- package/dist/components/ui/pipeline-board.mjs +5 -5
- package/dist/components/ui/pipeline-primitives.js +1 -1
- package/dist/components/ui/pipeline-primitives.mjs +2 -2
- package/dist/components/ui/property-asset-card.js +1 -1
- package/dist/components/ui/property-asset-card.mjs +1 -1
- package/dist/components/ui/resource-center.js +1 -1
- package/dist/components/ui/resource-center.mjs +2 -2
- package/dist/components/ui/share-details-dialog.js +326 -30
- package/dist/components/ui/share-details-dialog.mjs +4 -1
- package/dist/components/ui/stage-timeline.js +1 -1
- package/dist/components/ui/stage-timeline.mjs +3 -3
- package/dist/index.js +473 -197
- package/dist/index.mjs +44 -42
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/index.tsx +4 -0
- package/src/components/ui/advisor-card.tsx +75 -25
- package/src/components/ui/appointment-book-dialog.tsx +26 -3
- package/src/components/ui/appointment-calendar-view.tsx +7 -6
- package/src/components/ui/appointment-time-slot-picker.tsx +1 -0
- package/src/components/ui/badge.tsx +1 -1
- package/src/components/ui/contact-alert-dialog/contact-alert-dialog.tsx +19 -1
- package/src/components/ui/opportunity-card.tsx +56 -20
- package/src/components/ui/share-details-dialog.tsx +251 -0
- package/src/styles/styles-css.ts +1 -1
- package/dist/chunk-OZ2R6ERP.mjs +0 -174
package/package.json
CHANGED
package/src/components/index.tsx
CHANGED
|
@@ -73,6 +73,7 @@ export type {
|
|
|
73
73
|
AdvisorCardProps,
|
|
74
74
|
AdvisorInviteCardProps,
|
|
75
75
|
AdvisorAppointmentStrip,
|
|
76
|
+
AdvisorCardMenuItem,
|
|
76
77
|
} from "./ui/advisor-card";
|
|
77
78
|
|
|
78
79
|
export { AppointmentUpcomingCard } from "./ui/appointment-upcoming-card";
|
|
@@ -492,10 +493,13 @@ export type {
|
|
|
492
493
|
export {
|
|
493
494
|
ShareDetailsDialog,
|
|
494
495
|
EmailTemplateDialog,
|
|
496
|
+
ShareContactDialog,
|
|
495
497
|
} from "./ui/share-details-dialog";
|
|
496
498
|
export type {
|
|
497
499
|
ShareDetailsDialogProps,
|
|
498
500
|
EmailTemplateDialogProps,
|
|
501
|
+
ShareContactDialogProps,
|
|
502
|
+
InternalAdvisor,
|
|
499
503
|
} from "./ui/share-details-dialog";
|
|
500
504
|
|
|
501
505
|
export { FilePreviewDialog } from "./ui/file-preview-dialog";
|
|
@@ -3,6 +3,12 @@ import { Badge } from "./badge";
|
|
|
3
3
|
import { Button } from "./button";
|
|
4
4
|
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
|
5
5
|
import { Separator } from "./separator";
|
|
6
|
+
import {
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuItem,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from "./dropdown-menu";
|
|
6
12
|
import {
|
|
7
13
|
Calendar,
|
|
8
14
|
CalendarCheck,
|
|
@@ -18,6 +24,18 @@ import type { AppointmentStatus } from "./appointment-time-slot-picker";
|
|
|
18
24
|
// Types
|
|
19
25
|
// ---------------------------------------------------------------------------
|
|
20
26
|
|
|
27
|
+
/** A single item in the ⋮ overflow DropdownMenu */
|
|
28
|
+
export interface AdvisorCardMenuItem {
|
|
29
|
+
/** Display label */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Called when the item is clicked */
|
|
32
|
+
onClick: () => void;
|
|
33
|
+
/** Visual variant — use "destructive" for delete/remove actions */
|
|
34
|
+
variant?: "default" | "destructive";
|
|
35
|
+
/** Disables the item without removing it */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
/** Appointment data shown in the strip inside the advisor card */
|
|
22
40
|
export interface AdvisorAppointmentStrip {
|
|
23
41
|
status: AppointmentStatus;
|
|
@@ -57,7 +75,12 @@ export interface AdvisorCardProps {
|
|
|
57
75
|
appointments?: AdvisorAppointmentStrip[] | null;
|
|
58
76
|
/** Called when "Refer [name] to Others" is clicked */
|
|
59
77
|
onRefer?: () => void;
|
|
60
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Items to render in the ⋮ overflow DropdownMenu.
|
|
80
|
+
* When provided, the button opens an inline dropdown — `onMoreOptions` is ignored.
|
|
81
|
+
*/
|
|
82
|
+
menuItems?: AdvisorCardMenuItem[];
|
|
83
|
+
/** @deprecated Pass `menuItems` for an inline DropdownMenu instead */
|
|
61
84
|
onMoreOptions?: () => void;
|
|
62
85
|
/** Called when "Book Appointment" is clicked */
|
|
63
86
|
onBookAppointment?: () => void;
|
|
@@ -113,6 +136,7 @@ export function AdvisorCard({
|
|
|
113
136
|
isPrimary = false,
|
|
114
137
|
appointments,
|
|
115
138
|
onRefer,
|
|
139
|
+
menuItems,
|
|
116
140
|
onMoreOptions,
|
|
117
141
|
onBookAppointment,
|
|
118
142
|
onViewAppointment,
|
|
@@ -132,7 +156,7 @@ export function AdvisorCard({
|
|
|
132
156
|
{companyLogoUrl && (
|
|
133
157
|
<AvatarImage src={companyLogoUrl} alt={`${name} company logo`} />
|
|
134
158
|
)}
|
|
135
|
-
<AvatarFallback className="text-
|
|
159
|
+
<AvatarFallback className="text-caption">
|
|
136
160
|
{avatarInitials ??
|
|
137
161
|
(companyName
|
|
138
162
|
? companyName
|
|
@@ -146,14 +170,14 @@ export function AdvisorCard({
|
|
|
146
170
|
</Avatar>
|
|
147
171
|
|
|
148
172
|
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
149
|
-
<p className="text-
|
|
150
|
-
<p className="text-
|
|
173
|
+
<p className="text-label-large leading-tight">{name}</p>
|
|
174
|
+
<p className="text-body-small text-muted-foreground">{role}</p>
|
|
151
175
|
<div className="mt-2 flex flex-col gap-1">
|
|
152
|
-
<div className="flex items-center gap-1.5 text-
|
|
176
|
+
<div className="flex items-center gap-1.5 text-body-small text-muted-foreground">
|
|
153
177
|
<Phone className="h-4 w-4 shrink-0" />
|
|
154
178
|
<span>{phone}</span>
|
|
155
179
|
</div>
|
|
156
|
-
<div className="flex items-center gap-1.5 text-
|
|
180
|
+
<div className="flex items-center gap-1.5 text-body-small text-muted-foreground">
|
|
157
181
|
<Mail className="h-4 w-4 shrink-0" />
|
|
158
182
|
<span>{email}</span>
|
|
159
183
|
</div>
|
|
@@ -163,15 +187,44 @@ export function AdvisorCard({
|
|
|
163
187
|
{/* Badge + overflow menu */}
|
|
164
188
|
<div className="flex shrink-0 items-center gap-1.5">
|
|
165
189
|
{isPrimary && <Badge variant="success">Primary</Badge>}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
190
|
+
{menuItems && menuItems.length > 0 ? (
|
|
191
|
+
<DropdownMenu>
|
|
192
|
+
<DropdownMenuTrigger
|
|
193
|
+
render={
|
|
194
|
+
<Button
|
|
195
|
+
variant="ghost"
|
|
196
|
+
size="icon"
|
|
197
|
+
className="h-7 w-7"
|
|
198
|
+
aria-label="More options"
|
|
199
|
+
/>
|
|
200
|
+
}
|
|
201
|
+
>
|
|
202
|
+
<MoreVertical className="h-4 w-4" />
|
|
203
|
+
</DropdownMenuTrigger>
|
|
204
|
+
<DropdownMenuContent align="end">
|
|
205
|
+
{menuItems.map((item, i) => (
|
|
206
|
+
<DropdownMenuItem
|
|
207
|
+
key={i}
|
|
208
|
+
variant={item.variant}
|
|
209
|
+
disabled={item.disabled}
|
|
210
|
+
onClick={item.onClick}
|
|
211
|
+
>
|
|
212
|
+
{item.label}
|
|
213
|
+
</DropdownMenuItem>
|
|
214
|
+
))}
|
|
215
|
+
</DropdownMenuContent>
|
|
216
|
+
</DropdownMenu>
|
|
217
|
+
) : (
|
|
218
|
+
<Button
|
|
219
|
+
variant="ghost"
|
|
220
|
+
size="icon"
|
|
221
|
+
className="h-7 w-7"
|
|
222
|
+
onClick={onMoreOptions}
|
|
223
|
+
aria-label="More options"
|
|
224
|
+
>
|
|
225
|
+
<MoreVertical className="h-4 w-4" />
|
|
226
|
+
</Button>
|
|
227
|
+
)}
|
|
175
228
|
</div>
|
|
176
229
|
</div>
|
|
177
230
|
|
|
@@ -189,19 +242,16 @@ export function AdvisorCard({
|
|
|
189
242
|
<CalendarCheck className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
190
243
|
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
191
244
|
<div className="flex items-center gap-2">
|
|
192
|
-
<Badge
|
|
193
|
-
variant={STATUS_VARIANT[appt.status]}
|
|
194
|
-
className="text-[10px]"
|
|
195
|
-
>
|
|
245
|
+
<Badge variant={STATUS_VARIANT[appt.status]}>
|
|
196
246
|
{STATUS_LABEL[appt.status]}
|
|
197
247
|
</Badge>
|
|
198
248
|
{appt.appointmentType && (
|
|
199
|
-
<span className="truncate text-
|
|
249
|
+
<span className="truncate text-h6">
|
|
200
250
|
{appt.appointmentType}
|
|
201
251
|
</span>
|
|
202
252
|
)}
|
|
203
253
|
</div>
|
|
204
|
-
<p className="whitespace-nowrap text-
|
|
254
|
+
<p className="whitespace-nowrap text-body-small text-muted-foreground">
|
|
205
255
|
{appt.date} · {appt.timeStart}–{appt.timeEnd}
|
|
206
256
|
</p>
|
|
207
257
|
</div>
|
|
@@ -223,7 +273,7 @@ export function AdvisorCard({
|
|
|
223
273
|
/* Empty state */
|
|
224
274
|
<div className="flex items-center gap-3 px-4 py-3">
|
|
225
275
|
<Calendar className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
226
|
-
<p className="flex-1 text-
|
|
276
|
+
<p className="flex-1 text-caption text-muted-foreground">
|
|
227
277
|
No upcoming appointments
|
|
228
278
|
</p>
|
|
229
279
|
</div>
|
|
@@ -246,7 +296,7 @@ export function AdvisorCard({
|
|
|
246
296
|
)}
|
|
247
297
|
{onRefer && (
|
|
248
298
|
<Button
|
|
249
|
-
variant="
|
|
299
|
+
variant="default"
|
|
250
300
|
size="sm"
|
|
251
301
|
className="w-full"
|
|
252
302
|
onClick={onRefer}
|
|
@@ -274,8 +324,8 @@ export function AdvisorInviteCard({ onInvite }: AdvisorInviteCardProps) {
|
|
|
274
324
|
<Plus className="h-5 w-5 text-muted-foreground" />
|
|
275
325
|
</div>
|
|
276
326
|
<div className="flex flex-col gap-1">
|
|
277
|
-
<p className="text-
|
|
278
|
-
<p className="text-
|
|
327
|
+
<p className="text-label-large">Add Another Advisor</p>
|
|
328
|
+
<p className="text-caption text-muted-foreground">
|
|
279
329
|
Connect more advisors to your account
|
|
280
330
|
</p>
|
|
281
331
|
</div>
|
|
@@ -236,11 +236,14 @@ function ClientSearch({
|
|
|
236
236
|
// Meeting format sub-component
|
|
237
237
|
// ---------------------------------------------------------------------------
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
interface FormatOption {
|
|
240
240
|
value: AppointmentMeetingFormat;
|
|
241
241
|
label: string;
|
|
242
242
|
icon: React.ReactNode;
|
|
243
|
-
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** All four formats — shown in advisor mode */
|
|
246
|
+
const FORMAT_OPTIONS: FormatOption[] = [
|
|
244
247
|
{ value: "call", label: "Call", icon: <Phone className="h-4 w-4" /> },
|
|
245
248
|
{
|
|
246
249
|
value: "google-meet",
|
|
@@ -259,6 +262,21 @@ const FORMAT_OPTIONS: {
|
|
|
259
262
|
},
|
|
260
263
|
];
|
|
261
264
|
|
|
265
|
+
/** Simplified three-option set shown in client mode */
|
|
266
|
+
const CLIENT_FORMAT_OPTIONS: FormatOption[] = [
|
|
267
|
+
{ value: "call", label: "Call", icon: <Phone className="h-4 w-4" /> },
|
|
268
|
+
{
|
|
269
|
+
value: "online",
|
|
270
|
+
label: "Online Meeting",
|
|
271
|
+
icon: <Video className="h-4 w-4" />,
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
value: "offline",
|
|
275
|
+
label: "Offline Meeting",
|
|
276
|
+
icon: <MapPin className="h-4 w-4" />,
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
|
|
262
280
|
function MeetingFormatSection({
|
|
263
281
|
format,
|
|
264
282
|
onFormatChange,
|
|
@@ -269,6 +287,7 @@ function MeetingFormatSection({
|
|
|
269
287
|
advisorOfficeAddress,
|
|
270
288
|
clientHomeAddress,
|
|
271
289
|
isClientMode,
|
|
290
|
+
formatOptions,
|
|
272
291
|
}: {
|
|
273
292
|
format: AppointmentMeetingFormat;
|
|
274
293
|
onFormatChange: (f: AppointmentMeetingFormat) => void;
|
|
@@ -279,11 +298,12 @@ function MeetingFormatSection({
|
|
|
279
298
|
advisorOfficeAddress?: string;
|
|
280
299
|
clientHomeAddress?: string;
|
|
281
300
|
isClientMode?: boolean;
|
|
301
|
+
formatOptions: FormatOption[];
|
|
282
302
|
}) {
|
|
283
303
|
return (
|
|
284
304
|
<div className="flex flex-col gap-2">
|
|
285
305
|
<div className="flex gap-2">
|
|
286
|
-
{
|
|
306
|
+
{formatOptions.map((opt) => (
|
|
287
307
|
<Toggle
|
|
288
308
|
key={opt.value}
|
|
289
309
|
variant="outline"
|
|
@@ -681,6 +701,9 @@ export function AppointmentBookDialog({
|
|
|
681
701
|
advisorOfficeAddress={advisorOfficeAddress}
|
|
682
702
|
clientHomeAddress={clientHomeAddress}
|
|
683
703
|
isClientMode={isClientMode}
|
|
704
|
+
formatOptions={
|
|
705
|
+
isClientMode ? CLIENT_FORMAT_OPTIONS : FORMAT_OPTIONS
|
|
706
|
+
}
|
|
684
707
|
/>
|
|
685
708
|
</div>
|
|
686
709
|
)}
|
|
@@ -481,9 +481,7 @@ export function AppointmentCalendarView({
|
|
|
481
481
|
|
|
482
482
|
<Tabs
|
|
483
483
|
defaultValue={defaultView}
|
|
484
|
-
onValueChange={(v) =>
|
|
485
|
-
onViewChange?.(v as "day" | "week" | "month")
|
|
486
|
-
}
|
|
484
|
+
onValueChange={(v) => onViewChange?.(v as "day" | "week" | "month")}
|
|
487
485
|
className="flex flex-col"
|
|
488
486
|
>
|
|
489
487
|
<div className="px-4 pt-3">
|
|
@@ -493,7 +491,7 @@ export function AppointmentCalendarView({
|
|
|
493
491
|
<TabsTrigger value="month">Month</TabsTrigger>
|
|
494
492
|
</TabsList>
|
|
495
493
|
</div>
|
|
496
|
-
<TabsContent value="day" className="mt-0">
|
|
494
|
+
<TabsContent value="day" className="mt-0 max-h-[75vh] overflow-y-auto">
|
|
497
495
|
<DayView
|
|
498
496
|
appointments={appointments}
|
|
499
497
|
date={effectiveViewDate}
|
|
@@ -502,7 +500,7 @@ export function AppointmentCalendarView({
|
|
|
502
500
|
onSelectAppointment={onSelectAppointment}
|
|
503
501
|
/>
|
|
504
502
|
</TabsContent>
|
|
505
|
-
<TabsContent value="week" className="mt-0">
|
|
503
|
+
<TabsContent value="week" className="mt-0 max-h-[75vh] overflow-y-auto">
|
|
506
504
|
<WeekView
|
|
507
505
|
appointments={appointments}
|
|
508
506
|
weekDays={weekDays}
|
|
@@ -511,7 +509,10 @@ export function AppointmentCalendarView({
|
|
|
511
509
|
onSelectAppointment={onSelectAppointment}
|
|
512
510
|
/>
|
|
513
511
|
</TabsContent>
|
|
514
|
-
<TabsContent
|
|
512
|
+
<TabsContent
|
|
513
|
+
value="month"
|
|
514
|
+
className="mt-0 max-h-[75vh] overflow-y-auto"
|
|
515
|
+
>
|
|
515
516
|
<MonthView
|
|
516
517
|
appointments={appointments}
|
|
517
518
|
viewDate={effectiveViewDate}
|
|
@@ -14,7 +14,7 @@ import { Slot } from "@/lib/slot";
|
|
|
14
14
|
* This gives softer, more readable badges and works correctly in light and dark mode.
|
|
15
15
|
*/
|
|
16
16
|
const badgeVariants = cva(
|
|
17
|
-
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2.5 py-0.5 text-
|
|
17
|
+
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2.5 py-0.5 text-caption whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
18
18
|
{
|
|
19
19
|
variants: {
|
|
20
20
|
variant: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { AlertCircleIcon, EyeIcon, InfoIcon } from "lucide-react";
|
|
2
3
|
import {
|
|
3
4
|
Query,
|
|
4
5
|
Utils as QbUtils,
|
|
@@ -17,6 +18,7 @@ import { Checkbox } from "@/components/ui/checkbox";
|
|
|
17
18
|
import { Label } from "@/components/ui/label";
|
|
18
19
|
import { Field, FieldLabel } from "@/components/ui/field";
|
|
19
20
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
21
|
+
import { cn } from "@/lib/utils";
|
|
20
22
|
import type {
|
|
21
23
|
ContactAlertDialogProps,
|
|
22
24
|
ContactAlertQueryBuilderProps,
|
|
@@ -161,7 +163,23 @@ export function ContactAlertDialog({
|
|
|
161
163
|
{(
|
|
162
164
|
["NEED_ACTION", "WATCH", "HEALTHY"] as ContactAlertSeverity[]
|
|
163
165
|
).map((s) => (
|
|
164
|
-
<ToggleGroupItem
|
|
166
|
+
<ToggleGroupItem
|
|
167
|
+
key={s}
|
|
168
|
+
value={s}
|
|
169
|
+
className={cn(
|
|
170
|
+
s === "NEED_ACTION" &&
|
|
171
|
+
"data-pressed:bg-destructive/10 data-pressed:inset-ring-destructive data-pressed:text-destructive-text data-pressed:hover:bg-destructive/10",
|
|
172
|
+
s === "WATCH" &&
|
|
173
|
+
"data-pressed:bg-warning/10 data-pressed:inset-ring-warning data-pressed:text-warning-text data-pressed:hover:bg-warning/10",
|
|
174
|
+
s === "HEALTHY" &&
|
|
175
|
+
"data-pressed:bg-info/10 data-pressed:inset-ring-info data-pressed:text-info-text data-pressed:hover:bg-info/10",
|
|
176
|
+
)}
|
|
177
|
+
>
|
|
178
|
+
{s === "NEED_ACTION" && (
|
|
179
|
+
<AlertCircleIcon className="size-3.5" />
|
|
180
|
+
)}
|
|
181
|
+
{s === "WATCH" && <EyeIcon className="size-3.5" />}
|
|
182
|
+
{s === "HEALTHY" && <InfoIcon className="size-3.5" />}
|
|
165
183
|
{SEVERITY_LABELS[s]}
|
|
166
184
|
</ToggleGroupItem>
|
|
167
185
|
))}
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
Users,
|
|
7
7
|
Calendar,
|
|
8
8
|
Check,
|
|
9
|
+
Copy,
|
|
10
|
+
Link2,
|
|
9
11
|
MoreVertical,
|
|
10
12
|
Clock,
|
|
11
13
|
ArrowRight,
|
|
@@ -782,6 +784,54 @@ export function OpportunityCard({
|
|
|
782
784
|
// Primary action: send them the loan application link.
|
|
783
785
|
// ---------------------------------------------------------------------------
|
|
784
786
|
|
|
787
|
+
function normalizeUrl(url: string): string {
|
|
788
|
+
return `https://${url.replace(/^https?:\/\//, "")}`;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/** Shortened + copyable loan application URL row. */
|
|
792
|
+
function LoanApplicationLink({ url }: { url: string }) {
|
|
793
|
+
const [copied, setCopied] = useState(false);
|
|
794
|
+
const href = normalizeUrl(url);
|
|
795
|
+
|
|
796
|
+
function handleCopy() {
|
|
797
|
+
navigator.clipboard.writeText(href).then(() => {
|
|
798
|
+
setCopied(true);
|
|
799
|
+
setTimeout(() => setCopied(false), 2000);
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
return (
|
|
804
|
+
<div className="flex flex-col gap-1">
|
|
805
|
+
<p className="text-caption text-muted-foreground">
|
|
806
|
+
Or the link below to fill out the loan application directly.
|
|
807
|
+
</p>
|
|
808
|
+
<div className="flex items-center gap-1.5">
|
|
809
|
+
<Link2 className="size-4 shrink-0 text-muted-foreground" />
|
|
810
|
+
<a
|
|
811
|
+
href={href}
|
|
812
|
+
target="_blank"
|
|
813
|
+
rel="noreferrer"
|
|
814
|
+
className="min-w-0 flex-1 truncate text-body-small text-primary underline-offset-2 hover:underline"
|
|
815
|
+
>
|
|
816
|
+
{url}
|
|
817
|
+
</a>
|
|
818
|
+
<button
|
|
819
|
+
type="button"
|
|
820
|
+
onClick={handleCopy}
|
|
821
|
+
className="shrink-0 rounded p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
822
|
+
aria-label="Copy link"
|
|
823
|
+
>
|
|
824
|
+
{copied ? (
|
|
825
|
+
<Check className="size-4 text-success-text" />
|
|
826
|
+
) : (
|
|
827
|
+
<Copy className="size-4" />
|
|
828
|
+
)}
|
|
829
|
+
</button>
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
785
835
|
export interface LeadCardProps {
|
|
786
836
|
id: string;
|
|
787
837
|
customerName: string;
|
|
@@ -816,18 +866,18 @@ export function LeadCard({
|
|
|
816
866
|
{/* ── Customer info + delete menu ── */}
|
|
817
867
|
<div className="flex items-start justify-between gap-2">
|
|
818
868
|
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
819
|
-
<span className="text-
|
|
869
|
+
<span className="text-label-medium text-foreground">
|
|
820
870
|
{customerName}
|
|
821
871
|
</span>
|
|
822
872
|
{customerPhone && (
|
|
823
|
-
<span className="flex items-center gap-1.5 text-
|
|
824
|
-
<Phone className="size-3 shrink-0" aria-hidden="true" />
|
|
873
|
+
<span className="flex items-center gap-1.5 text-caption text-muted-foreground">
|
|
874
|
+
<Phone className="size-3.5 shrink-0" aria-hidden="true" />
|
|
825
875
|
{customerPhone}
|
|
826
876
|
</span>
|
|
827
877
|
)}
|
|
828
878
|
{customerEmail && (
|
|
829
|
-
<span className="flex items-center gap-1.5 text-
|
|
830
|
-
<Mail className="size-3 shrink-0" aria-hidden="true" />
|
|
879
|
+
<span className="flex items-center gap-1.5 text-caption text-muted-foreground">
|
|
880
|
+
<Mail className="size-3.5 shrink-0" aria-hidden="true" />
|
|
831
881
|
<span className="truncate">{customerEmail}</span>
|
|
832
882
|
</span>
|
|
833
883
|
)}
|
|
@@ -871,21 +921,7 @@ export function LeadCard({
|
|
|
871
921
|
Send Loan Application Request
|
|
872
922
|
</Button>
|
|
873
923
|
{loanApplicationUrl && (
|
|
874
|
-
<
|
|
875
|
-
Or the link below to fill out the loan application directly.
|
|
876
|
-
<br />
|
|
877
|
-
<a
|
|
878
|
-
href={`https://${loanApplicationUrl.replace(
|
|
879
|
-
/^https?:\/\//,
|
|
880
|
-
"",
|
|
881
|
-
)}`}
|
|
882
|
-
target="_blank"
|
|
883
|
-
rel="noreferrer"
|
|
884
|
-
className="text-primary underline-offset-2 hover:underline"
|
|
885
|
-
>
|
|
886
|
-
{loanApplicationUrl}
|
|
887
|
-
</a>
|
|
888
|
-
</p>
|
|
924
|
+
<LoanApplicationLink url={loanApplicationUrl} />
|
|
889
925
|
)}
|
|
890
926
|
</div>
|
|
891
927
|
)}
|