braid-ui 1.0.97 → 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.
Files changed (145) hide show
  1. package/README.md +44 -327
  2. package/components.json +20 -0
  3. package/eslint.config.js +29 -0
  4. package/index.html +24 -0
  5. package/package.json +55 -115
  6. package/postcss.config.js +6 -0
  7. package/public/favicon.ico +0 -0
  8. package/public/placeholder.svg +1 -0
  9. package/public/robots.txt +14 -0
  10. package/src/App.css +42 -0
  11. package/src/App.tsx +94 -0
  12. package/src/components/MainLayout.tsx +15 -0
  13. package/src/components/alerts/AlertDocuments.tsx +320 -0
  14. package/src/components/alerts/AlertNotes.tsx +185 -0
  15. package/src/components/alerts/AlertTimeline.tsx +79 -0
  16. package/src/components/alerts/ContextSection.tsx +155 -0
  17. package/src/components/app-sidebar.tsx +341 -0
  18. package/src/components/form-sections/ACHBankCard.tsx +78 -0
  19. package/src/components/form-sections/ACHBasicInfoCard.tsx +100 -0
  20. package/src/components/form-sections/ACHTransferSection.tsx +64 -0
  21. package/src/components/form-sections/AddressForm.tsx +94 -0
  22. package/src/components/form-sections/BankAddressCard.tsx +95 -0
  23. package/src/components/form-sections/BankingDetailsCard.tsx +46 -0
  24. package/src/components/form-sections/BasicInfoCard.tsx +103 -0
  25. package/src/components/form-sections/BasicInfoSection.tsx +34 -0
  26. package/src/components/form-sections/BeneficiaryAddress.tsx +19 -0
  27. package/src/components/form-sections/BeneficiaryCard.tsx +41 -0
  28. package/src/components/form-sections/BeneficiaryDomesticWire.tsx +23 -0
  29. package/src/components/form-sections/BusinessProfileCard.tsx +131 -0
  30. package/src/components/form-sections/BusinessStatusCard.tsx +53 -0
  31. package/src/components/form-sections/ContactInfoCard.tsx +63 -0
  32. package/src/components/form-sections/CounterpartyBasicInfo.tsx +101 -0
  33. package/src/components/form-sections/CounterpartyProfileCard.tsx +104 -0
  34. package/src/components/form-sections/CounterpartyRecordsCard.tsx +41 -0
  35. package/src/components/form-sections/IntermediaryCard.tsx +77 -0
  36. package/src/components/form-sections/IntermediaryFI.tsx +41 -0
  37. package/src/components/form-sections/IntermediaryFIAddress.tsx +14 -0
  38. package/src/components/form-sections/OriginatorCard.tsx +49 -0
  39. package/src/components/form-sections/OriginatorFI.tsx +42 -0
  40. package/src/components/form-sections/OriginatorFIAddress.tsx +14 -0
  41. package/src/components/form-sections/PaymentInformationSection.tsx +163 -0
  42. package/src/components/form-sections/ReceiverCard.tsx +94 -0
  43. package/src/components/form-sections/WireTransferSection.tsx +75 -0
  44. package/src/components/layouts/list-page.tsx +103 -0
  45. package/src/components/transaction/ACHDetailsSection.tsx +95 -0
  46. package/src/components/transaction/WireDetailsSection.tsx +112 -0
  47. package/src/components/ui/account-card.tsx +94 -0
  48. package/src/components/ui/badge.tsx +75 -0
  49. package/src/components/ui/breadcrumb.tsx +78 -0
  50. package/src/components/ui/business-type-badge.tsx +42 -0
  51. package/src/components/ui/button.tsx +56 -0
  52. package/src/components/ui/calendar.tsx +49 -0
  53. package/src/components/ui/card.tsx +223 -0
  54. package/src/components/ui/container.tsx +45 -0
  55. package/src/components/ui/counterparty-type-badge.tsx +53 -0
  56. package/src/components/ui/data-grid.tsx +99 -0
  57. package/src/components/ui/data-table.tsx +152 -0
  58. package/src/components/ui/detail-page-layout.tsx +83 -0
  59. package/src/components/ui/dialog.tsx +120 -0
  60. package/src/components/ui/dropdown-menu.tsx +82 -0
  61. package/src/components/ui/editable-form-card.tsx +106 -0
  62. package/src/components/ui/editable-info-field.tsx +67 -0
  63. package/src/components/ui/enhanced-input.tsx +78 -0
  64. package/src/components/ui/enhanced-select.tsx +101 -0
  65. package/src/components/ui/enhanced-textarea.tsx +64 -0
  66. package/src/components/ui/entity-card.tsx +140 -0
  67. package/src/components/ui/form-card.tsx +40 -0
  68. package/src/components/ui/form-field.tsx +50 -0
  69. package/src/components/ui/form-input.tsx +29 -0
  70. package/src/components/ui/form-provider.tsx +18 -0
  71. package/src/components/ui/form-section.tsx +66 -0
  72. package/src/components/ui/form-select.tsx +35 -0
  73. package/src/components/ui/info-field.tsx +36 -0
  74. package/src/components/ui/json-viewer.tsx +146 -0
  75. package/src/components/ui/label.tsx +24 -0
  76. package/src/components/ui/metric-card.tsx +80 -0
  77. package/src/components/ui/page-layout.tsx +183 -0
  78. package/src/components/ui/popover.tsx +29 -0
  79. package/src/components/ui/responsive-grid.tsx +46 -0
  80. package/src/components/ui/separator.tsx +31 -0
  81. package/src/components/ui/sheet.tsx +140 -0
  82. package/src/components/ui/sidebar.tsx +775 -0
  83. package/src/components/ui/sonner.tsx +29 -0
  84. package/src/components/ui/stack.tsx +77 -0
  85. package/src/components/ui/status-badge.tsx +68 -0
  86. package/src/components/ui/tabs.tsx +52 -0
  87. package/src/components/ui/toast.tsx +127 -0
  88. package/src/components/ui/toaster.tsx +33 -0
  89. package/src/components/ui/tooltip.tsx +28 -0
  90. package/src/components/ui/use-toast.ts +3 -0
  91. package/src/components/ui-kit/dashboard-demo.tsx +156 -0
  92. package/src/components/ui-kit/pattern-library.tsx +248 -0
  93. package/src/components/ui-kit/showcase.tsx +211 -0
  94. package/src/hooks/use-mobile.tsx +19 -0
  95. package/src/hooks/use-toast.ts +191 -0
  96. package/src/hooks/useEditState.ts +70 -0
  97. package/src/hooks/useFormWithEditState.ts +115 -0
  98. package/src/{styles.css → index.css} +10 -4
  99. package/src/lib/constants.ts +25 -0
  100. package/src/lib/mock-data/alert-data.ts +275 -0
  101. package/src/lib/mock-data/banking-data.ts +72 -0
  102. package/src/lib/mock-data/business-data.ts +71 -0
  103. package/src/lib/mock-data/counterparty-data.ts +70 -0
  104. package/src/lib/mock-data/index.ts +5 -0
  105. package/src/lib/mock-data/transaction-data.ts +283 -0
  106. package/src/lib/mock-data/wire-data.ts +103 -0
  107. package/src/lib/mock-data.tsx +180 -0
  108. package/src/lib/schemas/banking-schemas.ts +30 -0
  109. package/src/lib/schemas/business-schemas.ts +36 -0
  110. package/src/lib/schemas/counterparty-schemas.ts +43 -0
  111. package/src/lib/schemas/index.ts +5 -0
  112. package/src/lib/schemas/wire-schemas.ts +44 -0
  113. package/src/lib/utils.ts +6 -0
  114. package/src/main.tsx +10 -0
  115. package/src/pages/Cases.tsx +16 -0
  116. package/src/pages/Dashboard.tsx +16 -0
  117. package/src/pages/NotFound.tsx +27 -0
  118. package/src/pages/TransactionHistory.tsx +532 -0
  119. package/src/pages/UIKit.tsx +51 -0
  120. package/src/pages/alerts/AlertDetail.tsx +193 -0
  121. package/src/pages/alerts/Alerts.tsx +373 -0
  122. package/src/pages/business/Business.tsx +48 -0
  123. package/src/pages/business/Create.tsx +173 -0
  124. package/src/pages/counterparty/Create.tsx +48 -0
  125. package/src/pages/counterparty/DomesticWire.tsx +78 -0
  126. package/src/pages/counterparty/Manage.tsx +79 -0
  127. package/src/pages/transactions/NewTransaction.tsx +527 -0
  128. package/src/pages/transactions/TransactionDetail.tsx +192 -0
  129. package/src/vite-env.d.ts +1 -0
  130. package/tailwind.config.ts +124 -0
  131. package/tsconfig.app.json +30 -0
  132. package/tsconfig.json +19 -0
  133. package/tsconfig.node.json +22 -0
  134. package/vite.config.ts +22 -0
  135. package/dist/css/braid-ui-variables.css +0 -88
  136. package/dist/css/braid-ui.css +0 -4484
  137. package/dist/css/braid-ui.min.css +0 -1
  138. package/dist/index.cjs +0 -4
  139. package/dist/index.cjs.map +0 -1
  140. package/dist/index.d.cts +0 -2429
  141. package/dist/index.d.ts +0 -2429
  142. package/dist/index.js +0 -4
  143. package/dist/index.js.map +0 -1
  144. package/src/styles-only.css +0 -121
  145. /package/{dist/braid-logo-343BOQZ2.png → src/assets/braid-logo.png} +0 -0
@@ -0,0 +1,42 @@
1
+ import { EnhancedInput } from "@/components/ui/enhanced-input"
2
+ import { EnhancedSelect } from "@/components/ui/enhanced-select"
3
+ import { FI_ID_TYPE_OPTIONS } from "@/lib/constants"
4
+
5
+ export const OriginatorFI = () => {
6
+ return (
7
+ <div className="space-y-6">
8
+ <div className="border-b border-border pb-4">
9
+ <h2 className="text-lg font-semibold text-foreground">Originator FI Details</h2>
10
+ <p className="text-sm text-muted-foreground">Financial institution details for the originator</p>
11
+ </div>
12
+
13
+ <div className="space-y-6">
14
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
15
+ <EnhancedInput
16
+ label="Originator FI Account Number"
17
+ placeholder="Enter account number"
18
+ required
19
+ />
20
+
21
+ <EnhancedInput
22
+ label="Originator FI Name"
23
+ placeholder="Enter FI name"
24
+ hint="Full name of the originating financial institution"
25
+ />
26
+
27
+ <EnhancedSelect
28
+ label="Originator FI ID Type"
29
+ placeholder="Select ID type"
30
+ options={FI_ID_TYPE_OPTIONS}
31
+ />
32
+
33
+ <EnhancedInput
34
+ label="Originator FI ID Number"
35
+ placeholder="Enter ID number"
36
+ hint="Financial institution identification number"
37
+ />
38
+ </div>
39
+ </div>
40
+ </div>
41
+ )
42
+ }
@@ -0,0 +1,14 @@
1
+ import { AddressForm } from "./AddressForm"
2
+
3
+ export const OriginatorFIAddress = () => {
4
+ return (
5
+ <AddressForm
6
+ title="Originator FI Address"
7
+ description="Address of the originating financial institution"
8
+ fieldPrefix="Originator FI"
9
+ fieldOverrides={{
10
+ streetAddress: { hint: "Originating financial institution street address" }
11
+ }}
12
+ />
13
+ )
14
+ }
@@ -0,0 +1,163 @@
1
+ import { useState } from "react"
2
+ import { Button } from "@/components/ui/button"
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
4
+ import { Plus, Trash2, ChevronDown, ChevronUp } from "lucide-react"
5
+ import { ACHBasicInfoCard } from "./ACHBasicInfoCard"
6
+ import { ACHBankCard } from "./ACHBankCard"
7
+ import { BankAddressCard } from "./BankAddressCard"
8
+ import { ReceiverCard } from "./ReceiverCard"
9
+ import { BeneficiaryCard } from "./BeneficiaryCard"
10
+ import { OriginatorCard } from "./OriginatorCard"
11
+ import { IntermediaryCard } from "./IntermediaryCard"
12
+ import { defaultACHTransfer } from "@/lib/mock-data/banking-data"
13
+ import { type ACHTransfer } from "@/lib/schemas/banking-schemas"
14
+
15
+ interface PaymentMethod {
16
+ id: string
17
+ type: "ach" | "wire"
18
+ name: string
19
+ collapsed: boolean
20
+ }
21
+
22
+ export const PaymentInformationSection = () => {
23
+ const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([])
24
+ const [showAddMenu, setShowAddMenu] = useState(false)
25
+ const [achData, setAchData] = useState<ACHTransfer>(defaultACHTransfer)
26
+
27
+ const addPaymentMethod = (type: "ach" | "wire") => {
28
+ const newMethod: PaymentMethod = {
29
+ id: `${type}-${Date.now()}`,
30
+ type,
31
+ name: type === "ach" ? "ACH Payment" : "Wire Transfer",
32
+ collapsed: false
33
+ }
34
+ setPaymentMethods([...paymentMethods, newMethod])
35
+ setShowAddMenu(false)
36
+ }
37
+
38
+ const removePaymentMethod = (id: string) => {
39
+ setPaymentMethods(paymentMethods.filter(method => method.id !== id))
40
+ }
41
+
42
+ const toggleCollapse = (id: string) => {
43
+ setPaymentMethods(paymentMethods.map(method =>
44
+ method.id === id ? { ...method, collapsed: !method.collapsed } : method
45
+ ))
46
+ }
47
+
48
+ const handleACHDataChange = (newData: ACHTransfer) => {
49
+ setAchData(newData)
50
+ }
51
+
52
+ const renderPaymentMethodContent = (method: PaymentMethod) => {
53
+ if (method.type === "ach") {
54
+ return (
55
+ <div className="space-y-4">
56
+ <ACHBasicInfoCard
57
+ data={achData}
58
+ onDataChange={handleACHDataChange}
59
+ isEditing={true}
60
+ hideActions={true}
61
+ />
62
+ <ACHBankCard
63
+ data={achData}
64
+ onDataChange={handleACHDataChange}
65
+ isEditing={true}
66
+ hideActions={true}
67
+ />
68
+ <BankAddressCard isEditing={true} hideActions={true} />
69
+ </div>
70
+ )
71
+ } else {
72
+ return (
73
+ <div className="space-y-4">
74
+ <ReceiverCard isEditing={true} hideActions={true} />
75
+ <BeneficiaryCard isEditing={true} hideActions={true} />
76
+ <OriginatorCard isEditing={true} hideActions={true} />
77
+ <IntermediaryCard isEditing={true} hideActions={true} />
78
+ </div>
79
+ )
80
+ }
81
+ }
82
+
83
+ return (
84
+ <div className="space-y-6">
85
+ <div className="border-b border-border pb-4">
86
+ <h2 className="text-lg font-semibold text-foreground">Payment Information</h2>
87
+ <p className="text-sm text-muted-foreground">Add payment methods for this counterparty</p>
88
+ </div>
89
+
90
+ {paymentMethods.map((method) => (
91
+ <Card key={method.id} className="relative">
92
+ <CardHeader className="pb-3">
93
+ <div className="flex items-center justify-between">
94
+ <div className="flex items-center gap-3">
95
+ <Button
96
+ variant="ghost"
97
+ size="icon"
98
+ onClick={() => toggleCollapse(method.id)}
99
+ className="h-6 w-6"
100
+ >
101
+ {method.collapsed ? (
102
+ <ChevronDown className="h-4 w-4" />
103
+ ) : (
104
+ <ChevronUp className="h-4 w-4" />
105
+ )}
106
+ </Button>
107
+ <CardTitle className="text-base">{method.name}</CardTitle>
108
+ <span className="text-xs bg-muted px-2 py-1 rounded-md uppercase font-medium">
109
+ {method.type}
110
+ </span>
111
+ </div>
112
+ <Button
113
+ variant="ghost"
114
+ size="icon"
115
+ onClick={() => removePaymentMethod(method.id)}
116
+ className="h-6 w-6 text-destructive hover:text-destructive/80 hover:bg-destructive/10"
117
+ >
118
+ <Trash2 className="h-4 w-4" />
119
+ </Button>
120
+ </div>
121
+ </CardHeader>
122
+ {!method.collapsed && (
123
+ <CardContent>
124
+ {renderPaymentMethodContent(method)}
125
+ </CardContent>
126
+ )}
127
+ </Card>
128
+ ))}
129
+
130
+ <div className="relative">
131
+ <Button
132
+ variant="outline"
133
+ onClick={() => setShowAddMenu(!showAddMenu)}
134
+ className="w-full border-dashed"
135
+ >
136
+ <Plus className="h-4 w-4 mr-2" />
137
+ Add Payment Information
138
+ </Button>
139
+
140
+ {showAddMenu && (
141
+ <div className="absolute top-full left-0 right-0 mt-2 bg-background border border-border rounded-md shadow-lg z-10">
142
+ <div className="p-2 space-y-1">
143
+ <Button
144
+ variant="ghost"
145
+ onClick={() => addPaymentMethod("ach")}
146
+ className="w-full justify-start"
147
+ >
148
+ Add ACH Payment
149
+ </Button>
150
+ <Button
151
+ variant="ghost"
152
+ onClick={() => addPaymentMethod("wire")}
153
+ className="w-full justify-start"
154
+ >
155
+ Add Wire Transfer
156
+ </Button>
157
+ </div>
158
+ </div>
159
+ )}
160
+ </div>
161
+ </div>
162
+ )
163
+ }
@@ -0,0 +1,94 @@
1
+ import { EditableFormCard } from "@/components/ui/editable-form-card"
2
+ import { DataGrid } from "@/components/ui/data-grid"
3
+ import { FormSection } from "@/components/ui/form-section"
4
+ import { FormProvider } from "@/components/ui/form-provider"
5
+ import { FormInput } from "@/components/ui/form-input"
6
+ import { useFormWithEditState } from "@/hooks/useFormWithEditState"
7
+ import { z } from "zod"
8
+ import { defaultReceiverInfo } from "@/lib/mock-data/wire-data"
9
+
10
+ const receiverSchema = z.object({
11
+ routingNumber: z.string().min(9, "Routing number must be 9 digits").max(9, "Routing number must be 9 digits"),
12
+ bankShortName: z.string().min(1, "Bank short name is required"),
13
+ })
14
+
15
+ type ReceiverInfo = z.infer<typeof receiverSchema>
16
+
17
+ interface ReceiverCardProps {
18
+ data?: Partial<ReceiverInfo>
19
+ onDataChange?: (data: ReceiverInfo) => void
20
+ isEditing?: boolean
21
+ onToggleEdit?: () => void
22
+ className?: string
23
+ hideActions?: boolean
24
+ }
25
+
26
+ export const ReceiverCard = ({
27
+ data,
28
+ onDataChange,
29
+ isEditing,
30
+ onToggleEdit,
31
+ className,
32
+ hideActions
33
+ }: ReceiverCardProps) => {
34
+ const form = useFormWithEditState<ReceiverInfo>({
35
+ schema: receiverSchema,
36
+ defaultValues: { ...defaultReceiverInfo, ...data },
37
+ initialEditing: isEditing,
38
+ onToggleEdit,
39
+ onSave: onDataChange,
40
+ })
41
+
42
+ const editContent = (
43
+ <FormProvider form={form}>
44
+ <FormSection>
45
+ <div className="space-y-6">
46
+ <div className="border-b border-border pb-4">
47
+ <h2 className="text-lg font-semibold text-foreground">Receiver Details</h2>
48
+ <p className="text-sm text-muted-foreground">Receiving bank information</p>
49
+ </div>
50
+
51
+ <div className="grid grid-cols-1 gap-6">
52
+ <FormInput
53
+ name="routingNumber"
54
+ label="Receiver Routing Number"
55
+ placeholder="Enter routing number"
56
+ hint="9-digit routing number"
57
+ pattern="[0-9]{9}"
58
+ maxLength={9}
59
+ required
60
+ />
61
+
62
+ <FormInput
63
+ name="bankShortName"
64
+ label="Receiver Bank Short Name"
65
+ placeholder="Enter bank short name"
66
+ required
67
+ />
68
+ </div>
69
+ </div>
70
+ </FormSection>
71
+ </FormProvider>
72
+ )
73
+
74
+ const formValues = form.watch()
75
+ const viewData = [
76
+ { label: "Routing Number", value: formValues?.routingNumber },
77
+ { label: "Bank Short Name", value: formValues?.bankShortName }
78
+ ]
79
+
80
+ return (
81
+ <EditableFormCard
82
+ title="FedWire Receiver"
83
+ variant="subtle"
84
+ className={className}
85
+ isEditing={form.isEditing}
86
+ onToggleEdit={form.handleToggleEdit}
87
+ onSave={form.handleSave}
88
+ onCancel={form.handleCancel}
89
+ hideActions={hideActions}
90
+ editContent={editContent}
91
+ viewContent={<DataGrid data={viewData} columns={2} />}
92
+ />
93
+ )
94
+ }
@@ -0,0 +1,75 @@
1
+ import { useState } from "react"
2
+ import { EditableFormCard } from "@/components/ui/editable-form-card"
3
+ import { ResponsiveGrid } from "@/components/ui/responsive-grid"
4
+ import { EnhancedSelect } from "@/components/ui/enhanced-select"
5
+ import { ReceiverCard } from "./ReceiverCard"
6
+ import { BeneficiaryCard } from "./BeneficiaryCard"
7
+ import { OriginatorCard } from "./OriginatorCard"
8
+ import { IntermediaryCard } from "./IntermediaryCard"
9
+
10
+ interface WireTransferSectionProps {
11
+ isEditing?: boolean
12
+ onToggleEdit?: () => void
13
+ className?: string
14
+ hideActions?: boolean
15
+ }
16
+
17
+ export const WireTransferSection = ({ isEditing, onToggleEdit, className, hideActions }: WireTransferSectionProps) => {
18
+ const [wireTransferType, setWireTransferType] = useState("INTERNATIONAL")
19
+
20
+ const editContent = (
21
+ <div className="space-y-6">
22
+ <div className="flex items-center gap-4">
23
+ <EnhancedSelect
24
+ label="Transfer Type"
25
+ value={wireTransferType}
26
+ onValueChange={setWireTransferType}
27
+ options={[
28
+ { value: "DOMESTIC", label: "Domestic" },
29
+ { value: "INTERNATIONAL", label: "International" }
30
+ ]}
31
+ placeholder="Select transfer type"
32
+ />
33
+ </div>
34
+
35
+ <ResponsiveGrid type="forms" gap="md" editMode={isEditing}>
36
+ <ReceiverCard isEditing={isEditing} hideActions={true} />
37
+ <BeneficiaryCard isEditing={isEditing} hideActions={true} />
38
+ <OriginatorCard isEditing={isEditing} hideActions={true} />
39
+ <IntermediaryCard isEditing={isEditing} hideActions={true} />
40
+ </ResponsiveGrid>
41
+ </div>
42
+ )
43
+
44
+ const viewContent = (
45
+ <div className="space-y-6">
46
+ <div className="flex items-center gap-2">
47
+ <span className="text-sm font-medium text-muted-foreground">Transfer Type:</span>
48
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-secondary text-secondary-foreground">
49
+ {wireTransferType}
50
+ </span>
51
+ </div>
52
+
53
+ <ResponsiveGrid type="forms" gap="md">
54
+ <ReceiverCard isEditing={false} hideActions={true} />
55
+ <BeneficiaryCard isEditing={false} hideActions={true} />
56
+ <OriginatorCard isEditing={false} hideActions={true} />
57
+ <IntermediaryCard isEditing={false} hideActions={true} />
58
+ </ResponsiveGrid>
59
+ </div>
60
+ )
61
+
62
+ return (
63
+ <EditableFormCard
64
+ title="Wire Transfer Configuration"
65
+ description="Configure wire transfer settings and participants"
66
+ variant="subtle"
67
+ className={className}
68
+ isEditing={isEditing}
69
+ onToggleEdit={onToggleEdit}
70
+ hideActions={hideActions}
71
+ editContent={editContent}
72
+ viewContent={viewContent}
73
+ />
74
+ )
75
+ }
@@ -0,0 +1,103 @@
1
+ import * as React from "react"
2
+ import { Container } from "@/components/ui/container"
3
+ import { Stack } from "@/components/ui/stack"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Breadcrumb, type BreadcrumbItem } from "@/components/ui/breadcrumb"
6
+ import { ResponsiveGrid } from "@/components/ui/responsive-grid"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ interface PageAction {
10
+ label: string
11
+ onClick: () => void
12
+ variant?: "default" | "outline" | "secondary"
13
+ icon?: React.ReactNode
14
+ }
15
+
16
+ interface ListPageProps {
17
+ title: string
18
+ description?: string
19
+ breadcrumbs?: BreadcrumbItem[]
20
+ actions?: PageAction[]
21
+ children: React.ReactNode
22
+ maxWidth?: "sm" | "md" | "lg" | "xl" | "full"
23
+ loading?: boolean
24
+ emptyState?: React.ReactNode
25
+ className?: string
26
+ }
27
+
28
+ export const ListPage = React.forwardRef<HTMLDivElement, ListPageProps>(
29
+ ({
30
+ title,
31
+ description,
32
+ breadcrumbs,
33
+ actions,
34
+ children,
35
+ maxWidth = "xl",
36
+ loading = false,
37
+ emptyState,
38
+ className
39
+ }, ref) => {
40
+ const hasData = React.Children.count(children) > 0
41
+
42
+ return (
43
+ <div ref={ref} className={cn("min-h-screen bg-gradient-subtle", className)}>
44
+ <Container size={maxWidth}>
45
+ <Stack spacing="lg">
46
+ {/* Breadcrumbs */}
47
+ {breadcrumbs && breadcrumbs.length > 0 && (
48
+ <Breadcrumb items={breadcrumbs} />
49
+ )}
50
+
51
+ {/* Page Header */}
52
+ <div className="flex items-start justify-between">
53
+ <Stack spacing="sm">
54
+ <h1 className="text-3xl font-bold text-foreground">{title}</h1>
55
+ {description && (
56
+ <p className="text-muted-foreground max-w-2xl">{description}</p>
57
+ )}
58
+ </Stack>
59
+
60
+ {actions && actions.length > 0 && (
61
+ <Stack direction="horizontal" spacing="sm">
62
+ {actions.map((action, index) => (
63
+ <Button
64
+ key={index}
65
+ variant={action.variant || "default"}
66
+ onClick={action.onClick}
67
+ >
68
+ {action.icon}
69
+ {action.label}
70
+ </Button>
71
+ ))}
72
+ </Stack>
73
+ )}
74
+ </div>
75
+
76
+ {/* Content */}
77
+ {loading ? (
78
+ <div className="flex items-center justify-center py-12">
79
+ <div className="animate-pulse text-muted-foreground">Loading...</div>
80
+ </div>
81
+ ) : hasData ? (
82
+ <ResponsiveGrid type="cards" gap="lg">
83
+ {children}
84
+ </ResponsiveGrid>
85
+ ) : emptyState ? (
86
+ <div className="flex items-center justify-center py-12">
87
+ {emptyState}
88
+ </div>
89
+ ) : (
90
+ <div className="flex items-center justify-center py-12">
91
+ <div className="text-center">
92
+ <p className="text-muted-foreground">No items found</p>
93
+ </div>
94
+ </div>
95
+ )}
96
+ </Stack>
97
+ </Container>
98
+ </div>
99
+ )
100
+ }
101
+ )
102
+
103
+ ListPage.displayName = "ListPage"
@@ -0,0 +1,95 @@
1
+ import { FormCard } from "@/components/ui/form-card"
2
+ import { InfoField } from "@/components/ui/info-field"
3
+ import { Button } from "@/components/ui/button"
4
+ import { JsonViewer } from "@/components/ui/json-viewer"
5
+
6
+ interface ACHDetails {
7
+ type: string
8
+ originatorName: string
9
+ originatorAccountNumber: string
10
+ receiverName: string
11
+ receiverAccountNumber: string
12
+ receiverRoutingNumber: string
13
+ amount: number
14
+ secCode: string
15
+ companyEntryDescription: string
16
+ companyDiscretionaryData?: string
17
+ individualIdNumber?: string
18
+ individualName?: string
19
+ traceNumber: string
20
+ raw?: any
21
+ }
22
+
23
+ interface ACHDetailsSectionProps {
24
+ data: ACHDetails
25
+ }
26
+
27
+ export const ACHDetailsSection = ({ data }: ACHDetailsSectionProps) => {
28
+ const formatCurrency = (value: number) => {
29
+ return new Intl.NumberFormat('en-US', {
30
+ style: 'currency',
31
+ currency: 'USD',
32
+ minimumFractionDigits: 2
33
+ }).format(value)
34
+ }
35
+
36
+ return (
37
+ <FormCard title="ACH Transfer Details">
38
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4">
39
+ <InfoField label="Type" value={data.type} layout="horizontal" />
40
+ <InfoField label="Originator Name" value={data.originatorName} layout="horizontal" />
41
+ <InfoField
42
+ label="Originator Account Number"
43
+ value={
44
+ <Button variant="link" className="h-auto p-0 text-sm">
45
+ {data.originatorAccountNumber}
46
+ </Button>
47
+ }
48
+ layout="horizontal"
49
+ />
50
+ <InfoField label="Receiver Name" value={data.receiverName} layout="horizontal" />
51
+ <InfoField
52
+ label="Receiver Account Number"
53
+ value={
54
+ <Button variant="link" className="h-auto p-0 text-sm">
55
+ {data.receiverAccountNumber}
56
+ </Button>
57
+ }
58
+ layout="horizontal"
59
+ />
60
+ <InfoField
61
+ label="Receiver Routing Number"
62
+ value={
63
+ <Button variant="link" className="h-auto p-0 text-sm">
64
+ {data.receiverRoutingNumber}
65
+ </Button>
66
+ }
67
+ layout="horizontal"
68
+ />
69
+ <InfoField label="Amount" value={formatCurrency(data.amount)} layout="horizontal" />
70
+ <InfoField label="SEC Code" value={data.secCode} layout="horizontal" />
71
+ <InfoField label="Company Entry Description" value={data.companyEntryDescription} layout="horizontal" />
72
+ {data.companyDiscretionaryData && (
73
+ <InfoField label="Company Discretionary Data" value={data.companyDiscretionaryData} layout="horizontal" />
74
+ )}
75
+ {data.individualIdNumber && (
76
+ <InfoField label="Individual ID Number" value={data.individualIdNumber} layout="horizontal" />
77
+ )}
78
+ {data.individualName && (
79
+ <InfoField label="Individual Name" value={data.individualName} layout="horizontal" />
80
+ )}
81
+ <InfoField label="Trace Number" value={data.traceNumber} layout="horizontal" />
82
+ </div>
83
+
84
+ {/* Raw JSON - Full Width */}
85
+ {data.raw && (
86
+ <div className="mt-6 pt-6 border-t border-border">
87
+ <InfoField
88
+ label="Raw"
89
+ value={<JsonViewer data={data.raw} className="mt-2" compact={true} />}
90
+ />
91
+ </div>
92
+ )}
93
+ </FormCard>
94
+ )
95
+ }