Skip to content

Commit 6e0fd20

Browse files
committed
[TOOL-2964] Dashboard: Fix crash on unix time converter page for invalid unix time, UI improvements (#6036)
=
1 parent 69b1ee9 commit 6e0fd20

File tree

4 files changed

+172
-145
lines changed

4 files changed

+172
-145
lines changed

apps/dashboard/src/app/(dashboard)/tools/components/tabs.tsx

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { SidebarLayout } from "@/components/blocks/SidebarLayout";
12
import type { Metadata } from "next";
2-
import { ToolsTabs } from "./components/tabs";
33

44
export const metadata: Metadata = {
55
title: "thirdweb Blockchain Tools",
@@ -11,13 +11,31 @@ export default function ToolLayout({
1111
children,
1212
}: { children: React.ReactNode }) {
1313
return (
14-
<section className="flex h-full flex-col gap-8">
15-
<main className="container flex-1 pb-20">
16-
<div className="h-14" />
17-
<ToolsTabs />
18-
<div className="h-8" />
19-
<div>{children}</div>
20-
</main>
21-
</section>
14+
<SidebarLayout
15+
sidebarLinks={[
16+
{
17+
label: "Transaction Simulator",
18+
href: "/tools/transaction-simulator",
19+
},
20+
{
21+
label: "Wei Converter",
22+
href: "/tools/wei-converter",
23+
},
24+
{
25+
label: "Hex Converter",
26+
href: "/tools/hex-converter",
27+
},
28+
{
29+
label: "Unix Time Converter",
30+
href: "/tools/unixtime-converter",
31+
},
32+
{
33+
label: "Keccak-256 Converter",
34+
href: "/tools/keccak256-converter",
35+
},
36+
]}
37+
>
38+
{children}
39+
</SidebarLayout>
2240
);
2341
}
Lines changed: 139 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,151 @@
11
"use client";
22

3-
import { CopyTextButton } from "@/components/ui/CopyTextButton";
3+
import {} from "@/components/ui/alert";
4+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
45
import { Input } from "@/components/ui/input";
56
import { Label } from "@/components/ui/label";
6-
import { formatDistanceToNow } from "date-fns";
7-
import { useState } from "react";
8-
import { ShareButton } from "../../components/share";
9-
10-
export const UnixTimeConverter = () => {
11-
const getCurrentUnixMilliseconds = () => Math.floor(new Date().getTime());
12-
const [unix, setUnix] = useState<number>(getCurrentUnixMilliseconds());
13-
14-
// `unix` may be in milliseconds or seconds.
15-
// Treat any value < 100000000000 as seconds, otherwise milliseconds.
16-
const isSeconds = unix < 100000000000;
17-
const date = new Date(isSeconds ? unix * 1000 : unix);
18-
const dateLocal = date.toLocaleString(undefined, {
19-
timeZoneName: "short",
20-
});
21-
const dateUTC = date.toLocaleString(undefined, {
22-
timeZoneName: "short",
23-
timeZone: "UTC",
7+
import {
8+
formatDistanceToNow,
9+
formatISO9075,
10+
formatRFC3339,
11+
formatRFC7231,
12+
fromUnixTime,
13+
} from "date-fns";
14+
import { useMemo, useState } from "react";
15+
16+
export function UnixTimeConverter() {
17+
const [unixTime, setUnixTime] = useState(() => {
18+
return Math.floor(Date.now() / 1000);
2419
});
2520

21+
const { date, error } = useMemo(() => {
22+
if (Number.isNaN(unixTime) || !Number.isInteger(unixTime)) {
23+
return {
24+
error: "Invalid Unix Timestamp",
25+
date: null,
26+
};
27+
}
28+
29+
const date = fromUnixTime(unixTime);
30+
31+
if (!isValidDate(date)) {
32+
return {
33+
date: null,
34+
error: "Invalid Unix Timestamp",
35+
};
36+
}
37+
38+
return {
39+
date,
40+
error: null,
41+
};
42+
}, [unixTime]);
43+
2644
return (
27-
<div className="space-y-24">
28-
<div className="space-y-4">
29-
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
30-
<Label htmlFor="unix-input" className="min-w-64 text-xl">
31-
Unix Time
32-
</Label>
33-
<Input
34-
id="unix-input"
35-
type="number"
36-
value={unix}
37-
onChange={(e) => {
38-
if (!Number.isNaN(e.target.valueAsNumber)) {
39-
setUnix(e.target.valueAsNumber);
40-
}
41-
}}
42-
placeholder="Enter a Unix time in milliseconds or seconds"
43-
className="p-6 text-xl"
44-
/>
45-
</div>
46-
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
47-
<Label className="min-w-64 text-xl">Time (local)</Label>
48-
<div>
49-
<CopyTextButton
50-
textToCopy={dateLocal}
51-
textToShow={dateLocal}
52-
tooltip="Copy"
53-
copyIconPosition="right"
54-
className="font-mono text-xl"
45+
<div className="mx-auto max-w-2xl">
46+
<Card>
47+
<CardHeader className="mb-6 border-b">
48+
<CardTitle className="font-bold text-2xl">
49+
Unix Time Converter
50+
</CardTitle>
51+
</CardHeader>
52+
53+
<CardContent>
54+
<div className="mb-4">
55+
<Label htmlFor="unixTime">Enter Unix Timestamp</Label>
56+
<Input
57+
id="unixTime"
58+
type="number"
59+
value={unixTime}
60+
onChange={(e) => {
61+
setUnixTime(Number(e.target.value));
62+
}}
63+
placeholder="e.g., 1609459200"
64+
className="mt-1"
5565
/>
66+
{error && (
67+
<p className="mt-2 text-destructive-text text-sm">{error}</p>
68+
)}
5669
</div>
57-
<p className="text-sm italic">
58-
&larr; {formatDistanceToNow(date, { addSuffix: true })}
59-
</p>
60-
</div>
61-
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
62-
<Label className="min-w-64 text-xl">Time (UTC)</Label>
63-
<div>
64-
<CopyTextButton
65-
textToCopy={dateUTC}
66-
textToShow={dateUTC}
67-
tooltip="Copy"
68-
copyIconPosition="right"
69-
className="font-mono text-xl"
70-
/>
70+
71+
<div className="flex flex-col gap-4">
72+
<div>
73+
<Label>Relative Time</Label>
74+
<p className="font-mono text-muted-foreground">
75+
{date
76+
? formatDistanceToNow(date, {
77+
addSuffix: true,
78+
})
79+
: "---"}
80+
</p>
81+
</div>
82+
<div>
83+
<Label>Local Time</Label>
84+
<p className="font-mono text-muted-foreground">
85+
{date ? formatLocal(date) : "---"}
86+
</p>
87+
</div>
88+
<div>
89+
<Label>UTC Time</Label>
90+
<p className="font-mono text-muted-foreground">
91+
{date ? formatUTC(date) : "---"}
92+
</p>
93+
</div>
94+
95+
<div>
96+
<Label>ISO 8601</Label>
97+
<p className="font-mono text-muted-foreground">
98+
{date ? formatISO8601(date) : "---"}
99+
</p>
100+
</div>
101+
102+
<div>
103+
<Label>ISO 9075</Label>
104+
<p className="font-mono text-muted-foreground">
105+
{date ? formatISO9075(date) : "---"}
106+
</p>
107+
</div>
108+
<div>
109+
<Label>RFC 7231</Label>
110+
<p className="font-mono text-muted-foreground">
111+
{date ? formatRFC7231(date) : "---"}
112+
</p>
113+
</div>
114+
<div>
115+
<Label>RFC 3339</Label>
116+
<p className="font-mono text-muted-foreground">
117+
{date ? formatRFC3339(date) : "---"}
118+
</p>
119+
</div>
120+
<div>
121+
<Label>SQL Timestamp</Label>
122+
<p className="font-mono text-muted-foreground">
123+
{date ? formatSQL(date) : "---"}
124+
</p>
125+
</div>
71126
</div>
72-
</div>
73-
<div className="mt-4 flex justify-end">
74-
<ShareButton
75-
cta="Share on X"
76-
href="https://twitter.com/intent/tweet?text=Easy-to-use%20Unix%20time%20converter%20by%20thirdweb%20%F0%9F%98%8D&url=https%3A%2F%2Fthirdweb.com%2Ftools%2Funixtime-converter"
77-
/>
78-
</div>
79-
</div>
80-
81-
<div className="space-y-4">
82-
<h1 className="font-bold text-2xl">
83-
Convert Unix time in milliseconds or seconds
84-
</h1>
85-
86-
<p>
87-
Timestamps are often represented in Unix time for several key reasons:
88-
</p>
89-
<h2 className="font-bold text-md">Simplicity</h2>
90-
<p>
91-
Counts seconds since January 1, 1970, making it easy to understand and
92-
manipulate.
93-
</p>
94-
<h2 className="font-bold text-md">Consistency</h2>
95-
<p>
96-
Provides a uniform time representation across systems, unaffected by
97-
time zones or calendar changes.
98-
</p>
99-
<h2 className="font-bold text-md">Standardization</h2>
100-
<p>
101-
Widely supported in computing, ensuring compatibility across
102-
platforms.
103-
</p>
104-
<h2 className="font-bold text-md">Calculation</h2>
105-
<p>
106-
Allows straightforward arithmetic operations for time differences.
107-
</p>
108-
<h2 className="font-bold text-md">Efficiency</h2>
109-
<p>
110-
Stores time as a single integer, conserving memory and computational
111-
resources.
112-
</p>
113-
<h2 className="font-bold text-md">Interoperability</h2>
114-
<p>Facilitates seamless data exchange between systems and languages.</p>
115-
</div>
127+
</CardContent>
128+
</Card>
116129
</div>
117130
);
118-
};
131+
}
132+
133+
function formatLocal(date: Date): string {
134+
return date.toLocaleString();
135+
}
136+
137+
function formatUTC(date: Date): string {
138+
return date.toUTCString();
139+
}
140+
141+
function isValidDate(date: Date): boolean {
142+
return !Number.isNaN(date.getTime());
143+
}
144+
145+
function formatISO8601(date: Date): string {
146+
return date.toISOString();
147+
}
148+
149+
function formatSQL(date: Date): string {
150+
return date.toISOString().slice(0, 19).replace("T", " ");
151+
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Metadata } from "next";
2+
import { ClientOnly } from "../../../../components/ClientOnly/ClientOnly";
23
import { UnixTimeConverter } from "./components/UnixTimeConverter";
34

45
export const metadata: Metadata = {
@@ -7,5 +8,9 @@ export const metadata: Metadata = {
78
};
89

910
export default async function Page() {
10-
return <UnixTimeConverter />;
11+
return (
12+
<ClientOnly ssr={null}>
13+
<UnixTimeConverter />
14+
</ClientOnly>
15+
);
1116
}

0 commit comments

Comments
 (0)