mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 04:33:11 +00:00
Adds a My Account page, reachable from the sidebar, with the signed-in user's read-only details and a Change Password section. The new password is checked live against the shared password rules (6+ characters, an uppercase letter, a number and a special character), which the invite registration flow reuses. The current password is still verified by the server first, and its rejection is shown on the current password field.
132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { useForm, useWatch } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { Box } from "@mui/material";
|
|
import { KeyRound } from "lucide-react";
|
|
|
|
import { PasswordChecklist } from "@/components/auth/password-checklist";
|
|
import { Button } from "@/components/ui/button";
|
|
import { FormField } from "@/components/ui/form-field";
|
|
import { PasswordField } from "@/components/ui/password-field";
|
|
import { Text } from "@/components/ui/text";
|
|
import {
|
|
CURRENT_PASSWORD_INCORRECT_MESSAGE,
|
|
changePasswordSchema,
|
|
type ChangePasswordFormValues,
|
|
} from "@/domain/auth/schemas/change-password-schema";
|
|
import { useChangePassword } from "@/domain/auth/use-cases/use-change-password";
|
|
|
|
const EMPTY_VALUES: ChangePasswordFormValues = {
|
|
currentPassword: "",
|
|
newPassword: "",
|
|
confirmPassword: "",
|
|
};
|
|
|
|
export function ChangePasswordSection() {
|
|
const changePassword = useChangePassword();
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
control,
|
|
reset,
|
|
setError,
|
|
formState: { errors },
|
|
} = useForm<ChangePasswordFormValues>({
|
|
resolver: zodResolver(changePasswordSchema),
|
|
defaultValues: EMPTY_VALUES,
|
|
mode: "onTouched",
|
|
});
|
|
|
|
const values = useWatch({ control });
|
|
const newPassword = values.newPassword ?? "";
|
|
const canSubmit = changePasswordSchema.safeParse(values).success;
|
|
const serverError = changePassword.error?.message;
|
|
const showServerError =
|
|
Boolean(serverError) && serverError !== CURRENT_PASSWORD_INCORRECT_MESSAGE;
|
|
|
|
const onSubmit = (form: ChangePasswordFormValues) => {
|
|
changePassword.mutate(
|
|
{
|
|
Currentpassword: form.currentPassword,
|
|
Newpassword: form.newPassword,
|
|
Confirmpassword: form.confirmPassword,
|
|
},
|
|
{
|
|
onSuccess: () => reset(EMPTY_VALUES),
|
|
onError: (error) => {
|
|
if (error.message === CURRENT_PASSWORD_INCORRECT_MESSAGE) {
|
|
setError("currentPassword", { message: CURRENT_PASSWORD_INCORRECT_MESSAGE });
|
|
}
|
|
},
|
|
},
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
component="form"
|
|
aria-labelledby="change-password-heading"
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
className="grid gap-4 rounded-lg border border-(--color-border) bg-(--color-bg-surface) p-5"
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<KeyRound size={15} aria-hidden="true" className="text-primary" />
|
|
<Text id="change-password-heading" variant="heading" className="m-0 text-[15px]!">
|
|
Change Password
|
|
</Text>
|
|
</div>
|
|
|
|
<FormField
|
|
label="Current password"
|
|
htmlFor="account-current-password"
|
|
required
|
|
error={Boolean(errors.currentPassword)}
|
|
helperText={errors.currentPassword?.message}
|
|
>
|
|
<PasswordField
|
|
{...register("currentPassword")}
|
|
id="account-current-password"
|
|
autoComplete="current-password"
|
|
error={Boolean(errors.currentPassword)}
|
|
/>
|
|
</FormField>
|
|
|
|
<FormField label="New password" htmlFor="account-new-password" required>
|
|
<PasswordField
|
|
{...register("newPassword")}
|
|
id="account-new-password"
|
|
autoComplete="new-password"
|
|
slotProps={{ htmlInput: { "aria-describedby": "account-password-rules" } }}
|
|
/>
|
|
<PasswordChecklist id="account-password-rules" password={newPassword} />
|
|
</FormField>
|
|
|
|
<FormField
|
|
label="Confirm new password"
|
|
htmlFor="account-confirm-password"
|
|
required
|
|
error={Boolean(errors.confirmPassword)}
|
|
helperText={errors.confirmPassword?.message}
|
|
>
|
|
<PasswordField
|
|
{...register("confirmPassword")}
|
|
id="account-confirm-password"
|
|
autoComplete="new-password"
|
|
error={Boolean(errors.confirmPassword)}
|
|
/>
|
|
</FormField>
|
|
|
|
{showServerError && <Text variant="error">{serverError}</Text>}
|
|
|
|
<div className="flex justify-end">
|
|
<Button
|
|
type="submit"
|
|
disabled={!canSubmit}
|
|
loading={changePassword.isPending}
|
|
loadingText="Updating…"
|
|
>
|
|
Update password
|
|
</Button>
|
|
</div>
|
|
</Box>
|
|
);
|
|
}
|