diff --git a/client/src/components/Trips/TripFormModal.test.tsx b/client/src/components/Trips/TripFormModal.test.tsx index 6ede18b7..3c8b390d 100644 --- a/client/src/components/Trips/TripFormModal.test.tsx +++ b/client/src/components/Trips/TripFormModal.test.tsx @@ -288,4 +288,26 @@ describe('TripFormModal', () => { await user.click(submitBtn.closest('button')!); await waitFor(() => expect(screen.getByText('Saving...')).toBeInTheDocument()); }); + + it('FE-COMP-TRIPFORM-029: clearing the day count leaves the field empty (no snap to 1)', () => { + render(); + const dayInput = document.querySelector('input[max="365"]') as HTMLInputElement; + expect(dayInput).toBeInTheDocument(); + expect(dayInput.value).toBe('7'); + fireEvent.change(dayInput, { target: { value: '' } }); + expect(dayInput.value).toBe(''); + }); + + it('FE-COMP-TRIPFORM-030: empty day count blocks submit with an error', async () => { + const user = userEvent.setup(); + const onSave = vi.fn(); + render(); + await user.type(screen.getByPlaceholderText(/Summer in Japan/i), 'No-date Trip'); + const dayInput = document.querySelector('input[max="365"]') as HTMLInputElement; + fireEvent.change(dayInput, { target: { value: '' } }); + const submitBtn = screen.getAllByText('Create New Trip').find(el => el.closest('button'))!; + await user.click(submitBtn.closest('button')!); + await screen.findByText('Number of days is required'); + expect(onSave).not.toHaveBeenCalled(); + }); }); diff --git a/client/src/components/Trips/TripFormModal.tsx b/client/src/components/Trips/TripFormModal.tsx index 7fe17302..291428b1 100644 --- a/client/src/components/Trips/TripFormModal.tsx +++ b/client/src/components/Trips/TripFormModal.tsx @@ -40,7 +40,7 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp start_date: '', end_date: '', reminder_days: 0 as number, - day_count: 7, + day_count: 7 as number | '', }) const [customReminder, setCustomReminder] = useState(false) const [error, setError] = useState('') @@ -100,6 +100,12 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp if (formData.start_date && formData.end_date && new Date(formData.end_date) < new Date(formData.start_date)) { setError(t('dashboard.endDateError')); return } + if (!formData.start_date && !formData.end_date) { + const dc = Number(formData.day_count) + if (formData.day_count === '' || !Number.isInteger(dc) || dc < 1 || dc > 365) { + setError(t('dashboard.dayCountRequired')); return + } + } setIsLoading(true) try { const result = await onSave({ @@ -108,7 +114,7 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp start_date: formData.start_date || null, end_date: formData.end_date || null, reminder_days: formData.reminder_days, - ...(!formData.start_date && !formData.end_date ? { day_count: formData.day_count } : {}), + ...(!formData.start_date && !formData.end_date ? { day_count: Number(formData.day_count) } : {}), }) const createdTrip = result ? result.trip : undefined // Add selected members for newly created trips @@ -320,7 +326,12 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp {t('dashboard.dayCount')} update('day_count', Math.max(1, Math.min(365, Number(e.target.value) || 1)))} + onChange={e => { + const raw = e.target.value + if (raw === '') { update('day_count', ''); return } + const n = Math.floor(Number(raw)) + if (Number.isFinite(n)) update('day_count', Math.min(365, Math.max(1, n))) + }} className={inputCls} />

{t('dashboard.dayCountHint')}

diff --git a/shared/src/i18n/ar/dashboard.ts b/shared/src/i18n/ar/dashboard.ts index 12b12e0e..3384150f 100644 --- a/shared/src/i18n/ar/dashboard.ts +++ b/shared/src/i18n/ar/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'تبديل العملات', 'dashboard.aria.addTimezone': 'إضافة منطقة زمنية', 'dashboard.aria.removeTimezone': 'إزالة {city}', + 'dashboard.dayCountRequired': 'عدد الأيام مطلوب', }; export default dashboard; diff --git a/shared/src/i18n/br/dashboard.ts b/shared/src/i18n/br/dashboard.ts index 9b124b58..f9e256c7 100644 --- a/shared/src/i18n/br/dashboard.ts +++ b/shared/src/i18n/br/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Trocar moedas', 'dashboard.aria.addTimezone': 'Adicionar fuso horário', 'dashboard.aria.removeTimezone': 'Remover {city}', + 'dashboard.dayCountRequired': 'O número de dias é obrigatório', }; export default dashboard; diff --git a/shared/src/i18n/cs/dashboard.ts b/shared/src/i18n/cs/dashboard.ts index e3ded619..690cc905 100644 --- a/shared/src/i18n/cs/dashboard.ts +++ b/shared/src/i18n/cs/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Prohodit měny', 'dashboard.aria.addTimezone': 'Přidat časové pásmo', 'dashboard.aria.removeTimezone': 'Odebrat {city}', + 'dashboard.dayCountRequired': 'Počet dní je povinný', }; export default dashboard; diff --git a/shared/src/i18n/de/dashboard.ts b/shared/src/i18n/de/dashboard.ts index f3acd449..33d2a74d 100644 --- a/shared/src/i18n/de/dashboard.ts +++ b/shared/src/i18n/de/dashboard.ts @@ -164,5 +164,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Währungen tauschen', 'dashboard.aria.addTimezone': 'Zeitzone hinzufügen', 'dashboard.aria.removeTimezone': '{city} entfernen', + 'dashboard.dayCountRequired': 'Anzahl der Tage ist erforderlich', }; export default dashboard; diff --git a/shared/src/i18n/en/dashboard.ts b/shared/src/i18n/en/dashboard.ts index d2ee9b69..08be632a 100644 --- a/shared/src/i18n/en/dashboard.ts +++ b/shared/src/i18n/en/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Swap currencies', 'dashboard.aria.addTimezone': 'Add timezone', 'dashboard.aria.removeTimezone': 'Remove {city}', + 'dashboard.dayCountRequired': 'Number of days is required', }; export default dashboard; diff --git a/shared/src/i18n/es/dashboard.ts b/shared/src/i18n/es/dashboard.ts index d1f53d80..6c3aec54 100644 --- a/shared/src/i18n/es/dashboard.ts +++ b/shared/src/i18n/es/dashboard.ts @@ -164,5 +164,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Intercambiar monedas', 'dashboard.aria.addTimezone': 'Añadir zona horaria', 'dashboard.aria.removeTimezone': 'Eliminar {city}', + 'dashboard.dayCountRequired': 'El número de días es obligatorio', }; export default dashboard; diff --git a/shared/src/i18n/fr/dashboard.ts b/shared/src/i18n/fr/dashboard.ts index 42e39430..f19bd430 100644 --- a/shared/src/i18n/fr/dashboard.ts +++ b/shared/src/i18n/fr/dashboard.ts @@ -167,5 +167,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Inverser les devises', 'dashboard.aria.addTimezone': 'Ajouter un fuseau horaire', 'dashboard.aria.removeTimezone': 'Supprimer {city}', + 'dashboard.dayCountRequired': 'Le nombre de jours est requis', }; export default dashboard; diff --git a/shared/src/i18n/gr/dashboard.ts b/shared/src/i18n/gr/dashboard.ts index ed7a018c..ee81cbc9 100644 --- a/shared/src/i18n/gr/dashboard.ts +++ b/shared/src/i18n/gr/dashboard.ts @@ -166,5 +166,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Swap currencies', // en-fallback 'dashboard.aria.addTimezone': 'Add timezone', // en-fallback 'dashboard.aria.removeTimezone': 'Remove {city}', // en-fallback + 'dashboard.dayCountRequired': 'Ο αριθμός ημερών είναι υποχρεωτικός', }; export default dashboard; diff --git a/shared/src/i18n/hu/dashboard.ts b/shared/src/i18n/hu/dashboard.ts index f829ffd2..91d35438 100644 --- a/shared/src/i18n/hu/dashboard.ts +++ b/shared/src/i18n/hu/dashboard.ts @@ -164,5 +164,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Pénznemek cseréje', 'dashboard.aria.addTimezone': 'Időzóna hozzáadása', 'dashboard.aria.removeTimezone': '{city} eltávolítása', + 'dashboard.dayCountRequired': 'A napok száma kötelező', }; export default dashboard; diff --git a/shared/src/i18n/id/dashboard.ts b/shared/src/i18n/id/dashboard.ts index bb84b36e..6def30a8 100644 --- a/shared/src/i18n/id/dashboard.ts +++ b/shared/src/i18n/id/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Tukar mata uang', 'dashboard.aria.addTimezone': 'Tambah zona waktu', 'dashboard.aria.removeTimezone': 'Hapus {city}', + 'dashboard.dayCountRequired': 'Jumlah hari wajib diisi', }; export default dashboard; diff --git a/shared/src/i18n/it/dashboard.ts b/shared/src/i18n/it/dashboard.ts index 3a704c2f..b6c73a80 100644 --- a/shared/src/i18n/it/dashboard.ts +++ b/shared/src/i18n/it/dashboard.ts @@ -167,5 +167,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Inverti valute', 'dashboard.aria.addTimezone': 'Aggiungi fuso orario', 'dashboard.aria.removeTimezone': 'Rimuovi {city}', + 'dashboard.dayCountRequired': 'Il numero di giorni è obbligatorio', }; export default dashboard; diff --git a/shared/src/i18n/ja/dashboard.ts b/shared/src/i18n/ja/dashboard.ts index ab1e1f27..c49f1094 100644 --- a/shared/src/i18n/ja/dashboard.ts +++ b/shared/src/i18n/ja/dashboard.ts @@ -162,5 +162,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': '通貨を入れ替え', 'dashboard.aria.addTimezone': 'タイムゾーンを追加', 'dashboard.aria.removeTimezone': '{city}を削除', + 'dashboard.dayCountRequired': '日数は必須です', }; export default dashboard; diff --git a/shared/src/i18n/ko/dashboard.ts b/shared/src/i18n/ko/dashboard.ts index 98f4fe86..801272c2 100644 --- a/shared/src/i18n/ko/dashboard.ts +++ b/shared/src/i18n/ko/dashboard.ts @@ -162,5 +162,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': '통화 바꾸기', 'dashboard.aria.addTimezone': '시간대 추가', 'dashboard.aria.removeTimezone': '{city} 제거', + 'dashboard.dayCountRequired': '일수는 필수입니다', }; export default dashboard; diff --git a/shared/src/i18n/nl/dashboard.ts b/shared/src/i18n/nl/dashboard.ts index 9f3c61d6..e339c596 100644 --- a/shared/src/i18n/nl/dashboard.ts +++ b/shared/src/i18n/nl/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': "Valuta's omwisselen", 'dashboard.aria.addTimezone': 'Tijdzone toevoegen', 'dashboard.aria.removeTimezone': '{city} verwijderen', + 'dashboard.dayCountRequired': 'Aantal dagen is verplicht', }; export default dashboard; diff --git a/shared/src/i18n/pl/dashboard.ts b/shared/src/i18n/pl/dashboard.ts index dc823707..9aeab647 100644 --- a/shared/src/i18n/pl/dashboard.ts +++ b/shared/src/i18n/pl/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Zamień waluty', 'dashboard.aria.addTimezone': 'Dodaj strefę czasową', 'dashboard.aria.removeTimezone': 'Usuń {city}', + 'dashboard.dayCountRequired': 'Liczba dni jest wymagana', }; export default dashboard; diff --git a/shared/src/i18n/ru/dashboard.ts b/shared/src/i18n/ru/dashboard.ts index c9065b81..c2ac8cf1 100644 --- a/shared/src/i18n/ru/dashboard.ts +++ b/shared/src/i18n/ru/dashboard.ts @@ -163,5 +163,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Поменять валюты', 'dashboard.aria.addTimezone': 'Добавить часовой пояс', 'dashboard.aria.removeTimezone': 'Удалить {city}', + 'dashboard.dayCountRequired': 'Количество дней обязательно', }; export default dashboard; diff --git a/shared/src/i18n/tr/dashboard.ts b/shared/src/i18n/tr/dashboard.ts index 40fffa8f..f3003626 100644 --- a/shared/src/i18n/tr/dashboard.ts +++ b/shared/src/i18n/tr/dashboard.ts @@ -162,5 +162,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Para birimlerini değiştir', 'dashboard.aria.addTimezone': 'Saat dilimi ekle', 'dashboard.aria.removeTimezone': '{city} kaldır', + 'dashboard.dayCountRequired': 'Gün sayısı gereklidir', }; export default dashboard; diff --git a/shared/src/i18n/uk/dashboard.ts b/shared/src/i18n/uk/dashboard.ts index 15ab12ee..0060d66e 100644 --- a/shared/src/i18n/uk/dashboard.ts +++ b/shared/src/i18n/uk/dashboard.ts @@ -164,5 +164,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': 'Поміняти валюти', 'dashboard.aria.addTimezone': 'Додати часовий пояс', 'dashboard.aria.removeTimezone': 'Вилучити {city}', + 'dashboard.dayCountRequired': 'Вкажіть кількість днів', }; export default dashboard; diff --git a/shared/src/i18n/zh-TW/dashboard.ts b/shared/src/i18n/zh-TW/dashboard.ts index 9ba7d90a..61edec6f 100644 --- a/shared/src/i18n/zh-TW/dashboard.ts +++ b/shared/src/i18n/zh-TW/dashboard.ts @@ -161,5 +161,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': '交換貨幣', 'dashboard.aria.addTimezone': '新增時區', 'dashboard.aria.removeTimezone': '移除 {city}', + 'dashboard.dayCountRequired': '天數為必填項', }; export default dashboard; diff --git a/shared/src/i18n/zh/dashboard.ts b/shared/src/i18n/zh/dashboard.ts index 16e50e8d..688eeb0a 100644 --- a/shared/src/i18n/zh/dashboard.ts +++ b/shared/src/i18n/zh/dashboard.ts @@ -161,5 +161,6 @@ const dashboard: TranslationStrings = { 'dashboard.aria.swapCurrencies': '交换货币', 'dashboard.aria.addTimezone': '添加时区', 'dashboard.aria.removeTimezone': '移除 {city}', + 'dashboard.dayCountRequired': '天数为必填项', }; export default dashboard;