forked from Creditra/Creditra-Frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPreviewSection.tsx
More file actions
144 lines (136 loc) · 5.7 KB
/
Copy pathPreviewSection.tsx
File metadata and controls
144 lines (136 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { CreditLine } from "@/types/draw-credit.types";
import { DollarSign, TrendingUp } from "lucide-react";
import { formatMoney } from "@/utils/amountValidation";
interface PreviewSectionProps {
/** The credit line the draw is from. */
creditLine: CreditLine;
/** Draw amount the user entered in step 2 (whole number, in USD). */
amount: number;
}
/**
* Step 3 of the draw-credit wizard: a preview of the projected outcome.
*
* Shows four numbers side by side: draw amount, fee, post-draw balance,
* and the new utilization percentage. The fee, APR, and projected monthly
* interest are mocked here (1 % flat fee, 12.5 % APR) so the UX is
* exercised before the backend exposes a real quote endpoint. Replace
* the constants with a fetched quote when the API lands.
*
* The component is purely presentational — it does not mutate the
* wizard's amount or selected line.
*
* Why this exists as a dedicated step: see UX_RATIONALE.md "Show APR
* and total cost, not just APR". A user about to confirm an irreversible
* action should see what they will actually pay, not just the rate.
*/
export function PreviewSection({ creditLine, amount }: PreviewSectionProps) {
const utilizedBalance = creditLine.limit - creditLine.available;
const safeAmount = Math.max(amount, 0);
// Mock pricing keeps the UX explicit until backend quote fields are available.
const fee = safeAmount > 0 ? Math.round(safeAmount * 0.01 * 100) / 100 : 0;
const apr = 12.5;
const estimatedMonthlyInterest =
safeAmount > 0 ? Math.round(((safeAmount * apr) / 100 / 12) * 100) / 100 : 0;
const newBalance = utilizedBalance + safeAmount + fee;
const newUtilization = Math.round(
(newBalance / creditLine.limit) * 100,
);
const remainingAvailable = Math.max(creditLine.limit - newBalance, 0);
return (
<div className="space-y-6">
<header>
<p className="text-xs font-semibold uppercase text-muted">Step 3</p>
<h2 className="mt-1 text-lg font-semibold text-foreground">
Preview draw
</h2>
<p className="mt-1 text-sm text-muted">
Review estimated costs and projected balance before confirming.
</p>
</header>
<div className="grid gap-3 sm:grid-cols-2">
<div className="rounded-lg border border-blue-500/30 bg-blue-500/10 p-4 shadow-lg shadow-blue-500/5">
<div className="flex items-start justify-between">
<div>
<p className="text-xs text-muted font-medium mb-2">Draw Amount</p>
<p className="text-2xl font-bold text-blue-400">
{formatMoney(safeAmount)}
</p>
</div>
<DollarSign className="w-5 h-5 text-blue-500 shrink-0" />
</div>
</div>
<div className="rounded-lg border border-green-500/30 bg-green-500/10 p-4 shadow-lg shadow-green-500/5">
<div className="flex items-start justify-between">
<div>
<p className="text-xs text-muted font-medium mb-2">
New Utilization
</p>
<p className="text-2xl font-bold text-green-400">
{newUtilization}%
</p>
</div>
<TrendingUp className="w-5 h-5 text-green-500 shrink-0" />
</div>
</div>
</div>
<dl className="grid gap-3 text-sm sm:grid-cols-2">
<div className="rounded-lg border border-border bg-background/60 p-3">
<dt className="text-muted">Estimated fee</dt>
<dd className="mt-1 font-semibold text-foreground">
{formatMoney(fee)}
</dd>
</div>
<div className="rounded-lg border border-border bg-background/60 p-3">
<dt className="text-muted">Estimated monthly interest</dt>
<dd className="mt-1 font-semibold text-foreground">
{formatMoney(estimatedMonthlyInterest)}
</dd>
</div>
<div className="rounded-lg border border-border bg-background/60 p-3">
<dt className="text-muted">New balance</dt>
<dd className="mt-1 font-semibold text-foreground">
{formatMoney(newBalance)}
</dd>
</div>
<div className="rounded-lg border border-border bg-background/60 p-3">
<dt className="text-muted">Available after draw</dt>
<dd className="mt-1 font-semibold text-foreground">
{formatMoney(remainingAvailable)}
</dd>
</div>
</dl>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-muted">Current utilization</span>
<span className="font-semibold text-foreground">
{creditLine.utilization}%
</span>
</div>
<div className="w-full bg-border rounded-full h-2.5">
<div
className="bg-blue-500 h-2.5 rounded-full"
style={{ width: `${creditLine.utilization}%` }}
/>
</div>
</div>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-muted">After draw</span>
<span
className={`font-semibold ${newUtilization > 80 ? "text-yellow-500" : "text-foreground"}`}
>
{newUtilization}%
</span>
</div>
<div className="w-full bg-border rounded-full h-2.5">
<div
className={`h-2.5 rounded-full transition-all ${
newUtilization > 80 ? "bg-yellow-500" : "bg-blue-500"
}`}
style={{ width: `${newUtilization}%` }}
/>
</div>
</div>
</div>
);
}