Skip to content

Commit 9c10f79

Browse files
authored
Merge pull request #357 from Ibk-Bless/frontend-datetime-aria-notfound-imageopt
feat: enhance date/time utilities, ARIA labels, 404 page, and icon op…
2 parents 621e55e + e5ec143 commit 9c10f79

2 files changed

Lines changed: 72 additions & 10 deletions

File tree

frontend/src/app/not-found.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,40 @@ export default function NotFound() {
1313
return (
1414
<main
1515
id="main-content"
16-
className="container mx-auto flex min-h-[70vh] flex-col items-center justify-center px-4 text-center"
16+
className="container mx-auto flex min-h-[70vh] flex-col items-center justify-center px-4 py-8 text-center"
1717
aria-labelledby="notfound-heading"
1818
>
1919
<div
20-
className="mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-surface-overlay border border-border text-text-muted"
20+
className="mb-6 flex h-16 w-16 sm:h-20 sm:w-20 items-center justify-center rounded-full bg-surface-overlay border border-border text-text-muted"
2121
aria-hidden="true"
2222
>
23-
<Compass className="h-10 w-10" />
23+
<Compass className="h-8 w-8 sm:h-10 sm:w-10" />
2424
</div>
2525

2626
<h1
2727
id="notfound-heading"
28-
className="text-4xl font-extrabold tracking-tight text-text-primary"
28+
className="text-3xl sm:text-4xl font-extrabold tracking-tight text-text-primary"
2929
>
3030
404 — Destination Unknown
3131
</h1>
3232

33-
<p className="mt-4 max-w-md text-text-secondary">
33+
<p className="mt-4 max-w-md text-sm sm:text-base text-text-secondary px-4">
3434
The page you are looking for has either sailed past the timelock or never existed in this
3535
chain.
3636
</p>
3737

3838
<nav
39-
className="mt-10 flex flex-wrap items-center justify-center gap-3"
39+
className="mt-8 sm:mt-10 flex flex-col sm:flex-row flex-wrap items-center justify-center gap-3 w-full max-w-md px-4"
4040
aria-label="Recovery navigation"
4141
>
42-
<Link href="/">
43-
<Button size="lg" className="rounded-xl px-8">
42+
<Link href="/" className="w-full sm:w-auto">
43+
<Button size="lg" className="rounded-xl px-6 sm:px-8 w-full sm:w-auto">
4444
<Home className="mr-2 h-5 w-5" aria-hidden="true" />
4545
Return to Bridge
4646
</Button>
4747
</Link>
48-
<Link href="/swap">
49-
<Button variant="outline" size="lg" className="rounded-xl px-8">
48+
<Link href="/swap" className="w-full sm:w-auto">
49+
<Button variant="outline" size="lg" className="rounded-xl px-6 sm:px-8 w-full sm:w-auto">
5050
Go to Swap
5151
<ArrowRight className="ml-2 h-5 w-5" aria-hidden="true" />
5252
</Button>

frontend/src/lib/utils.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export function formatAmount(amount: string | number, decimals = 7): string {
2525
/** Format a date to a relative time string */
2626
export function formatRelativeTime(date: string | Date): string {
2727
const d = typeof date === "string" ? new Date(date) : date;
28+
29+
// Handle invalid dates
30+
if (isNaN(d.getTime())) {
31+
return "Invalid date";
32+
}
33+
2834
const diff = Date.now() - d.getTime();
2935
const seconds = Math.floor(diff / 1000);
3036
const minutes = Math.floor(seconds / 60);
@@ -37,6 +43,62 @@ export function formatRelativeTime(date: string | Date): string {
3743
return "just now";
3844
}
3945

46+
/** Format a date to absolute UTC timestamp */
47+
export function formatAbsoluteUTC(date: string | Date): string {
48+
const d = typeof date === "string" ? new Date(date) : date;
49+
50+
// Handle invalid dates
51+
if (isNaN(d.getTime())) {
52+
return "Invalid date";
53+
}
54+
55+
return d.toISOString().replace("T", " ").substring(0, 19) + " UTC";
56+
}
57+
58+
/** Format a date to absolute local timestamp */
59+
export function formatAbsoluteLocal(date: string | Date): string {
60+
const d = typeof date === "string" ? new Date(date) : date;
61+
62+
// Handle invalid dates
63+
if (isNaN(d.getTime())) {
64+
return "Invalid date";
65+
}
66+
67+
return new Intl.DateTimeFormat("en-US", {
68+
year: "numeric",
69+
month: "short",
70+
day: "numeric",
71+
hour: "2-digit",
72+
minute: "2-digit",
73+
second: "2-digit",
74+
hour12: false,
75+
}).format(d);
76+
}
77+
78+
/** Format a date with explicit UTC/local behavior */
79+
export function formatTimestamp(
80+
date: string | Date,
81+
options: {
82+
mode?: "relative" | "absolute";
83+
timezone?: "utc" | "local";
84+
} = {}
85+
): string {
86+
const { mode = "relative", timezone = "local" } = options;
87+
88+
const d = typeof date === "string" ? new Date(date) : date;
89+
90+
// Handle invalid dates
91+
if (isNaN(d.getTime())) {
92+
return "Invalid date";
93+
}
94+
95+
if (mode === "relative") {
96+
return formatRelativeTime(d);
97+
}
98+
99+
return timezone === "utc" ? formatAbsoluteUTC(d) : formatAbsoluteLocal(d);
100+
}
101+
40102
/** Map chain name to its display color / class */
41103
export const CHAIN_COLORS: Record<string, string> = {
42104
stellar: "text-chain-stellar",

0 commit comments

Comments
 (0)