Skip to content

Commit b0a8fd9

Browse files
authored
feat: display isCompleted column in transactions table (#1039)
1 parent 4ca3bae commit b0a8fd9

2 files changed

Lines changed: 170 additions & 150 deletions

File tree

src/components/compliance/transactions-tab.tsx

Lines changed: 169 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -95,161 +95,180 @@ export function TransactionsTable({
9595
)}
9696
</div>
9797
{pdfError && <p className="text-xs text-primary-red mb-1">{pdfError}</p>}
98-
<table className="w-full border-collapse">
99-
<thead className="sticky top-0 bg-dfxGray-300">
100-
<tr>
101-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">ID</th>
102-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">UID</th>
103-
<th className="px-3 py-2 text-left text-sm font-semibold text-dfxBlue-800">Type</th>
104-
<th className="px-3 py-2 text-left text-sm font-semibold text-dfxBlue-800">Source</th>
105-
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Input</th>
106-
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Amount (CHF)</th>
107-
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Amount (EUR)</th>
108-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">AML Check</th>
109-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Chargeback</th>
110-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Bank TX</th>
111-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Crypto In</th>
112-
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Created</th>
113-
</tr>
114-
</thead>
115-
<tbody>
116-
{transactions?.length > 0 ? (
117-
transactions.map((tx) => {
118-
const bankTx = bankTxByTxId.get(tx.id);
119-
const cryptoInput = cryptoInputByTxId.get(tx.id);
120-
const isBankTxExpanded = expandedBankTxId === bankTx?.id;
121-
const isCryptoExpanded = expandedCryptoInputId === cryptoInput?.id;
122-
123-
return (
124-
<Fragment key={tx.id}>
125-
<tr className="border-b border-dfxGray-300 transition-colors hover:bg-dfxGray-300">
126-
<td className="px-3 py-2 text-sm text-dfxBlue-800">{tx.id}</td>
127-
<td
128-
className="px-3 py-2 text-sm text-dfxBlue-800 font-mono text-xs cursor-pointer text-dfxBlue-300 underline hover:text-dfxBlue-800"
129-
onClick={() => handleUidClick(tx.uid)}
130-
>
131-
{tx.uid}
132-
</td>
133-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-left">{tx.type || '-'}</td>
134-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-left">{tx.sourceType}</td>
135-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">
136-
{tx.inputAmount != null ? `${tx.inputAmount.toFixed(2)} ${tx.inputAsset ?? ''}` : '-'}
137-
</td>
138-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">{tx.amountInChf?.toFixed(2) || '-'}</td>
139-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">{tx.amountInEur?.toFixed(2) || '-'}</td>
140-
<td className="px-3 py-2 text-sm text-dfxBlue-800">{tx.amlCheck ? statusBadge(tx.amlCheck) : '-'}</td>
141-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
142-
{tx.chargebackDate ? (
143-
<span className="text-primary-red">
144-
{formatDate(tx.chargebackDate)}
145-
{tx.amlReason && (
146-
<>
147-
<br />
148-
<span className="text-xs">{tx.amlReason}</span>
149-
</>
150-
)}
151-
</span>
152-
) : (
153-
'-'
154-
)}
155-
</td>
156-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
157-
{bankTx ? (
158-
<button
159-
className="text-dfxBlue-300 underline hover:text-dfxBlue-800"
160-
onClick={() => onExpandBankTx(isBankTxExpanded ? undefined : bankTx.id)}
161-
>
162-
{bankTx.id}
163-
</button>
164-
) : (
165-
'-'
166-
)}
167-
</td>
168-
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
169-
{cryptoInput ? (
170-
<button
171-
className="text-dfxBlue-300 underline hover:text-dfxBlue-800"
172-
onClick={() => onExpandCryptoInput(isCryptoExpanded ? undefined : cryptoInput.id)}
173-
>
174-
{cryptoInput.id}
175-
</button>
176-
) : (
177-
'-'
178-
)}
179-
</td>
180-
<td className="px-3 py-2 text-sm text-dfxBlue-800">{formatDate(tx.created)}</td>
181-
</tr>
98+
<table className="w-full border-collapse">
99+
<thead className="sticky top-0 bg-dfxGray-300">
100+
<tr>
101+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">ID</th>
102+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">UID</th>
103+
<th className="px-3 py-2 text-left text-sm font-semibold text-dfxBlue-800">Type</th>
104+
<th className="px-3 py-2 text-left text-sm font-semibold text-dfxBlue-800">Source</th>
105+
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Input</th>
106+
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Amount (CHF)</th>
107+
<th className="px-3 py-2 text-right text-sm font-semibold text-dfxBlue-800">Amount (EUR)</th>
108+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">AML Check</th>
109+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Chargeback</th>
110+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Bank TX</th>
111+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Crypto In</th>
112+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Created</th>
113+
<th className="px-3 py-2 text-center text-sm font-semibold text-dfxBlue-800">Completed</th>
114+
</tr>
115+
</thead>
116+
<tbody>
117+
{transactions?.length > 0 ? (
118+
transactions.map((tx) => {
119+
const bankTx = bankTxByTxId.get(tx.id);
120+
const cryptoInput = cryptoInputByTxId.get(tx.id);
121+
const isBankTxExpanded = expandedBankTxId === bankTx?.id;
122+
const isCryptoExpanded = expandedCryptoInputId === cryptoInput?.id;
182123

183-
{isBankTxExpanded && bankTx && (
184-
<tr key={`bankTx-${bankTx.id}`} className="bg-dfxGray-300/50">
185-
<td colSpan={12} className="px-6 py-3">
186-
<table className="text-sm text-dfxBlue-800 text-left">
187-
<tbody>
188-
<DetailRow label="Account Service Ref" value={bankTx.accountServiceRef} />
189-
<DetailRow label="Name" value={bankTx.name} />
190-
<DetailRow label="IBAN" value={bankTx.iban} mono />
191-
<DetailRow label="Remittance Info" value={bankTx.remittanceInfo} />
192-
</tbody>
193-
</table>
124+
return (
125+
<Fragment key={tx.id}>
126+
<tr className="border-b border-dfxGray-300 transition-colors hover:bg-dfxGray-300">
127+
<td className="px-3 py-2 text-sm text-dfxBlue-800">{tx.id}</td>
128+
<td
129+
className="px-3 py-2 text-sm text-dfxBlue-800 font-mono text-xs cursor-pointer text-dfxBlue-300 underline hover:text-dfxBlue-800"
130+
onClick={() => handleUidClick(tx.uid)}
131+
>
132+
{tx.uid}
194133
</td>
195-
</tr>
196-
)}
197-
198-
{isCryptoExpanded && cryptoInput && (
199-
<tr key={`ci-${cryptoInput.id}`} className="bg-dfxGray-300/50">
200-
<td colSpan={12} className="px-6 py-3">
201-
<table className="text-sm text-dfxBlue-800 text-left">
202-
<tbody>
203-
<DetailRow label="TX ID" value={cryptoInput.inTxId} url={cryptoInput.inTxExplorerUrl} mono />
204-
<DetailRow
205-
label="Asset"
206-
value={
207-
cryptoInput.assetName
208-
? `${cryptoInput.assetName} (${cryptoInput.blockchain})`
209-
: cryptoInput.blockchain
210-
}
211-
/>
212-
<DetailRow label="Amount" value={cryptoInput.amount} />
213-
<DetailRow label="Status" value={cryptoInput.status} />
214-
<DetailRow label="Sender" value={cryptoInput.senderAddresses} mono />
215-
<DetailRow
216-
label="Return TX"
217-
value={cryptoInput.returnTxId}
218-
url={cryptoInput.returnTxExplorerUrl}
219-
mono
220-
/>
221-
<DetailRow label="Purpose" value={cryptoInput.purpose} />
222-
</tbody>
223-
</table>
134+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-left">{tx.type || '-'}</td>
135+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-left">{tx.sourceType}</td>
136+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">
137+
{tx.inputAmount != null ? `${tx.inputAmount.toFixed(2)} ${tx.inputAsset ?? ''}` : '-'}
224138
</td>
225-
</tr>
226-
)}
227-
228-
{expandedTxUid === tx.uid && (
229-
<tr key={`txDetail-${tx.uid}`} className="bg-dfxGray-300/50">
230-
<td colSpan={12} className="px-6 py-3">
231-
{txDetailLoading === tx.uid ? (
232-
<StyledLoadingSpinner size={SpinnerSize.SM} />
233-
) : txDetailError && !txDetailCache.has(tx.uid) ? (
234-
<p className="text-primary-red text-sm">{txDetailError}</p>
235-
) : txDetailCache.has(tx.uid) ? (
236-
<TransactionDetailRows tx={txDetailCache.get(tx.uid) as Transaction} />
237-
) : null}
139+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">
140+
{tx.amountInChf?.toFixed(2) || '-'}
141+
</td>
142+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-right">
143+
{tx.amountInEur?.toFixed(2) || '-'}
144+
</td>
145+
<td className="px-3 py-2 text-sm text-dfxBlue-800">
146+
{tx.amlCheck ? statusBadge(tx.amlCheck) : '-'}
147+
</td>
148+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
149+
{tx.chargebackDate ? (
150+
<span className="text-primary-red">
151+
{formatDate(tx.chargebackDate)}
152+
{tx.amlReason && (
153+
<>
154+
<br />
155+
<span className="text-xs">{tx.amlReason}</span>
156+
</>
157+
)}
158+
</span>
159+
) : (
160+
'-'
161+
)}
162+
</td>
163+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
164+
{bankTx ? (
165+
<button
166+
className="text-dfxBlue-300 underline hover:text-dfxBlue-800"
167+
onClick={() => onExpandBankTx(isBankTxExpanded ? undefined : bankTx.id)}
168+
>
169+
{bankTx.id}
170+
</button>
171+
) : (
172+
'-'
173+
)}
174+
</td>
175+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
176+
{cryptoInput ? (
177+
<button
178+
className="text-dfxBlue-300 underline hover:text-dfxBlue-800"
179+
onClick={() => onExpandCryptoInput(isCryptoExpanded ? undefined : cryptoInput.id)}
180+
>
181+
{cryptoInput.id}
182+
</button>
183+
) : (
184+
'-'
185+
)}
186+
</td>
187+
<td className="px-3 py-2 text-sm text-dfxBlue-800">{formatDate(tx.created)}</td>
188+
<td className="px-3 py-2 text-sm text-dfxBlue-800 text-center">
189+
{tx.isCompleted ? (
190+
<span className="text-green-500">Yes</span>
191+
) : (
192+
<span className="text-yellow-500">No</span>
193+
)}
238194
</td>
239195
</tr>
240-
)}
241-
</Fragment>
242-
);
243-
})
244-
) : (
245-
<tr>
246-
<td colSpan={12} className="px-3 py-4 text-center text-dfxGray-700">
247-
No transactions
248-
</td>
249-
</tr>
250-
)}
251-
</tbody>
252-
</table>
196+
197+
{isBankTxExpanded && bankTx && (
198+
<tr key={`bankTx-${bankTx.id}`} className="bg-dfxGray-300/50">
199+
<td colSpan={13} className="px-6 py-3">
200+
<table className="text-sm text-dfxBlue-800 text-left">
201+
<tbody>
202+
<DetailRow label="Account Service Ref" value={bankTx.accountServiceRef} />
203+
<DetailRow label="Name" value={bankTx.name} />
204+
<DetailRow label="IBAN" value={bankTx.iban} mono />
205+
<DetailRow label="Remittance Info" value={bankTx.remittanceInfo} />
206+
</tbody>
207+
</table>
208+
</td>
209+
</tr>
210+
)}
211+
212+
{isCryptoExpanded && cryptoInput && (
213+
<tr key={`ci-${cryptoInput.id}`} className="bg-dfxGray-300/50">
214+
<td colSpan={13} className="px-6 py-3">
215+
<table className="text-sm text-dfxBlue-800 text-left">
216+
<tbody>
217+
<DetailRow
218+
label="TX ID"
219+
value={cryptoInput.inTxId}
220+
url={cryptoInput.inTxExplorerUrl}
221+
mono
222+
/>
223+
<DetailRow
224+
label="Asset"
225+
value={
226+
cryptoInput.assetName
227+
? `${cryptoInput.assetName} (${cryptoInput.blockchain})`
228+
: cryptoInput.blockchain
229+
}
230+
/>
231+
<DetailRow label="Amount" value={cryptoInput.amount} />
232+
<DetailRow label="Status" value={cryptoInput.status} />
233+
<DetailRow label="Sender" value={cryptoInput.senderAddresses} mono />
234+
<DetailRow
235+
label="Return TX"
236+
value={cryptoInput.returnTxId}
237+
url={cryptoInput.returnTxExplorerUrl}
238+
mono
239+
/>
240+
<DetailRow label="Purpose" value={cryptoInput.purpose} />
241+
</tbody>
242+
</table>
243+
</td>
244+
</tr>
245+
)}
246+
247+
{expandedTxUid === tx.uid && (
248+
<tr key={`txDetail-${tx.uid}`} className="bg-dfxGray-300/50">
249+
<td colSpan={13} className="px-6 py-3">
250+
{txDetailLoading === tx.uid ? (
251+
<StyledLoadingSpinner size={SpinnerSize.SM} />
252+
) : txDetailError && !txDetailCache.has(tx.uid) ? (
253+
<p className="text-primary-red text-sm">{txDetailError}</p>
254+
) : txDetailCache.has(tx.uid) ? (
255+
<TransactionDetailRows tx={txDetailCache.get(tx.uid) as Transaction} />
256+
) : null}
257+
</td>
258+
</tr>
259+
)}
260+
</Fragment>
261+
);
262+
})
263+
) : (
264+
<tr>
265+
<td colSpan={13} className="px-3 py-4 text-center text-dfxGray-700">
266+
No transactions
267+
</td>
268+
</tr>
269+
)}
270+
</tbody>
271+
</table>
253272
</div>
254273
);
255274
}

src/hooks/compliance.hook.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ export interface TransactionInfo {
259259
amlCheck?: string;
260260
chargebackDate?: string;
261261
amlReason?: string;
262+
isCompleted: boolean;
262263
created: string;
263264
}
264265

0 commit comments

Comments
 (0)