Implement user authentication and account pages

Adds AuthContext with Orval API integration, user login/logout/register flows, and account settings page. Refactors navigation and layouts to use authentication context. Introduces new account-related pages (Login, Register, Logout, AccountSettings), updates PrivateRoute to use AuthContext, and improves error handling in Downloader. Removes obsolete context example and adds comprehensive usage documentation for AuthContext.
This commit is contained in:
2026-01-05 11:53:44 +01:00
parent f7605812c1
commit 4f56f4bbc5
15 changed files with 1030 additions and 163 deletions

View File

@@ -0,0 +1,108 @@
import { useState } from "react";
import { useAuth } from "@/context/AuthContext";
import { useNavigate, Link } from "react-router-dom";
import { FaEnvelope, FaLock, FaSpinner } from "react-icons/fa";
export default function LoginPage() {
const { login, isLoading } = useAuth();
const navigate = useNavigate();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError("");
if (!username.trim() || !password.trim()) {
setError("Vyplňte prosím všechna pole");
return;
}
try {
await login({ username, password });
navigate("/");
} catch (err: any) {
const errorMessage = err.response?.data?.error || err.message;
setError(errorMessage);
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 p-4">
<div className="bg-white rounded-lg shadow-xl p-8 w-full max-w-md">
<h1 className="text-3xl font-bold text-center mb-2 text-gray-800">Přihlášení</h1>
<p className="text-center text-gray-600 mb-8">Vítejte zpět na vontor.cz</p>
{error && (
<div className="mb-6 p-4 bg-red-50 border border-red-200 text-red-700 rounded-lg">
<strong>Chyba:</strong> {error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label className="block mb-2 font-medium text-gray-700 flex items-center gap-2">
<FaEnvelope className="text-gray-500" />
Email nebo uživatelské jméno
</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="email@example.com nebo username"
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
disabled={isLoading}
required
/>
</div>
<div>
<label className="block mb-2 font-medium text-gray-700 flex items-center gap-2">
<FaLock className="text-gray-500" />
Heslo
</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
disabled={isLoading}
required
/>
</div>
<div className="flex items-center justify-between text-sm">
<Link to="/password-reset" className="text-blue-600 hover:text-blue-800 hover:underline">
Zapomenuté heslo?
</Link>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition flex items-center justify-center gap-2"
>
{isLoading ? (
<>
<FaSpinner className="animate-spin" />
Přihlašování...
</>
) : (
"Přihlásit se"
)}
</button>
</form>
<div className="mt-6 text-center text-gray-600">
Ještě nemáte účet?{" "}
<Link to="/register" className="text-blue-600 hover:text-blue-800 font-semibold hover:underline">
Zaregistrujte se
</Link>
</div>
</div>
</div>
);
}