diff --git a/client/src/components/Journey/JourneyDetailPageEntryEditor.tsx b/client/src/components/Journey/JourneyDetailPageEntryEditor.tsx index 6c8d9d5e2..e4c49980d 100644 --- a/client/src/components/Journey/JourneyDetailPageEntryEditor.tsx +++ b/client/src/components/Journey/JourneyDetailPageEntryEditor.tsx @@ -149,7 +149,7 @@ export function EntryEditor({ entry, journeyId, tripDates, galleryPhotos, trips, handleSave, handleFileChange, pickGalleryPhoto, removePhoto, searchLocation, pickLocation, handleUseCurrentLocation, } = useJourneyEntryForm({ entry, journeyId, trips, galleryPhotos, onSave, onUploadPhotos, onAddProviderPhotos, onDone, - blankVerdictRow: true, dirtyOnCoordinates: true, toastSaveError: true, autoFillWeather: showWeather, + blankVerdictRow: true, dirtyOnCoordinates: true, autoFillWeather: showWeather, }) const [photoTab, setPhotoTab] = useState<'upload' | 'gallery' | 'external'>('upload') const [availableProviders, setAvailableProviders] = useState<{ id: string; name: string }[]>([]) diff --git a/client/src/components/Journey/useJourneyEntryForm.test.ts b/client/src/components/Journey/useJourneyEntryForm.test.ts index 43877bb63..49225ae4c 100644 --- a/client/src/components/Journey/useJourneyEntryForm.test.ts +++ b/client/src/components/Journey/useJourneyEntryForm.test.ts @@ -1,4 +1,4 @@ -// FE-JRN-ENTRYFORM-001 to FE-JRN-ENTRYFORM-020: the journey entry form logic behind both +// FE-JRN-ENTRYFORM-001 to FE-JRN-ENTRYFORM-021: the journey entry form logic behind both // the desktop editor dialog and the phone entry sheet. import { act, renderHook, waitFor } from '@testing-library/react'; import type React from 'react'; @@ -198,9 +198,9 @@ describe('useJourneyEntryForm', () => { ); }); - it('FE-JRN-ENTRYFORM-007: a failed save toasts for the desktop dialog and rejects for the sheet', async () => { + it('FE-JRN-ENTRYFORM-007: a failed save toasts for the desktop dialog and the sheet alike', async () => { const onSave = vi.fn().mockRejectedValue({ response: { data: { error: 'Locked' } } }); - const desktop = setup({ onSave, toastSaveError: true }); + const desktop = setup({ onSave, blankVerdictRow: true, dirtyOnCoordinates: true }); await act(() => desktop.result.current.handleSave()); expect(addToast).toHaveBeenCalledWith('Locked', 'error', undefined); expect(desktop.result.current.saving).toBe(false); @@ -208,9 +208,9 @@ describe('useJourneyEntryForm', () => { const sheet = setup({ onSave }); let rejected = false; await act(() => sheet.result.current.handleSave().catch(() => void (rejected = true))); - expect(rejected).toBe(true); + expect(rejected).toBe(false); expect(sheet.result.current.saving).toBe(false); - expect(addToast).toHaveBeenCalledTimes(1); + expect(addToast).toHaveBeenCalledTimes(2); }); it('FE-JRN-ENTRYFORM-008: links gallery photos picked before the first save', async () => { @@ -378,4 +378,14 @@ describe('useJourneyEntryForm', () => { .offersStatsToggle ).toBe(true); }); + + it('FE-JRN-ENTRYFORM-021: a failed save on the phone sheet toasts and keeps the form open', async () => { + const onSave = vi.fn().mockRejectedValue({ response: { data: { error: 'Locked' } } }); + const onDone = vi.fn(); + const sheet = setup({ onSave, onDone, withTags: true }); + await act(() => sheet.result.current.handleSave()); + expect(addToast).toHaveBeenCalledWith('Locked', 'error', undefined); + expect(onDone).not.toHaveBeenCalled(); + expect(sheet.result.current.saving).toBe(false); + }); }); diff --git a/client/src/components/Journey/useJourneyEntryForm.ts b/client/src/components/Journey/useJourneyEntryForm.ts index 33e998826..808f18d7d 100644 --- a/client/src/components/Journey/useJourneyEntryForm.ts +++ b/client/src/components/Journey/useJourneyEntryForm.ts @@ -49,8 +49,6 @@ export interface JourneyEntryFormOptions { withTags?: boolean; /** Moving the point alone, without renaming it, counts as an unsaved change (the desktop dialog). */ dirtyOnCoordinates?: boolean; - /** Toast a failed save and swallow it (the desktop dialog); otherwise the save rejects. */ - toastSaveError?: boolean; /** Show a failed "use my location" under the field (the phone sheet) instead of as a toast. */ inlineLocateError?: boolean; /** Fill an empty weather field from the forecast once the place and day are known (the desktop dialog). */ @@ -81,7 +79,6 @@ export function useJourneyEntryForm({ blankVerdictRow = false, withTags = false, dirtyOnCoordinates = false, - toastSaveError = false, inlineLocateError = false, autoFillWeather = false, }: JourneyEntryFormOptions) { @@ -378,7 +375,6 @@ export function useJourneyEntryForm({ } onDone(); } catch (err) { - if (!toastSaveError) throw err; // Neither the page callback nor journeyStore toasts, so without this the // whole entry just fails to save with no sign of it. toast.error(getApiErrorMessage(err, t('journey.settings.saveFailed')));