diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index 6bda595..67431b5 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useState, useEffect } from "react";
+import { useState, useEffect, useRef } from "react";
import { signInWithPopup, GoogleAuthProvider, signInWithEmailAndPassword, createUserWithEmailAndPassword, sendPasswordResetEmail } from "firebase/auth";
import { auth } from "@/lib/firebase";
import { logError, logWarn } from "@/lib/logger";
@@ -11,9 +11,19 @@ import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/
import { useRouter } from "next/navigation";
import { useToast } from "@/hooks/use-toast";
import { ThemeToggle } from "@/components/theme-toggle";
+import { Skeleton } from "@/components/ui/skeleton";
export default function LoginPage() {
- const [loading, setLoading] = useState(false);
+ // Which sign-in path is in flight — the pressed button is the one that
+ // shows progress; the other just disables. (#280)
+ const [loadingKind, setLoadingKind] = useState<"email" | "google" | null>(
+ null,
+ );
+ const loading = loadingKind !== null;
+ // Synchronous in-flight guard — `disabled` only applies after React
+ // re-renders, so a same-tick second activation must bail here.
+ const inFlight = useRef(false);
+ const [resetting, setResetting] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [mounted, setMounted] = useState(false);
@@ -46,14 +56,16 @@ export default function LoginPage() {
return (
);
}
const handleGoogleSignIn = async () => {
- setLoading(true);
+ if (inFlight.current) return;
+ inFlight.current = true;
+ setLoadingKind("google");
try {
const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
@@ -96,13 +108,16 @@ export default function LoginPage() {
variant: "destructive",
});
} finally {
- setLoading(false);
+ inFlight.current = false;
+ setLoadingKind(null);
}
};
const handleEmailSignIn = async (e: React.FormEvent) => {
e.preventDefault();
- setLoading(true);
+ if (inFlight.current) return;
+ inFlight.current = true;
+ setLoadingKind("email");
try {
const result = await signInWithEmailAndPassword(auth, email, password);
@@ -145,13 +160,16 @@ export default function LoginPage() {
variant: "destructive",
});
} finally {
- setLoading(false);
+ inFlight.current = false;
+ setLoadingKind(null);
}
};
const handleEmailSignUp = async (e: React.FormEvent) => {
e.preventDefault();
- setLoading(true);
+ if (inFlight.current) return;
+ inFlight.current = true;
+ setLoadingKind("email");
try {
const result = await createUserWithEmailAndPassword(auth, email, password);
@@ -178,7 +196,8 @@ export default function LoginPage() {
variant: "destructive",
});
} finally {
- setLoading(false);
+ inFlight.current = false;
+ setLoadingKind(null);
}
};
@@ -200,6 +219,7 @@ export default function LoginPage() {
return;
}
+ setResetting(true);
try {
await sendPasswordResetEmail(auth, email);
toast({
@@ -215,6 +235,8 @@ export default function LoginPage() {
: "Failed to send password reset email. Please try again.",
variant: "destructive",
});
+ } finally {
+ setResetting(false);
}
};
@@ -282,6 +304,8 @@ export default function LoginPage() {
variant="link"
size="sm"
className="h-auto p-0 text-xs"
+ loading={resetting}
+ loadingText="Sending reset email…"
onClick={handleForgotPassword}
>
Forgot your password?
@@ -291,11 +315,13 @@ export default function LoginPage() {
@@ -312,12 +338,14 @@ export default function LoginPage() {
diff --git a/src/components/animal-adoptions/animal-adoptions-page.tsx b/src/components/animal-adoptions/animal-adoptions-page.tsx
index 683e7f4..1187757 100644
--- a/src/components/animal-adoptions/animal-adoptions-page.tsx
+++ b/src/components/animal-adoptions/animal-adoptions-page.tsx
@@ -155,7 +155,7 @@ export function AnimalAdoptions({
transition={reduceMotion ? instantTransition : { duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
>
-
+
{animal.name}
diff --git a/src/components/animal-registration/animal-registration-page.tsx b/src/components/animal-registration/animal-registration-page.tsx
index b31a5cb..8203ef8 100644
--- a/src/components/animal-registration/animal-registration-page.tsx
+++ b/src/components/animal-registration/animal-registration-page.tsx
@@ -536,8 +536,13 @@ export function AnimalRegistration({
-