diff --git a/client/src/i18n/locales/en-US.ts b/client/src/i18n/locales/en-US.ts index fad1f80..d8e8d49 100644 --- a/client/src/i18n/locales/en-US.ts +++ b/client/src/i18n/locales/en-US.ts @@ -283,6 +283,7 @@ export default { departureDate: 'Departure date', roomRateNote: 'Room rate note', roomRateNotePlaceholder: 'e.g. including BF', + datePlaceholder: 'yyyy-mm-dd', extraBedRate: 'Extra bed rate', description: 'Description', roomType: 'Room type', diff --git a/client/src/i18n/locales/th-TH.ts b/client/src/i18n/locales/th-TH.ts index 24bebef..4321fa5 100644 --- a/client/src/i18n/locales/th-TH.ts +++ b/client/src/i18n/locales/th-TH.ts @@ -283,6 +283,7 @@ export default { departureDate: 'Departure Date', roomRateNote: 'Room Rate Note', roomRateNotePlaceholder: 'เช่น including BF', + datePlaceholder: 'yyyy-mm-dd', extraBedRate: 'Extra Bed Rate', description: 'Description', roomType: 'Room Type', diff --git a/client/src/i18n/locales/zh-CN.ts b/client/src/i18n/locales/zh-CN.ts index cb15082..53b9158 100644 --- a/client/src/i18n/locales/zh-CN.ts +++ b/client/src/i18n/locales/zh-CN.ts @@ -283,6 +283,7 @@ export default { departureDate: '离店日期(Departure Date)', roomRateNote: '房价备注(Room Rate Note)', roomRateNotePlaceholder: '例如 including BF', + datePlaceholder: '年-月-日', extraBedRate: '加床价格(Extra Bed Rate)', description: '费用描述(Description)', roomType: '房型(Room Type)', diff --git a/client/src/tests/manualInvoiceView.spec.ts b/client/src/tests/manualInvoiceView.spec.ts index 3def374..4c95079 100644 --- a/client/src/tests/manualInvoiceView.spec.ts +++ b/client/src/tests/manualInvoiceView.spec.ts @@ -1,5 +1,6 @@ import { flushPromises, mount } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' +import PrimeVue from 'primevue/config' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { nextTick } from 'vue' import { createI18n } from 'vue-i18n' @@ -89,7 +90,7 @@ function mountView(options: { timeZone?: string; extraHotels?: AuthHotelResult[] const wrapper = mount(ReservationManualInvoiceView, { global: { - plugins: [i18n, pinia], + plugins: [i18n, pinia, PrimeVue], }, }) return Object.assign(wrapper, { @@ -120,6 +121,20 @@ describe('ReservationManualInvoiceView', () => { beforeEach(() => { vi.mocked(service.generateManualReservationInvoice).mockReset() sessionStorage.clear() + Object.defineProperty(window, 'matchMedia', { + configurable: true, + writable: true, + value: vi.fn().mockImplementation((query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }) Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: vi.fn(() => 'blob:manual-invoice-pdf'), @@ -135,6 +150,7 @@ describe('ReservationManualInvoiceView', () => { vi.useRealTimers() vi.unstubAllGlobals() vi.restoreAllMocks() + document.body.innerHTML = '' if (nativeCreateObjectURL) { Object.defineProperty(URL, 'createObjectURL', { configurable: true, @@ -276,13 +292,26 @@ describe('ReservationManualInvoiceView', () => { expect(wrapper.text()).not.toContain('说明') }) - it('passes current locale to native date inputs', () => { + it('uses localized DatePicker controls for date fields', async () => { const wrapper = mountView({ locale: 'th-TH' }) + const arrivalDateInput = wrapper.find('[data-testid="booking-arrival-date"]') - expect(wrapper.find('[data-testid="document-booking-date"]').attributes('lang')).toBe('th-TH') - expect(wrapper.find('[data-testid="booking-arrival-date"]').attributes('lang')).toBe('th-TH') - expect(wrapper.find('[data-testid="booking-departure-date"]').attributes('lang')).toBe('th-TH') - expect(wrapper.find('[data-testid="document-due-date"]').attributes('lang')).toBe('th-TH') + expect(arrivalDateInput.attributes('type')).toBe('text') + expect(arrivalDateInput.attributes('placeholder')).toBe(thTH.manualInvoice.datePlaceholder) + expect(arrivalDateInput.attributes('lang')).toBe('th-TH') + expect(wrapper.find('[data-testid="document-booking-date"]').attributes('type')).toBe('text') + expect(wrapper.find('[data-testid="booking-departure-date"]').attributes('type')).toBe('text') + expect(wrapper.find('[data-testid="document-due-date"]').attributes('type')).toBe('text') + + await arrivalDateInput.setValue('2026-07-17') + await arrivalDateInput.trigger('click') + await flushPromises() + + const arrivalDatePanel = document.body.querySelector('[data-testid="booking-arrival-date-panel"]') + + expect(wrapper.find('[data-testid="booking-arrival-date-panel"]').exists()).toBe(false) + expect(arrivalDatePanel?.textContent).toContain('กรกฎาคม') + expect(arrivalDatePanel?.textContent).not.toContain('2026年07月') }) it('marks required fields and highlights empty inputs after submit', async () => { diff --git a/client/src/views/reservation/ReservationManualInvoiceView.vue b/client/src/views/reservation/ReservationManualInvoiceView.vue index 593568d..8578657 100644 --- a/client/src/views/reservation/ReservationManualInvoiceView.vue +++ b/client/src/views/reservation/ReservationManualInvoiceView.vue @@ -201,17 +201,23 @@ >Booking Date
Arrival Date
Departure Date
Due Date