improvement: error handeling structure changed and unified all across the app. user no longer sees inappropriate messages.

This commit is contained in:
2026-07-12 18:27:38 +03:30
parent 901d838a2c
commit fab5111aa8
45 changed files with 977 additions and 241 deletions

View File

@@ -3,7 +3,7 @@
import { useCallback, useEffect, useState, type KeyboardEvent } from 'react';
import { useTranslations } from 'next-intl';
import { Eye, EyeOff, Send } from 'lucide-react';
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
import { getUserFacingError } from '@/components/shared/formatApiError';
import type { LabCaseComment } from '@/types/cases';
interface LabCaseCommentsPanelProps {
@@ -36,6 +36,7 @@ export function LabCaseCommentsPanel({
onComposerValueChange,
}: LabCaseCommentsPanelProps) {
const t = useTranslations('caseComments');
const tErrors = useTranslations('errors');
const [comments, setComments] = useState<LabCaseComment[]>([]);
const [loading, setLoading] = useState(false);
const [posting, setPosting] = useState(false);
@@ -48,7 +49,7 @@ export function LabCaseCommentsPanel({
const items = await loadComments();
setComments(items);
} catch (error: unknown) {
onError?.(formatApiErrorMessage(error, t('errorLoad')));
onError?.(getUserFacingError(error, tErrors, t('errorLoad')));
} finally {
setLoading(false);
}
@@ -68,7 +69,7 @@ export function LabCaseCommentsPanel({
setBody('');
setVisibleToClinic(false);
} catch (error: unknown) {
onError?.(formatApiErrorMessage(error, t('errorPost')));
onError?.(getUserFacingError(error, tErrors, t('errorPost')));
} finally {
setPosting(false);
}
@@ -87,7 +88,7 @@ export function LabCaseCommentsPanel({
const updated = await onToggleVisibility(comment.id, !comment.visibleToClinic);
setComments((prev) => prev.map((c) => (c.id === updated.id ? updated : c)));
} catch (error: unknown) {
onError?.(formatApiErrorMessage(error, t('errorToggle')));
onError?.(getUserFacingError(error, tErrors, t('errorToggle')));
}
}

View File

@@ -2,7 +2,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
import { getUserFacingError } from '@/components/shared/formatApiError';
import { canEditCases } from '@/components/shared/permissions';
import { useAuth } from '@/lib/hooks/useAuth';
import { useToast } from '@/lib/hooks/useToast';
@@ -37,6 +37,7 @@ export function ConnectionCaseHistoryContent({
onBack,
}: ConnectionCaseHistoryContentProps) {
const t = useTranslations('organizations');
const tErrors = useTranslations('errors');
const tCases = useTranslations('cases');
const tCommon = useTranslations('common');
const { currentOrganization, user } = useAuth();
@@ -102,7 +103,7 @@ export function ConnectionCaseHistoryContent({
setPagination(response.data.pagination);
} catch (error: unknown) {
if (cancelled) return;
showError(formatApiErrorMessage(error, tRef.current('caseHistoryErrorLoadList')));
showError(getUserFacingError(error, tErrors, tRef.current('caseHistoryErrorLoadList')));
} finally {
if (!cancelled) setLoadingList(false);
}
@@ -142,7 +143,7 @@ export function ConnectionCaseHistoryContent({
setSelectedCase(response.data);
} catch (error: unknown) {
if (cancelled) return;
showError(formatApiErrorMessage(error, tRef.current('caseHistoryErrorLoadDetail')));
showError(getUserFacingError(error, tErrors, tRef.current('caseHistoryErrorLoadDetail')));
setSelectedCase(null);
} finally {
if (!cancelled) setLoadingDetail(false);
@@ -182,7 +183,7 @@ export function ConnectionCaseHistoryContent({
setSelectedCase(response.data);
} catch (error: unknown) {
setSelectedCase(previousCase);
showError(formatApiErrorMessage(error, tCases('errorUpdateTask')));
showError(getUserFacingError(error, tErrors, tCases('errorUpdateTask')));
} finally {
setUpdatingImportant(false);
}

View File

@@ -2,6 +2,7 @@
import { useMemo, useState } from 'react';
import { useTranslations } from 'next-intl';
import { getUserFacingError } from '@/components/shared/formatApiError';
import { useAuth } from '@/lib/hooks/useAuth';
import { canCreateOrganizationFromCurrentOrg } from '@/components/shared/permissions';
import { Building2, Beaker, Mail } from 'lucide-react';
@@ -12,13 +13,14 @@ export function OrganizationSelectorContent() {
const t = useTranslations('organizations');
const tAuth = useTranslations('auth');
const tCommon = useTranslations('common');
const tErrors = useTranslations('errors');
const {
organizations,
currentOrganization,
selectOrganization,
createOrganization,
isLoading,
error,
apiError,
clearError,
} = useAuth();
const canCreateOrganization = useMemo(
@@ -82,6 +84,12 @@ export function OrganizationSelectorContent() {
)}
</div>
{apiError && (
<div className="p-3 bg-red-950/30 border border-red-600/40 rounded-[var(--radius-md)]">
<p className="text-sm text-red-600">{getUserFacingError(apiError, tErrors)}</p>
</div>
)}
{canCreateOrganization && isCreateOpen && (
<div className="surface-card p-4 sm:p-6 space-y-4">
<Input
@@ -128,11 +136,6 @@ export function OrganizationSelectorContent() {
</button>
</div>
</div>
{error && (
<div className="p-3 bg-red-950/30 border border-red-600/40 rounded-[var(--radius-md)]">
<p className="text-sm text-red-600">{error}</p>
</div>
)}
<div className="flex flex-col sm:flex-row sm:justify-end">
<Button
type="button"

View File

@@ -22,7 +22,7 @@ import { treatmentCatalogApi } from '@/lib/api/treatment-catalog';
import { treatmentsApi } from '@/lib/api/treatments';
import { pickAutoAppointment } from '@/components/shared/treatmentSelection';
import { canEditTreatment, canViewTreatment } from '@/components/shared/permissions';
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
import { getUserFacingError } from '@/components/shared/formatApiError';
import { useToast } from '@/lib/hooks/useToast';
import type { Organization } from '@/types/organization';
import type { AppointmentRecord } from '@/types/appointment';
@@ -264,6 +264,7 @@ export function TreatmentWorkspace({
initialAppointmentId = null,
}: TreatmentWorkspaceProps) {
const t = useTranslations('treatment');
const tErrors = useTranslations('errors');
const router = useRouter();
const { showError, showSuccess, messages: toastMessages } = useToast();
const canView = canViewTreatment(currentOrganization);
@@ -493,7 +494,7 @@ export function TreatmentWorkspace({
}
} catch (error: unknown) {
if (!cancelled) {
showError(formatApiErrorMessage(error, t('errorLoadAppointments')));
showError(getUserFacingError(error, tErrors, t('errorLoadAppointments')));
}
} finally {
if (!cancelled) setApptsLoading(false);
@@ -534,7 +535,7 @@ export function TreatmentWorkspace({
);
} catch (error: unknown) {
if (!cancelled) {
showError(formatApiErrorMessage(error, t('errorLoadOrgs')));
showError(getUserFacingError(error, tErrors, t('errorLoadOrgs')));
}
}
})();
@@ -563,7 +564,7 @@ export function TreatmentWorkspace({
if (!cancelled) setHistory(response.data);
} catch (error: unknown) {
if (!cancelled) {
showError(formatApiErrorMessage(error, t('errorLoadHistory')));
showError(getUserFacingError(error, tErrors, t('errorLoadHistory')));
}
} finally {
if (!cancelled) setHistoryLoading(false);
@@ -621,7 +622,7 @@ export function TreatmentWorkspace({
setSaveStatus('idle');
} catch (error: unknown) {
if (!cancelled) {
showError(formatApiErrorMessage(error, t('errorLoadDraft')));
showError(getUserFacingError(error, tErrors, t('errorLoadDraft')));
}
} finally {
if (!cancelled) {
@@ -693,7 +694,7 @@ export function TreatmentWorkspace({
setSaveStatus('saved');
} catch (error: unknown) {
setSaveStatus('error');
showError(formatApiErrorMessage(error, t('errorSaveDraft')));
showError(getUserFacingError(error, tErrors, t('errorSaveDraft')));
throw error;
} finally {
saveInFlightRef.current = false;
@@ -711,7 +712,7 @@ export function TreatmentWorkspace({
const response = await treatmentsApi.listPatientHistory(patientId);
setHistory(response.data);
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorLoadHistory')));
showError(getUserFacingError(error, tErrors, t('errorLoadHistory')));
}
}, [showError, t]);
@@ -864,7 +865,7 @@ export function TreatmentWorkspace({
);
showSuccess(t('successFilesUploaded', { count: uploaded.data.length }));
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorUpload')));
showError(getUserFacingError(error, tErrors, t('errorUpload')));
} finally {
setUploadBusyDetailId(null);
}
@@ -954,7 +955,7 @@ export function TreatmentWorkspace({
const saved = await persistDraft({ force: true });
await persistLabCases(saved, cleaned);
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorSaveLabShipments')));
showError(getUserFacingError(error, tErrors, t('errorSaveLabShipments')));
}
})();
}
@@ -999,7 +1000,7 @@ export function TreatmentWorkspace({
const saved = await persistDraft({ force: true });
await persistLabCases(saved, updatedLabCases);
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorSaveLabShipments')));
showError(getUserFacingError(error, tErrors, t('errorSaveLabShipments')));
}
return;
}
@@ -1016,7 +1017,7 @@ export function TreatmentWorkspace({
const saved = await persistDraft({ force: true });
await persistLabCases(saved, updatedLabCases);
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorSaveLabShipments')));
showError(getUserFacingError(error, tErrors, t('errorSaveLabShipments')));
}
}, [
activeDetailId,
@@ -1104,7 +1105,7 @@ export function TreatmentWorkspace({
});
showSuccess(t('successCaseSent'));
} catch (error: unknown) {
showError(formatApiErrorMessage(error, t('errorSendCase')));
showError(getUserFacingError(error, tErrors, t('errorSendCase')));
} finally {
setSendBusyId(null);
}