Skip to content

Commit e7fbb42

Browse files
committed
fix: update next-ws to version 2.1.8, enhance login and register page UI, improve logout feedback, and adjust websocket error logging
1 parent 7d1ecd8 commit e7fbb42

File tree

8 files changed

+235
-103
lines changed

8 files changed

+235
-103
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"elysia": "1.4.17",
1919
"jose": "6.1.3",
2020
"next": "16.0.7",
21-
"next-ws": "2.1.7",
21+
"next-ws": "2.1.8",
2222
"react": "19.2.1",
2323
"react-dom": "19.2.1",
2424
"ws": "8.18.3"

src/app/(auth)/login/page.tsx

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,51 +31,89 @@ export default function LoginPage(props: LoginPageProps) {
3131
};
3232

3333
return (
34-
<form onSubmit={onSubmit} className="space-y-2">
35-
<div className="flex flex-col border">
36-
<label htmlFor="username">Username</label>
37-
<input
38-
id="username"
39-
type="text"
40-
value={username}
41-
minLength={authenticationSchema.properties.username.minLength}
42-
maxLength={authenticationSchema.properties.username.maxLength}
43-
onChange={(e) => setUsername(e.target.value)}
44-
required
45-
autoComplete="username"
46-
/>
47-
</div>
48-
<div className="flex flex-col border">
49-
<label htmlFor="password">Password</label>
50-
<input
51-
id="password"
52-
type="password"
53-
minLength={authenticationSchema.properties.password.minLength}
54-
maxLength={authenticationSchema.properties.password.maxLength}
55-
value={password}
56-
onChange={(e) => setPassword(e.target.value)}
57-
required
58-
autoComplete="current-password"
59-
/>
60-
</div>
61-
<div className="flex justify-between">
62-
<button
63-
className="bg-red-600 disabled:bg-gray-600"
64-
type="submit"
65-
disabled={
66-
loginMutation.isPending ||
67-
!safeParse(authenticationChecker, {
68-
username,
69-
password,
70-
}).success
71-
}
72-
>
73-
Login
74-
</button>
34+
<div className="flex min-h-screen items-center justify-center p-8">
35+
<div className="w-full max-w-md space-y-8">
36+
<div className="text-center">
37+
<h1 className="text-3xl font-bold tracking-tight">Login</h1>
38+
<p className="mt-2 text-sm text-gray-400">
39+
Sign in to your account
40+
</p>
41+
</div>
42+
43+
<form onSubmit={onSubmit} className="space-y-6">
44+
<div className="space-y-2">
45+
<label
46+
htmlFor="username"
47+
className="block text-sm font-medium text-gray-300"
48+
>
49+
Username
50+
</label>
51+
<input
52+
id="username"
53+
type="text"
54+
value={username}
55+
minLength={authenticationSchema.properties.username.minLength}
56+
maxLength={authenticationSchema.properties.username.maxLength}
57+
onChange={(e) => setUsername(e.target.value)}
58+
required
59+
autoComplete="username"
60+
className="w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-white placeholder-gray-500 transition-colors focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
61+
placeholder="Enter your username"
62+
/>
63+
</div>
64+
65+
<div className="space-y-2">
66+
<label
67+
htmlFor="password"
68+
className="block text-sm font-medium text-gray-300"
69+
>
70+
Password
71+
</label>
72+
<input
73+
id="password"
74+
type="password"
75+
minLength={authenticationSchema.properties.password.minLength}
76+
maxLength={authenticationSchema.properties.password.maxLength}
77+
value={password}
78+
onChange={(e) => setPassword(e.target.value)}
79+
required
80+
autoComplete="current-password"
81+
className="w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-white placeholder-gray-500 transition-colors focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
82+
placeholder="Enter your password"
83+
/>
84+
</div>
85+
86+
<button
87+
type="submit"
88+
disabled={
89+
loginMutation.isPending ||
90+
!safeParse(authenticationChecker, {
91+
username,
92+
password,
93+
}).success
94+
}
95+
className="w-full rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-950 disabled:cursor-not-allowed disabled:bg-gray-700 disabled:text-gray-400"
96+
>
97+
{loginMutation.isPending ? "Logging in..." : "Login"}
98+
</button>
99+
100+
{status && (
101+
<div className="rounded-lg border border-red-800 bg-red-950/50 px-4 py-3 text-sm text-red-400">
102+
{status}
103+
</div>
104+
)}
75105

76-
<Link href="/register">Register</Link>
106+
<div className="text-center text-sm text-gray-400">
107+
Don't have an account?{" "}
108+
<Link
109+
href="/register"
110+
className="font-medium text-blue-500 hover:text-blue-400 hover:underline"
111+
>
112+
Register
113+
</Link>
114+
</div>
115+
</form>
77116
</div>
78-
{status && <div>{status}</div>}
79-
</form>
117+
</div>
80118
);
81119
}

src/app/(auth)/logout/page.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,15 @@ export default function LogoutPage(props: LogoutPageProps) {
2020
.catch((error) => console.error(error));
2121
}, []);
2222

23-
return <></>;
23+
return (
24+
<div className="flex min-h-screen items-center justify-center p-8">
25+
<div className="text-center">
26+
<div className="mb-4 inline-block h-12 w-12 animate-spin rounded-full border-4 border-gray-700 border-t-blue-500"></div>
27+
<h2 className="text-xl font-semibold text-gray-300">Logging out...</h2>
28+
<p className="mt-2 text-sm text-gray-500">
29+
Please wait while we sign you out
30+
</p>
31+
</div>
32+
</div>
33+
);
2434
}

src/app/(auth)/register/page.tsx

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -29,51 +29,89 @@ export default function RegisterPage() {
2929
};
3030

3131
return (
32-
<form onSubmit={onSubmit} className="space-y-2">
33-
<div className="flex flex-col border">
34-
<label htmlFor="username">Username</label>
35-
<input
36-
id="username"
37-
type="text"
38-
value={username}
39-
onChange={(e) => setUsername(e.target.value)}
40-
minLength={authenticationSchema.properties.username.minLength}
41-
maxLength={authenticationSchema.properties.username.maxLength}
42-
required
43-
autoComplete="username"
44-
/>
45-
</div>
46-
<div className="flex flex-col border">
47-
<label htmlFor="password">Password</label>
48-
<input
49-
id="password"
50-
type="password"
51-
value={password}
52-
minLength={authenticationSchema.properties.password.minLength}
53-
maxLength={authenticationSchema.properties.password.maxLength}
54-
onChange={(e) => setPassword(e.target.value)}
55-
required
56-
autoComplete="current-password"
57-
/>
58-
</div>
59-
<div className="flex justify-between">
60-
<button
61-
className="bg-red-600 disabled:bg-gray-600"
62-
type="submit"
63-
disabled={
64-
registerMutation.isPending ||
65-
!safeParse(authenticationChecker, {
66-
username,
67-
password,
68-
}).success
69-
}
70-
>
71-
Register
72-
</button>
32+
<div className="flex min-h-screen items-center justify-center p-8">
33+
<div className="w-full max-w-md space-y-8">
34+
<div className="text-center">
35+
<h1 className="text-3xl font-bold tracking-tight">Register</h1>
36+
<p className="mt-2 text-sm text-gray-400">
37+
Create a new account
38+
</p>
39+
</div>
40+
41+
<form onSubmit={onSubmit} className="space-y-6">
42+
<div className="space-y-2">
43+
<label
44+
htmlFor="username"
45+
className="block text-sm font-medium text-gray-300"
46+
>
47+
Username
48+
</label>
49+
<input
50+
id="username"
51+
type="text"
52+
value={username}
53+
onChange={(e) => setUsername(e.target.value)}
54+
minLength={authenticationSchema.properties.username.minLength}
55+
maxLength={authenticationSchema.properties.username.maxLength}
56+
required
57+
autoComplete="username"
58+
className="w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-white placeholder-gray-500 transition-colors focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
59+
placeholder="Choose a username"
60+
/>
61+
</div>
62+
63+
<div className="space-y-2">
64+
<label
65+
htmlFor="password"
66+
className="block text-sm font-medium text-gray-300"
67+
>
68+
Password
69+
</label>
70+
<input
71+
id="password"
72+
type="password"
73+
value={password}
74+
minLength={authenticationSchema.properties.password.minLength}
75+
maxLength={authenticationSchema.properties.password.maxLength}
76+
onChange={(e) => setPassword(e.target.value)}
77+
required
78+
autoComplete="current-password"
79+
className="w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-white placeholder-gray-500 transition-colors focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
80+
placeholder="Choose a password"
81+
/>
82+
</div>
83+
84+
<button
85+
type="submit"
86+
disabled={
87+
registerMutation.isPending ||
88+
!safeParse(authenticationChecker, {
89+
username,
90+
password,
91+
}).success
92+
}
93+
className="w-full rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-950 disabled:cursor-not-allowed disabled:bg-gray-700 disabled:text-gray-400"
94+
>
95+
{registerMutation.isPending ? "Creating account..." : "Register"}
96+
</button>
97+
98+
{status && (
99+
<div className="rounded-lg border border-red-800 bg-red-950/50 px-4 py-3 text-sm text-red-400">
100+
{status}
101+
</div>
102+
)}
73103

74-
<Link href="/login">Login</Link>
104+
<div className="text-center text-sm text-gray-400">
105+
Already have an account?{" "}
106+
<Link
107+
href="/login"
108+
className="font-medium text-blue-500 hover:text-blue-400 hover:underline"
109+
>
110+
Login
111+
</Link>
112+
</div>
113+
</form>
75114
</div>
76-
{status && <div>{status}</div>}
77-
</form>
115+
</div>
78116
);
79117
}

src/app/(home)/page.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,34 @@ import Link from "next/link";
22

33
export default function Home() {
44
return (
5-
<main className="flex min-h-screen flex-col items-center p-24">
6-
<Link href="/login">Login</Link>
7-
<Link href="/register">Register</Link>
8-
<Link href="/dashboard">Dashboard</Link>
5+
<main className="flex min-h-screen flex-col items-center justify-center p-8">
6+
<div className="w-full max-w-md space-y-8 text-center">
7+
<div className="space-y-2">
8+
<h1 className="text-4xl font-bold tracking-tight">Welcome</h1>
9+
<p className="text-gray-400">Choose an option to continue</p>
10+
</div>
11+
12+
<div className="flex flex-col gap-3">
13+
<Link
14+
href="/login"
15+
className="rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-950"
16+
>
17+
Login
18+
</Link>
19+
<Link
20+
href="/register"
21+
className="rounded-lg bg-gray-800 px-6 py-3 font-medium text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-950"
22+
>
23+
Register
24+
</Link>
25+
<Link
26+
href="/dashboard"
27+
className="rounded-lg border border-gray-700 px-6 py-3 font-medium text-gray-300 transition-colors hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-950"
28+
>
29+
Dashboard
30+
</Link>
31+
</div>
32+
</div>
933
</main>
1034
);
1135
}

src/app/(main)/dashboard/page.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,35 @@ export default function MainPage(props: MainPageProps) {
1010
const meQuery = useMeQuery();
1111

1212
return (
13-
<main>
14-
<div>id: {meQuery.data?.id}</div>
15-
<div>user: {meQuery.data?.username}</div>
16-
<ChatComponent />
17-
<Link className="bg-red-500" href="/logout">
18-
Logout
19-
</Link>
13+
<main className="min-h-screen p-8">
14+
<div className="mx-auto max-w-4xl">
15+
<div className="mb-8 flex items-center justify-between">
16+
<div>
17+
<h1 className="text-3xl font-bold tracking-tight">Dashboard</h1>
18+
<div className="mt-2 space-y-1 text-sm text-gray-400">
19+
<div>
20+
<span className="font-medium text-gray-500">ID:</span>{" "}
21+
{meQuery.data?.id || "Loading..."}
22+
</div>
23+
<div>
24+
<span className="font-medium text-gray-500">User:</span>{" "}
25+
{meQuery.data?.username || "Loading..."}
26+
</div>
27+
</div>
28+
</div>
29+
<Link
30+
href="/logout"
31+
className="rounded-lg border border-red-800 bg-red-950/50 px-4 py-2 font-medium text-red-400 transition-colors hover:bg-red-900/50 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-gray-950"
32+
>
33+
Logout
34+
</Link>
35+
</div>
36+
37+
<div className="rounded-xl border border-gray-800 bg-gray-900/50 p-6">
38+
<h2 className="mb-4 text-xl font-semibold">Chat</h2>
39+
<ChatComponent />
40+
</div>
41+
</div>
2042
</main>
2143
);
2244
}

src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function RootLayout({
88
}) {
99
return (
1010
<html lang="en">
11-
<body>
11+
<body className="min-h-screen bg-gray-950 text-gray-100 antialiased">
1212
<QueryProvider>{children}</QueryProvider>
1313
</body>
1414
</html>

src/hooks/websocket-hook.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function useWebSocket() {
2929
};
3030

3131
ws.current.onerror = (error) => {
32-
console.error("WebSocket error:", error);
32+
console.warn("WebSocket warn:", error);
3333
setIsConnected(false);
3434
};
3535

0 commit comments

Comments
 (0)