Quran_Tech_Server / F_Pro /src /components /auth /GoogleAuth.tsx
aboalaa147's picture
Initial deployment
eb6a2f9
Raw
History Blame Contribute Delete
6.26 kB
import { useState, useEffect, useCallback, CSSProperties } from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "../../lib/authContext";
type Role = "STUDENT" | "SHEIKH";
type StatusType = "waiting" | "success" | "error";
interface Status {
text: string;
type: StatusType;
}
declare global {
interface Window {
google: {
accounts: {
id: {
initialize: (config: {
client_id: string;
callback: (response: { credential: string }) => void;
}) => void;
renderButton: (
element: HTMLElement | null,
options: object
) => void;
};
};
};
}
}
const CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID as string;
const STATUS_COLORS: Record<StatusType, { bg: string; color: string }> = {
waiting: { bg: "#fff3cd", color: "#856404" },
success: { bg: "#d4edda", color: "#155724" },
error: { bg: "#f8d7da", color: "#721c24" },
};
export default function GoogleAuth() {
const navigate = useNavigate();
const { signInWithGoogle } = useAuth();
const [selectedRole, setSelectedRole] = useState<Role>("STUDENT");
const [status, setStatus] = useState<Status>({ text: "⏳ Waiting for sign-in", type: "waiting" });
const handleCredentialResponse = useCallback(
async (googleResponse: { credential: string }): Promise<void> => {
const idToken = googleResponse.credential;
setStatus({ text: "⏳ Signing in...", type: "waiting" });
try {
const user = await signInWithGoogle(idToken, selectedRole);
setStatus({ text: "βœ… Success! Redirecting...", type: "success" });
// Navigate based on role and profile completion status
const role = user.role.toLowerCase();
if (role === "admin") {
navigate("/admin/dashboard", { replace: true });
} else if (role === "sheikh") {
const approvalStatus = user.sheikhApprovalStatus?.toUpperCase();
if (approvalStatus === "REJECTED") {
navigate("/sheikh/dashboard", { replace: true });
}
else if (!user.profileCompleted) {
navigate("/complete-profile/sheikh", { replace: true });
} else {
const approvalStatus = user.sheikhApprovalStatus?.toUpperCase();
if (approvalStatus === "APPROVED") {
navigate("/sheikh/dashboard", { replace: true });
} else {
navigate("/sheikh/interview", { replace: true });
}
}
} else {
// student
if (!user.profileCompleted) {
navigate("/complete-profile/student", { replace: true });
} else {
navigate("/student/dashboard", { replace: true });
}
}
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Sign-in failed";
setStatus({ text: `❌ ${message}`, type: "error" });
}
},
[selectedRole, signInWithGoogle, navigate]
);
useEffect(() => {
const script = document.createElement("script");
script.src = "https://accounts.google.com/gsi/client";
script.async = true;
script.defer = true;
script.onload = () => {
window.google.accounts.id.initialize({
client_id: CLIENT_ID,
callback: handleCredentialResponse,
});
window.google.accounts.id.renderButton(
document.getElementById("googleBtn"),
{ theme: "outline", size: "large", text: "sign_in_with", shape: "rectangular", width: 300 }
);
};
document.body.appendChild(script);
return () => { document.body.removeChild(script); };
}, [handleCredentialResponse]);
return (
<div style={styles.page}>
<div style={styles.container}>
<div style={styles.header}>
<div style={styles.icon}>πŸ“–</div>
<h1 style={styles.h1}>Quran App β€” Sign In</h1>
<p style={styles.subtitle}>Sign in with Google to continue</p>
</div>
<div style={styles.roleSelect}>
{(["STUDENT", "SHEIKH"] as Role[]).map((role) => (
<button
key={role}
onClick={() => setSelectedRole(role)}
style={{ ...styles.roleBtn, ...(selectedRole === role ? styles.roleBtnActive : {}) }}
>
{role === "STUDENT" ? "πŸŽ“ Student" : "πŸ“š Sheikh"}
</button>
))}
</div>
<div style={styles.signinSection}>
<p style={styles.signinHint}>Click the button below to sign in with Google</p>
<div
style={{
...styles.statusBadge,
background: STATUS_COLORS[status.type].bg,
color: STATUS_COLORS[status.type].color,
}}
>
{status.text}
</div>
<div id="googleBtn" style={{ display: "flex", justifyContent: "center" }} />
</div>
</div>
</div>
);
}
const styles: Record<string, CSSProperties> = {
page: { fontFamily: "'Segoe UI', sans-serif", background: "#f0f4f8", minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center" },
container: { background: "white", borderRadius: 16, padding: 40, width: 600, boxShadow: "0 4px 24px rgba(0,0,0,0.10)" },
header: { textAlign: "center", marginBottom: 32 },
icon: { fontSize: 48, marginBottom: 12 },
h1: { color: "#1a6b3c", fontSize: 24, marginBottom: 6 },
subtitle: { color: "#777", fontSize: 14 },
roleSelect: { display: "flex", gap: 12, marginBottom: 24 },
roleBtn: { flex: 1, padding: 10, border: "2px solid #ddd", borderRadius: 8, background: "white", cursor: "pointer", fontSize: 14, fontWeight: 600, color: "#555" },
roleBtnActive: { borderColor: "#1a6b3c", background: "#e8f5e9", color: "#1a6b3c" },
signinSection: { textAlign: "center", marginBottom: 24 },
signinHint: { color: "#888", fontSize: 13, marginBottom: 16 },
statusBadge: { display: "inline-block", padding: "6px 16px", borderRadius: 20, fontSize: 12, fontWeight: 600, marginBottom: 16 },
infoRow: { display: "flex", justifyContent: "flex-end", fontSize: 12, color: "#888", marginTop: 16 },
};