import { applyValidationErrorCollection } from "@erp/core"; import { formHasAnyDirty, getUserErrorMessage } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; import { useIdentityAuthSession } from "@erp/identity/client"; import { isValidationErrorCollection } from "@repo/rdx-ddd"; import { focusFirstInputFormError, getFirstFormErrorMessage, preventEnterKeySubmitForm, showErrorToast, showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; import { useCallback, useEffect, useId, useMemo, useState, type SubmitEvent } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../i18n"; import { type AccountPasswordForm, AccountPasswordFormSchema, type AccountProfileForm, AccountProfileFormSchema, } from "../entities"; import { useChangeCurrentAccountPasswordMutation, useCurrentAccountQuery, useUpdateCurrentAccountMutation, } from "../shared"; import { buildAccountPageErrorViewModel, buildAccountProfileForm, buildUpdateAccountProfileParams, } from "../utils"; const EMPTY_PROFILE_FORM: AccountProfileForm = { name: "", email: "", avatarUrl: "", languageCode: "es", }; const EMPTY_PASSWORD_FORM: AccountPasswordForm = { currentPassword: "", newPassword: "", confirmPassword: "", }; export const useAccountPageController = () => { const { t } = useTranslation(); const { account: sessionAccount, logout, updateAccount } = useIdentityAuthSession(); const profileFormId = useId(); const passwordFormId = useId(); const [passwordSuccessVersion, setPasswordSuccessVersion] = useState(0); const accountQuery = useCurrentAccountQuery(); const updateProfileMutation = useUpdateCurrentAccountMutation(); const changePasswordMutation = useChangeCurrentAccountPasswordMutation(); const profileInitialValues = useMemo(() => { if (!accountQuery.data) { return EMPTY_PROFILE_FORM; } return buildAccountProfileForm(accountQuery.data); }, [accountQuery.data]); const profileForm = useHookForm({ resolverSchema: AccountProfileFormSchema, initialValues: profileInitialValues, disabled: updateProfileMutation.isPending, }); const passwordForm = useHookForm({ resolverSchema: AccountPasswordFormSchema, initialValues: EMPTY_PASSWORD_FORM, disabled: changePasswordMutation.isPending, }); useEffect(() => { if (!accountQuery.data || profileForm.formState.isDirty) { return; } profileForm.reset(buildAccountProfileForm(accountQuery.data), { keepDirty: false, }); }, [accountQuery.data, profileForm, profileForm.formState.isDirty]); const pageError = useMemo(() => { if (!accountQuery.error) { return null; } return buildAccountPageErrorViewModel(accountQuery.error, t); }, [accountQuery.error, t]); const inlineProfileError = useMemo(() => { if (!updateProfileMutation.error) { return null; } const userMessage = getUserErrorMessage(updateProfileMutation.error); return userMessage; }, [updateProfileMutation.error]); const inlinePasswordError = useMemo(() => { if (!changePasswordMutation.error) { return null; } const userMessage = getUserErrorMessage(changePasswordMutation.error); return userMessage; }, [changePasswordMutation.error]); const resetProfileForm = useCallback(() => { if (!accountQuery.data) { return; } profileForm.reset(buildAccountProfileForm(accountQuery.data), { keepDirty: false, }); }, [accountQuery.data, profileForm]); const resetPasswordForm = useCallback(() => { passwordForm.reset(EMPTY_PASSWORD_FORM, { keepDirty: false, }); }, [passwordForm]); const clearPasswordDialogState = useCallback(() => { passwordForm.reset(EMPTY_PASSWORD_FORM, { keepDirty: false, keepErrors: false, keepTouched: false, keepIsSubmitted: false, keepSubmitCount: false, }); passwordForm.clearErrors(); changePasswordMutation.reset(); }, [changePasswordMutation, passwordForm]); const profileSubmitHandler = profileForm.handleSubmit( async (formData) => { if (!formHasAnyDirty(profileForm.formState.dirtyFields)) { showWarningToast( t("account.feedback.noProfileChanges.title"), t("account.feedback.noProfileChanges.message") ); return; } try { const updated = await updateProfileMutation.mutateAsync( buildUpdateAccountProfileParams({ form: formData, dirtyFields: profileForm.formState.dirtyFields, }) ); updateAccount({ id: updated.id, email: updated.email, name: updated.name, avatarUrl: updated.avatarUrl, languageCode: updated.languageCode, }); profileForm.reset(buildAccountProfileForm(updated), { keepDirty: false, }); showSuccessToast( t("account.feedback.profileSuccess.title"), t("account.feedback.profileSuccess.message") ); } catch (error: unknown) { if (isValidationErrorCollection(error)) { applyValidationErrorCollection(profileForm, error); focusFirstInputFormError(profileForm.formState.errors, profileForm); showWarningToast( t("account.feedback.validation.title"), t("account.feedback.validation.message") ); return; } const userMessage = getUserErrorMessage(error); showErrorToast(userMessage.title, userMessage.description); } }, (errors: FieldErrors) => { focusFirstInputFormError(errors, profileForm); showWarningToast( t("account.feedback.validation.title"), getFirstFormErrorMessage(errors, profileForm) ?? t("account.feedback.validation.message") ); } ); const passwordSubmitHandler = passwordForm.handleSubmit( async (formData) => { if (!formHasAnyDirty(passwordForm.formState.dirtyFields)) { showWarningToast( t("account.feedback.noPasswordChanges.title"), t("account.feedback.noPasswordChanges.message") ); return; } try { await changePasswordMutation.mutateAsync({ current_password: formData.currentPassword, new_password: formData.newPassword, }); passwordForm.reset(EMPTY_PASSWORD_FORM, { keepDirty: false, }); setPasswordSuccessVersion((value) => value + 1); showSuccessToast( t("account.feedback.passwordSuccess.title"), t("account.feedback.passwordSuccess.message") ); } catch (error: unknown) { if (isValidationErrorCollection(error)) { applyValidationErrorCollection(passwordForm, error); focusFirstInputFormError(passwordForm.formState.errors, passwordForm); showWarningToast( t("account.feedback.validation.title"), t("account.feedback.validation.message") ); return; } const userMessage = getUserErrorMessage(error); showErrorToast(userMessage.title, userMessage.description); } }, (errors: FieldErrors) => { focusFirstInputFormError(errors, passwordForm); showWarningToast( t("account.feedback.validation.title"), getFirstFormErrorMessage(errors, passwordForm) ?? t("account.feedback.validation.message") ); } ); const onProfileSubmit = (event: SubmitEvent) => { event.stopPropagation(); profileSubmitHandler(event); }; const onPasswordSubmit = (event: SubmitEvent) => { event.stopPropagation(); passwordSubmitHandler(event); }; const retry = useCallback(async () => { await accountQuery.refetch(); }, [accountQuery]); const handleLogout = useCallback(() => { logout() .then(() => { showSuccessToast( t("account.feedback.logoutSuccess.title"), t("account.feedback.logoutSuccess.message") ); }) .catch((error: unknown) => { const userMessage = getUserErrorMessage(error); showErrorToast(userMessage.title, userMessage.description); }); }, [logout, t]); return { account: accountQuery.data, sessionAccount, profileForm, profileFormId, passwordForm, passwordFormId, pageError, inlineProfileError, inlinePasswordError, isInitialLoading: accountQuery.isLoading, isRetrying: accountQuery.isFetching && !accountQuery.isLoading, isSavingProfile: updateProfileMutation.isPending, isChangingPassword: changePasswordMutation.isPending, onFormKeyDown: preventEnterKeySubmitForm, onProfileSubmit, onPasswordSubmit, retry, resetProfileForm, resetPasswordForm, clearPasswordDialogState, passwordSuccessVersion, handleLogout, }; };