refactor: adapt frontend error handling to support translated error variables

This commit is contained in:
Nicolas Meienberger 2023-05-10 22:41:46 +02:00 committed by Nicolas Meienberger
parent e9b7f9a73f
commit 20eedd9ca3
9 changed files with 24 additions and 30 deletions

View file

@ -20,6 +20,9 @@ export type RpcErrorResponse = {
stack: string;
path: string; // TQuery
zodError?: Record<string, string>;
tError: {
message: string;
};
};
};
};
@ -45,6 +48,9 @@ const jsonRpcErrorResponse = (path: string, status: number, message: string, zod
stack: 'Error: Internal Server Error',
path,
zodError,
tError: {
message,
},
},
},
},

View file

@ -47,8 +47,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
},
onError: (e) => {
invalidate();
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables }));
},
});
@ -61,7 +60,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
invalidate();
toast.success(t('apps.app-details.uninstall-success'));
},
onError: (e) => toast.error(t(e.data?.translatedError || (e.message as MessageKey))),
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const stop = trpc.app.stopApp.useMutation({
@ -73,7 +72,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
invalidate();
toast.success(t('apps.app-details.stop-success'));
},
onError: (e) => toast.error(t(e.data?.translatedError || (e.message as MessageKey))),
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const update = trpc.app.updateApp.useMutation({
@ -85,7 +84,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
invalidate();
toast.success(t('apps.app-details.update-success'));
},
onError: (e) => toast.error(t(e.data?.translatedError || (e.message as MessageKey))),
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const start = trpc.app.startApp.useMutation({
@ -96,7 +95,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
invalidate();
toast.success(t('apps.app-details.start-success'));
},
onError: (e) => toast.error(t(e.data?.translatedError || (e.message as MessageKey))),
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const updateConfig = trpc.app.updateAppConfig.useMutation({
@ -105,7 +104,7 @@ export const AppDetailsContainer: React.FC<IProps> = ({ app }) => {
invalidate();
toast.success(t('apps.app-details.update-config-success'));
},
onError: (e) => toast.error(t(e.data?.translatedError || (e.message as MessageKey))),
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const updateAvailable = Number(app.version || 0) < Number(app?.latestVersion || 0);

View file

@ -1,8 +1,8 @@
import { NextPage } from 'next';
import React from 'react';
import { useRouter } from 'next/router';
import type { MessageKey } from '@/server/utils/errors';
import { useTranslations } from 'next-intl';
import type { MessageKey } from '@/server/utils/errors';
import { Layout } from '../../../../components/Layout';
import { ErrorPage } from '../../../../components/ui/ErrorPage';
import { trpc } from '../../../../utils/trpc';
@ -36,7 +36,7 @@ export const AppDetailsPage: NextPage<IProps> = ({ appId }) => {
return (
<Layout title={data?.info.name || ''} breadcrumbs={breadcrumb}>
{data?.info && <AppDetailsContainer app={data} />}
{error && <ErrorPage error={t(error.data?.translatedError || (error.message as MessageKey))} />}
{error && <ErrorPage error={t(error.data?.tError.message as MessageKey, { ...error.data?.tError.variables })} />}
</Layout>
);
};

View file

@ -2,7 +2,7 @@ import React from 'react';
import { useRouter } from 'next/router';
import { NextPage } from 'next';
import { useTranslations } from 'next-intl';
import { MessageKey } from '@/server/utils/errors';
import type { MessageKey } from '@/server/utils/errors';
import { AppTile } from '../../../../components/AppTile';
import { Layout } from '../../../../components/Layout';
import { EmptyPage } from '../../../../components/ui/EmptyPage';
@ -35,7 +35,7 @@ export const AppsPage: NextPage = () => {
{!isLoading && data?.length === 0 && (
<EmptyPage title={t('apps.my-apps.empty-title')} subtitle={t('apps.my-apps.empty-subtitle')} onAction={() => router.push('/app-store')} actionLabel={t('apps.my-apps.empty-action')} />
)}
{error && <ErrorPage error={t(error.data?.translatedError || (error.message as MessageKey))} />}
{error && <ErrorPage error={t(error.data?.tError.message as MessageKey, { ...error.data?.tError?.variables })} />}
</div>
</Layout>
);

View file

@ -16,10 +16,7 @@ export const LoginContainer: React.FC = () => {
const router = useRouter();
const utils = trpc.useContext();
const login = trpc.auth.login.useMutation({
onError: (e) => {
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
onSuccess: (data) => {
if (data.totpSessionId) {
setTotpSessionId(data.totpSessionId);
@ -31,10 +28,7 @@ export const LoginContainer: React.FC = () => {
});
const verifyTotp = trpc.auth.verifyTotp.useMutation({
onError: (e) => {
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
onSuccess: () => {
utils.auth.me.invalidate();
router.push('/');

View file

@ -16,10 +16,7 @@ export const RegisterContainer: React.FC = () => {
const router = useRouter();
const utils = trpc.useContext();
const register = trpc.auth.register.useMutation({
onError: (e) => {
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
onSuccess: () => {
utils.auth.me.invalidate();
router.push('/');

View file

@ -22,10 +22,7 @@ export const ResetPasswordContainer: React.FC<Props> = ({ isRequested }) => {
onSuccess: () => {
utils.auth.checkPasswordChangeRequest.invalidate();
},
onError: (e) => {
const toastMessage = t(e.data?.translatedError || (e.message as MessageKey));
toast.error(toastMessage);
},
onError: (e) => toast.error(t(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables })),
});
const cancelRequest = trpc.auth.cancelPasswordChangeRequest.useMutation({
onSuccess: () => {

View file

@ -14,7 +14,7 @@ export const DashboardPage: NextPage = () => {
return (
<Layout title={t('dashboard.title')}>
{data && <DashboardContainer data={data} />}
{error && <ErrorPage error={t(error.data?.translatedError || (error.message as MessageKey))} />}
{error && <ErrorPage error={t(error.data?.tError.message as MessageKey, { ...error.data?.tError.variables })} />}
</Layout>
);
};

View file

@ -5,7 +5,7 @@ import { Locale } from '@/shared/internationalization/locales';
import { type Context } from './context';
import { AuthQueries } from './queries/auth/auth.queries';
import { db } from './db';
import { MessageKey, TranslatedError } from './utils/errors';
import { type MessageKey, TranslatedError } from './utils/errors';
const authQueries = new AuthQueries(db);
@ -35,7 +35,8 @@ const t = initTRPC.context<Context>().create({
data: {
...shape.data,
zodError: error.code === 'BAD_REQUEST' && error.cause instanceof ZodError ? zodErrorsToRecord(error.cause.flatten()) : null,
translatedError: error.cause instanceof TranslatedError ? (error.cause.message as MessageKey) : null,
tError:
error.cause instanceof TranslatedError ? { message: error.cause.message as MessageKey, variables: error.cause.variableValues } : { message: error.message as MessageKey, variables: {} },
},
};
},