forked from InsurNiffy/niff-Stellar-shurance
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpage.tsx
More file actions
83 lines (72 loc) · 3.08 KB
/
Copy pathpage.tsx
File metadata and controls
83 lines (72 loc) · 3.08 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
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { getPolicy } from '@/lib/api/chain';
import { formatXlm } from '@/features/policies/components/PolicyItem';
import { SECS_PER_LEDGER } from '@/lib/schemas/vote';
interface Props {
params: Promise<{ holder: string; policyId: string }>;
}
export default async function PolicyDetailPage({ params }: Props) {
const { holder, policyId } = await params;
const id = parseInt(policyId, 10);
if (isNaN(id)) notFound();
const policy = await getPolicy(decodeURIComponent(holder), id).catch(() => null);
if (!policy) notFound();
// policy is Record<string, unknown> from chain read — cast to known shape
const p = policy as {
policy_id: number;
policy_type: string;
region: string;
is_active: boolean;
coverage: string;
premium: string;
start_ledger: number;
end_ledger: number;
strike_count: number;
};
const statusLabel = p.is_active ? 'Active' : 'Inactive';
return (
<main className="container mx-auto px-4 py-8 max-w-2xl space-y-6">
<nav aria-label="Breadcrumb" className="text-sm text-gray-500">
<Link href="/dashboard" className="hover:underline text-blue-600">My Policies</Link>
{' / '}
<span>Policy #{p.policy_id}</span>
</nav>
<div className="rounded-xl border border-gray-200 bg-white p-6 space-y-4 shadow-sm">
<div className="flex items-center justify-between">
<h1 className="text-xl font-bold text-gray-900">Policy #{p.policy_id}</h1>
<span className={`rounded-full px-3 py-1 text-xs font-medium ${p.is_active ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-600'}`}>
{statusLabel}
</span>
</div>
<dl className="grid grid-cols-2 gap-x-6 gap-y-3 text-sm">
<Detail label="Type" value={p.policy_type} />
<Detail label="Region" value={p.region} />
<Detail label="Coverage" value={`${formatXlm(p.coverage)} XLM`} />
<Detail label="Premium / yr" value={`${formatXlm(p.premium)} XLM`} />
<Detail label="Start ledger" value={String(p.start_ledger)} />
<Detail label="End ledger" value={String(p.end_ledger)} />
<Detail label="Strike count" value={String(p.strike_count)} />
</dl>
<p className="text-xs text-gray-400">
ⓘ This data is read directly from the Soroban ledger (no indexer lag).
Amounts are in XLM (7 decimal places). Ledger timing: ~{SECS_PER_LEDGER}s per ledger.
</p>
</div>
<Link
href={`/policy/${encodeURIComponent(holder)}/${policyId}/claim`}
className="inline-flex items-center rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 min-h-[44px]"
>
File a claim
</Link>
</main>
);
}
function Detail({ label, value }: { label: string; value: string }) {
return (
<div>
<dt className="text-xs text-gray-500">{label}</dt>
<dd className="font-medium text-gray-900 tabular-nums">{value}</dd>
</div>
);
}