This repository has been archived on 2026-07-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
cloud-reader/frontend/src/pages/AuthPage.tsx
T
crisleo94 6b4c0c43f8 feat: uv config other feats
- add uv configuration for the backend
- update frontend to make auth work
- add new auth endpoints
- add bookmars feat
- add reader feat
2026-06-03 22:06:01 -05:00

61 lines
4.5 KiB
TypeScript

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18n-lite";
import { useAuth } from "../context/AuthContext";
import { getApiErrorMessage } from "../api/errors";
function AuthForm({ isLogin, onToggle }: { isLogin: boolean; onToggle: () => void }) {
const { t } = useTranslation();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const { login, register } = useAuth();
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); setError(null);
if (!isLogin && password !== confirmPassword) { setError(t("auth.passwordsMismatch")); return; }
setLoading(true);
try {
if (isLogin) await login(email, password);
else await register(email, password);
navigate("/");
} catch (err) { setError(getApiErrorMessage(err, isLogin ? t("auth.loginFailed") : t("auth.registrationFailed"))); }
finally { setLoading(false); }
};
return (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", minHeight: "100vh", background: "#f8f9fa", padding: 16 }}>
<div style={{ width: "100%", maxWidth: 400, background: "#fff", borderRadius: 12, padding: 32, boxShadow: "0 2px 16px rgba(0,0,0,0.08)" }}>
<h1 style={{ fontSize: 28, fontWeight: 700, color: "#1a1a2e", textAlign: "center", marginBottom: 4 }}>{t("common.appName")}</h1>
<h2 style={{ fontSize: 16, color: "#888", textAlign: "center", marginBottom: 24, fontWeight: 400 }}>{isLogin ? t("auth.signIn") : t("auth.createAccount")}</h2>
<form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: 16 }}>
{error && <div style={{ background: "#fde8e8", padding: 12, borderRadius: 6, color: "#e74c3c", fontSize: 14 }}>{error}</div>}
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>{t("auth.email")}</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder={t("auth.emailPlaceholder")} required style={{ padding: "10px 12px", borderRadius: 6, border: "1px solid #ddd", fontSize: 16, outline: "none" }} />
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>{t("auth.password")}</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder={t("auth.passwordPlaceholder")} required minLength={8} style={{ padding: "10px 12px", borderRadius: 6, border: "1px solid #ddd", fontSize: 16, outline: "none" }} />
</div>
{!isLogin && <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
<label style={{ fontSize: 14, fontWeight: 600, color: "#555" }}>{t("auth.confirmPassword")}</label>
<input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} placeholder={t("auth.confirmPasswordPlaceholder")} required minLength={8} style={{ padding: "10px 12px", borderRadius: 6, border: "1px solid #ddd", fontSize: 16, outline: "none" }} />
</div>}
<button type="submit" disabled={loading} style={{ padding: "12px 24px", borderRadius: 8, border: "none", background: "#1a1a2e", color: "#fff", fontSize: 16, fontWeight: 600, cursor: "pointer", opacity: loading ? 0.6 : 1, marginTop: 8 }}>{loading ? (isLogin ? t("auth.signingIn") : t("auth.creatingAccount")) : (isLogin ? t("auth.signIn") : t("auth.createAccount"))}</button>
</form>
<p style={{ textAlign: "center", marginTop: 20, color: "#888", fontSize: 14 }}>
{isLogin ? t("auth.noAccount") + " " : t("auth.hasAccount") + " "}
<button onClick={onToggle} style={{ background: "none", border: "none", color: "#1a1a2e", fontWeight: 600, cursor: "pointer", fontSize: 14, textDecoration: "underline" }}>{isLogin ? t("auth.register") : t("auth.signIn")}</button>
</p>
</div>
</div>
);
}
export function LoginPage({ onToggle }: { onToggle: () => void }) { return <AuthForm isLogin={true} onToggle={onToggle} />; }
export function RegisterPage({ onToggle }: { onToggle: () => void }) { return <AuthForm isLogin={false} onToggle={onToggle} />; }