braid-ui 1.0.98 → 1.0.99
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/README.md +44 -327
- package/components.json +20 -0
- package/eslint.config.js +29 -0
- package/index.html +24 -0
- package/package.json +55 -115
- package/postcss.config.js +6 -0
- package/public/favicon.ico +0 -0
- package/public/placeholder.svg +1 -0
- package/public/robots.txt +14 -0
- package/src/App.css +42 -0
- package/src/App.tsx +94 -0
- package/src/components/MainLayout.tsx +15 -0
- package/src/components/alerts/AlertDocuments.tsx +320 -0
- package/src/components/alerts/AlertNotes.tsx +185 -0
- package/src/components/alerts/AlertTimeline.tsx +79 -0
- package/src/components/alerts/ContextSection.tsx +155 -0
- package/src/components/app-sidebar.tsx +341 -0
- package/src/components/form-sections/ACHBankCard.tsx +78 -0
- package/src/components/form-sections/ACHBasicInfoCard.tsx +100 -0
- package/src/components/form-sections/ACHTransferSection.tsx +64 -0
- package/src/components/form-sections/AddressForm.tsx +94 -0
- package/src/components/form-sections/BankAddressCard.tsx +95 -0
- package/src/components/form-sections/BankingDetailsCard.tsx +46 -0
- package/src/components/form-sections/BasicInfoCard.tsx +103 -0
- package/src/components/form-sections/BasicInfoSection.tsx +34 -0
- package/src/components/form-sections/BeneficiaryAddress.tsx +19 -0
- package/src/components/form-sections/BeneficiaryCard.tsx +41 -0
- package/src/components/form-sections/BeneficiaryDomesticWire.tsx +23 -0
- package/src/components/form-sections/BusinessProfileCard.tsx +131 -0
- package/src/components/form-sections/BusinessStatusCard.tsx +53 -0
- package/src/components/form-sections/ContactInfoCard.tsx +63 -0
- package/src/components/form-sections/CounterpartyBasicInfo.tsx +101 -0
- package/src/components/form-sections/CounterpartyProfileCard.tsx +104 -0
- package/src/components/form-sections/CounterpartyRecordsCard.tsx +41 -0
- package/src/components/form-sections/IntermediaryCard.tsx +77 -0
- package/src/components/form-sections/IntermediaryFI.tsx +41 -0
- package/src/components/form-sections/IntermediaryFIAddress.tsx +14 -0
- package/src/components/form-sections/OriginatorCard.tsx +49 -0
- package/src/components/form-sections/OriginatorFI.tsx +42 -0
- package/src/components/form-sections/OriginatorFIAddress.tsx +14 -0
- package/src/components/form-sections/PaymentInformationSection.tsx +163 -0
- package/src/components/form-sections/ReceiverCard.tsx +94 -0
- package/src/components/form-sections/WireTransferSection.tsx +75 -0
- package/src/components/layouts/list-page.tsx +103 -0
- package/src/components/transaction/ACHDetailsSection.tsx +95 -0
- package/src/components/transaction/WireDetailsSection.tsx +112 -0
- package/src/components/ui/account-card.tsx +94 -0
- package/src/components/ui/badge.tsx +75 -0
- package/src/components/ui/breadcrumb.tsx +78 -0
- package/src/components/ui/business-type-badge.tsx +42 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/calendar.tsx +49 -0
- package/src/components/ui/card.tsx +223 -0
- package/src/components/ui/container.tsx +45 -0
- package/src/components/ui/counterparty-type-badge.tsx +53 -0
- package/src/components/ui/data-grid.tsx +99 -0
- package/src/components/ui/data-table.tsx +152 -0
- package/src/components/ui/detail-page-layout.tsx +83 -0
- package/src/components/ui/dialog.tsx +120 -0
- package/src/components/ui/dropdown-menu.tsx +82 -0
- package/src/components/ui/editable-form-card.tsx +106 -0
- package/src/components/ui/editable-info-field.tsx +67 -0
- package/src/components/ui/enhanced-input.tsx +78 -0
- package/src/components/ui/enhanced-select.tsx +101 -0
- package/src/components/ui/enhanced-textarea.tsx +64 -0
- package/src/components/ui/entity-card.tsx +140 -0
- package/src/components/ui/form-card.tsx +40 -0
- package/src/components/ui/form-field.tsx +50 -0
- package/src/components/ui/form-input.tsx +29 -0
- package/src/components/ui/form-provider.tsx +18 -0
- package/src/components/ui/form-section.tsx +66 -0
- package/src/components/ui/form-select.tsx +35 -0
- package/src/components/ui/info-field.tsx +36 -0
- package/src/components/ui/json-viewer.tsx +146 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/metric-card.tsx +80 -0
- package/src/components/ui/page-layout.tsx +183 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/responsive-grid.tsx +46 -0
- package/src/components/ui/separator.tsx +31 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/sidebar.tsx +775 -0
- package/src/components/ui/sonner.tsx +29 -0
- package/src/components/ui/stack.tsx +77 -0
- package/src/components/ui/status-badge.tsx +68 -0
- package/src/components/ui/tabs.tsx +52 -0
- package/src/components/ui/toast.tsx +127 -0
- package/src/components/ui/toaster.tsx +33 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/components/ui/use-toast.ts +3 -0
- package/src/components/ui-kit/dashboard-demo.tsx +156 -0
- package/src/components/ui-kit/pattern-library.tsx +248 -0
- package/src/components/ui-kit/showcase.tsx +211 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-toast.ts +191 -0
- package/src/hooks/useEditState.ts +70 -0
- package/src/hooks/useFormWithEditState.ts +115 -0
- package/src/{styles.css → index.css} +10 -4
- package/src/lib/constants.ts +25 -0
- package/src/lib/mock-data/alert-data.ts +275 -0
- package/src/lib/mock-data/banking-data.ts +72 -0
- package/src/lib/mock-data/business-data.ts +71 -0
- package/src/lib/mock-data/counterparty-data.ts +70 -0
- package/src/lib/mock-data/index.ts +5 -0
- package/src/lib/mock-data/transaction-data.ts +283 -0
- package/src/lib/mock-data/wire-data.ts +103 -0
- package/src/lib/mock-data.tsx +180 -0
- package/src/lib/schemas/banking-schemas.ts +30 -0
- package/src/lib/schemas/business-schemas.ts +36 -0
- package/src/lib/schemas/counterparty-schemas.ts +43 -0
- package/src/lib/schemas/index.ts +5 -0
- package/src/lib/schemas/wire-schemas.ts +44 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/src/pages/Cases.tsx +16 -0
- package/src/pages/Dashboard.tsx +16 -0
- package/src/pages/NotFound.tsx +27 -0
- package/src/pages/TransactionHistory.tsx +532 -0
- package/src/pages/UIKit.tsx +51 -0
- package/src/pages/alerts/AlertDetail.tsx +193 -0
- package/src/pages/alerts/Alerts.tsx +373 -0
- package/src/pages/business/Business.tsx +48 -0
- package/src/pages/business/Create.tsx +173 -0
- package/src/pages/counterparty/Create.tsx +48 -0
- package/src/pages/counterparty/DomesticWire.tsx +78 -0
- package/src/pages/counterparty/Manage.tsx +79 -0
- package/src/pages/transactions/NewTransaction.tsx +527 -0
- package/src/pages/transactions/TransactionDetail.tsx +192 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.ts +124 -0
- package/tsconfig.app.json +30 -0
- package/tsconfig.json +19 -0
- package/tsconfig.node.json +22 -0
- package/vite.config.ts +22 -0
- package/dist/css/braid-ui-variables.css +0 -88
- package/dist/css/braid-ui.css +0 -4484
- package/dist/css/braid-ui.min.css +0 -1
- package/dist/index.cjs +0 -4
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -2429
- package/dist/index.d.ts +0 -2429
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/src/styles-only.css +0 -121
- /package/{dist/braid-logo-343BOQZ2.png → src/assets/braid-logo.png} +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { TimelineEvent } from "@/lib/mock-data/alert-data"
|
|
2
|
+
import { CheckCircle, Circle, UserPlus, Edit, XCircle } from "lucide-react"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
interface AlertTimelineProps {
|
|
6
|
+
events: TimelineEvent[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const AlertTimeline = ({ events }: AlertTimelineProps) => {
|
|
10
|
+
const getIcon = (action: string) => {
|
|
11
|
+
if (action.includes("Created")) return <Circle className="h-4 w-4" />
|
|
12
|
+
if (action.includes("Assigned")) return <UserPlus className="h-4 w-4" />
|
|
13
|
+
if (action.includes("Updated") || action.includes("Modified")) return <Edit className="h-4 w-4" />
|
|
14
|
+
if (action.includes("Closed") || action.includes("Resolved")) return <CheckCircle className="h-4 w-4" />
|
|
15
|
+
if (action.includes("Rejected")) return <XCircle className="h-4 w-4" />
|
|
16
|
+
return <Circle className="h-4 w-4" />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const getStatusColor = (status?: string) => {
|
|
20
|
+
switch (status) {
|
|
21
|
+
case "Unassigned":
|
|
22
|
+
return "text-destructive"
|
|
23
|
+
case "Closed":
|
|
24
|
+
return "text-success"
|
|
25
|
+
case "In Progress":
|
|
26
|
+
return "text-warning"
|
|
27
|
+
default:
|
|
28
|
+
return "text-muted-foreground"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (events.length === 0) {
|
|
33
|
+
return (
|
|
34
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
35
|
+
No timeline events yet
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="space-y-3">
|
|
42
|
+
{events.map((event, index) => (
|
|
43
|
+
<div key={event.id} className="relative pl-6 pb-3">
|
|
44
|
+
{/* Timeline line */}
|
|
45
|
+
{index !== events.length - 1 && (
|
|
46
|
+
<div className="absolute left-[7px] top-5 bottom-0 w-[2px] bg-border" />
|
|
47
|
+
)}
|
|
48
|
+
|
|
49
|
+
{/* Icon */}
|
|
50
|
+
<div className={cn(
|
|
51
|
+
"absolute left-0 top-0 flex-none",
|
|
52
|
+
getStatusColor(event.status)
|
|
53
|
+
)}>
|
|
54
|
+
<div className="h-4 w-4">
|
|
55
|
+
{getIcon(event.action)}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{/* Content */}
|
|
60
|
+
<div className="space-y-0.5">
|
|
61
|
+
<p className="text-xs font-medium text-foreground">{event.action}</p>
|
|
62
|
+
<p className="text-xs text-muted-foreground">by {event.user}</p>
|
|
63
|
+
{event.details && (
|
|
64
|
+
<p className="text-xs text-muted-foreground">{event.details}</p>
|
|
65
|
+
)}
|
|
66
|
+
{event.status && (
|
|
67
|
+
<p className={cn("text-xs font-medium", getStatusColor(event.status))}>
|
|
68
|
+
Status: {event.status}
|
|
69
|
+
</p>
|
|
70
|
+
)}
|
|
71
|
+
<p className="text-xs text-muted-foreground/70 pt-0.5">
|
|
72
|
+
{event.timestamp}
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
import { Alert } from "@/lib/mock-data/alert-data"
|
|
3
|
+
import { FormCard } from "@/components/ui/form-card"
|
|
4
|
+
import { InfoField } from "@/components/ui/info-field"
|
|
5
|
+
import { Badge } from "@/components/ui/badge"
|
|
6
|
+
import { Button } from "@/components/ui/button"
|
|
7
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
|
8
|
+
import { JsonViewer } from "@/components/ui/json-viewer"
|
|
9
|
+
import { ChevronRight } from "lucide-react"
|
|
10
|
+
|
|
11
|
+
interface ContextSectionProps {
|
|
12
|
+
alert: Alert
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ContextSection = ({ alert }: ContextSectionProps) => {
|
|
16
|
+
const [isDialogOpen, setIsDialogOpen] = useState(false)
|
|
17
|
+
|
|
18
|
+
const renderOFACContext = () => {
|
|
19
|
+
const data = alert.contextData
|
|
20
|
+
if (!data) return null
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="space-y-4">
|
|
24
|
+
<div className="grid grid-cols-2 gap-4">
|
|
25
|
+
<InfoField label="Entity Name" value={data.entityName} />
|
|
26
|
+
<InfoField label="Entity Type" value={data.entityType} />
|
|
27
|
+
<InfoField label="Match Score" value={<Badge variant="alert-ofac">{data.matchScore}</Badge>} />
|
|
28
|
+
<InfoField label="List Name" value={data.listName} />
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div>
|
|
32
|
+
<h4 className="text-sm font-medium mb-3">Flagged Items</h4>
|
|
33
|
+
<div className="overflow-x-auto border rounded-lg">
|
|
34
|
+
<table className="w-full">
|
|
35
|
+
<thead className="border-b bg-muted/50">
|
|
36
|
+
<tr>
|
|
37
|
+
<th className="px-4 py-3 text-left text-sm font-medium">Field</th>
|
|
38
|
+
<th className="px-4 py-3 text-left text-sm font-medium">Value</th>
|
|
39
|
+
<th className="px-4 py-3 text-left text-sm font-medium">Match Score</th>
|
|
40
|
+
</tr>
|
|
41
|
+
</thead>
|
|
42
|
+
<tbody>
|
|
43
|
+
{data.flaggedItems?.map((item: any, index: number) => (
|
|
44
|
+
<tr key={index} className="border-b hover:bg-muted/50">
|
|
45
|
+
<td className="px-4 py-3 text-sm font-medium">{item.field}</td>
|
|
46
|
+
<td className="px-4 py-3 text-sm">{item.value}</td>
|
|
47
|
+
<td className="px-4 py-3">
|
|
48
|
+
<Badge variant={parseInt(item.matchScore) > 90 ? "alert-ofac" : "outline"}>
|
|
49
|
+
{item.matchScore}
|
|
50
|
+
</Badge>
|
|
51
|
+
</td>
|
|
52
|
+
</tr>
|
|
53
|
+
))}
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{alert.contextData && (
|
|
60
|
+
<div>
|
|
61
|
+
<Button
|
|
62
|
+
variant="ghost"
|
|
63
|
+
className="w-full justify-between h-auto py-3 px-4 border rounded-lg hover:bg-muted/50"
|
|
64
|
+
onClick={() => setIsDialogOpen(true)}
|
|
65
|
+
>
|
|
66
|
+
<span className="text-sm font-medium">Detail Result</span>
|
|
67
|
+
<ChevronRight className="h-4 w-4" />
|
|
68
|
+
</Button>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const renderTransactionContext = () => {
|
|
76
|
+
return (
|
|
77
|
+
<div className="grid grid-cols-2 gap-4">
|
|
78
|
+
<InfoField label="Transaction ID" value="TXN-12345" />
|
|
79
|
+
<InfoField label="Amount" value="$30,000.00" />
|
|
80
|
+
<InfoField label="Transaction Type" value="Wire Transfer" />
|
|
81
|
+
<InfoField label="Date" value="2025-09-30" />
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const renderFileRecordContext = () => {
|
|
87
|
+
return (
|
|
88
|
+
<div className="grid grid-cols-2 gap-4">
|
|
89
|
+
<InfoField label="File Name" value="wire_batch_093025.csv" />
|
|
90
|
+
<InfoField label="Record Count" value="1,234" />
|
|
91
|
+
<InfoField label="Error Count" value="3" />
|
|
92
|
+
<InfoField label="Status" value={<Badge variant="alert-error">Failed</Badge>} />
|
|
93
|
+
</div>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const renderProductContext = () => {
|
|
98
|
+
return (
|
|
99
|
+
<div className="grid grid-cols-2 gap-4">
|
|
100
|
+
<InfoField label="Product ID" value="PROD-2787" />
|
|
101
|
+
<InfoField label="Product Name" value="Business Checking Account" />
|
|
102
|
+
<InfoField label="Status" value="Pending Deactivation" />
|
|
103
|
+
<InfoField label="Reason" value="Compliance review required" />
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const getContextTitle = () => {
|
|
109
|
+
switch (alert.contextType) {
|
|
110
|
+
case "Ofac":
|
|
111
|
+
return "OFAC Context"
|
|
112
|
+
case "Transaction":
|
|
113
|
+
return "Transaction Context"
|
|
114
|
+
case "File Record":
|
|
115
|
+
return "File Processing Context"
|
|
116
|
+
case "Product":
|
|
117
|
+
return "Product Context"
|
|
118
|
+
default:
|
|
119
|
+
return "Context Information"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const renderContext = () => {
|
|
124
|
+
switch (alert.contextType) {
|
|
125
|
+
case "Ofac":
|
|
126
|
+
return renderOFACContext()
|
|
127
|
+
case "Transaction":
|
|
128
|
+
return renderTransactionContext()
|
|
129
|
+
case "File Record":
|
|
130
|
+
return renderFileRecordContext()
|
|
131
|
+
case "Product":
|
|
132
|
+
return renderProductContext()
|
|
133
|
+
default:
|
|
134
|
+
return <p className="text-sm text-muted-foreground">No additional context available</p>
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
<FormCard title={getContextTitle()}>
|
|
142
|
+
{renderContext()}
|
|
143
|
+
</FormCard>
|
|
144
|
+
|
|
145
|
+
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
146
|
+
<DialogContent className="max-w-4xl">
|
|
147
|
+
<DialogHeader>
|
|
148
|
+
<DialogTitle>{getContextTitle()} - Detail Result</DialogTitle>
|
|
149
|
+
</DialogHeader>
|
|
150
|
+
<JsonViewer data={alert.contextData} />
|
|
151
|
+
</DialogContent>
|
|
152
|
+
</Dialog>
|
|
153
|
+
</>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LayoutDashboard, Bell, Briefcase, ArrowLeftRight, FileText, Users, Building2,
|
|
3
|
+
CreditCard, Shield, RefreshCw, DollarSign, Zap, Settings, User, Key,
|
|
4
|
+
Upload, AlertCircle, TrendingUp, Box, BarChart3, FileCheck, Receipt,
|
|
5
|
+
Landmark, Search, Repeat, CheckCircle, ChevronRight
|
|
6
|
+
} from "lucide-react"
|
|
7
|
+
import { NavLink, useLocation } from "react-router-dom"
|
|
8
|
+
import { useState } from "react"
|
|
9
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible"
|
|
10
|
+
import braidLogo from "@/assets/braid-logo.png"
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
Sidebar,
|
|
14
|
+
SidebarContent,
|
|
15
|
+
SidebarGroup,
|
|
16
|
+
SidebarGroupContent,
|
|
17
|
+
SidebarGroupLabel,
|
|
18
|
+
SidebarHeader,
|
|
19
|
+
SidebarMenu,
|
|
20
|
+
SidebarMenuButton,
|
|
21
|
+
SidebarMenuItem,
|
|
22
|
+
SidebarMenuBadge,
|
|
23
|
+
SidebarMenuSub,
|
|
24
|
+
SidebarMenuSubItem,
|
|
25
|
+
SidebarMenuSubButton,
|
|
26
|
+
SidebarTrigger,
|
|
27
|
+
useSidebar,
|
|
28
|
+
} from "@/components/ui/sidebar"
|
|
29
|
+
|
|
30
|
+
const navigationItems = [
|
|
31
|
+
{
|
|
32
|
+
title: "Dashboard",
|
|
33
|
+
url: "/dashboard",
|
|
34
|
+
icon: LayoutDashboard,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
title: "Alerts and Cases",
|
|
38
|
+
icon: Bell,
|
|
39
|
+
items: [
|
|
40
|
+
{
|
|
41
|
+
title: "Alerts",
|
|
42
|
+
url: "/alerts",
|
|
43
|
+
icon: Bell,
|
|
44
|
+
badge: "99+"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: "Cases",
|
|
48
|
+
url: "/cases",
|
|
49
|
+
icon: Briefcase,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: "Transactions",
|
|
55
|
+
icon: ArrowLeftRight,
|
|
56
|
+
items: [
|
|
57
|
+
{
|
|
58
|
+
title: "Transaction History",
|
|
59
|
+
url: "/transactions/history",
|
|
60
|
+
icon: FileText,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: "Transaction Review",
|
|
64
|
+
url: "/transactions/review",
|
|
65
|
+
icon: FileCheck,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: "New Transaction",
|
|
69
|
+
url: "/transactions/new",
|
|
70
|
+
icon: Receipt,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: "Statement",
|
|
76
|
+
url: "/statement",
|
|
77
|
+
icon: FileText,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
title: "Individuals",
|
|
81
|
+
url: "/individuals",
|
|
82
|
+
icon: Users,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: "Businesses",
|
|
86
|
+
url: "/business",
|
|
87
|
+
icon: Building2,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
title: "Counterparty",
|
|
91
|
+
icon: Briefcase,
|
|
92
|
+
items: [
|
|
93
|
+
{
|
|
94
|
+
title: "Management",
|
|
95
|
+
url: "/counterparty/manage",
|
|
96
|
+
icon: FileCheck,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: "Create Counterparty",
|
|
100
|
+
url: "/counterparty/create",
|
|
101
|
+
icon: Receipt,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
title: "Domestic Wire",
|
|
105
|
+
url: "/counterparty/domestic-wire",
|
|
106
|
+
icon: Zap,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
title: "Accounts",
|
|
112
|
+
url: "/accounts",
|
|
113
|
+
icon: CreditCard,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
title: "Compliance",
|
|
117
|
+
icon: Shield,
|
|
118
|
+
items: [
|
|
119
|
+
{
|
|
120
|
+
title: "OFAC",
|
|
121
|
+
url: "/compliance/ofac",
|
|
122
|
+
icon: Shield,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
title: "314a",
|
|
126
|
+
url: "/compliance/314a",
|
|
127
|
+
icon: FileText,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
title: "Velocity Limits",
|
|
131
|
+
url: "/compliance/velocity",
|
|
132
|
+
icon: TrendingUp,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
title: "Recon",
|
|
138
|
+
icon: RefreshCw,
|
|
139
|
+
items: [
|
|
140
|
+
{
|
|
141
|
+
title: "File Upload",
|
|
142
|
+
url: "/recon/upload",
|
|
143
|
+
icon: Upload,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
title: "Exception Review",
|
|
147
|
+
url: "/recon/exceptions",
|
|
148
|
+
icon: AlertCircle,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
title: "ACH",
|
|
154
|
+
icon: Repeat,
|
|
155
|
+
items: [
|
|
156
|
+
{
|
|
157
|
+
title: "Processing",
|
|
158
|
+
url: "/ach/processing",
|
|
159
|
+
icon: FileText,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
title: "Settlement",
|
|
163
|
+
url: "/ach/settlement",
|
|
164
|
+
icon: Landmark,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
title: "Return",
|
|
168
|
+
url: "/ach/return",
|
|
169
|
+
icon: Search,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
title: "NOC",
|
|
173
|
+
url: "/ach/noc",
|
|
174
|
+
icon: RefreshCw,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
title: "Wire",
|
|
180
|
+
icon: Zap,
|
|
181
|
+
items: [
|
|
182
|
+
{
|
|
183
|
+
title: "Processing",
|
|
184
|
+
url: "/wire/processing",
|
|
185
|
+
icon: FileText,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
title: "Settlement",
|
|
189
|
+
url: "/wire/settlement",
|
|
190
|
+
icon: Landmark,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: "Configurations",
|
|
196
|
+
icon: Settings,
|
|
197
|
+
items: [
|
|
198
|
+
{
|
|
199
|
+
title: "Developers",
|
|
200
|
+
url: "/configurations/developers",
|
|
201
|
+
icon: Box,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
title: "Programs",
|
|
205
|
+
url: "/configurations/programs",
|
|
206
|
+
icon: LayoutDashboard,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
title: "Products",
|
|
210
|
+
url: "/configurations/products",
|
|
211
|
+
icon: FileText,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
title: "ClearSight",
|
|
217
|
+
url: "/clearsight",
|
|
218
|
+
icon: BarChart3,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
title: "Settings",
|
|
222
|
+
icon: Settings,
|
|
223
|
+
items: [
|
|
224
|
+
{
|
|
225
|
+
title: "User Management",
|
|
226
|
+
url: "/settings/users",
|
|
227
|
+
icon: Users,
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
title: "API Key",
|
|
231
|
+
url: "/settings/api-key",
|
|
232
|
+
icon: Key,
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
export function AppSidebar() {
|
|
239
|
+
const { state } = useSidebar()
|
|
240
|
+
const location = useLocation()
|
|
241
|
+
const currentPath = location.pathname
|
|
242
|
+
|
|
243
|
+
// Track which menu items are open
|
|
244
|
+
const [openItems, setOpenItems] = useState<string[]>([])
|
|
245
|
+
|
|
246
|
+
const isActive = (path: string) => currentPath === path
|
|
247
|
+
|
|
248
|
+
const toggleItem = (title: string) => {
|
|
249
|
+
setOpenItems(prev =>
|
|
250
|
+
prev.includes(title)
|
|
251
|
+
? prev.filter(item => item !== title)
|
|
252
|
+
: [...prev, title]
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<Sidebar collapsible="icon">
|
|
258
|
+
<SidebarHeader className="border-b border-sidebar-border">
|
|
259
|
+
<div className="flex items-center justify-between gap-2 px-4 py-3">
|
|
260
|
+
{state !== "collapsed" ? (
|
|
261
|
+
<img src={braidLogo} alt="Braid" className="h-8" />
|
|
262
|
+
) : (
|
|
263
|
+
<img src={braidLogo} alt="Braid" className="h-6" />
|
|
264
|
+
)}
|
|
265
|
+
<SidebarTrigger />
|
|
266
|
+
</div>
|
|
267
|
+
</SidebarHeader>
|
|
268
|
+
|
|
269
|
+
<SidebarContent>
|
|
270
|
+
<SidebarMenu>
|
|
271
|
+
{navigationItems.map((item) => {
|
|
272
|
+
// Check if it's a group with sub-items
|
|
273
|
+
if (item.items) {
|
|
274
|
+
const isOpen = openItems.includes(item.title)
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<Collapsible
|
|
278
|
+
key={item.title}
|
|
279
|
+
open={isOpen}
|
|
280
|
+
onOpenChange={() => toggleItem(item.title)}
|
|
281
|
+
>
|
|
282
|
+
<SidebarMenuItem>
|
|
283
|
+
<CollapsibleTrigger asChild>
|
|
284
|
+
<SidebarMenuButton
|
|
285
|
+
tooltip={state === "collapsed" ? item.title : undefined}
|
|
286
|
+
>
|
|
287
|
+
<item.icon className="w-4 h-4" />
|
|
288
|
+
<span>{item.title}</span>
|
|
289
|
+
<ChevronRight
|
|
290
|
+
className={`ml-auto h-4 w-4 transition-transform ${isOpen ? 'rotate-90' : ''}`}
|
|
291
|
+
/>
|
|
292
|
+
</SidebarMenuButton>
|
|
293
|
+
</CollapsibleTrigger>
|
|
294
|
+
<CollapsibleContent>
|
|
295
|
+
<SidebarMenuSub>
|
|
296
|
+
{item.items.map((subItem) => (
|
|
297
|
+
<SidebarMenuSubItem key={subItem.title}>
|
|
298
|
+
<SidebarMenuSubButton
|
|
299
|
+
asChild
|
|
300
|
+
isActive={isActive(subItem.url)}
|
|
301
|
+
>
|
|
302
|
+
<NavLink to={subItem.url}>
|
|
303
|
+
<subItem.icon className="w-4 h-4" />
|
|
304
|
+
<span>{subItem.title}</span>
|
|
305
|
+
{subItem.badge && (
|
|
306
|
+
<SidebarMenuBadge className="ml-auto">
|
|
307
|
+
{subItem.badge}
|
|
308
|
+
</SidebarMenuBadge>
|
|
309
|
+
)}
|
|
310
|
+
</NavLink>
|
|
311
|
+
</SidebarMenuSubButton>
|
|
312
|
+
</SidebarMenuSubItem>
|
|
313
|
+
))}
|
|
314
|
+
</SidebarMenuSub>
|
|
315
|
+
</CollapsibleContent>
|
|
316
|
+
</SidebarMenuItem>
|
|
317
|
+
</Collapsible>
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Single item without sub-items
|
|
322
|
+
return (
|
|
323
|
+
<SidebarMenuItem key={item.title}>
|
|
324
|
+
<SidebarMenuButton
|
|
325
|
+
asChild
|
|
326
|
+
isActive={isActive(item.url!)}
|
|
327
|
+
tooltip={state === "collapsed" ? item.title : undefined}
|
|
328
|
+
>
|
|
329
|
+
<NavLink to={item.url!}>
|
|
330
|
+
<item.icon className="w-4 h-4" />
|
|
331
|
+
<span>{item.title}</span>
|
|
332
|
+
</NavLink>
|
|
333
|
+
</SidebarMenuButton>
|
|
334
|
+
</SidebarMenuItem>
|
|
335
|
+
)
|
|
336
|
+
})}
|
|
337
|
+
</SidebarMenu>
|
|
338
|
+
</SidebarContent>
|
|
339
|
+
</Sidebar>
|
|
340
|
+
)
|
|
341
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EditableFormCard } from "@/components/ui/editable-form-card"
|
|
2
|
+
import { InfoField } from "@/components/ui/info-field"
|
|
3
|
+
import { FormProvider } from "@/components/ui/form-provider"
|
|
4
|
+
import { FormInput } from "@/components/ui/form-input"
|
|
5
|
+
import { useFormWithEditState } from "@/hooks/useFormWithEditState"
|
|
6
|
+
import { achTransferSchema, type ACHTransfer } from "@/lib/schemas/banking-schemas"
|
|
7
|
+
import { defaultACHTransfer } from "@/lib/mock-data/banking-data"
|
|
8
|
+
|
|
9
|
+
interface ACHBankCardProps {
|
|
10
|
+
data?: Partial<ACHTransfer>
|
|
11
|
+
onDataChange?: (data: ACHTransfer) => void
|
|
12
|
+
isEditing?: boolean
|
|
13
|
+
onToggleEdit?: () => void
|
|
14
|
+
className?: string
|
|
15
|
+
hideActions?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ACHBankCard = ({ data, onDataChange, isEditing, onToggleEdit, className, hideActions }: ACHBankCardProps) => {
|
|
19
|
+
const form = useFormWithEditState<ACHTransfer>({
|
|
20
|
+
schema: achTransferSchema,
|
|
21
|
+
defaultValues: { ...defaultACHTransfer, ...data },
|
|
22
|
+
initialEditing: isEditing,
|
|
23
|
+
onToggleEdit,
|
|
24
|
+
onSave: onDataChange,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const editContent = (
|
|
28
|
+
<FormProvider form={form}>
|
|
29
|
+
<div className="space-y-6">
|
|
30
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
31
|
+
<FormInput
|
|
32
|
+
name="bankDetails.gatewayRoutingNumber"
|
|
33
|
+
label="Gateway Routing Number"
|
|
34
|
+
placeholder="Enter gateway routing number"
|
|
35
|
+
pattern="[0-9]{9}"
|
|
36
|
+
maxLength={9}
|
|
37
|
+
hint="9-digit gateway routing number"
|
|
38
|
+
required
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<FormInput
|
|
42
|
+
name="bankDetails.rdfiNumberQualifier"
|
|
43
|
+
label="RDFI Number Qualifier"
|
|
44
|
+
placeholder="Enter RDFI qualifier"
|
|
45
|
+
maxLength={2}
|
|
46
|
+
hint="2-digit RDFI number qualifier"
|
|
47
|
+
required
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</FormProvider>
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const viewContent = (
|
|
55
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
56
|
+
<InfoField label="Gateway Routing Number" value={form.getValues("bankDetails.gatewayRoutingNumber")} layout="horizontal" />
|
|
57
|
+
<InfoField label="RDFI Number Qualifier" value={form.getValues("bankDetails.rdfiNumberQualifier")} layout="horizontal" />
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<EditableFormCard
|
|
63
|
+
title="ACH Bank Details"
|
|
64
|
+
description="ACH-specific routing and qualification information"
|
|
65
|
+
variant="subtle"
|
|
66
|
+
className={className}
|
|
67
|
+
isEditing={form.isEditing}
|
|
68
|
+
onToggleEdit={form.handleToggleEdit}
|
|
69
|
+
onSave={form.handleSave}
|
|
70
|
+
onCancel={form.handleCancel}
|
|
71
|
+
hideActions={hideActions}
|
|
72
|
+
isFormValid={form.isFormValid}
|
|
73
|
+
isDirty={form.isDirty}
|
|
74
|
+
editContent={editContent}
|
|
75
|
+
viewContent={viewContent}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|