mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
fix(forms): show the form's own sentence when a save is refused
The custom metric, allergy, vaccination, booster, visit, address-book, workout and quick-add dose forms printed the server's error string as it came: English in every locale, and for a network failure the browser's own TypeError text. They now resolve the error code through localizedApiError like the other entry forms, so a known refusal reads in the person's language and anything else falls back to the form's "could not save" sentence. A custom metric whose name is taken keeps its specific message, now translated. The workout form's browser test expected the server prose and now expects the form's sentence.
This commit is contained in:
1 parent
7001308636
commit
121b23f4a2
21 files changed
+57
-52
No files matched your search
@@ -11,7 +11,8 @@ import { DateTimeField } from "@/components/ui/date-time-field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ApiError, apiPost } from "@/lib/api/api-fetch";
|
||||
import { apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
|
||||
@@ -99,11 +100,7 @@ export function CustomMetricEntryForm({
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof ApiError
|
||||
? err.message
|
||||
: t("customMetrics.entry.saveError");
|
||||
setError(message);
|
||||
setError(localizedApiError(err, t, "customMetrics.entry.saveError"));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ApiError, apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
import type { CustomMetricDto } from "./types";
|
||||
@@ -122,11 +123,11 @@ export function CustomMetricForm({
|
||||
);
|
||||
onSuccess?.(saved);
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof ApiError
|
||||
? err.message
|
||||
: t("customMetrics.form.saveError");
|
||||
setError(message);
|
||||
setError(
|
||||
err instanceof ApiError && err.status === 409
|
||||
? t("customMetrics.form.nameTaken")
|
||||
: localizedApiError(err, t, "customMetrics.form.saveError"),
|
||||
);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import {
|
||||
} from "@/components/ui/table";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useTableSort } from "@/hooks/use-table-sort";
|
||||
import { ApiError, apiDelete, apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
import { apiDelete, apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { formatDateShort } from "@/lib/format";
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
@@ -140,11 +141,7 @@ export function CustomMetricHistoryList({
|
||||
closeEdit();
|
||||
},
|
||||
onError: (err) => {
|
||||
setEditError(
|
||||
err instanceof ApiError
|
||||
? err.message
|
||||
: t("customMetrics.entry.editError"),
|
||||
);
|
||||
setEditError(localizedApiError(err, t, "customMetrics.entry.editError"));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
} from "@/lib/medications/default-medication";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useActiveRecordName } from "@/hooks/use-record-capabilities";
|
||||
import { ApiError, apiGet, apiPost } from "@/lib/api/api-fetch";
|
||||
import { apiGet, apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import {
|
||||
intakeToastOptions,
|
||||
runUndoIntake,
|
||||
@@ -270,9 +271,11 @@ export function MedicationIntakeQuickAdd({
|
||||
void invalidateMedicationReads(queryClient);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof ApiError && err.message
|
||||
? err.message
|
||||
: t("dashboard.medicationIntakeQuickAdd.saveError"),
|
||||
localizedApiError(
|
||||
err,
|
||||
t,
|
||||
"dashboard.medicationIntakeQuickAdd.saveError",
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { toastWrittenOutcome } from "@/components/outcome/outcome-toast";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { useEncounterMutations, type Encounter } from "@/hooks/use-encounters";
|
||||
import {
|
||||
@@ -128,8 +129,7 @@ export function EncounterSheet({
|
||||
onSaved?.(saved);
|
||||
onOpenChange(false);
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : t("encounters.saveFailed");
|
||||
const message = localizedApiError(err, t, "encounters.saveFailed");
|
||||
setError(message);
|
||||
showErrorToast(message, { cause: err });
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { FieldGroup } from "@/components/ui/field-group";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import {
|
||||
usePractitionerMutations,
|
||||
@@ -104,8 +105,7 @@ export function PractitionerSheet({
|
||||
onSaved?.(saved);
|
||||
onOpenChange(false);
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : t("practitioners.saveFailed");
|
||||
const message = localizedApiError(err, t, "practitioners.saveFailed");
|
||||
setError(message);
|
||||
showErrorToast(message, { cause: err });
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ApiError, apiPost, apiPut } from "@/lib/api/api-fetch";
|
||||
import { apiPost, apiPut } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import {
|
||||
allergyCategoryEnum,
|
||||
allergySeverityEnum,
|
||||
@@ -113,11 +114,7 @@ export function AllergyForm({
|
||||
);
|
||||
onSuccess?.(saved);
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof ApiError
|
||||
? err.message
|
||||
: t("records.allergies.form.saveError");
|
||||
setError(message);
|
||||
setError(localizedApiError(err, t, "records.allergies.form.saveError"));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
import { FieldGroup } from "@/components/ui/field-group";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { NativeSelect } from "@/components/ui/native-select";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import {
|
||||
customLookupOf,
|
||||
@@ -105,10 +106,9 @@ export function BoosterMintPrompt({
|
||||
});
|
||||
toastWrittenOutcome("success", t("vaccinations.booster.planned"));
|
||||
} catch (err) {
|
||||
showErrorToast(
|
||||
err instanceof Error ? err.message : t("vaccinations.booster.failed"),
|
||||
{ cause: err },
|
||||
);
|
||||
showErrorToast(localizedApiError(err, t, "vaccinations.booster.failed"), {
|
||||
cause: err,
|
||||
});
|
||||
} finally {
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { toastWrittenOutcome } from "@/components/outcome/outcome-toast";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { RelatedRecordsBlock } from "@/components/links/related-records";
|
||||
import type { RelatedLink } from "@/lib/links/related";
|
||||
@@ -99,8 +100,7 @@ export function VaccinationSheet({
|
||||
onCreated?.(created);
|
||||
}
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : t("vaccinations.saveFailed");
|
||||
const message = localizedApiError(err, t, "vaccinations.saveFailed");
|
||||
setError(message);
|
||||
showErrorToast(message, { cause: err });
|
||||
}
|
||||
|
||||
@@ -128,9 +128,10 @@ describe("<ManualWorkoutForm>", () => {
|
||||
await pickSport("Running");
|
||||
await screen.getByLabelText("Minutes").fill("30");
|
||||
await save.click();
|
||||
// The form's own sentence, never the server's English prose.
|
||||
await expect
|
||||
.element(screen.getByRole("alert"))
|
||||
.toHaveTextContent("Service unavailable");
|
||||
.toHaveTextContent("The workout could not be saved");
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
|
||||
api.on("POST", BATCH, {
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useUnitDisplay } from "@/hooks/use-unit-display";
|
||||
import { ApiError, apiPost } from "@/lib/api/api-fetch";
|
||||
import { apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { getQuantityTransform } from "@/lib/measurements/display-transform";
|
||||
import {
|
||||
@@ -232,9 +233,7 @@ export function ManualWorkoutForm({
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
setSaveError(
|
||||
err instanceof ApiError && err.message
|
||||
? err.message
|
||||
: t("insights.workouts.manual.saveError"),
|
||||
localizedApiError(err, t, "insights.workouts.manual.saveError"),
|
||||
);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
|
||||
Reference in new issue
Block a user