Files
jobtrackingapp/job-tracker-ui/src/components/GoogleAuthCard.tsx
T
2026-03-21 11:55:27 +01:00

109 lines
3.2 KiB
TypeScript

import React, { useEffect, useMemo, useState } from "react";
import { Box, Button, Paper, Typography } from "@mui/material";
import { clearAuthToken, decodeJwtPayload, getAuthToken, setAuthToken } from "../auth";
import { useToast } from "../toast";
declare global {
interface Window {
google?: any;
}
}
function loadGoogleScript(): Promise<void> {
return new Promise((resolve, reject) => {
if (window.google?.accounts?.id) return resolve();
const existing = document.getElementById("google-gsi");
if (existing) {
existing.addEventListener("load", () => resolve());
return;
}
const s = document.createElement("script");
s.id = "google-gsi";
s.src = "https://accounts.google.com/gsi/client";
s.async = true;
s.defer = true;
s.onload = () => resolve();
s.onerror = () => reject(new Error("Failed to load Google script"));
document.head.appendChild(s);
});
}
export default function GoogleAuthCard({ onSignedIn }: { onSignedIn?: () => void }) {
const { toast } = useToast();
const [token, setToken] = useState<string | null>(() => getAuthToken());
const clientId = (process.env.REACT_APP_GOOGLE_CLIENT_ID || "").trim();
const payload = useMemo(() => (token ? decodeJwtPayload(token) : null), [token]);
const email = payload?.email as string | undefined;
useEffect(() => {
if (!clientId) return;
void loadGoogleScript()
.then(() => {
if (!window.google?.accounts?.id) return;
window.google.accounts.id.initialize({
client_id: clientId,
callback: (resp: any) => {
if (resp?.credential) {
setAuthToken(resp.credential);
setToken(resp.credential);
toast("Signed in.", "success");
onSignedIn?.();
}
},
});
window.google.accounts.id.renderButton(document.getElementById("gsi-btn"), {
theme: "outline",
size: "large",
type: "standard",
shape: "pill",
});
})
.catch(() => toast("Google auth script failed to load.", "error"));
}, [clientId, onSignedIn, toast]);
return (
<Paper sx={{ mt: 2, p: 2 }}>
<Typography variant="h6" sx={{ mb: 1 }}>
Authentication (Google)
</Typography>
{!clientId && (
<Typography sx={{ color: "text.secondary" }}>
Set `REACT_APP_GOOGLE_CLIENT_ID` in your UI environment to enable sign-in.
</Typography>
)}
{clientId && (
<Box sx={{ display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap" }}>
<div id="gsi-btn" />
{token ? (
<>
<Typography sx={{ color: "text.secondary" }}>
Signed in{email ? ` as ${email}` : ""}.
</Typography>
<Button
variant="outlined"
onClick={() => {
clearAuthToken();
setToken(null);
toast("Signed out.", "info");
}}
>
Sign out
</Button>
</>
) : (
<Typography sx={{ color: "text.secondary" }}>
Sign in to unlock API access.
</Typography>
)}
</Box>
)}
</Paper>
);
}