improvement: error handeling structure changed and unified all across the app. user no longer sees inappropriate messages.
This commit is contained in:
@@ -20,7 +20,7 @@ import { ScheduleDayPicker } from '@/components/ui/shared/ScheduleDayPicker';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
import type { AppointmentPurpose } from '@/types/appointment';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { compareLocalDayStart, getLocalDayIsoRange, startOfLocalDay } from '@/components/appointments/appointmentTime';
|
||||
|
||||
const EMPTY_PATIENT_FORM: CreatePatientInput = {
|
||||
@@ -32,6 +32,7 @@ const EMPTY_PATIENT_FORM: CreatePatientInput = {
|
||||
|
||||
export default function AppointmentsPage() {
|
||||
const t = useTranslations('appointments');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tPatients = useTranslations('patients');
|
||||
const { currentOrganization } = useAuth();
|
||||
const [scheduleDate, setScheduleDate] = useState(() => startOfLocalDay(new Date()));
|
||||
@@ -105,7 +106,7 @@ export default function AppointmentsPage() {
|
||||
if (gen !== scheduleLoadGen.current) {
|
||||
return;
|
||||
}
|
||||
toast.showError(formatApiErrorMessage(err, t('errorLoadSchedule')));
|
||||
toast.showError(getUserFacingError(err, tErrors, t('errorLoadSchedule')));
|
||||
} finally {
|
||||
if (gen === scheduleLoadGen.current) {
|
||||
setLoadingSchedule(false);
|
||||
@@ -178,11 +179,7 @@ export default function AppointmentsPage() {
|
||||
);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err && typeof err === 'object' && 'message' in err
|
||||
? String((err as { message: unknown }).message)
|
||||
: tPatients('errorSavePatient');
|
||||
toast.showError(message);
|
||||
toast.showError(getUserFacingError(err, tErrors, tPatients('errorSavePatient')));
|
||||
} finally {
|
||||
setSavingPatient(false);
|
||||
}
|
||||
@@ -248,13 +245,13 @@ export default function AppointmentsPage() {
|
||||
toast.showSuccess(activeEditingAppointment ? t('successUpdated') : t('successSaved'));
|
||||
await loadSchedule();
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err && typeof err === 'object' && 'message' in err
|
||||
? String((err as { message: unknown }).message)
|
||||
: activeEditingAppointment
|
||||
? t('errorUpdate')
|
||||
: t('errorSave');
|
||||
toast.showError(message);
|
||||
toast.showError(
|
||||
getUserFacingError(
|
||||
err,
|
||||
tErrors,
|
||||
activeEditingAppointment ? t('errorUpdate') : t('errorSave'),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
setSavingAppointment(false);
|
||||
}
|
||||
@@ -276,11 +273,7 @@ export default function AppointmentsPage() {
|
||||
toast.showSuccess(t('successRemoved'));
|
||||
await loadSchedule();
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err && typeof err === 'object' && 'message' in err
|
||||
? String((err as { message: unknown }).message)
|
||||
: t('errorDelete');
|
||||
toast.showError(message);
|
||||
toast.showError(getUserFacingError(err, tErrors, t('errorDelete')));
|
||||
} finally {
|
||||
setDeletingAppointment(false);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
import { canEditCases, canEditTasks } from '@/components/shared/permissions';
|
||||
@@ -35,6 +35,7 @@ const PAGE_SIZE = 20;
|
||||
|
||||
export default function CasesPage() {
|
||||
const t = useTranslations('cases');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tCommon = useTranslations('common');
|
||||
const { currentOrganization, user } = useAuth();
|
||||
const toast = useToast();
|
||||
@@ -112,7 +113,7 @@ export default function CasesPage() {
|
||||
setCases(response.data.items);
|
||||
setPagination(response.data.pagination);
|
||||
} catch (error: unknown) {
|
||||
toast.showError(formatApiErrorMessage(error, t('errorLoadList')));
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorLoadList')));
|
||||
} finally {
|
||||
setLoadingList(false);
|
||||
}
|
||||
@@ -127,7 +128,7 @@ export default function CasesPage() {
|
||||
const response = await casesApi.getOne(caseId);
|
||||
setSelectedCase(response.data);
|
||||
} catch (error: unknown) {
|
||||
toast.showError(formatApiErrorMessage(error, t('errorLoadDetail')));
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorLoadDetail')));
|
||||
if (!options?.silent) {
|
||||
setSelectedCase(null);
|
||||
}
|
||||
@@ -218,7 +219,7 @@ export default function CasesPage() {
|
||||
setSelectedCase(response.data);
|
||||
} catch (error: unknown) {
|
||||
setSelectedCase(previousCase);
|
||||
toast.showError(formatApiErrorMessage(error, t('errorUpdateTask')));
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorUpdateTask')));
|
||||
} finally {
|
||||
setUpdatingImportant(false);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import { Input } from '@/components/ui/shared/Input';
|
||||
import { SearchBar } from '@/components/ui/shared/SearchBar';
|
||||
import { Table } from '@/components/ui/shared/Table';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import type { ApiError } from '@/types/api';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
|
||||
function formatOrganizationStatusLabel(status: string): string {
|
||||
if (!status) return status;
|
||||
@@ -42,6 +42,7 @@ type TableMode = 'existing' | 'search';
|
||||
|
||||
export default function OrganizationsPage() {
|
||||
const t = useTranslations('organizations');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tNav = useTranslations('nav');
|
||||
const tCommon = useTranslations('common');
|
||||
const { currentOrganization } = useAuth();
|
||||
@@ -49,14 +50,8 @@ export default function OrganizationsPage() {
|
||||
const toast = useToast();
|
||||
|
||||
const formatApiMessage = useCallback(
|
||||
(err: unknown): string => {
|
||||
if (!err || typeof err !== 'object') return tCommon('errorGeneric');
|
||||
const m = (err as ApiError).message;
|
||||
if (Array.isArray(m)) return m.join(', ');
|
||||
if (typeof m === 'string') return m;
|
||||
return tCommon('errorGeneric');
|
||||
},
|
||||
[tCommon],
|
||||
(err: unknown): string => getUserFacingError(err, tErrors, tCommon('errorGeneric')),
|
||||
[tCommon, tErrors],
|
||||
);
|
||||
|
||||
const formatConnectionStatusLabel = useCallback(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslations } from 'next-intl';
|
||||
import { Button } from '@/components/ui/shared/Button';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import { patientsApi } from '@/lib/api/patients';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
import { hasPermission } from '@/components/shared/permissions';
|
||||
@@ -23,6 +23,7 @@ const EMPTY_PATIENT_FORM: CreatePatientInput = {
|
||||
|
||||
export default function PatientsPage() {
|
||||
const t = useTranslations('patients');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tCommon = useTranslations('common');
|
||||
const { currentOrganization } = useAuth();
|
||||
const toast = useToast();
|
||||
@@ -67,7 +68,7 @@ export default function PatientsPage() {
|
||||
setSelectedPatient(freshSelected);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
toast.showError(formatApiErrorMessage(error, t('errorLoadPatients')));
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorLoadPatients')));
|
||||
} finally {
|
||||
setLoadingPatients(false);
|
||||
}
|
||||
@@ -98,7 +99,7 @@ export default function PatientsPage() {
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
toast.showError(formatApiErrorMessage(error, t('errorSavePatient')));
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorSavePatient')));
|
||||
} finally {
|
||||
setSavingPatient(false);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Input } from '@/components/ui/shared/Input';
|
||||
import { Toast } from '@/components/ui/shared/Toast';
|
||||
import { Checkbox } from '@/components/ui/shared/Checkbox';
|
||||
import { DialogCloseButton } from '@/components/ui/shared/DialogCloseButton';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { OwnerWorkingHoursDialog } from '@/components/settings/OwnerWorkingHoursDialog';
|
||||
|
||||
type PasswordForm = {
|
||||
@@ -26,6 +27,7 @@ type PasswordForm = {
|
||||
|
||||
export default function AccountSettingsPage() {
|
||||
const t = useTranslations('settings');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tAuth = useTranslations('auth');
|
||||
const tCommon = useTranslations('common');
|
||||
const tValidation = useTranslations('validation');
|
||||
@@ -152,8 +154,7 @@ export default function AccountSettingsPage() {
|
||||
await syncSessionAfterParticipationChange();
|
||||
setSuccessMessage(t('participateEnabledTasks'));
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('participateUpdateFailed');
|
||||
setError(message || t('participateUpdateFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('participateUpdateFailed')));
|
||||
setParticipatesInTasks(false);
|
||||
} finally {
|
||||
setParticipationLoading(false);
|
||||
@@ -180,8 +181,7 @@ export default function AccountSettingsPage() {
|
||||
setRevokeConfirmOpen(false);
|
||||
setPendingRevokeType(null);
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('participateUpdateFailed');
|
||||
setError(message || t('participateUpdateFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('participateUpdateFailed')));
|
||||
} finally {
|
||||
setParticipationLoading(false);
|
||||
}
|
||||
@@ -214,8 +214,7 @@ export default function AccountSettingsPage() {
|
||||
try {
|
||||
await enableClinicParticipation(options);
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('participateUpdateFailed');
|
||||
setError(message || t('participateUpdateFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('participateUpdateFailed')));
|
||||
throw err;
|
||||
} finally {
|
||||
setParticipationLoading(false);
|
||||
@@ -249,8 +248,7 @@ export default function AccountSettingsPage() {
|
||||
setSuccessMessage(t('passwordChanged'));
|
||||
router.replace('/login');
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('passwordChangeFailed');
|
||||
setError(message || t('passwordChangeFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('passwordChangeFailed')));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import { Input } from '@/components/ui/shared/Input';
|
||||
import { Checkbox } from '@/components/ui/shared/Checkbox';
|
||||
import { Table } from '@/components/ui/shared/Table';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { StaffMembersMobileList } from '@/components/staff/StaffMembersMobileList';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
|
||||
@@ -147,6 +147,7 @@ function PermissionGrid({
|
||||
export default function StaffPage() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations('staff');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tCommon = useTranslations('common');
|
||||
const tFeatures = useTranslations('staff.features');
|
||||
const tWorkingHours = useTranslations('staff.workingHours');
|
||||
@@ -229,7 +230,7 @@ export default function StaffPage() {
|
||||
setMembers(res.data.members);
|
||||
setSeats(res.data.seats);
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorLoadStaff')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorLoadStaff')));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -304,7 +305,7 @@ export default function StaffPage() {
|
||||
await load();
|
||||
}
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorCopyInvite')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorCopyInvite')));
|
||||
} finally {
|
||||
setCopyingInviteMembershipId(null);
|
||||
}
|
||||
@@ -387,7 +388,7 @@ export default function StaffPage() {
|
||||
resetInviteForm();
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorSendInvite')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorSendInvite')));
|
||||
} finally {
|
||||
setInviteLoading(false);
|
||||
}
|
||||
@@ -410,7 +411,7 @@ export default function StaffPage() {
|
||||
setEditWorkingHoursDays(state.days);
|
||||
setEditAutoRepeatWeekly(state.autoRepeatWeekly);
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorLoadWorkingHours')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorLoadWorkingHours')));
|
||||
} finally {
|
||||
setEditLoadingWorkingHours(false);
|
||||
}
|
||||
@@ -449,7 +450,7 @@ export default function StaffPage() {
|
||||
setEditStep(1);
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorUpdateMember')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorUpdateMember')));
|
||||
} finally {
|
||||
setEditLoading(false);
|
||||
}
|
||||
@@ -470,7 +471,7 @@ export default function StaffPage() {
|
||||
setDisableTarget(null);
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorDisableMember')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorDisableMember')));
|
||||
} finally {
|
||||
setDisablingMembershipId(null);
|
||||
}
|
||||
@@ -487,7 +488,7 @@ export default function StaffPage() {
|
||||
setEnableTarget(null);
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorEnableMember')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorEnableMember')));
|
||||
} finally {
|
||||
setEnablingMembershipId(null);
|
||||
}
|
||||
@@ -600,7 +601,7 @@ export default function StaffPage() {
|
||||
setCopiedInviteMembershipId(lastInviteInfo.membershipId);
|
||||
setTimeout(() => setCopiedInviteMembershipId(null), 1500);
|
||||
} catch (e) {
|
||||
toast.showError(formatApiErrorMessage(e, t('errorCopyInvite')));
|
||||
toast.showError(getUserFacingError(e, tErrors, t('errorCopyInvite')));
|
||||
} finally {
|
||||
setCopyingInviteMembershipId(null);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
formatToothList,
|
||||
prosthesisTypeBadgeStyle,
|
||||
} from '@/components/ui/treatment/prosthesisTypeDisplay';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { canEditTasks, canViewTasks } from '@/components/shared/permissions';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
@@ -38,6 +38,7 @@ function formatPatientName(patient: { firstName: string; lastName: string }) {
|
||||
|
||||
export default function TasksPage() {
|
||||
const t = useTranslations('tasks');
|
||||
const tErrors = useTranslations('errors');
|
||||
const { currentOrganization, user, isAuthReady } = useAuth();
|
||||
const { showError, setError, messages: toastMessages } = useToast();
|
||||
|
||||
@@ -107,7 +108,7 @@ export default function TasksPage() {
|
||||
setTasks(response.data.items);
|
||||
setPagination(response.data.pagination);
|
||||
} catch (error: unknown) {
|
||||
showError(formatApiErrorMessage(error, tRef.current('errorLoadList')));
|
||||
showError(getUserFacingError(error, tErrors, tRef.current('errorLoadList')));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -127,7 +128,7 @@ export default function TasksPage() {
|
||||
await tasksApi.updateStatus(taskId, status);
|
||||
await loadTasks();
|
||||
} catch (error: unknown) {
|
||||
showError(formatApiErrorMessage(error, t('errorUpdateTask')));
|
||||
showError(getUserFacingError(error, tErrors, t('errorUpdateTask')));
|
||||
} finally {
|
||||
setUpdatingTaskId(null);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { TodayDashboard } from '@/components/today/TodayDashboard';
|
||||
import { TodayLoadErrorBanner } from '@/components/today/TodayLoadErrorBanner';
|
||||
import { TodaySectionErrorFallback } from '@/components/today/TodaySectionErrorFallback';
|
||||
@@ -13,6 +13,7 @@ import { useTodaySummary } from '@/lib/hooks/useTodaySummary';
|
||||
|
||||
export default function TodayPage() {
|
||||
const t = useTranslations('today');
|
||||
const tErrors = useTranslations('errors');
|
||||
const { currentOrganization } = useAuth();
|
||||
const orgId = currentOrganization?.id;
|
||||
const { data, loading, isInitialLoad, error, reload } = useTodaySummary(orgId);
|
||||
@@ -54,7 +55,7 @@ export default function TodayPage() {
|
||||
|
||||
{error ? (
|
||||
<TodayLoadErrorBanner
|
||||
message={formatApiErrorMessage(error, t('loadError'))}
|
||||
message={getUserFacingError(error, tErrors, t('loadError'))}
|
||||
retryLabel={t('retryLoad')}
|
||||
onRetry={() => void reload()}
|
||||
isRetrying={loading && Boolean(data)}
|
||||
|
||||
@@ -7,10 +7,12 @@ import { Link, useRouter } from '@/i18n/navigation';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { Button } from '@/components/ui/shared/Button';
|
||||
import { Input } from '@/components/ui/shared/Input';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { staffApi } from '@/lib/api/staff';
|
||||
|
||||
function AcceptInviteContent() {
|
||||
const t = useTranslations('auth');
|
||||
const tErrors = useTranslations('errors');
|
||||
const params = useSearchParams();
|
||||
const router = useRouter();
|
||||
const token = useMemo(() => params.get('token') || '', [params]);
|
||||
@@ -49,8 +51,7 @@ function AcceptInviteContent() {
|
||||
setSuccess(t('invitationAlreadyAccepted'));
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : '';
|
||||
setError(message || t('errorLoadInvitation'));
|
||||
setError(getUserFacingError(e, tErrors, t('errorLoadInvitation')));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -86,8 +87,7 @@ function AcceptInviteContent() {
|
||||
router.replace('/login');
|
||||
}, 1000);
|
||||
} catch (e: unknown) {
|
||||
const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : '';
|
||||
setError(message || t('errorAcceptInvitation'));
|
||||
setError(getUserFacingError(e, tErrors, t('errorAcceptInvitation')));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Button } from '@/components/ui/shared/Button';
|
||||
import { Input } from '@/components/ui/shared/Input';
|
||||
import { OrganizationDetailsFields } from '@/components/ui/auth/OrganizationDetailsFields';
|
||||
import { RegistrationProgressSteps } from '@/components/ui/auth/RegistrationProgressSteps';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { organizationApi } from '@/lib/api/organization';
|
||||
|
||||
type AcceptOrganizationInviteForm = {
|
||||
@@ -27,6 +28,7 @@ type AcceptOrganizationInviteForm = {
|
||||
|
||||
function AcceptOrganizationInviteContent() {
|
||||
const t = useTranslations('auth');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tCommon = useTranslations('common');
|
||||
const tValidation = useTranslations('validation');
|
||||
const params = useSearchParams();
|
||||
@@ -115,8 +117,7 @@ function AcceptOrganizationInviteContent() {
|
||||
setSuccess(t('invitationAlreadyAccepted'));
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : '';
|
||||
setError(message || t('errorLoadInvitation'));
|
||||
setError(getUserFacingError(e, tErrors, t('errorLoadInvitation')));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -148,8 +149,7 @@ function AcceptOrganizationInviteContent() {
|
||||
setSuccess(t('organizationAcceptedRedirect'));
|
||||
setTimeout(() => router.replace('/login'), 1000);
|
||||
} catch (e: unknown) {
|
||||
const message = e && typeof e === 'object' && 'message' in e ? String(e.message) : '';
|
||||
setError(message || t('errorAcceptInvitation'));
|
||||
setError(getUserFacingError(e, tErrors, t('errorAcceptInvitation')));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useTranslations } from 'next-intl';
|
||||
import { Link, useRouter } from '@/i18n/navigation';
|
||||
import { Phone, ShieldCheck } from 'lucide-react';
|
||||
import { authApi } from '@/lib/api/auth';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { AuthPageShell } from '@/components/ui/auth/AuthPageShell';
|
||||
import { Button } from '@/components/ui/shared/Button';
|
||||
@@ -29,6 +30,7 @@ export default function ForgotPasswordPage() {
|
||||
const t = useTranslations('auth');
|
||||
const tCommon = useTranslations('common');
|
||||
const tValidation = useTranslations('validation');
|
||||
const tErrors = useTranslations('errors');
|
||||
const router = useRouter();
|
||||
const { refreshSession } = useAuth();
|
||||
const [step, setStep] = useState<'mobile' | 'code'>('mobile');
|
||||
@@ -76,8 +78,7 @@ export default function ForgotPasswordPage() {
|
||||
setSentMobile(mobile);
|
||||
setStep('code');
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('codeSendFailed');
|
||||
setError(message || t('codeSendFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('codeSendFailed')));
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
@@ -116,8 +117,7 @@ export default function ForgotPasswordPage() {
|
||||
await refreshSession();
|
||||
router.push('/settings/account?reset=1');
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('verifyFailed');
|
||||
setError(message || t('verifyFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('verifyFailed')));
|
||||
} finally {
|
||||
setIsVerifying(false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as z from 'zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { Mail, Lock } from 'lucide-react';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { getRememberedEmail } from '@/lib/auth/rememberMe';
|
||||
import { AuthPageShell } from '@/components/ui/auth/AuthPageShell';
|
||||
@@ -25,6 +26,7 @@ export default function LoginPage() {
|
||||
const t = useTranslations('auth');
|
||||
const tCommon = useTranslations('common');
|
||||
const tValidation = useTranslations('validation');
|
||||
const tErrors = useTranslations('errors');
|
||||
const { login, isLoading, user, isAuthReady } = useAuth();
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -67,8 +69,7 @@ export default function LoginPage() {
|
||||
setError(null);
|
||||
await login(data.email, data.password, data.rememberMe);
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('invalidCredentials');
|
||||
setError(message || t('invalidCredentials'));
|
||||
setError(getUserFacingError(err, tErrors, t('invalidCredentials')));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as z from 'zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { Mail, Lock, User, Phone } from 'lucide-react';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { AuthPageShell } from '@/components/ui/auth/AuthPageShell';
|
||||
import { OrganizationDetailsFields } from '@/components/ui/auth/OrganizationDetailsFields';
|
||||
@@ -36,6 +37,7 @@ export default function RegisterPage() {
|
||||
const t = useTranslations('auth');
|
||||
const tCommon = useTranslations('common');
|
||||
const tValidation = useTranslations('validation');
|
||||
const tErrors = useTranslations('errors');
|
||||
const { registerTrial, isLoading } = useAuth();
|
||||
const [step, setStep] = useState(1);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -110,8 +112,7 @@ export default function RegisterPage() {
|
||||
data.organizationType,
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : t('registrationFailed');
|
||||
setError(message || t('registrationFailed'));
|
||||
setError(getUserFacingError(err, tErrors, t('registrationFailed')));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user