From c0f8a1fda9ecd17a5765e9c47a8f3c2000ef8991 Mon Sep 17 00:00:00 2001 From: Feyisara2108 Date: Mon, 30 Mar 2026 04:37:23 +0100 Subject: [PATCH 1/3] feat(frontend): add defaulted loan recovery UI --- .../src/app/[locale]/loans/[loanId]/page.tsx | 127 +++++++++++--- frontend/src/app/[locale]/loans/page.tsx | 163 ++++++++++++++---- frontend/src/app/hooks/useApi.ts | 2 +- 3 files changed, 239 insertions(+), 53 deletions(-) diff --git a/frontend/src/app/[locale]/loans/[loanId]/page.tsx b/frontend/src/app/[locale]/loans/[loanId]/page.tsx index a62b078b..92bed0fc 100644 --- a/frontend/src/app/[locale]/loans/[loanId]/page.tsx +++ b/frontend/src/app/[locale]/loans/[loanId]/page.tsx @@ -2,13 +2,15 @@ import Link from "next/link"; import { useParams } from "next/navigation"; -import { ChevronRight, Clock, Wallet } from "lucide-react"; +import { AlertTriangle, ChevronRight, Clock, ExternalLink, Wallet } from "lucide-react"; import { LoanDetailSkeleton } from "../../../components/skeletons/LoanDetailSkeleton"; import { useLoan } from "../../../hooks/useApi"; import { RepaymentProgress } from "../../../components/ui/RepaymentProgress"; import { LoanTimeline } from "../../../components/ui/LoanTimeline"; import { TxHashLink } from "../../../components/ui/TxHashLink"; +const SUPPORT_URL = "https://t.me/+DOylgFv1jyJlNzM0"; + function formatCurrency(value: number) { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(value); } @@ -63,29 +65,29 @@ export default function LoanDetailsPage() { } const latestTxHash = loan.events.find((event) => Boolean(event.txHash))?.txHash; - // Some API responses include nextPaymentDeadline in the extended loan object const nextDeadline = (loan as unknown as { nextPaymentDeadline?: string }).nextPaymentDeadline; const daysRemaining = getDaysRemaining(nextDeadline); + const isDefaulted = loan.status === "defaulted"; + const penaltyFees = Math.max(loan.totalOwed - (loan.principal + loan.accruedInterest), 0); + const collateralSeized = loan.events.some((event) => event.type === "Seized"); return (
- {/* Breadcrumb */} - {/* Header */}

Borrower Portal @@ -95,7 +97,6 @@ export default function LoanDetailsPage() { Track repayment timing, lender terms, and the current outstanding balance for this loan.

- {/* Loan metadata row */}
{loan.interestRate > 0 && ( @@ -122,10 +123,67 @@ export default function LoanDetailsPage() { )}
+ + {isDefaulted && ( +
+
+
+
+ +
+
+

+ Defaulted loan +

+

+ This loan has entered default. Review the recovery details below and contact support if you need a repayment plan review or want to raise a dispute. +

+
+
+ + Contact Support + + +
+ +
+
+

+ Outstanding amount +

+

+ {formatCurrency(loan.totalOwed)} +

+
+
+

+ Penalty fees +

+

+ {formatCurrency(penaltyFees)} +

+
+
+

+ Collateral seizure status +

+

+ {collateralSeized + ? "Collateral has been seized." + : "Collateral is still in review while recovery options are evaluated."} +

+
+
+
+ )}
- {/* Main content */}

Repayment plan

@@ -163,9 +221,7 @@ export default function LoanDetailsPage() {
- {/* Sidebar */}