diff --git a/frontend/src/components/Shared/DateRangePicker.vue b/frontend/src/components/Shared/DateRangePicker.vue index c1a2de7..7f42c1b 100644 --- a/frontend/src/components/Shared/DateRangePicker.vue +++ b/frontend/src/components/Shared/DateRangePicker.vue @@ -80,8 +80,8 @@ const isValidRange = computed(() => { }); const togglePicker = (event) => { - tempRange.value.start = props.startDate; - tempRange.value.end = props.endDate; + tempRange.value.start = props.startDate ? new Date(props.startDate) : null; + tempRange.value.end = props.endDate ? new Date(props.endDate) : null; op.value.toggle(event); }; @@ -105,8 +105,8 @@ const applyPreset = (preset) => { start.setMonth(0, 1); } - tempRange.value.start = start.toISOString().split('T')[0]; - tempRange.value.end = end.toISOString().split('T')[0]; + tempRange.value.start = start; + tempRange.value.end = end; }; const applyRange = () => { diff --git a/frontend/src/services/utils.js b/frontend/src/services/utils.js index 70ca906..3f4aefa 100644 --- a/frontend/src/services/utils.js +++ b/frontend/src/services/utils.js @@ -37,6 +37,8 @@ export const createDateUTC = (year, month, day, hours = 12) => { }; export const formatDateForInput = (date) => { + if (!date) return ''; + if (typeof date === 'string') return date.split('T')[0]; return date.toISOString().split('T')[0]; }; diff --git a/frontend/tests/components/DateRangePicker.spec.js b/frontend/tests/components/DateRangePicker.spec.js index e4340f9..b7bb45d 100644 --- a/frontend/tests/components/DateRangePicker.spec.js +++ b/frontend/tests/components/DateRangePicker.spec.js @@ -68,9 +68,68 @@ describe('DateRangePicker.vue', () => { // Check emitted event const emitted = wrapper.emitted('update:range'); expect(emitted).toBeTruthy(); - expect(emitted[0][0]).toEqual({ - start: '2026-01-01', - end: '2026-01-31' - }); + + const { start, end } = emitted[0][0]; + // Our stubbed DatePicker emits strings because we use setValue on an input + // But the component logic should ideally convert them if it was doing so. + // Wait, the component logic only converts PROPS to Dates in togglePicker. + // Manual selection in the DatePicker stub sets tempRange.start/end directly via v-model. + // In the real app, PrimeVue DatePicker v-model would be a Date object. + // For the test, we'll verify it's at least truthy and matches our input. + expect(start).toBe('2026-01-01'); + expect(end).toBe('2026-01-31'); + }); + + it('emits Date objects when Last 30 Days preset is selected', async () => { + const wrapper = mountComponent(); + + // Open picker + await wrapper.find('button').trigger('click'); + + // Click "Last 30 Days" preset + const presetBtn = wrapper.findAll('button').filter(b => b.text() === 'Last 30 Days')[0]; + await presetBtn.trigger('click'); + + // Click Apply + const applyBtn = wrapper.findAll('button').filter(b => b.text() === 'Apply')[0]; + await applyBtn.trigger('click'); + + const emitted = wrapper.emitted('update:range'); + expect(emitted).toBeTruthy(); + const { start, end } = emitted[0][0]; + + expect(start).toBeInstanceOf(Date); + expect(end).toBeInstanceOf(Date); + + // Verify the range is roughly 30 days + const diffDays = Math.round((end - start) / (1000 * 60 * 60 * 24)); + expect(diffDays).toBe(30); + }); + + it('emits Date objects when This Year preset is selected', async () => { + const wrapper = mountComponent(); + + // Open picker + await wrapper.find('button').trigger('click'); + + // Click "This Year" preset + const presetBtn = wrapper.findAll('button').filter(b => b.text() === 'This Year')[0]; + await presetBtn.trigger('click'); + + // Click Apply + const applyBtn = wrapper.findAll('button').filter(b => b.text() === 'Apply')[0]; + await applyBtn.trigger('click'); + + const emitted = wrapper.emitted('update:range'); + expect(emitted).toBeTruthy(); + const { start, end } = emitted[0][0]; + + expect(start).toBeInstanceOf(Date); + expect(end).toBeInstanceOf(Date); + + const now = new Date(); + expect(start.getFullYear()).toBe(now.getFullYear()); + expect(start.getMonth()).toBe(0); + expect(start.getDate()).toBe(1); }); });