@wealthx/shadcn 1.1.0 → 1.2.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.
- package/.turbo/turbo-build.log +235 -154
- package/CHANGELOG.md +6 -0
- package/dist/{chunk-6OJF6XRN.mjs → chunk-24FUO7TD.mjs} +4 -8
- package/dist/{chunk-4AJ5HWHD.mjs → chunk-2I5S2AMY.mjs} +3 -3
- package/dist/chunk-2SF672SZ.mjs +161 -0
- package/dist/{chunk-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
- package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
- package/dist/chunk-3WMX6KWS.mjs +245 -0
- package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
- package/dist/chunk-4CX4SBRO.mjs +153 -0
- package/dist/chunk-4MN6UQHG.mjs +443 -0
- package/dist/{chunk-GLW2UO6O.mjs → chunk-5QQVZTVZ.mjs} +82 -61
- package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
- package/dist/chunk-6FCGKSZX.mjs +268 -0
- package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
- package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
- package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
- package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
- package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
- package/dist/chunk-A56YQQHG.mjs +402 -0
- package/dist/chunk-AH52LG6N.mjs +315 -0
- package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
- package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
- package/dist/chunk-D4ILTPOG.mjs +293 -0
- package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
- package/dist/chunk-DOH3EHX7.mjs +378 -0
- package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
- package/dist/{chunk-YBXCIF5Q.mjs → chunk-ERGGHC2V.mjs} +36 -49
- package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
- package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
- package/dist/chunk-FMAXJ2SI.mjs +71 -0
- package/dist/chunk-FZIXGLMV.mjs +173 -0
- package/dist/{chunk-DS2AMHN2.mjs → chunk-GYMYRIZP.mjs} +2 -2
- package/dist/{chunk-KQDD5MU3.mjs → chunk-H45TKD34.mjs} +5 -5
- package/dist/{chunk-BBJBJSXQ.mjs → chunk-J5UICVJS.mjs} +1 -1
- package/dist/{chunk-RL772EH7.mjs → chunk-JHJHG4GO.mjs} +4 -12
- package/dist/{chunk-RN67642N.mjs → chunk-KMCGSZTX.mjs} +47 -41
- package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
- package/dist/chunk-LE6YFY6D.mjs +209 -0
- package/dist/{chunk-NLLKTU4B.mjs → chunk-LLVQKSU3.mjs} +21 -17
- package/dist/{chunk-KKHTJNMM.mjs → chunk-MARPPFOJ.mjs} +8 -4
- package/dist/{chunk-6AFMNC42.mjs → chunk-N2PT566P.mjs} +15 -11
- package/dist/chunk-NLCKVHWB.mjs +161 -0
- package/dist/{chunk-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
- package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
- package/dist/chunk-NXA3CZ7A.mjs +248 -0
- package/dist/chunk-OGOYQ7BG.mjs +150 -0
- package/dist/{chunk-3NQGYJEZ.mjs → chunk-P6AM5V7O.mjs} +10 -18
- package/dist/{chunk-CZ3BW5GL.mjs → chunk-P76HMUI6.mjs} +5 -11
- package/dist/chunk-PCPLO5HT.mjs +671 -0
- package/dist/chunk-PG6K5XEC.mjs +475 -0
- package/dist/{chunk-5JGQAAQV.mjs → chunk-PJHPSRYD.mjs} +84 -62
- package/dist/{chunk-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
- package/dist/chunk-PR6V5XKM.mjs +209 -0
- package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
- package/dist/chunk-QVKWW6KE.mjs +272 -0
- package/dist/chunk-RGU7HOEC.mjs +140 -0
- package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
- package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
- package/dist/chunk-RRBS6D63.mjs +163 -0
- package/dist/{chunk-UEL4RD5P.mjs → chunk-SMQ3DG25.mjs} +80 -67
- package/dist/chunk-SPJ5KXW7.mjs +199 -0
- package/dist/chunk-SYOD63OZ.mjs +225 -0
- package/dist/chunk-UFYSFDER.mjs +42 -0
- package/dist/chunk-VACKZOMY.mjs +190 -0
- package/dist/chunk-VLQZANBF.mjs +42 -0
- package/dist/chunk-WA6O6EUR.mjs +1885 -0
- package/dist/{chunk-E3K6O4FZ.mjs → chunk-WAZD7NFU.mjs} +5 -2
- package/dist/chunk-WG6JGJXB.mjs +165 -0
- package/dist/{chunk-I64K754C.mjs → chunk-WNGWBVLV.mjs} +2 -2
- package/dist/{chunk-3U7SD3MS.mjs → chunk-WOEHFRGB.mjs} +3 -3
- package/dist/{chunk-DKZRJOMF.mjs → chunk-XIRTEFKH.mjs} +12 -12
- package/dist/chunk-Y6DWJSKZ.mjs +79 -0
- package/dist/chunk-YKPROFLB.mjs +161 -0
- package/dist/{chunk-CJ46PDXE.mjs → chunk-ZRO5JO3H.mjs} +106 -66
- package/dist/{chunk-VYMHBV6D.mjs → chunk-ZU4NV6RG.mjs} +5 -3
- package/dist/components/ui/accordion.js +40 -4
- package/dist/components/ui/accordion.mjs +2 -2
- package/dist/components/ui/add-column-modal.js +789 -0
- package/dist/components/ui/add-column-modal.mjs +17 -0
- package/dist/components/ui/add-lead-modal.js +647 -0
- package/dist/components/ui/add-lead-modal.mjs +16 -0
- package/dist/components/ui/ai-assistant-drawer.js +686 -0
- package/dist/components/ui/ai-assistant-drawer.mjs +16 -0
- package/dist/components/ui/alert-dialog.js +37 -5
- package/dist/components/ui/alert-dialog.mjs +4 -4
- package/dist/components/ui/alert.js +37 -11
- package/dist/components/ui/alert.mjs +2 -2
- package/dist/components/ui/avatar.js +36 -8
- package/dist/components/ui/avatar.mjs +2 -2
- package/dist/components/ui/backoffice-alert-history-chart.js +624 -0
- package/dist/components/ui/backoffice-alert-history-chart.mjs +16 -0
- package/dist/components/ui/backoffice-contact-history-chart.js +687 -0
- package/dist/components/ui/backoffice-contact-history-chart.mjs +16 -0
- package/dist/components/ui/badge.js +37 -2
- package/dist/components/ui/badge.mjs +2 -2
- package/dist/components/ui/borrowing-capacity-line-chart.js +639 -0
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +16 -0
- package/dist/components/ui/button.js +35 -3
- package/dist/components/ui/button.mjs +2 -2
- package/dist/components/ui/calendar.js +43 -19
- package/dist/components/ui/calendar.mjs +3 -3
- package/dist/components/ui/card.js +40 -4
- package/dist/components/ui/card.mjs +2 -2
- package/dist/components/ui/cash-balance-line-chart.js +627 -0
- package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
- package/dist/components/ui/cashflow-bar-chart.js +123 -69
- package/dist/components/ui/cashflow-bar-chart.mjs +8 -8
- package/dist/components/ui/checkbox.js +36 -5
- package/dist/components/ui/checkbox.mjs +2 -3
- package/dist/components/ui/chip.js +37 -2
- package/dist/components/ui/chip.mjs +3 -3
- package/dist/components/ui/combobox.js +68 -49
- package/dist/components/ui/combobox.mjs +2 -2
- package/dist/components/ui/data-table.js +160 -88
- package/dist/components/ui/data-table.mjs +10 -11
- package/dist/components/ui/date-picker.js +44 -20
- package/dist/components/ui/date-picker.mjs +6 -7
- package/dist/components/ui/dialog.js +44 -12
- package/dist/components/ui/dialog.mjs +4 -4
- package/dist/components/ui/drawer.js +46 -10
- package/dist/components/ui/drawer.mjs +3 -3
- package/dist/components/ui/dropdown-menu.js +40 -16
- package/dist/components/ui/dropdown-menu.mjs +3 -3
- package/dist/components/ui/empty.js +41 -5
- package/dist/components/ui/empty.mjs +2 -2
- package/dist/components/ui/expense-bar-chart.js +165 -66
- package/dist/components/ui/expense-bar-chart.mjs +8 -8
- package/dist/components/ui/field.js +53 -21
- package/dist/components/ui/field.mjs +4 -4
- package/dist/components/ui/financial-cards.js +1002 -0
- package/dist/components/ui/financial-cards.mjs +24 -0
- package/dist/components/ui/financial-drawers.js +637 -0
- package/dist/components/ui/financial-drawers.mjs +17 -0
- package/dist/components/ui/financial-primitives.js +218 -0
- package/dist/components/ui/financial-primitives.mjs +22 -0
- package/dist/components/ui/financial-sections.js +1422 -0
- package/dist/components/ui/financial-sections.mjs +30 -0
- package/dist/components/ui/form-primitives.js +682 -0
- package/dist/components/ui/form-primitives.mjs +19 -0
- package/dist/components/ui/income-bar-chart.js +163 -65
- package/dist/components/ui/income-bar-chart.mjs +8 -8
- package/dist/components/ui/input-group.js +43 -7
- package/dist/components/ui/input-group.mjs +5 -5
- package/dist/components/ui/input-otp.js +39 -3
- package/dist/components/ui/input-otp.mjs +2 -2
- package/dist/components/ui/input.js +34 -2
- package/dist/components/ui/input.mjs +2 -2
- package/dist/components/ui/kanban-column.js +1143 -0
- package/dist/components/ui/kanban-column.mjs +20 -0
- package/dist/components/ui/label.js +35 -7
- package/dist/components/ui/label.mjs +2 -2
- package/dist/components/ui/opportunity-card.js +960 -0
- package/dist/components/ui/opportunity-card.mjs +20 -0
- package/dist/components/ui/opportunity-edit-modals.js +3360 -0
- package/dist/components/ui/opportunity-edit-modals.mjs +37 -0
- package/dist/components/ui/opportunity-summary-tab.js +4365 -0
- package/dist/components/ui/opportunity-summary-tab.mjs +34 -0
- package/dist/components/ui/pagination.js +35 -3
- package/dist/components/ui/pagination.mjs +3 -3
- package/dist/components/ui/pipeline-alerts.js +103 -0
- package/dist/components/ui/pipeline-alerts.mjs +8 -0
- package/dist/components/ui/pipeline-board.js +1408 -0
- package/dist/components/ui/pipeline-board.mjs +24 -0
- package/dist/components/ui/pipeline-chart.js +216 -0
- package/dist/components/ui/pipeline-chart.mjs +10 -0
- package/dist/components/ui/pipeline-dialogs.js +1183 -0
- package/dist/components/ui/pipeline-dialogs.mjs +23 -0
- package/dist/components/ui/pipeline-primitives.js +300 -0
- package/dist/components/ui/pipeline-primitives.mjs +11 -0
- package/dist/components/ui/popover.js +45 -4
- package/dist/components/ui/popover.mjs +3 -3
- package/dist/components/ui/progress.js +33 -1
- package/dist/components/ui/progress.mjs +2 -2
- package/dist/components/ui/property-cashflow-doughnut-chart.js +523 -0
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +521 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.js +682 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +16 -0
- package/dist/components/ui/radio-group.js +33 -1
- package/dist/components/ui/radio-group.mjs +2 -2
- package/dist/components/ui/select.js +66 -26
- package/dist/components/ui/select.mjs +3 -3
- package/dist/components/ui/separator.js +33 -1
- package/dist/components/ui/separator.mjs +2 -2
- package/dist/components/ui/sheet.js +37 -9
- package/dist/components/ui/sheet.mjs +3 -3
- package/dist/components/ui/skeleton.js +33 -1
- package/dist/components/ui/skeleton.mjs +2 -2
- package/dist/components/ui/slider.js +86 -102
- package/dist/components/ui/slider.mjs +2 -2
- package/dist/components/ui/spinner.js +33 -1
- package/dist/components/ui/spinner.mjs +2 -2
- package/dist/components/ui/stage-timeline.js +579 -0
- package/dist/components/ui/stage-timeline.mjs +15 -0
- package/dist/components/ui/switch.js +37 -4
- package/dist/components/ui/switch.mjs +2 -3
- package/dist/components/ui/table.js +37 -5
- package/dist/components/ui/table.mjs +2 -2
- package/dist/components/ui/tabs.js +36 -12
- package/dist/components/ui/tabs.mjs +2 -2
- package/dist/components/ui/textarea.js +34 -2
- package/dist/components/ui/textarea.mjs +2 -2
- package/dist/components/ui/toggle-group.js +35 -4
- package/dist/components/ui/toggle-group.mjs +3 -4
- package/dist/components/ui/toggle.js +35 -4
- package/dist/components/ui/toggle.mjs +2 -3
- package/dist/components/ui/tooltip.js +51 -22
- package/dist/components/ui/tooltip.mjs +3 -3
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +16 -0
- package/dist/components/ui/transactions-income-expense-bar-chart.js +76 -38
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +8 -8
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +16 -0
- package/dist/index.js +11616 -3831
- package/dist/index.mjs +333 -161
- package/dist/lib/theme-provider.js +10 -1
- package/dist/lib/theme-provider.mjs +1 -1
- package/dist/lib/typography.js +8 -0
- package/dist/lib/typography.mjs +3 -1
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.mjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +140 -5
- package/src/components/index.tsx +296 -42
- package/src/components/ui/accordion.tsx +6 -3
- package/src/components/ui/add-column-modal.tsx +339 -0
- package/src/components/ui/add-lead-modal.tsx +290 -0
- package/src/components/ui/ai-assistant-drawer.tsx +408 -0
- package/src/components/ui/alert-dialog.tsx +80 -54
- package/src/components/ui/alert.tsx +28 -28
- package/src/components/ui/avatar.tsx +30 -29
- package/src/components/ui/backoffice-alert-history-chart.tsx +260 -0
- package/src/components/ui/backoffice-contact-history-chart.tsx +325 -0
- package/src/components/ui/badge.tsx +17 -15
- package/src/components/ui/borrowing-capacity-line-chart.tsx +357 -0
- package/src/components/ui/button.tsx +30 -27
- package/src/components/ui/calendar.tsx +53 -67
- package/src/components/ui/card.tsx +27 -24
- package/src/components/ui/cash-balance-line-chart.tsx +302 -0
- package/src/components/ui/cashflow-bar-chart.tsx +104 -77
- package/src/components/ui/chart-shared.tsx +176 -15
- package/src/components/ui/checkbox.tsx +30 -26
- package/src/components/ui/combobox.tsx +78 -72
- package/src/components/ui/data-table.tsx +160 -99
- package/src/components/ui/date-picker.tsx +0 -2
- package/src/components/ui/dialog.tsx +70 -60
- package/src/components/ui/drawer.tsx +57 -48
- package/src/components/ui/dropdown-menu.tsx +90 -82
- package/src/components/ui/empty.tsx +31 -27
- package/src/components/ui/expense-bar-chart.tsx +83 -65
- package/src/components/ui/field.tsx +70 -62
- package/src/components/ui/financial-cards.tsx +830 -0
- package/src/components/ui/financial-drawers.tsx +339 -0
- package/src/components/ui/financial-primitives.tsx +331 -0
- package/src/components/ui/financial-sections.tsx +672 -0
- package/src/components/ui/form-primitives.tsx +536 -0
- package/src/components/ui/income-bar-chart.tsx +79 -60
- package/src/components/ui/input-group.tsx +41 -34
- package/src/components/ui/input-otp.tsx +29 -24
- package/src/components/ui/input.tsx +8 -8
- package/src/components/ui/kanban-column.tsx +333 -0
- package/src/components/ui/label.tsx +9 -12
- package/src/components/ui/opportunity-card.tsx +616 -0
- package/src/components/ui/opportunity-edit-modals.tsx +2528 -0
- package/src/components/ui/opportunity-summary-tab.tsx +579 -0
- package/src/components/ui/pipeline-alerts.tsx +74 -0
- package/src/components/ui/pipeline-board.tsx +268 -0
- package/src/components/ui/pipeline-chart.tsx +173 -0
- package/src/components/ui/pipeline-dialogs.tsx +303 -0
- package/src/components/ui/pipeline-primitives.tsx +108 -0
- package/src/components/ui/popover.tsx +41 -36
- package/src/components/ui/property-cashflow-doughnut-chart.tsx +188 -0
- package/src/components/ui/property-debt-equity-doughnut-chart.tsx +185 -0
- package/src/components/ui/property-mobile-estimate-line-chart.tsx +393 -0
- package/src/components/ui/select.tsx +65 -52
- package/src/components/ui/sheet.tsx +55 -52
- package/src/components/ui/slider.tsx +54 -77
- package/src/components/ui/stage-timeline.tsx +205 -0
- package/src/components/ui/switch.tsx +42 -29
- package/src/components/ui/table.tsx +28 -28
- package/src/components/ui/tabs.tsx +22 -28
- package/src/components/ui/textarea.tsx +8 -8
- package/src/components/ui/toggle-group.tsx +0 -2
- package/src/components/ui/toggle.tsx +13 -15
- package/src/components/ui/tooltip.tsx +30 -28
- package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +191 -0
- package/src/components/ui/transactions-income-expense-bar-chart.tsx +45 -38
- package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +191 -0
- package/src/lib/theme-provider.tsx +10 -0
- package/src/lib/typography.ts +9 -0
- package/src/lib/utils.ts +41 -3
- package/src/styles/globals.css +371 -124
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +27 -0
- package/dist/chunk-3EQP72AW.mjs +0 -58
- package/dist/chunk-K74JRTJR.mjs +0 -105
- package/dist/chunk-V7CNWJT3.mjs +0 -10
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Phone,
|
|
3
|
+
Mail,
|
|
4
|
+
User,
|
|
5
|
+
Users,
|
|
6
|
+
Calendar,
|
|
7
|
+
MoreVertical,
|
|
8
|
+
Clock,
|
|
9
|
+
ArrowRight,
|
|
10
|
+
Bot,
|
|
11
|
+
} from "lucide-react";
|
|
12
|
+
import { cn } from "@/lib/utils";
|
|
13
|
+
import { Badge } from "@/components/ui/badge";
|
|
14
|
+
import { Button, buttonVariants } from "@/components/ui/button";
|
|
15
|
+
import { Separator } from "@/components/ui/separator";
|
|
16
|
+
import {
|
|
17
|
+
DropdownMenu,
|
|
18
|
+
DropdownMenuContent,
|
|
19
|
+
DropdownMenuItem,
|
|
20
|
+
DropdownMenuSeparator,
|
|
21
|
+
DropdownMenuTrigger,
|
|
22
|
+
} from "@/components/ui/dropdown-menu";
|
|
23
|
+
import {
|
|
24
|
+
Accordion,
|
|
25
|
+
AccordionContent,
|
|
26
|
+
AccordionItem,
|
|
27
|
+
AccordionTrigger,
|
|
28
|
+
} from "@/components/ui/accordion";
|
|
29
|
+
import { TaskCheckItem } from "@/components/ui/pipeline-primitives";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* OpportunityCard — WealthX DS (L3 Card)
|
|
33
|
+
*
|
|
34
|
+
* Kanban card for a single loan opportunity in the Pipeline board.
|
|
35
|
+
* Renders customer info, loan metadata, task progress, and quick actions.
|
|
36
|
+
*
|
|
37
|
+
* This component is display-only — drag-and-drop is handled by the
|
|
38
|
+
* KanbanColumn wrapper in the app.
|
|
39
|
+
*
|
|
40
|
+
* Data source: `listLoans()` → `Opportunity` in `loan-crm.ts`
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Types
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
export type Priority = "HIGH" | "MEDIUM" | "LOW" | "NONE";
|
|
48
|
+
|
|
49
|
+
export interface OpportunityTask {
|
|
50
|
+
id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
completed: boolean;
|
|
53
|
+
aiAgentId?: string | null;
|
|
54
|
+
aiAgentName?: string | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface OpportunityCardProps {
|
|
58
|
+
// ── Core data ────────────────────────────────────────────────
|
|
59
|
+
id: string;
|
|
60
|
+
customerName: string;
|
|
61
|
+
customerPhone?: string;
|
|
62
|
+
customerEmail?: string;
|
|
63
|
+
/** Number of additional co-applicants beyond the primary contact. */
|
|
64
|
+
additionalContacts?: number;
|
|
65
|
+
loanType?: string;
|
|
66
|
+
loanPurposeLabel?: string;
|
|
67
|
+
/** Loan amount in dollars. */
|
|
68
|
+
amount: number;
|
|
69
|
+
/** ISO date string of the opportunity creation date. */
|
|
70
|
+
date: string;
|
|
71
|
+
|
|
72
|
+
// ── Status ───────────────────────────────────────────────────
|
|
73
|
+
priority: Priority;
|
|
74
|
+
/** Days the opportunity has been in its current stage. */
|
|
75
|
+
daysSinceColumnChanged?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Stage threshold for MEDIUM (orange) warning color.
|
|
78
|
+
* Comes from `Stage.warningDays`.
|
|
79
|
+
*/
|
|
80
|
+
warningDays?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Stage threshold for HIGH (red) priority color.
|
|
83
|
+
* Comes from `Stage.priorityDays`.
|
|
84
|
+
*/
|
|
85
|
+
priorityDays?: number;
|
|
86
|
+
/** ISO date string — if set, the card shows an "On Hold" banner. */
|
|
87
|
+
onHoldTo?: string | null;
|
|
88
|
+
/** Whether this opportunity represents a modification of a completed loan. */
|
|
89
|
+
isModifyCompletedLoan?: boolean;
|
|
90
|
+
|
|
91
|
+
// ── Tasks ────────────────────────────────────────────────────
|
|
92
|
+
tasks?: OpportunityTask[];
|
|
93
|
+
/** Title of the next pending task (shown as a hint below the task list). */
|
|
94
|
+
nextTask?: string | null;
|
|
95
|
+
|
|
96
|
+
// ── Lead data (passed through by KanbanColumn to LeadCard) ───
|
|
97
|
+
/**
|
|
98
|
+
* URL for the loan application form.
|
|
99
|
+
* Not rendered by OpportunityCard — used by KanbanColumn to forward
|
|
100
|
+
* to LeadCard when this opportunity is in the Leads stage.
|
|
101
|
+
*/
|
|
102
|
+
loanApplicationUrl?: string;
|
|
103
|
+
|
|
104
|
+
// ── Actions ──────────────────────────────────────────────────
|
|
105
|
+
onViewDetails?: () => void;
|
|
106
|
+
onTaskToggle?: (taskId: string) => void;
|
|
107
|
+
onMarkAsDone?: () => void;
|
|
108
|
+
onMoveToNextStage?: () => void;
|
|
109
|
+
onLaunchAssistant?: () => void;
|
|
110
|
+
onChangePriority?: () => void;
|
|
111
|
+
onDelete?: () => void;
|
|
112
|
+
onPutOnHold?: () => void;
|
|
113
|
+
|
|
114
|
+
/** Shows a loading state on action buttons while an async op is in flight. */
|
|
115
|
+
isSubmitting?: boolean;
|
|
116
|
+
className?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Internal helpers
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
/** Used for the priority dot background — full-saturation token is fine on colored bg. */
|
|
124
|
+
const PRIORITY_COLORS: Record<Priority, string> = {
|
|
125
|
+
HIGH: "var(--color-destructive)",
|
|
126
|
+
MEDIUM: "var(--color-warning)",
|
|
127
|
+
LOW: "var(--color-success)",
|
|
128
|
+
NONE: "var(--color-muted-foreground)",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Used for text / icons on white/light backgrounds — darkened tokens ensure 4.5:1 contrast. */
|
|
132
|
+
const PRIORITY_TEXT_COLORS: Record<Priority, string> = {
|
|
133
|
+
HIGH: "var(--color-destructive-text)",
|
|
134
|
+
MEDIUM: "var(--color-warning-text)",
|
|
135
|
+
LOW: "var(--color-success-text)",
|
|
136
|
+
NONE: "var(--color-muted-foreground)",
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
function resolvePriority(
|
|
140
|
+
days: number | undefined,
|
|
141
|
+
warningDays: number | undefined,
|
|
142
|
+
priorityDays: number | undefined,
|
|
143
|
+
priority: Priority,
|
|
144
|
+
): Priority {
|
|
145
|
+
if (days === undefined) return priority;
|
|
146
|
+
if (priorityDays !== undefined && days >= priorityDays) return "HIGH";
|
|
147
|
+
if (warningDays !== undefined && days >= warningDays) return "MEDIUM";
|
|
148
|
+
if (warningDays !== undefined || priorityDays !== undefined) return "LOW";
|
|
149
|
+
return priority;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatAmount(amount: number): string {
|
|
153
|
+
return new Intl.NumberFormat("en-AU", {
|
|
154
|
+
style: "currency",
|
|
155
|
+
currency: "AUD",
|
|
156
|
+
maximumFractionDigits: 0,
|
|
157
|
+
}).format(amount);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function formatDate(iso: string): string {
|
|
161
|
+
try {
|
|
162
|
+
return new Date(iso).toLocaleDateString("en-AU", {
|
|
163
|
+
day: "2-digit",
|
|
164
|
+
month: "short",
|
|
165
|
+
year: "numeric",
|
|
166
|
+
});
|
|
167
|
+
} catch {
|
|
168
|
+
return iso;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function formatHoldDate(iso: string): string {
|
|
173
|
+
try {
|
|
174
|
+
return new Date(iso).toLocaleDateString("en-AU", {
|
|
175
|
+
day: "2-digit",
|
|
176
|
+
month: "short",
|
|
177
|
+
});
|
|
178
|
+
} catch {
|
|
179
|
+
return iso;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function formatLoanType(type: string): string {
|
|
184
|
+
return type
|
|
185
|
+
.split("-")
|
|
186
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
187
|
+
.join(" ");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// OpportunityCard
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
export function OpportunityCard({
|
|
195
|
+
customerName,
|
|
196
|
+
customerPhone,
|
|
197
|
+
customerEmail,
|
|
198
|
+
additionalContacts,
|
|
199
|
+
loanType,
|
|
200
|
+
loanPurposeLabel,
|
|
201
|
+
amount,
|
|
202
|
+
date,
|
|
203
|
+
priority,
|
|
204
|
+
daysSinceColumnChanged,
|
|
205
|
+
warningDays,
|
|
206
|
+
priorityDays,
|
|
207
|
+
onHoldTo,
|
|
208
|
+
isModifyCompletedLoan,
|
|
209
|
+
tasks = [],
|
|
210
|
+
nextTask,
|
|
211
|
+
onViewDetails,
|
|
212
|
+
onTaskToggle,
|
|
213
|
+
onMarkAsDone,
|
|
214
|
+
onMoveToNextStage,
|
|
215
|
+
onLaunchAssistant,
|
|
216
|
+
onChangePriority,
|
|
217
|
+
onDelete,
|
|
218
|
+
onPutOnHold,
|
|
219
|
+
isSubmitting = false,
|
|
220
|
+
className,
|
|
221
|
+
}: OpportunityCardProps) {
|
|
222
|
+
const resolvedPriority = resolvePriority(
|
|
223
|
+
daysSinceColumnChanged,
|
|
224
|
+
warningDays,
|
|
225
|
+
priorityDays,
|
|
226
|
+
priority,
|
|
227
|
+
);
|
|
228
|
+
const priorityColor = PRIORITY_COLORS[resolvedPriority];
|
|
229
|
+
const priorityTextColor = PRIORITY_TEXT_COLORS[resolvedPriority];
|
|
230
|
+
|
|
231
|
+
const completedCount = tasks.filter((t) => t.completed).length;
|
|
232
|
+
const hasTasks = tasks.length > 0;
|
|
233
|
+
const hasActions = onMarkAsDone || onMoveToNextStage;
|
|
234
|
+
const hasMenu = onViewDetails || onChangePriority || onPutOnHold || onDelete;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div
|
|
238
|
+
className={cn(
|
|
239
|
+
"flex flex-col gap-2 border border-border bg-background p-4 text-foreground shadow-sm",
|
|
240
|
+
isSubmitting && "opacity-60",
|
|
241
|
+
className,
|
|
242
|
+
)}
|
|
243
|
+
data-slot="opportunity-card"
|
|
244
|
+
>
|
|
245
|
+
{/* ── On-hold banner ── */}
|
|
246
|
+
{onHoldTo && (
|
|
247
|
+
<div
|
|
248
|
+
className="flex items-center gap-1.5 rounded border border-warning/30 bg-warning/10 px-2 py-1 text-xs font-medium"
|
|
249
|
+
style={{ color: "var(--color-warning-text)" }}
|
|
250
|
+
>
|
|
251
|
+
<Clock className="size-3 shrink-0" />
|
|
252
|
+
On hold until {formatHoldDate(onHoldTo)}
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
|
|
256
|
+
{/* ── Modify-completed-loan tag ── */}
|
|
257
|
+
{isModifyCompletedLoan && (
|
|
258
|
+
<div
|
|
259
|
+
className="flex items-center gap-1.5 rounded border border-info/30 bg-info/10 px-2 py-1 text-xs font-medium"
|
|
260
|
+
style={{ color: "var(--color-info-text)" }}
|
|
261
|
+
>
|
|
262
|
+
Modify completed loan
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
265
|
+
|
|
266
|
+
{/* ── Header: loan purpose + amount + menu ── */}
|
|
267
|
+
<div className="flex items-start justify-between gap-2">
|
|
268
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
269
|
+
{(loanPurposeLabel || loanType) && (
|
|
270
|
+
<Badge variant="outline" className="self-start">
|
|
271
|
+
{loanPurposeLabel ?? formatLoanType(loanType!)}
|
|
272
|
+
</Badge>
|
|
273
|
+
)}
|
|
274
|
+
<span className="text-base font-bold tabular-nums text-foreground">
|
|
275
|
+
{formatAmount(amount)}
|
|
276
|
+
</span>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div className="flex items-center gap-1 -mr-1 -mt-1">
|
|
280
|
+
{onLaunchAssistant && (
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
className={cn(
|
|
284
|
+
buttonVariants({ variant: "ghost", size: "icon" }),
|
|
285
|
+
"size-7 shrink-0",
|
|
286
|
+
)}
|
|
287
|
+
onClick={onLaunchAssistant}
|
|
288
|
+
aria-label="Launch AI Assistant"
|
|
289
|
+
title="Launch AI Assistant"
|
|
290
|
+
>
|
|
291
|
+
<Bot className="size-4" />
|
|
292
|
+
</button>
|
|
293
|
+
)}
|
|
294
|
+
{hasMenu && (
|
|
295
|
+
<DropdownMenu>
|
|
296
|
+
<DropdownMenuTrigger
|
|
297
|
+
className={cn(
|
|
298
|
+
buttonVariants({ variant: "ghost", size: "icon" }),
|
|
299
|
+
"size-7 shrink-0",
|
|
300
|
+
)}
|
|
301
|
+
aria-label="Opportunity actions"
|
|
302
|
+
>
|
|
303
|
+
<MoreVertical className="size-4" />
|
|
304
|
+
</DropdownMenuTrigger>
|
|
305
|
+
<DropdownMenuContent align="end">
|
|
306
|
+
{onViewDetails && (
|
|
307
|
+
<DropdownMenuItem onClick={onViewDetails}>
|
|
308
|
+
View details
|
|
309
|
+
</DropdownMenuItem>
|
|
310
|
+
)}
|
|
311
|
+
{onChangePriority && (
|
|
312
|
+
<DropdownMenuItem onClick={onChangePriority}>
|
|
313
|
+
Change priority
|
|
314
|
+
</DropdownMenuItem>
|
|
315
|
+
)}
|
|
316
|
+
{onPutOnHold && (
|
|
317
|
+
<DropdownMenuItem onClick={onPutOnHold}>
|
|
318
|
+
Put on hold
|
|
319
|
+
</DropdownMenuItem>
|
|
320
|
+
)}
|
|
321
|
+
{onDelete && (
|
|
322
|
+
<>
|
|
323
|
+
<DropdownMenuSeparator />
|
|
324
|
+
<DropdownMenuItem onClick={onDelete} variant="destructive">
|
|
325
|
+
Delete
|
|
326
|
+
</DropdownMenuItem>
|
|
327
|
+
</>
|
|
328
|
+
)}
|
|
329
|
+
</DropdownMenuContent>
|
|
330
|
+
</DropdownMenu>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<Separator />
|
|
336
|
+
|
|
337
|
+
{/* ── Customer info ── */}
|
|
338
|
+
<div className="flex flex-col gap-1">
|
|
339
|
+
<div className="flex items-center justify-between gap-2">
|
|
340
|
+
<span className="truncate text-sm font-semibold text-foreground">
|
|
341
|
+
{customerName}
|
|
342
|
+
</span>
|
|
343
|
+
{additionalContacts && additionalContacts > 0 ? (
|
|
344
|
+
<Badge variant="secondary" className="shrink-0 gap-1">
|
|
345
|
+
<Users className="size-3" aria-hidden="true" />
|
|
346
|
+
Joint
|
|
347
|
+
</Badge>
|
|
348
|
+
) : (
|
|
349
|
+
<Badge variant="secondary" className="shrink-0 gap-1">
|
|
350
|
+
<User className="size-3" aria-hidden="true" />
|
|
351
|
+
Individual
|
|
352
|
+
</Badge>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
{customerPhone && (
|
|
356
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
357
|
+
<Phone className="size-3 shrink-0" aria-hidden="true" />
|
|
358
|
+
{customerPhone}
|
|
359
|
+
</span>
|
|
360
|
+
)}
|
|
361
|
+
{customerEmail && (
|
|
362
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
363
|
+
<Mail className="size-3 shrink-0" aria-hidden="true" />
|
|
364
|
+
<span className="truncate">{customerEmail}</span>
|
|
365
|
+
</span>
|
|
366
|
+
)}
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<Separator />
|
|
370
|
+
|
|
371
|
+
{/* ── Metadata: date (left) | days-since chip + priority dot (right, adjacent) ── */}
|
|
372
|
+
<div className="flex items-center justify-between">
|
|
373
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
374
|
+
<Calendar className="size-3 shrink-0" aria-hidden="true" />
|
|
375
|
+
{formatDate(date)}
|
|
376
|
+
</span>
|
|
377
|
+
|
|
378
|
+
<span className="flex items-center gap-1.5">
|
|
379
|
+
{daysSinceColumnChanged !== undefined && (
|
|
380
|
+
<>
|
|
381
|
+
<Clock
|
|
382
|
+
className="size-3 shrink-0"
|
|
383
|
+
style={{ color: priorityTextColor }}
|
|
384
|
+
aria-hidden="true"
|
|
385
|
+
/>
|
|
386
|
+
<span
|
|
387
|
+
className="text-xs font-medium tabular-nums"
|
|
388
|
+
style={{ color: priorityTextColor }}
|
|
389
|
+
>
|
|
390
|
+
{daysSinceColumnChanged}d
|
|
391
|
+
</span>
|
|
392
|
+
</>
|
|
393
|
+
)}
|
|
394
|
+
{/* Dot: full-saturation color on its own background — no contrast issue */}
|
|
395
|
+
<span
|
|
396
|
+
role="img"
|
|
397
|
+
className="inline-block size-2 shrink-0 rounded-full"
|
|
398
|
+
style={{ backgroundColor: priorityColor }}
|
|
399
|
+
aria-label={`Priority: ${resolvedPriority}`}
|
|
400
|
+
/>
|
|
401
|
+
</span>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
{/* ── Tasks: segmented progress bar + animated accordion ── */}
|
|
405
|
+
{hasTasks && (
|
|
406
|
+
<>
|
|
407
|
+
{/* Segmented bar — one segment per task, filled = completed */}
|
|
408
|
+
<div
|
|
409
|
+
className="flex gap-0.5"
|
|
410
|
+
role="progressbar"
|
|
411
|
+
aria-valuenow={completedCount}
|
|
412
|
+
aria-valuemin={0}
|
|
413
|
+
aria-valuemax={tasks.length}
|
|
414
|
+
aria-label={`${completedCount} of ${tasks.length} tasks complete`}
|
|
415
|
+
>
|
|
416
|
+
{tasks.map((t, i) => (
|
|
417
|
+
<div
|
|
418
|
+
key={t.id}
|
|
419
|
+
className={cn(
|
|
420
|
+
"h-[7px] flex-1",
|
|
421
|
+
i < completedCount ? "bg-primary" : "bg-muted",
|
|
422
|
+
)}
|
|
423
|
+
/>
|
|
424
|
+
))}
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
{/* Accordion — animated expand/collapse using shadcn Accordion */}
|
|
428
|
+
<Accordion type="single" collapsible className="-mx-4">
|
|
429
|
+
<AccordionItem value="tasks" className="border-0">
|
|
430
|
+
<AccordionTrigger className="px-4 py-1.5 text-xs font-normal text-muted-foreground hover:no-underline hover:text-foreground [&>svg]:size-3.5">
|
|
431
|
+
Tasks ({completedCount}/{tasks.length})
|
|
432
|
+
</AccordionTrigger>
|
|
433
|
+
<AccordionContent className="px-4">
|
|
434
|
+
<div className="flex flex-col">
|
|
435
|
+
{tasks.map((task) => (
|
|
436
|
+
<TaskCheckItem
|
|
437
|
+
key={task.id}
|
|
438
|
+
title={task.title}
|
|
439
|
+
completed={task.completed}
|
|
440
|
+
aiAgentName={task.aiAgentName}
|
|
441
|
+
onToggle={
|
|
442
|
+
onTaskToggle ? () => onTaskToggle(task.id) : undefined
|
|
443
|
+
}
|
|
444
|
+
disabled={isSubmitting}
|
|
445
|
+
/>
|
|
446
|
+
))}
|
|
447
|
+
</div>
|
|
448
|
+
</AccordionContent>
|
|
449
|
+
</AccordionItem>
|
|
450
|
+
</Accordion>
|
|
451
|
+
|
|
452
|
+
{/* Next task hint — only visible when accordion is collapsed */}
|
|
453
|
+
{nextTask && (
|
|
454
|
+
<div className="flex items-start gap-1.5 border border-primary/30 bg-primary/5 px-2 py-1.5 text-xs">
|
|
455
|
+
<ArrowRight
|
|
456
|
+
className="mt-0.5 size-3 shrink-0 text-primary"
|
|
457
|
+
aria-hidden="true"
|
|
458
|
+
/>
|
|
459
|
+
<span className="font-medium">Next:</span>
|
|
460
|
+
<span className="text-muted-foreground">{nextTask}</span>
|
|
461
|
+
</div>
|
|
462
|
+
)}
|
|
463
|
+
</>
|
|
464
|
+
)}
|
|
465
|
+
|
|
466
|
+
{/* ── Action buttons ── */}
|
|
467
|
+
{hasActions && (
|
|
468
|
+
<>
|
|
469
|
+
<Separator />
|
|
470
|
+
{(onMoveToNextStage || onMarkAsDone) && (
|
|
471
|
+
<div className="flex gap-2">
|
|
472
|
+
{onMoveToNextStage && (
|
|
473
|
+
<Button
|
|
474
|
+
variant="outline"
|
|
475
|
+
size="sm"
|
|
476
|
+
className="flex-1 text-caption"
|
|
477
|
+
onClick={onMoveToNextStage}
|
|
478
|
+
disabled={isSubmitting}
|
|
479
|
+
>
|
|
480
|
+
Move to next stage
|
|
481
|
+
</Button>
|
|
482
|
+
)}
|
|
483
|
+
{onMarkAsDone && (
|
|
484
|
+
<Button
|
|
485
|
+
variant="default"
|
|
486
|
+
size="sm"
|
|
487
|
+
className="flex-1 text-caption"
|
|
488
|
+
onClick={onMarkAsDone}
|
|
489
|
+
disabled={isSubmitting}
|
|
490
|
+
>
|
|
491
|
+
Mark as done
|
|
492
|
+
</Button>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
)}
|
|
496
|
+
</>
|
|
497
|
+
)}
|
|
498
|
+
</div>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ---------------------------------------------------------------------------
|
|
503
|
+
// LeadCard — simplified card for the Leads stage
|
|
504
|
+
//
|
|
505
|
+
// Leads are new contacts who haven't started their loan application yet.
|
|
506
|
+
// No loan amount, no purpose tag, no joint badge, no tasks, no date.
|
|
507
|
+
// Primary action: send them the loan application link.
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
|
|
510
|
+
export interface LeadCardProps {
|
|
511
|
+
id: string;
|
|
512
|
+
customerName: string;
|
|
513
|
+
customerPhone?: string;
|
|
514
|
+
customerEmail?: string;
|
|
515
|
+
onSendLoanApplication?: () => void;
|
|
516
|
+
loanApplicationUrl?: string;
|
|
517
|
+
onDelete?: () => void;
|
|
518
|
+
isSubmitting?: boolean;
|
|
519
|
+
className?: string;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export function LeadCard({
|
|
523
|
+
customerName,
|
|
524
|
+
customerPhone,
|
|
525
|
+
customerEmail,
|
|
526
|
+
onSendLoanApplication,
|
|
527
|
+
loanApplicationUrl,
|
|
528
|
+
onDelete,
|
|
529
|
+
isSubmitting = false,
|
|
530
|
+
className,
|
|
531
|
+
}: LeadCardProps) {
|
|
532
|
+
return (
|
|
533
|
+
<div
|
|
534
|
+
className={cn(
|
|
535
|
+
"flex flex-col gap-3 border border-border bg-background p-4 text-foreground shadow-sm",
|
|
536
|
+
isSubmitting && "opacity-60",
|
|
537
|
+
className,
|
|
538
|
+
)}
|
|
539
|
+
data-slot="lead-card"
|
|
540
|
+
>
|
|
541
|
+
{/* ── Customer info + delete menu ── */}
|
|
542
|
+
<div className="flex items-start justify-between gap-2">
|
|
543
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
544
|
+
<span className="text-sm font-semibold text-foreground">
|
|
545
|
+
{customerName}
|
|
546
|
+
</span>
|
|
547
|
+
{customerPhone && (
|
|
548
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
549
|
+
<Phone className="size-3 shrink-0" aria-hidden="true" />
|
|
550
|
+
{customerPhone}
|
|
551
|
+
</span>
|
|
552
|
+
)}
|
|
553
|
+
{customerEmail && (
|
|
554
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
555
|
+
<Mail className="size-3 shrink-0" aria-hidden="true" />
|
|
556
|
+
<span className="truncate">{customerEmail}</span>
|
|
557
|
+
</span>
|
|
558
|
+
)}
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
{onDelete && (
|
|
562
|
+
<DropdownMenu>
|
|
563
|
+
<DropdownMenuTrigger
|
|
564
|
+
className={cn(
|
|
565
|
+
buttonVariants({ variant: "ghost", size: "icon" }),
|
|
566
|
+
"-mr-1 -mt-1 size-7 shrink-0",
|
|
567
|
+
)}
|
|
568
|
+
aria-label="Lead actions"
|
|
569
|
+
>
|
|
570
|
+
<MoreVertical className="size-4" />
|
|
571
|
+
</DropdownMenuTrigger>
|
|
572
|
+
<DropdownMenuContent align="end">
|
|
573
|
+
<DropdownMenuItem
|
|
574
|
+
onClick={onDelete}
|
|
575
|
+
className="text-destructive focus:text-destructive"
|
|
576
|
+
>
|
|
577
|
+
Delete
|
|
578
|
+
</DropdownMenuItem>
|
|
579
|
+
</DropdownMenuContent>
|
|
580
|
+
</DropdownMenu>
|
|
581
|
+
)}
|
|
582
|
+
</div>
|
|
583
|
+
|
|
584
|
+
<Separator />
|
|
585
|
+
|
|
586
|
+
{/* ── Send loan application action ── */}
|
|
587
|
+
{onSendLoanApplication && (
|
|
588
|
+
<div className="flex flex-col gap-2">
|
|
589
|
+
<Button
|
|
590
|
+
variant="outline"
|
|
591
|
+
size="sm"
|
|
592
|
+
className="w-full text-caption"
|
|
593
|
+
onClick={onSendLoanApplication}
|
|
594
|
+
disabled={isSubmitting}
|
|
595
|
+
>
|
|
596
|
+
Send Loan Application Request
|
|
597
|
+
</Button>
|
|
598
|
+
{loanApplicationUrl && (
|
|
599
|
+
<p className="text-xs text-muted-foreground">
|
|
600
|
+
Or the link below to fill out the loan application directly.
|
|
601
|
+
<br />
|
|
602
|
+
<a
|
|
603
|
+
href={`https://${loanApplicationUrl.replace(/^https?:\/\//, "")}`}
|
|
604
|
+
target="_blank"
|
|
605
|
+
rel="noreferrer"
|
|
606
|
+
className="text-primary underline-offset-2 hover:underline"
|
|
607
|
+
>
|
|
608
|
+
{loanApplicationUrl}
|
|
609
|
+
</a>
|
|
610
|
+
</p>
|
|
611
|
+
)}
|
|
612
|
+
</div>
|
|
613
|
+
)}
|
|
614
|
+
</div>
|
|
615
|
+
);
|
|
616
|
+
}
|