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,527 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
import { useNavigate } from "react-router-dom"
|
|
3
|
+
import { PageLayout } from "@/components/ui/page-layout"
|
|
4
|
+
import { FormCard } from "@/components/ui/form-card"
|
|
5
|
+
import { Button } from "@/components/ui/button"
|
|
6
|
+
import { FormProvider } from "@/components/ui/form-provider"
|
|
7
|
+
import { FormSelect } from "@/components/ui/form-select"
|
|
8
|
+
import { FormInput } from "@/components/ui/form-input"
|
|
9
|
+
import { InfoField } from "@/components/ui/info-field"
|
|
10
|
+
import { DataGrid } from "@/components/ui/data-grid"
|
|
11
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"
|
|
12
|
+
import { useForm } from "react-hook-form"
|
|
13
|
+
import { zodResolver } from "@hookform/resolvers/zod"
|
|
14
|
+
import { z } from "zod"
|
|
15
|
+
import { ArrowLeftRight, Settings, Building2, Zap, Search, CheckCircle2, XCircle, AlertCircle, Edit } from "lucide-react"
|
|
16
|
+
import { toast } from "sonner"
|
|
17
|
+
import { cn } from "@/lib/utils"
|
|
18
|
+
|
|
19
|
+
const newTransactionSchema = z.object({
|
|
20
|
+
transactionType: z.string().min(1, "Transaction type is required"),
|
|
21
|
+
accountNumber: z.string().min(1, "Account number is required"),
|
|
22
|
+
counterpartyName: z.string().min(1, "Counterparty name is required"),
|
|
23
|
+
amount: z.string().min(1, "Amount is required"),
|
|
24
|
+
currency: z.string().min(1, "Currency is required"),
|
|
25
|
+
description: z.string().optional(),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
type NewTransactionForm = z.infer<typeof newTransactionSchema>
|
|
29
|
+
|
|
30
|
+
const TRANSACTION_TYPES = [
|
|
31
|
+
{ value: "transfer", label: "Transfer", icon: ArrowLeftRight },
|
|
32
|
+
{ value: "adjustment", label: "Adjustment", icon: Settings },
|
|
33
|
+
{ value: "ach", label: "ACH", icon: Building2 },
|
|
34
|
+
{ value: "wire", label: "Wire", icon: Zap },
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
const CURRENCY_OPTIONS = [
|
|
38
|
+
{ value: "USD", label: "USD - US Dollar" },
|
|
39
|
+
{ value: "EUR", label: "EUR - Euro" },
|
|
40
|
+
{ value: "GBP", label: "GBP - British Pound" },
|
|
41
|
+
{ value: "JPY", label: "JPY - Japanese Yen" },
|
|
42
|
+
{ value: "CAD", label: "CAD - Canadian Dollar" },
|
|
43
|
+
{ value: "AUD", label: "AUD - Australian Dollar" },
|
|
44
|
+
{ value: "CHF", label: "CHF - Swiss Franc" },
|
|
45
|
+
{ value: "CNY", label: "CNY - Chinese Yuan" },
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
// Mock account data - would come from API in real app
|
|
49
|
+
const mockAccountData = {
|
|
50
|
+
accountNumber: "****1234",
|
|
51
|
+
accountName: "Business Checking",
|
|
52
|
+
accountType: "Checking",
|
|
53
|
+
balance: "$125,450.00",
|
|
54
|
+
customerName: "Acme Corporation",
|
|
55
|
+
customerId: "CUST-001",
|
|
56
|
+
customerType: "Business",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Mock counterparty data - would come from API in real app
|
|
60
|
+
const mockCounterpartyData = {
|
|
61
|
+
counterpartyName: "Global Tech Solutions Inc.",
|
|
62
|
+
counterpartyId: "CP-5678",
|
|
63
|
+
counterpartyType: "Business",
|
|
64
|
+
status: "Active",
|
|
65
|
+
taxId: "98-7654321",
|
|
66
|
+
primaryContact: "Sarah Johnson",
|
|
67
|
+
contactEmail: "sarah.johnson@globaltech.com",
|
|
68
|
+
contactPhone: "+1 (555) 987-6543",
|
|
69
|
+
address: "456 Innovation Drive, San Francisco, CA 94105",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default function NewTransaction() {
|
|
73
|
+
const navigate = useNavigate()
|
|
74
|
+
const [accountLookedUp, setAccountLookedUp] = useState(false)
|
|
75
|
+
const [accountData, setAccountData] = useState<typeof mockAccountData | null>(null)
|
|
76
|
+
const [counterpartyLookedUp, setCounterpartyLookedUp] = useState(false)
|
|
77
|
+
const [counterpartyData, setCounterpartyData] = useState<typeof mockCounterpartyData | null>(null)
|
|
78
|
+
const [confirmationOpen, setConfirmationOpen] = useState(false)
|
|
79
|
+
const [submissionStatus, setSubmissionStatus] = useState<"success" | "error" | null>(null)
|
|
80
|
+
const [errorMessage, setErrorMessage] = useState("")
|
|
81
|
+
const [transactionId, setTransactionId] = useState("")
|
|
82
|
+
|
|
83
|
+
const form = useForm<NewTransactionForm>({
|
|
84
|
+
resolver: zodResolver(newTransactionSchema),
|
|
85
|
+
defaultValues: {
|
|
86
|
+
transactionType: "",
|
|
87
|
+
accountNumber: "",
|
|
88
|
+
counterpartyName: "",
|
|
89
|
+
amount: "",
|
|
90
|
+
currency: "USD",
|
|
91
|
+
description: "",
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const transactionType = form.watch("transactionType")
|
|
96
|
+
const accountNumber = form.watch("accountNumber")
|
|
97
|
+
const counterpartyName = form.watch("counterpartyName")
|
|
98
|
+
const amount = form.watch("amount")
|
|
99
|
+
|
|
100
|
+
const handleAccountLookup = () => {
|
|
101
|
+
const accNum = form.getValues("accountNumber")
|
|
102
|
+
if (!accNum) {
|
|
103
|
+
toast.error("Please enter an account number")
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
// Simulate account lookup
|
|
107
|
+
setAccountData(mockAccountData)
|
|
108
|
+
setAccountLookedUp(true)
|
|
109
|
+
toast.success("Account found")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const handleCounterpartyLookup = () => {
|
|
113
|
+
const cpName = form.getValues("counterpartyName")
|
|
114
|
+
if (!cpName) {
|
|
115
|
+
toast.error("Please enter a counterparty name")
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
// Simulate counterparty lookup
|
|
119
|
+
setCounterpartyData(mockCounterpartyData)
|
|
120
|
+
setCounterpartyLookedUp(true)
|
|
121
|
+
toast.success("Counterparty found")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const handleEditAccount = () => {
|
|
125
|
+
setAccountLookedUp(false)
|
|
126
|
+
setAccountData(null)
|
|
127
|
+
// Reset dependent sections
|
|
128
|
+
form.setValue("transactionType", "")
|
|
129
|
+
setCounterpartyLookedUp(false)
|
|
130
|
+
setCounterpartyData(null)
|
|
131
|
+
form.setValue("counterpartyName", "")
|
|
132
|
+
form.setValue("amount", "")
|
|
133
|
+
form.setValue("description", "")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const handleEditCounterparty = () => {
|
|
137
|
+
setCounterpartyLookedUp(false)
|
|
138
|
+
setCounterpartyData(null)
|
|
139
|
+
// Reset dependent sections
|
|
140
|
+
form.setValue("amount", "")
|
|
141
|
+
form.setValue("description", "")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const handleSubmit = () => {
|
|
145
|
+
const data = form.getValues()
|
|
146
|
+
|
|
147
|
+
if (!data.transactionType || !data.accountNumber || !data.counterpartyName || !data.amount) {
|
|
148
|
+
toast.error("Please complete all required fields")
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!accountLookedUp || !counterpartyLookedUp) {
|
|
153
|
+
toast.error("Please lookup both account and counterparty")
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Simulate transaction processing with random success/failure
|
|
158
|
+
const amount = parseFloat(data.amount)
|
|
159
|
+
const balance = 125450 // Current balance from mock data
|
|
160
|
+
|
|
161
|
+
// Simulate different error scenarios
|
|
162
|
+
const hasError = Math.random() > 0.7 // 30% chance of error for demo
|
|
163
|
+
|
|
164
|
+
if (hasError) {
|
|
165
|
+
const errorScenarios = [
|
|
166
|
+
"Insufficient funds. Current balance: $125,450.00, Required: $" + amount.toFixed(2),
|
|
167
|
+
"Transaction limit exceeded. Daily limit: $50,000.00",
|
|
168
|
+
"Counterparty account is temporarily unavailable. Please try again later.",
|
|
169
|
+
"Invalid routing number for the selected transaction type."
|
|
170
|
+
]
|
|
171
|
+
setSubmissionStatus("error")
|
|
172
|
+
setErrorMessage(errorScenarios[Math.floor(Math.random() * errorScenarios.length)])
|
|
173
|
+
setConfirmationOpen(true)
|
|
174
|
+
} else {
|
|
175
|
+
// Success scenario
|
|
176
|
+
const txId = "TXN-" + Math.random().toString(36).substr(2, 9).toUpperCase()
|
|
177
|
+
setTransactionId(txId)
|
|
178
|
+
setSubmissionStatus("success")
|
|
179
|
+
setConfirmationOpen(true)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const handleConfirmationClose = () => {
|
|
184
|
+
setConfirmationOpen(false)
|
|
185
|
+
if (submissionStatus === "success") {
|
|
186
|
+
navigate("/transactions/history")
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const handleCancel = () => {
|
|
191
|
+
navigate("/dashboard")
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const accountDataGrid = accountData ? [
|
|
195
|
+
{
|
|
196
|
+
title: "Account Information",
|
|
197
|
+
items: [
|
|
198
|
+
{ label: "Account Number", value: accountData.accountNumber },
|
|
199
|
+
{ label: "Account Name", value: accountData.accountName },
|
|
200
|
+
{ label: "Account Type", value: accountData.accountType },
|
|
201
|
+
{ label: "Balance", value: accountData.balance },
|
|
202
|
+
{ label: "Customer Name", value: accountData.customerName },
|
|
203
|
+
{ label: "Customer ID", value: accountData.customerId },
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
] : []
|
|
207
|
+
|
|
208
|
+
const counterpartyDataGrid = counterpartyData ? [
|
|
209
|
+
{
|
|
210
|
+
title: "Counterparty Information",
|
|
211
|
+
items: [
|
|
212
|
+
{ label: "Counterparty Name", value: counterpartyData.counterpartyName },
|
|
213
|
+
{ label: "Counterparty ID", value: counterpartyData.counterpartyId },
|
|
214
|
+
{ label: "Type", value: counterpartyData.counterpartyType },
|
|
215
|
+
{ label: "Status", value: counterpartyData.status },
|
|
216
|
+
{ label: "Tax ID", value: counterpartyData.taxId },
|
|
217
|
+
{ label: "Primary Contact", value: counterpartyData.primaryContact },
|
|
218
|
+
{ label: "Contact Email", value: counterpartyData.contactEmail },
|
|
219
|
+
{ label: "Contact Phone", value: counterpartyData.contactPhone },
|
|
220
|
+
{ label: "Address", value: counterpartyData.address },
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
] : []
|
|
224
|
+
|
|
225
|
+
const reviewData = [
|
|
226
|
+
{
|
|
227
|
+
title: "Transaction Summary",
|
|
228
|
+
items: [
|
|
229
|
+
{ label: "Transaction Type", value: TRANSACTION_TYPES.find(t => t.value === transactionType)?.label || "-" },
|
|
230
|
+
{ label: "Account Number", value: accountNumber || "-" },
|
|
231
|
+
{ label: "Counterparty", value: counterpartyName || "-" },
|
|
232
|
+
{ label: "Amount", value: amount ? `$${amount}` : "-" },
|
|
233
|
+
{ label: "Description", value: form.watch("description") || "N/A" },
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<PageLayout
|
|
240
|
+
title="New Transaction"
|
|
241
|
+
description="Complete the form below to initiate a new transaction"
|
|
242
|
+
>
|
|
243
|
+
<div className="max-w-4xl mx-auto space-y-6">
|
|
244
|
+
<FormProvider form={form}>
|
|
245
|
+
<form onSubmit={(e) => e.preventDefault()}>
|
|
246
|
+
{/* Account Lookup */}
|
|
247
|
+
{!accountLookedUp ? (
|
|
248
|
+
<FormCard
|
|
249
|
+
title="Account Lookup"
|
|
250
|
+
variant="default"
|
|
251
|
+
>
|
|
252
|
+
<div className="space-y-4">
|
|
253
|
+
<FormInput
|
|
254
|
+
name="accountNumber"
|
|
255
|
+
label="Account Number"
|
|
256
|
+
placeholder="Enter account number"
|
|
257
|
+
/>
|
|
258
|
+
<Button
|
|
259
|
+
onClick={handleAccountLookup}
|
|
260
|
+
className="w-full sm:w-auto"
|
|
261
|
+
disabled={!accountNumber}
|
|
262
|
+
>
|
|
263
|
+
<Search className="h-4 w-4 mr-2" />
|
|
264
|
+
Lookup Account
|
|
265
|
+
</Button>
|
|
266
|
+
</div>
|
|
267
|
+
</FormCard>
|
|
268
|
+
) : (
|
|
269
|
+
/* Account Information (Read-only) */
|
|
270
|
+
<FormCard
|
|
271
|
+
title="Account Information"
|
|
272
|
+
variant="subtle"
|
|
273
|
+
headerActions={
|
|
274
|
+
<div className="flex items-center gap-2">
|
|
275
|
+
<CheckCircle2 className="h-5 w-5 text-success" />
|
|
276
|
+
<Button
|
|
277
|
+
variant="ghost"
|
|
278
|
+
size="sm"
|
|
279
|
+
onClick={handleEditAccount}
|
|
280
|
+
>
|
|
281
|
+
<Edit className="h-4 w-4 mr-1" />
|
|
282
|
+
Edit
|
|
283
|
+
</Button>
|
|
284
|
+
</div>
|
|
285
|
+
}
|
|
286
|
+
>
|
|
287
|
+
<DataGrid data={accountDataGrid} columns={2} />
|
|
288
|
+
</FormCard>
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
{/* Transaction Type Selection */}
|
|
292
|
+
<FormCard
|
|
293
|
+
title="Transaction Type"
|
|
294
|
+
variant="default"
|
|
295
|
+
className={cn(!accountLookedUp && "opacity-50 pointer-events-none")}
|
|
296
|
+
>
|
|
297
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
|
|
298
|
+
{TRANSACTION_TYPES.map((type) => {
|
|
299
|
+
const Icon = type.icon
|
|
300
|
+
const isSelected = transactionType === type.value
|
|
301
|
+
return (
|
|
302
|
+
<button
|
|
303
|
+
key={type.value}
|
|
304
|
+
type="button"
|
|
305
|
+
onClick={() => form.setValue("transactionType", type.value)}
|
|
306
|
+
disabled={!accountLookedUp}
|
|
307
|
+
className={cn(
|
|
308
|
+
"flex flex-col items-center justify-center p-4 lg:p-3 rounded-lg border-2 transition-all",
|
|
309
|
+
"hover:border-primary/50 hover:shadow-md",
|
|
310
|
+
isSelected
|
|
311
|
+
? "border-primary bg-primary/5 shadow-sm"
|
|
312
|
+
: "border-border bg-card"
|
|
313
|
+
)}
|
|
314
|
+
>
|
|
315
|
+
<Icon className={cn(
|
|
316
|
+
"h-6 w-6 lg:h-5 lg:w-5 mb-2",
|
|
317
|
+
isSelected ? "text-primary" : "text-muted-foreground"
|
|
318
|
+
)} />
|
|
319
|
+
<span className={cn(
|
|
320
|
+
"text-sm font-medium",
|
|
321
|
+
isSelected ? "text-primary" : "text-foreground"
|
|
322
|
+
)}>
|
|
323
|
+
{type.label}
|
|
324
|
+
</span>
|
|
325
|
+
</button>
|
|
326
|
+
)
|
|
327
|
+
})}
|
|
328
|
+
</div>
|
|
329
|
+
</FormCard>
|
|
330
|
+
|
|
331
|
+
{/* Counterparty Lookup */}
|
|
332
|
+
{!counterpartyLookedUp ? (
|
|
333
|
+
<FormCard
|
|
334
|
+
title="Counterparty Lookup"
|
|
335
|
+
variant="default"
|
|
336
|
+
className={cn(!transactionType && "opacity-50 pointer-events-none")}
|
|
337
|
+
>
|
|
338
|
+
<div className="space-y-4">
|
|
339
|
+
<FormInput
|
|
340
|
+
name="counterpartyName"
|
|
341
|
+
label="Counterparty Name"
|
|
342
|
+
placeholder="Enter counterparty name"
|
|
343
|
+
disabled={!transactionType}
|
|
344
|
+
/>
|
|
345
|
+
<Button
|
|
346
|
+
onClick={handleCounterpartyLookup}
|
|
347
|
+
className="w-full sm:w-auto"
|
|
348
|
+
disabled={!transactionType || !counterpartyName}
|
|
349
|
+
>
|
|
350
|
+
<Search className="h-4 w-4 mr-2" />
|
|
351
|
+
Lookup Counterparty
|
|
352
|
+
</Button>
|
|
353
|
+
</div>
|
|
354
|
+
</FormCard>
|
|
355
|
+
) : (
|
|
356
|
+
/* Counterparty Information (Read-only) */
|
|
357
|
+
<FormCard
|
|
358
|
+
title="Counterparty Information"
|
|
359
|
+
variant="subtle"
|
|
360
|
+
headerActions={
|
|
361
|
+
<div className="flex items-center gap-2">
|
|
362
|
+
<CheckCircle2 className="h-5 w-5 text-success" />
|
|
363
|
+
<Button
|
|
364
|
+
variant="ghost"
|
|
365
|
+
size="sm"
|
|
366
|
+
onClick={handleEditCounterparty}
|
|
367
|
+
>
|
|
368
|
+
<Edit className="h-4 w-4 mr-1" />
|
|
369
|
+
Edit
|
|
370
|
+
</Button>
|
|
371
|
+
</div>
|
|
372
|
+
}
|
|
373
|
+
>
|
|
374
|
+
<DataGrid data={counterpartyDataGrid} columns={2} />
|
|
375
|
+
</FormCard>
|
|
376
|
+
)}
|
|
377
|
+
|
|
378
|
+
{/* Transaction Details */}
|
|
379
|
+
<FormCard
|
|
380
|
+
title="Transaction Details"
|
|
381
|
+
variant="default"
|
|
382
|
+
className={cn(!counterpartyLookedUp && "opacity-50 pointer-events-none")}
|
|
383
|
+
>
|
|
384
|
+
<div className="space-y-4">
|
|
385
|
+
<div className="grid grid-cols-1 sm:grid-cols-[1fr_auto] gap-4">
|
|
386
|
+
<FormInput
|
|
387
|
+
name="amount"
|
|
388
|
+
label="Amount"
|
|
389
|
+
placeholder="0.00"
|
|
390
|
+
type="number"
|
|
391
|
+
disabled={!counterpartyLookedUp}
|
|
392
|
+
/>
|
|
393
|
+
<FormSelect
|
|
394
|
+
name="currency"
|
|
395
|
+
label="Currency"
|
|
396
|
+
options={CURRENCY_OPTIONS}
|
|
397
|
+
disabled={!counterpartyLookedUp}
|
|
398
|
+
/>
|
|
399
|
+
</div>
|
|
400
|
+
<FormInput
|
|
401
|
+
name="description"
|
|
402
|
+
label="Description (Optional)"
|
|
403
|
+
placeholder="Enter transaction description"
|
|
404
|
+
disabled={!counterpartyLookedUp}
|
|
405
|
+
/>
|
|
406
|
+
</div>
|
|
407
|
+
</FormCard>
|
|
408
|
+
|
|
409
|
+
{/* Review Summary */}
|
|
410
|
+
<FormCard
|
|
411
|
+
title="Review & Submit"
|
|
412
|
+
variant="default"
|
|
413
|
+
>
|
|
414
|
+
<div className="space-y-6">
|
|
415
|
+
<DataGrid data={reviewData} columns={2} />
|
|
416
|
+
|
|
417
|
+
<div className="flex gap-3 justify-end pt-4 border-t border-border">
|
|
418
|
+
<Button
|
|
419
|
+
type="button"
|
|
420
|
+
variant="outline"
|
|
421
|
+
onClick={handleCancel}
|
|
422
|
+
className="w-32"
|
|
423
|
+
>
|
|
424
|
+
Cancel
|
|
425
|
+
</Button>
|
|
426
|
+
<Button
|
|
427
|
+
type="button"
|
|
428
|
+
onClick={handleSubmit}
|
|
429
|
+
className="w-48"
|
|
430
|
+
disabled={!transactionType || !accountNumber || !counterpartyName || !amount}
|
|
431
|
+
>
|
|
432
|
+
<CheckCircle2 className="h-4 w-4 mr-2" />
|
|
433
|
+
Submit Transaction
|
|
434
|
+
</Button>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
</FormCard>
|
|
438
|
+
</form>
|
|
439
|
+
</FormProvider>
|
|
440
|
+
|
|
441
|
+
{/* Confirmation Dialog */}
|
|
442
|
+
<Dialog open={confirmationOpen} onOpenChange={setConfirmationOpen}>
|
|
443
|
+
<DialogContent className="sm:max-w-md">
|
|
444
|
+
<DialogHeader>
|
|
445
|
+
<div className="flex items-center gap-3 mb-2">
|
|
446
|
+
{submissionStatus === "success" ? (
|
|
447
|
+
<div className="h-12 w-12 rounded-full bg-success/10 flex items-center justify-center">
|
|
448
|
+
<CheckCircle2 className="h-6 w-6 text-success" />
|
|
449
|
+
</div>
|
|
450
|
+
) : (
|
|
451
|
+
<div className="h-12 w-12 rounded-full bg-destructive/10 flex items-center justify-center">
|
|
452
|
+
<XCircle className="h-6 w-6 text-destructive" />
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
<DialogTitle className="text-xl">
|
|
456
|
+
{submissionStatus === "success" ? "Transaction Successful" : "Transaction Failed"}
|
|
457
|
+
</DialogTitle>
|
|
458
|
+
</div>
|
|
459
|
+
<DialogDescription className="text-left">
|
|
460
|
+
{submissionStatus === "success" ? (
|
|
461
|
+
<div className="space-y-4 pt-2">
|
|
462
|
+
<p className="text-foreground">Your transaction has been successfully submitted and is being processed.</p>
|
|
463
|
+
<div className="bg-muted/50 rounded-lg p-4 space-y-2">
|
|
464
|
+
<div className="flex justify-between text-sm">
|
|
465
|
+
<span className="text-muted-foreground">Transaction ID:</span>
|
|
466
|
+
<span className="font-mono font-medium">{transactionId}</span>
|
|
467
|
+
</div>
|
|
468
|
+
<div className="flex justify-between text-sm">
|
|
469
|
+
<span className="text-muted-foreground">Amount:</span>
|
|
470
|
+
<span className="font-medium">${amount}</span>
|
|
471
|
+
</div>
|
|
472
|
+
<div className="flex justify-between text-sm">
|
|
473
|
+
<span className="text-muted-foreground">Type:</span>
|
|
474
|
+
<span className="font-medium">{TRANSACTION_TYPES.find(t => t.value === transactionType)?.label}</span>
|
|
475
|
+
</div>
|
|
476
|
+
<div className="flex justify-between text-sm">
|
|
477
|
+
<span className="text-muted-foreground">Counterparty:</span>
|
|
478
|
+
<span className="font-medium">{counterpartyName}</span>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
<div className="flex items-start gap-2 p-3 bg-blue-500/10 border border-blue-500/20 rounded-lg">
|
|
482
|
+
<AlertCircle className="h-5 w-5 text-blue-500 mt-0.5 flex-shrink-0" />
|
|
483
|
+
<p className="text-sm text-blue-700 dark:text-blue-300">
|
|
484
|
+
You will receive a confirmation email once the transaction is completed.
|
|
485
|
+
</p>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
) : (
|
|
489
|
+
<div className="space-y-4 pt-2">
|
|
490
|
+
<div className="flex items-start gap-2 p-4 bg-destructive/10 border border-destructive/20 rounded-lg">
|
|
491
|
+
<XCircle className="h-5 w-5 text-destructive mt-0.5 flex-shrink-0" />
|
|
492
|
+
<div className="space-y-1">
|
|
493
|
+
<p className="text-sm font-medium text-destructive">Error Processing Transaction</p>
|
|
494
|
+
<p className="text-sm text-muted-foreground">{errorMessage}</p>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
<p className="text-sm text-muted-foreground">
|
|
498
|
+
Please review the error message above and try again. If the problem persists, contact support.
|
|
499
|
+
</p>
|
|
500
|
+
</div>
|
|
501
|
+
)}
|
|
502
|
+
</DialogDescription>
|
|
503
|
+
</DialogHeader>
|
|
504
|
+
<DialogFooter className="sm:justify-end gap-2">
|
|
505
|
+
{submissionStatus === "error" && (
|
|
506
|
+
<Button
|
|
507
|
+
type="button"
|
|
508
|
+
variant="outline"
|
|
509
|
+
onClick={() => setConfirmationOpen(false)}
|
|
510
|
+
>
|
|
511
|
+
Try Again
|
|
512
|
+
</Button>
|
|
513
|
+
)}
|
|
514
|
+
<Button
|
|
515
|
+
type="button"
|
|
516
|
+
onClick={handleConfirmationClose}
|
|
517
|
+
variant={submissionStatus === "success" ? "default" : "outline"}
|
|
518
|
+
>
|
|
519
|
+
{submissionStatus === "success" ? "View Transactions" : "Close"}
|
|
520
|
+
</Button>
|
|
521
|
+
</DialogFooter>
|
|
522
|
+
</DialogContent>
|
|
523
|
+
</Dialog>
|
|
524
|
+
</div>
|
|
525
|
+
</PageLayout>
|
|
526
|
+
)
|
|
527
|
+
}
|