diff --git a/package.json b/package.json index 5f4e1a8c0..f7845561a 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "react-select": "5.8.0", "remeda": "2.5.0", "sharp": "0.33.4", + "sonner": "1.5.0", "superjson": "2.2.1", "trpc-openapi": "1.2.0", "ts-pattern": "5.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82c2d665f..c16fb57b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,6 +164,9 @@ importers: sharp: specifier: 0.33.4 version: 0.33.4 + sonner: + specifier: 1.5.0 + version: 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) superjson: specifier: 2.2.1 version: 2.2.1 @@ -8925,6 +8928,12 @@ packages: sonic-boom@4.0.1: resolution: {integrity: sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==} + sonner@1.5.0: + resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + source-list-map@2.0.1: resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} @@ -20747,6 +20756,11 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonner@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + source-list-map@2.0.1: {} source-map-js@1.0.2: {} diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx index f27aa2883..15aaaf1f0 100644 --- a/src/app/Providers.tsx +++ b/src/app/Providers.tsx @@ -3,6 +3,7 @@ import React, { FC } from 'react'; import { CacheProvider } from '@chakra-ui/next-js'; import { ChakraProvider, createLocalStorageManager } from '@chakra-ui/react'; import { useTranslation } from 'react-i18next'; +import { Toaster } from 'sonner'; import '@/lib/dayjs/config'; import '@/lib/i18n/client'; @@ -28,6 +29,7 @@ export const Providers: FC> = ({ }} > {children} + ); diff --git a/src/components/Toast/docs.stories.tsx b/src/components/Toast/docs.stories.tsx new file mode 100644 index 000000000..59f9245b7 --- /dev/null +++ b/src/components/Toast/docs.stories.tsx @@ -0,0 +1,113 @@ +import React from 'react'; + +import { Box, Button, Flex } from '@chakra-ui/react'; +import { Meta } from '@storybook/react'; +import { toast } from 'sonner'; + +import { toastCustom } from '@/components/Toast'; + +export default { + title: 'Components/Toast', + decorators: [ + (Story) => ( + + + + ), + ], +} satisfies Meta; + +export const Default = () => { + const handleOpenToast = (props: { status: 'success' | 'error' | 'info' }) => { + toastCustom({ + status: props.status, + title: 'This is a toast', + }); + }; + + return ( + + + + + + ); +}; + +export const WithDescription = () => { + const handleOpenToast = (props: { status: 'success' | 'error' | 'info' }) => { + toastCustom({ + status: props.status, + title: 'This is a toast', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis id porta lacus. Nunc tellus ipsum, blandit commodo neque at, eleifend facilisis arcu. Phasellus nec pretium sapien.', + }); + }; + return ( + + + + + + ); +}; + +export const WithActions = () => { + const handleOpenToast = (props: { status: 'success' | 'error' | 'info' }) => { + toastCustom({ + status: props.status, + title: 'This is a toast', + actions: ( + + ), + }); + }; + return ( + + + + + + ); +}; + +export const HideIcon = () => { + const handleOpenToast = (props: { status: 'success' | 'error' | 'info' }) => { + toastCustom({ + status: props.status, + title: 'This is a toast', + hideIcon: true, + }); + }; + return ( + + + + + + ); +}; diff --git a/src/components/Toast/index.ts b/src/components/Toast/index.ts deleted file mode 100644 index e733caaa9..000000000 --- a/src/components/Toast/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { UseToastOptions, useToast } from '@chakra-ui/react'; - -export const toastDefaultConfig: UseToastOptions = { - duration: 3000, - isClosable: true, - position: 'top-right', - variant: 'solid', -}; - -export const useToastError = () => - useToast({ - ...toastDefaultConfig, - status: 'error', - duration: null, - }); - -export const useToastWarning = () => - useToast({ - ...toastDefaultConfig, - status: 'warning', - }); - -export const useToastSuccess = () => - useToast({ - ...toastDefaultConfig, - status: 'success', - }); - -export const useToastInfo = () => - useToast({ - ...toastDefaultConfig, - status: 'info', - }); diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx new file mode 100644 index 000000000..9a378145a --- /dev/null +++ b/src/components/Toast/index.tsx @@ -0,0 +1,107 @@ +import { ReactNode } from 'react'; + +import { + Box, + ButtonGroup, + Card, + CardBody, + Flex, + Heading, + IconButton, +} from '@chakra-ui/react'; +import i18n from 'i18next'; +import { LuCheckCircle2, LuInfo, LuX, LuXCircle } from 'react-icons/lu'; +import { ExternalToast, toast } from 'sonner'; +import { match } from 'ts-pattern'; + +import { Icon } from '@/components/Icons'; + +export const toastCustom = (params: { + status?: 'info' | 'success' | 'error'; + hideIcon?: boolean; + title: ReactNode; + description?: ReactNode; + actions?: ReactNode; +}) => { + const status = params.status ?? 'info'; + const icon = match(status) + .with('info', () => LuInfo) + .with('success', () => LuCheckCircle2) + .with('error', () => LuXCircle) + .exhaustive(); + + const options: ExternalToast = { + duration: status === 'error' ? Infinity : 3000, + }; + + toast.custom( + (t) => ( + + } + onClick={() => toast.dismiss(t)} + position="absolute" + top={-2.5} + right={-2.5} + borderRadius="full" + /> + + + + + + {!params.hideIcon && ( + + )} + {params.title} + + {!!params.actions && ( + {params.actions} + )} + + {!!params.description && ( + + {params.description} + + )} + + + + ), + options + ); +}; diff --git a/src/features/account/AccountDeleteButton.tsx b/src/features/account/AccountDeleteButton.tsx index a5dd575ee..ed6784c7c 100644 --- a/src/features/account/AccountDeleteButton.tsx +++ b/src/features/account/AccountDeleteButton.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { LuTrash2 } from 'react-icons/lu'; import { ConfirmModal } from '@/components/ConfirmModal'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AccountDeleteVerificationCodeModale, SEARCH_PARAM_VERIFY_EMAIL, @@ -25,7 +25,6 @@ export const AccountDeleteButton = () => { ); const account = trpc.account.get.useQuery(); - const toastError = useToastError(); const deleteAccountValidate = searchParams[SEARCH_PARAM_VERIFY_EMAIL]; const deleteAccount = trpc.account.deleteRequest.useMutation({ @@ -37,7 +36,8 @@ export const AccountDeleteButton = () => { }); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('account:deleteAccount.feedbacks.updateError.title'), }); }, diff --git a/src/features/account/AccountEmailForm.tsx b/src/features/account/AccountEmailForm.tsx index ad8d6feed..6c3bfbf31 100644 --- a/src/features/account/AccountEmailForm.tsx +++ b/src/features/account/AccountEmailForm.tsx @@ -14,7 +14,7 @@ import { FormFieldLabel, } from '@/components/Form'; import { LoaderFull } from '@/components/LoaderFull'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { EmailVerificationCodeModale } from '@/features/account/EmailVerificationCodeModal'; import { FormFieldsAccountEmail, @@ -33,8 +33,6 @@ export const AccountEmailForm = () => { staleTime: Infinity, }); - const toastError = useToastError(); - const updateEmail = trpc.account.updateEmail.useMutation({ onSuccess: async ({ token }, { email }) => { setSearchParams({ @@ -43,7 +41,8 @@ export const AccountEmailForm = () => { }); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('account:email.feedbacks.updateError.title'), }); }, diff --git a/src/features/account/AccountProfileForm.tsx b/src/features/account/AccountProfileForm.tsx index 505ab0d46..2b89a6566 100644 --- a/src/features/account/AccountProfileForm.tsx +++ b/src/features/account/AccountProfileForm.tsx @@ -13,7 +13,7 @@ import { FormFieldLabel, } from '@/components/Form'; import { LoaderFull } from '@/components/LoaderFull'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { FormFieldsAccountProfile, zFormFieldsAccountProfile, @@ -31,18 +31,17 @@ export const AccountProfileForm = () => { staleTime: Infinity, }); - const toastSuccess = useToastSuccess(); - const toastError = useToastError(); - const updateAccount = trpc.account.update.useMutation({ onSuccess: async () => { await trpcUtils.account.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('account:profile.feedbacks.updateSuccess.title'), }); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('account:profile.feedbacks.updateError.title'), }); }, diff --git a/src/features/account/EmailVerificationCodeModal.tsx b/src/features/account/EmailVerificationCodeModal.tsx index 3f8cc86e6..598d9a135 100644 --- a/src/features/account/EmailVerificationCodeModal.tsx +++ b/src/features/account/EmailVerificationCodeModal.tsx @@ -13,7 +13,7 @@ import { SubmitHandler, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { Form } from '@/components/Form'; -import { useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { VerificationCodeForm, useOnVerificationCodeError, @@ -32,7 +32,6 @@ export const EmailVerificationCodeModale = () => { attempts: parseAsInteger.withDefault(0), }); const trpcUtils = trpc.useUtils(); - const toastSuccess = useToastSuccess(); const onClose = () => { trpcUtils.account.get.reset(); @@ -56,7 +55,8 @@ export const EmailVerificationCodeModale = () => { const updateEmailValidate = trpc.account.updateEmailValidate.useMutation({ onSuccess: async () => { onClose(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('account:email.feedbacks.updateSuccess.title'), }); }, diff --git a/src/features/auth/LoginForm.tsx b/src/features/auth/LoginForm.tsx index cc4d051bf..d033abe34 100644 --- a/src/features/auth/LoginForm.tsx +++ b/src/features/auth/LoginForm.tsx @@ -13,7 +13,7 @@ import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { Form, FormField, FormFieldController } from '@/components/Form'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { FormFieldsLogin, zFormFieldsLogin } from '@/features/auth/schemas'; import { LoginHint } from '@/features/devtools/LoginHint'; import { trpc } from '@/lib/trpc/client'; @@ -33,12 +33,12 @@ export const LoginForm = ({ ...rest }: LoginFormProps) => { const { t } = useTranslation(['auth']); - const toastError = useToastError(); const login = trpc.auth.login.useMutation({ onSuccess, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('auth:login.feedbacks.loginError.title'), }); }, diff --git a/src/features/auth/PageRegister.tsx b/src/features/auth/PageRegister.tsx index a2486865a..0a32efa05 100644 --- a/src/features/auth/PageRegister.tsx +++ b/src/features/auth/PageRegister.tsx @@ -13,7 +13,7 @@ import { FormFieldController, FormFieldLabel, } from '@/components/Form'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { ROUTES_AUTH } from '@/features/auth/routes'; import { FormFieldsRegister, @@ -26,7 +26,6 @@ import { trpc } from '@/lib/trpc/client'; export default function PageRegister() { const { t, i18n } = useTranslation(['common', 'auth']); - const toastError = useToastError(); const router = useRouter(); const register = trpc.auth.register.useMutation({ @@ -39,7 +38,8 @@ export default function PageRegister() { ); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('auth:register.feedbacks.registrationError.title'), }); }, diff --git a/src/features/demo-mode/DemoModalInterceptor.tsx b/src/features/demo-mode/DemoModalInterceptor.tsx index bf25093aa..27e850c8f 100644 --- a/src/features/demo-mode/DemoModalInterceptor.tsx +++ b/src/features/demo-mode/DemoModalInterceptor.tsx @@ -17,19 +17,17 @@ import { ModalOverlay, Stack, Text, - useToast, } from '@chakra-ui/react'; +import { toast } from 'sonner'; export const DemoModalInterceptor = ({ onClose }: { onClose: () => void }) => { - const toast = useToast(); - - const toastCloseAll = toast.closeAll; - useEffect(() => { - setTimeout(() => { - toastCloseAll(); + const timer = setTimeout(() => { + toast.dismiss(); }); - }, [toastCloseAll]); + + return () => clearTimeout(timer); + }, []); return ( diff --git a/src/features/repositories/AdminRepositoryActions.tsx b/src/features/repositories/AdminRepositoryActions.tsx index d330774dd..e0e05b390 100644 --- a/src/features/repositories/AdminRepositoryActions.tsx +++ b/src/features/repositories/AdminRepositoryActions.tsx @@ -16,7 +16,7 @@ import { LuEye, LuPenLine, LuTrash2 } from 'react-icons/lu'; import { ActionsButton } from '@/components/ActionsButton'; import { ConfirmModal } from '@/components/ConfirmModal'; import { Icon } from '@/components/Icons'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { ROUTES_REPOSITORIES } from '@/features/repositories/routes'; import { trpc } from '@/lib/trpc/client'; import type { RouterOutputs } from '@/lib/trpc/types'; @@ -32,14 +32,13 @@ export const AdminRepositoryActions = ({ const { t } = useTranslation(['common', 'repositories']); const trpcUtils = trpc.useUtils(); - const toastError = useToastError(); - const repositoryRemove = trpc.repositories.removeById.useMutation({ onSuccess: async () => { await trpcUtils.repositories.getAll.invalidate(); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('repositories:feedbacks.deleteRepositoryError.title'), description: t( 'repositories:feedbacks.deleteRepositoryError.description' diff --git a/src/features/repositories/PageAdminRepository.tsx b/src/features/repositories/PageAdminRepository.tsx index d5056b1a1..82f230dce 100644 --- a/src/features/repositories/PageAdminRepository.tsx +++ b/src/features/repositories/PageAdminRepository.tsx @@ -20,7 +20,7 @@ import { ErrorPage } from '@/components/ErrorPage'; import { Icon } from '@/components/Icons'; import { LoaderFull } from '@/components/LoaderFull'; import { ResponsiveIconButton } from '@/components/ResponsiveIconButton'; -import { useToastError } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AdminBackButton } from '@/features/admin/AdminBackButton'; import { AdminLayoutPage, @@ -33,7 +33,6 @@ import { trpc } from '@/lib/trpc/client'; export default function PageAdminRepository() { const { t } = useTranslation(['common', 'repositories']); - const toastError = useToastError(); const trpcUtils = trpc.useUtils(); const router = useRouter(); @@ -48,7 +47,8 @@ export default function PageAdminRepository() { router.replace(ROUTES_REPOSITORIES.admin.root()); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('repositories:feedbacks.deleteRepositoryError.title'), description: t( 'repositories:feedbacks.deleteRepositoryError.description' diff --git a/src/features/repositories/PageAdminRepositoryCreate.tsx b/src/features/repositories/PageAdminRepositoryCreate.tsx index 68cc36f90..d7a795567 100644 --- a/src/features/repositories/PageAdminRepositoryCreate.tsx +++ b/src/features/repositories/PageAdminRepositoryCreate.tsx @@ -7,7 +7,7 @@ import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { Form } from '@/components/Form'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AdminBackButton } from '@/features/admin/AdminBackButton'; import { AdminCancelButton } from '@/features/admin/AdminCancelButton'; import { @@ -28,13 +28,11 @@ export default function PageAdminRepositoryCreate() { const trpcUtils = trpc.useUtils(); const router = useRouter(); - const toastError = useToastError(); - const toastSuccess = useToastSuccess(); - const createRepository = trpc.repositories.create.useMutation({ onSuccess: async () => { await trpcUtils.repositories.getAll.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('repositories:create.feedbacks.updateSuccess.title'), }); router.back(); @@ -46,7 +44,8 @@ export default function PageAdminRepositoryCreate() { }); return; } - toastError({ + toastCustom({ + status: 'error', title: t('repositories:create.feedbacks.updateError.title'), }); }, diff --git a/src/features/repositories/PageAdminRepositoryUpdate.tsx b/src/features/repositories/PageAdminRepositoryUpdate.tsx index 9dbf70e50..956483d3c 100644 --- a/src/features/repositories/PageAdminRepositoryUpdate.tsx +++ b/src/features/repositories/PageAdminRepositoryUpdate.tsx @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'; import { ErrorPage } from '@/components/ErrorPage'; import { Form } from '@/components/Form'; import { LoaderFull } from '@/components/LoaderFull'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AdminBackButton } from '@/features/admin/AdminBackButton'; import { AdminCancelButton } from '@/features/admin/AdminCancelButton'; import { @@ -42,13 +42,11 @@ export default function PageAdminRepositoryUpdate() { const isReady = !repository.isFetching; - const toastSuccess = useToastSuccess(); - const toastError = useToastError(); - const updateRepository = trpc.repositories.updateById.useMutation({ onSuccess: async () => { await trpcUtils.repositories.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('repositories:update.feedbacks.updateSuccess.title'), }); router.back(); @@ -60,7 +58,8 @@ export default function PageAdminRepositoryUpdate() { }); return; } - toastError({ + toastCustom({ + status: 'error', title: t('repositories:update.feedbacks.updateError.title'), }); }, diff --git a/src/features/users/AdminUserActions.tsx b/src/features/users/AdminUserActions.tsx index f74036f14..a28e05f8e 100644 --- a/src/features/users/AdminUserActions.tsx +++ b/src/features/users/AdminUserActions.tsx @@ -17,7 +17,7 @@ import { ActionsButton } from '@/components/ActionsButton'; import { ConfirmMenuItem } from '@/components/ConfirmMenuItem'; import { ConfirmModal } from '@/components/ConfirmModal'; import { Icon } from '@/components/Icons'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { ROUTES_USERS } from '@/features/users/routes'; import { trpc } from '@/lib/trpc/client'; import type { RouterOutputs } from '@/lib/trpc/types'; @@ -32,13 +32,11 @@ export const AdminUserActions = ({ user, ...rest }: AdminUserActionProps) => { const trpcUtils = trpc.useUtils(); const isCurrentUser = account.data?.id === user.id; - const toastSuccess = useToastSuccess(); - const toastError = useToastError(); - const activateUser = trpc.users.activate.useMutation({ onSuccess: async ({ email, name }) => { await trpcUtils.users.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('users:feedbacks.activateUserSuccess.title'), description: t('users:feedbacks.activateUserSuccess.description', { login: name ?? email, @@ -46,7 +44,8 @@ export const AdminUserActions = ({ user, ...rest }: AdminUserActionProps) => { }); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('users:feedbacks.activateUserError.title'), description: t('users:feedbacks.activateUserError.description', { login: user.name ?? user.email, @@ -57,7 +56,8 @@ export const AdminUserActions = ({ user, ...rest }: AdminUserActionProps) => { const deactivateUser = trpc.users.deactivate.useMutation({ onSuccess: async ({ email, name }) => { await trpcUtils.users.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('users:feedbacks.deactivateUserSuccess.title'), description: t('users:feedbacks.deactivateUserSuccess.description', { login: name ?? email, @@ -65,7 +65,8 @@ export const AdminUserActions = ({ user, ...rest }: AdminUserActionProps) => { }); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('users:feedbacks.deactivateUserError.title'), description: t('users:feedbacks.deactivateUserError.description', { login: user.name ?? user.email, @@ -79,7 +80,8 @@ export const AdminUserActions = ({ user, ...rest }: AdminUserActionProps) => { await trpcUtils.users.getAll.invalidate(); }, onError: () => { - toastError({ + toastCustom({ + status: 'error', title: t('users:feedbacks.deleteUserError.title'), description: t('users:feedbacks.deleteUserError.description', { login: user.name ?? user.email, diff --git a/src/features/users/PageAdminUserCreate.tsx b/src/features/users/PageAdminUserCreate.tsx index 6ccd7854a..d347993a1 100644 --- a/src/features/users/PageAdminUserCreate.tsx +++ b/src/features/users/PageAdminUserCreate.tsx @@ -7,7 +7,7 @@ import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { Form } from '@/components/Form'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AdminBackButton } from '@/features/admin/AdminBackButton'; import { AdminCancelButton } from '@/features/admin/AdminCancelButton'; import { @@ -26,13 +26,11 @@ export default function PageAdminUserCreate() { const router = useRouter(); const trpcUtils = trpc.useUtils(); - const toastError = useToastError(); - const toastSuccess = useToastSuccess(); - const createUser = trpc.users.create.useMutation({ onSuccess: async () => { await trpcUtils.users.getAll.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('users:create.feedbacks.updateSuccess.title'), }); router.back(); @@ -42,7 +40,8 @@ export default function PageAdminUserCreate() { form.setError('email', { message: t('users:data.email.alreadyUsed') }); return; } - toastError({ + toastCustom({ + status: 'error', title: t('users:create.feedbacks.updateError.title'), }); }, diff --git a/src/features/users/PageAdminUserUpdate.tsx b/src/features/users/PageAdminUserUpdate.tsx index 98325748f..e224dbea2 100644 --- a/src/features/users/PageAdminUserUpdate.tsx +++ b/src/features/users/PageAdminUserUpdate.tsx @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'; import { ErrorPage } from '@/components/ErrorPage'; import { Form } from '@/components/Form'; import { LoaderFull } from '@/components/LoaderFull'; -import { useToastError, useToastSuccess } from '@/components/Toast'; +import { toastCustom } from '@/components/Toast'; import { AdminBackButton } from '@/features/admin/AdminBackButton'; import { AdminCancelButton } from '@/features/admin/AdminCancelButton'; import { @@ -39,13 +39,11 @@ export default function PageAdminUserUpdate() { } ); - const toastSuccess = useToastSuccess(); - const toastError = useToastError(); - const userUpdate = trpc.users.updateById.useMutation({ onSuccess: async () => { await trpcUtils.users.invalidate(); - toastSuccess({ + toastCustom({ + status: 'success', title: t('users:update.feedbacks.updateSuccess.title'), }); router.back(); @@ -55,7 +53,8 @@ export default function PageAdminUserUpdate() { form.setError('email', { message: t('users:data.email.alreadyUsed') }); return; } - toastError({ + toastCustom({ + status: 'error', title: t('users:update.feedbacks.updateError.title'), }); }, diff --git a/src/locales/ar/components.json b/src/locales/ar/components.json index 65ab5ad6e..afb5b2568 100644 --- a/src/locales/ar/components.json +++ b/src/locales/ar/components.json @@ -93,5 +93,8 @@ "fieldPassword": { "showPassword": "عرض كلمة المرور", "hidePassword": "إخفاء كلمة المرور" + }, + "toast": { + "closeToast": "إغلاق الرسالة" } } diff --git a/src/locales/en/components.json b/src/locales/en/components.json index 6d24e97b7..49a36d86d 100644 --- a/src/locales/en/components.json +++ b/src/locales/en/components.json @@ -93,5 +93,8 @@ "fieldPassword": { "showPassword": "Show password", "hidePassword": "Hide password" + }, + "toast": { + "closeToast": "Close message" } } diff --git a/src/locales/fr/components.json b/src/locales/fr/components.json index 9e7391420..a80a02edf 100644 --- a/src/locales/fr/components.json +++ b/src/locales/fr/components.json @@ -93,5 +93,8 @@ "fieldPassword": { "showPassword": "Afficher le mot de passe", "hidePassword": "Masquer le mot de passe" + }, + "toast": { + "closeToast": "Fermer le message" } } diff --git a/src/locales/sw/components.json b/src/locales/sw/components.json index 66f4ba5b0..022730bc5 100644 --- a/src/locales/sw/components.json +++ b/src/locales/sw/components.json @@ -93,5 +93,8 @@ "fieldPassword": { "showPassword": "Onyesha nenosiri", "hidePassword": "Ficha nenosiri" + }, + "toast": { + "closeToast": "Funga ujumbe" } }