Files
hope-events/frontend/src/components/auth/LoginForm.tsx
T
joshua 3d381944d2 Initial commit
Next.js + Express event management app for Hope Family Church.
2026-07-23 15:26:47 +02:00

149 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useSearchParams } from "next/navigation";
import Link from "next/link";
export function LoginForm() {
const { login } = useAuth();
const searchParams = useSearchParams();
const raw = searchParams.get("redirect") || "";
const hasExplicitRedirect = raw.startsWith("/") && !raw.startsWith("//");
const redirectTo = hasExplicitRedirect ? raw : "/dashboard";
const registerHref = redirectTo !== "/dashboard" ? `/register?redirect=${encodeURIComponent(redirectTo)}` : "/register";
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const getErrorMessage = (err: any) => {
if (!err) return "Login failed";
// If it's already a normal Error
if (err.message && typeof err.message === "string") {
// Handle case where message is accidentally JSON string
try {
const parsed = JSON.parse(err.message);
return parsed.message || err.message;
} catch {
return err.message;
}
}
// If API response object was thrown directly
if (err.message) return err.message;
return "Login failed";
};
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setLoading(true);
try {
const loggedInUser = await login(email, password);
// Skip the generic /dashboard hop (which just redirects again once the role is known)
// and go straight to the role-specific dashboard, unless the caller asked for a specific
// page back (e.g. returning to a registration flow after login).
const target = hasExplicitRedirect ? redirectTo : `/dashboard/${loggedInUser.role || "user"}`;
window.location.href = target;
} catch (err: any) {
setError(getErrorMessage(err) || "Login failed. Please try again.");
} finally {
setLoading(false);
}
};
return (
<form onSubmit={onSubmit} className="space-y-4 max-w-sm w-full">
<div>
<label className="block text-sm font-medium mb-1">Email or phone</label>
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full border rounded px-3 py-2"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Password</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full border rounded px-3 py-2 pr-20"
required
/>
<button
type="button"
onClick={() => setShowPassword((s) => !s)}
className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-blue-600"
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? "Hide" : "Show"}
</button>
</div>
<div className="mt-1">
<a
href="/forgot-password"
className="text-xs text-blue-600 hover:underline"
>
Forgot your password?
</a>
</div>
<div className="mt-1">
<a
href={registerHref}
className="text-xs text-blue-600 hover:underline"
>
Don't have an account? Sign up here.
</a>
</div>
</div>
{error && (
<div className="text-sm text-red-600">
<p>{error}</p>
{error.toLowerCase().includes('not yet active') && (
<p className="mt-1 text-gray-600">Check your inbox for an activation email. If you didn&apos;t receive it, try logging in again to resend it.</p>
)}
</div>
)}
<button
type="submit"
disabled={loading}
className="w-full bg-blue-600 text-white rounded py-2 disabled:opacity-60"
>
{loading ? "Logging in..." : "Login"}
</button>
<p className="text-xs text-gray-500 text-center mt-3">
By signing in, you acknowledge that youve read and agree to our{" "}
<Link
href="/legal/terms"
target="_blank"
className="text-blue-600 hover:underline"
>
Terms of Use
</Link>{" "}
and{" "}
<Link
href="/legal/privacy"
target="_blank"
className="text-blue-600 hover:underline"
>
Privacy Policy
</Link>.
</p>
</form>
);
}