generated from nhcarrigan/template
a3daed1683
Sets up the full monorepo with pnpm workspaces. Includes shared types package, Hono API with Discord OAuth/JWT auth, Prisma v6 + MongoDB Atlas, and React + Vite frontend with game loop, five tabs, and Discord-linked save/load.
88 lines
2.2 KiB
TypeScript
88 lines
2.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { getAuthUrl, handleAuthCallback } from "../../api/client.js";
|
||
|
||
interface LoginPageProps {
|
||
onLogin: () => void;
|
||
}
|
||
|
||
export const LoginPage = ({ onLogin }: LoginPageProps): React.JSX.Element => {
|
||
const [authUrl, setAuthUrl] = useState<string | null>(null);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
// Handle OAuth callback
|
||
const params = new URLSearchParams(window.location.search);
|
||
const code = params.get("code");
|
||
|
||
if (code) {
|
||
setIsLoading(true);
|
||
handleAuthCallback(code)
|
||
.then(() => {
|
||
window.history.replaceState({}, "", "/");
|
||
onLogin();
|
||
})
|
||
.catch((err: unknown) => {
|
||
setError(err instanceof Error ? err.message : "Authentication failed");
|
||
setIsLoading(false);
|
||
});
|
||
return;
|
||
}
|
||
|
||
// Fetch the Discord OAuth URL
|
||
getAuthUrl()
|
||
.then((url) => {
|
||
setAuthUrl(url);
|
||
setIsLoading(false);
|
||
})
|
||
.catch(() => {
|
||
setError("Failed to load authentication URL");
|
||
setIsLoading(false);
|
||
});
|
||
}, [onLogin]);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-card">
|
||
<p>Loading...</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (error) {
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-card">
|
||
<p className="error">{error}</p>
|
||
<button
|
||
type="button"
|
||
onClick={() => { window.location.reload(); }}
|
||
>
|
||
Try Again
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-card">
|
||
<h1>⚔️ Elysium</h1>
|
||
<p>An idle fantasy RPG. Hire adventurers, defeat bosses, and ascend to glory.</p>
|
||
<a
|
||
className="discord-login-button"
|
||
href={authUrl ?? "#"}
|
||
>
|
||
Login with Discord
|
||
</a>
|
||
<p className="login-note">
|
||
Your progress is saved to your Discord account and shareable with others!
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|