import React, { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import LoadingButton from "@mui/lab/LoadingButton"; import { Alert, Backdrop, Box, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField, Typography, } from "@mui/material"; import { styled } from "@mui/material/styles"; import { useSnackbar } from "notistack"; import Brand from "../components/Brand"; import Content from "../containers/Content"; import { useApi } from "../contexts/ApiContext"; import { useI18n } from "../contexts/I18nContext"; import { LogoType } from "../types"; export interface ResetPasswordPageProps { title?: string; logo?: LogoType; logoHeight?: number | string; } const StyledDialog = styled(Dialog, { name: "BananasResetPasswordDialog", slot: "Root", })(() => ({})); const StyledDialogTitle = styled(DialogTitle, { name: "BananasResetPasswordDialog", slot: "Title", })(({ theme }) => theme.unstable_sx({ bgcolor: "primary.main", color: "primary.contrastText", }), ); const StyledBackdrop = styled(Backdrop, { name: "BananasResetPasswordDialog", slot: "Backdrop", })(({ theme }) => theme.unstable_sx({ bgcolor: "primary.dark", m: 0, p: 2, textAlign: "center", alignItems: "middle", justifyContent: "center", display: "flex", }), ); export const ResetPasswordPage: React.FC = ({ title, logo, logoHeight, }) => { const { enqueueSnackbar } = useSnackbar(); const { t } = useI18n(); const api = useApi(); const [searchParams] = useSearchParams(); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [tokenError, setTokenError] = useState(""); const token = searchParams.get("token"); useEffect(() => { if (!token) { setTokenError(t("No password reset token provided")); } }, [token, t]); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); setError(""); if (!token) { setTokenError(t("No password reset token provided")); return; } if (password.length < 8) { setError(t("Password must be at least 8 characters long")); return; } if (password !== confirmPassword) { setError(t("Passwords do not match")); return; } setLoading(true); try { const response = await api?.operations["user.staff:password-reset-confirm"].call({ body: { token, new_password: password, }, }); if (response?.ok) { const data = await response.json(); enqueueSnackbar(data.message || t("Password reset successfully!"), { variant: "success", }); // User is logged in on the backend after password reset, redirect to dashboard // Use full page navigation to reinitialize the UserContext window.location.href = "/"; } else { const errorData = await response?.json(); if (errorData?.detail && Array.isArray(errorData.detail)) { const tokenErrors = errorData.detail.filter((err: { loc?: string[]; msg?: string }) => err.loc?.includes("token"), ); if (tokenErrors.length > 0) { setTokenError(tokenErrors[0].msg); } else { setError(errorData.detail[0]?.msg || t("Failed to reset password")); } } else { setError(t("Failed to reset password. Please try again.")); } } } catch (error) { console.error("[RESET_PASSWORD]", error); setError(t("An unexpected error occurred. Please try again.")); } finally { setLoading(false); } }; return ( * > *": { width: "100%" } }} > {logo ? ( ) : ( {title} )} {t("Reset Your Password")} {t("Enter your new password below.")} {tokenError && {tokenError}} {error && !tokenError && {error}} {!tokenError && ( <> setPassword(e.target.value)} /> setConfirmPassword(e.target.value)} /> )} {!tokenError && ( {t("Reset Password")} )} ); }; export default ResetPasswordPage;