From 9f2f6442e4733f5be6c2a93446ca73c97c807fb0 Mon Sep 17 00:00:00 2001 From: Jesper Lauridsen Date: Fri, 5 Dec 2025 08:52:48 +0100 Subject: [PATCH 1/6] new picker, updated methods --- src/components/common/TimePicker.vue | 650 ++++++++++++++++++ .../common/timeSearch/DayPicker.vue | 100 +-- src/types/CalendarTypes.ts | 8 + src/utils/datepicker-utils.ts | 52 +- 4 files changed, 728 insertions(+), 82 deletions(-) create mode 100644 src/components/common/TimePicker.vue create mode 100644 src/types/CalendarTypes.ts diff --git a/src/components/common/TimePicker.vue b/src/components/common/TimePicker.vue new file mode 100644 index 00000000..4190f221 --- /dev/null +++ b/src/components/common/TimePicker.vue @@ -0,0 +1,650 @@ + + + + + diff --git a/src/components/common/timeSearch/DayPicker.vue b/src/components/common/timeSearch/DayPicker.vue index b9deb8c4..992a85bc 100644 --- a/src/components/common/timeSearch/DayPicker.vue +++ b/src/components/common/timeSearch/DayPicker.vue @@ -2,38 +2,11 @@
{{ t('datepicker.date') }}: - - -
- {{ t('datepicker.error', { start: formatDate(startYear), end: formatDate(endYear) }) }} -
-
+ :start-date="startYear" + :end-date="endYear" + />
import { defineComponent, onMounted, ref, watch } from 'vue'; import type { DatePickerInstance } from '@vuepic/vue-datepicker'; -import VueDatePicker from '@vuepic/vue-datepicker'; import { days, endDate, @@ -66,7 +38,8 @@ import { RouteLocationRaw } from 'vue-router'; import { addTestDataEnrichment } from '@/utils/test-enrichments'; import { resetAllSelectorValues } from '@/utils/time-search-utils'; import { useI18n } from 'vue-i18n'; -import { updateSelectedDate } from '@/utils/datepicker-utils'; +import { updateDate } from '@/utils/datepicker-utils'; +import TimePicker from '../TimePicker.vue'; interface MonthYearEvent { instance: number; @@ -77,13 +50,13 @@ interface MonthYearEvent { export default defineComponent({ name: 'DayPicker', components: { - VueDatePicker, + TimePicker, }, setup() { let inputTimer: ReturnType | null = null; const singleDatePicker = ref(); - const selectedDate = ref(new Date(2015, 0, 1, 0, 0, 0)); + const selectedDate = ref(new Date(2015, 0, 1, 0, 0, 0)); const singleDayStartDate = ref(new Date(2015, 0, 1, 0, 0, 0)); // January 1, 2015, 00:00:00 const singleDayEndDate = ref(new Date(2015, 0, 1, 23, 59, 59)); // January 1, 2015, 23:59:59 @@ -126,28 +99,20 @@ export default defineComponent({ if (inputTimer !== null) { clearTimeout(inputTimer); } - updateSeeMoreLink(); + updateDate(inputTimer, validDate, selectedDate, updateSeeMoreLink); }, + { deep: true }, ); - const setupInputTimer = (e: Event) => { + const setupInputTimer = () => { if (inputTimer !== null) { clearTimeout(inputTimer); } inputTimer = setTimeout(() => { - updateSelectedDate(e, inputTimer, validDate, selectedDate, updateSeeMoreLink); + updateDate(inputTimer, validDate, selectedDate, updateSeeMoreLink); }, 750); // 750 milliseconds (0.75 second) delay }; - const formatDate = (date: Date) => { - if (!(date instanceof Date)) return ''; - return date.getDate() + '-' + (date.getMonth() + 1) + '-' + date.getFullYear(); - }; - - const textInputOptions = { - format: 'd-M-yyyy', - }; - const specificDayLink = ref({ name: 'Search', query: { @@ -175,8 +140,8 @@ export default defineComponent({ window.scrollTo({ top: 0, behavior: 'smooth' }); }; - const executeUpdate = (e: Event) => { - updateSelectedDate(e, inputTimer, validDate, selectedDate, updateSeeMoreLink); // Pass .value here as well + const executeUpdate = () => { + updateDate(inputTimer, validDate, selectedDate, updateSeeMoreLink); // Pass .value here as well }; const updateSeeMoreLink = () => { @@ -239,13 +204,11 @@ export default defineComponent({ moveToSearchPage, HandleMonthYear, locale, - textInputOptions, - updateSelectedDate, + updateDate, setupInputTimer, inputTimer, validDate, executeUpdate, - formatDate, t, }; }, @@ -260,37 +223,12 @@ export default defineComponent({ } .date-span { - width: 320px; + padding-bottom: 10px; + width: 302px; position: relative; display: block; } -.error-container { - width: 50%; - position: absolute; - height: fit-content; - left: 0px; - top: 55px; - color: white; - padding: 5px; - background-color: rgb(184, 0, 0); - box-sizing: border-box; -} - -.error-container:before { - content: ''; - display: block; - width: 0; - height: 0; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid rgb(184, 0, 0); - top: -7px; - position: absolute; - left: 50%; - transform: translate(-50%, 0%); -} - .picker-container { display: flex; flex-direction: column; @@ -377,8 +315,8 @@ export default defineComponent({ justify-content: space-around; flex-direction: column; align-items: center; - padding-top: 20px; - width: 320px; + padding-top: 0px; + width: 300px; align-items: flex-end; padding-left: 8px; padding-right: 8px; diff --git a/src/types/CalendarTypes.ts b/src/types/CalendarTypes.ts new file mode 100644 index 00000000..b4a18d6a --- /dev/null +++ b/src/types/CalendarTypes.ts @@ -0,0 +1,8 @@ +export interface CalendarDay { + date: Date; + label: number; + inMonth: boolean; + key: string; + disabled: boolean; + ariaLabel: string; +} diff --git a/src/utils/datepicker-utils.ts b/src/utils/datepicker-utils.ts index aadc4b46..1483dc92 100644 --- a/src/utils/datepicker-utils.ts +++ b/src/utils/datepicker-utils.ts @@ -1,6 +1,56 @@ import { endYear, startYear } from '@/components/common/timeSearch/TimeSearchInitValues'; import { Ref } from 'vue'; +const updateDate = ( + timer: ReturnType | null, + isDateValid: Ref, + inputVar: Ref, + updateLink: () => void, +) => { + if (timer) { + clearTimeout(timer); + } + const constructedDate = + inputVar.value.getDate() + '/' + Number(inputVar.value.getMonth() + 1) + '/' + inputVar.value.getFullYear(); + const dateInput = constructedDate.replace(/[.-]/g, '/'); + const splitDateInput = dateInput.split('/'); + if (splitDateInput.length === 3) { + const [day, month, yearInitial] = splitDateInput.map(Number); + let year = yearInitial; + // Validate if day, month, and year are numbers + if (!isNaN(day) && !isNaN(month) && !isNaN(year)) { + // Months in JavaScript Date are 0-indexed, so subtract 1 from the month + if (year.toString().length === 2) { + if (year < Number(new Date().getFullYear().toString().slice(-2))) { + year = Number(`20${year}`); + } else { + year = Number(`19${year}`); + } + } + const setDate = new Date(year, month - 1, day); + // Ensure the date is valid + if (setDate.getDate() === day && setDate.getMonth() === month - 1 && setDate.getFullYear() === year) { + // Validate if the date is within the range + const minDate = new Date(startYear.value); + const maxDate = new Date(endYear.value); + + if (setDate >= minDate && setDate <= maxDate) { + updateLink(); + isDateValid.value = true; + } else { + isDateValid.value = false; + } + } else { + isDateValid.value = false; + } + } else { + isDateValid.value = false; + } + } else { + isDateValid.value = false; + } +}; + const updateSelectedDate = ( e: Event, timer: ReturnType | null, @@ -54,4 +104,4 @@ const updateSelectedDate = ( } }; -export { updateSelectedDate }; +export { updateSelectedDate, updateDate }; From f7bba4d8ecc77fbe8505bd9e79fa5a6d77e166b5 Mon Sep 17 00:00:00 2001 From: Jesper Lauridsen Date: Mon, 8 Dec 2025 14:40:00 +0100 Subject: [PATCH 2/6] improved based on comments from pbruun --- src/components/common/TimePicker.vue | 573 ++++++++++++++------------- src/locales/da.json | 6 + src/locales/en.json | 6 + src/utils/datepicker-utils.ts | 10 +- 4 files changed, 303 insertions(+), 292 deletions(-) diff --git a/src/components/common/TimePicker.vue b/src/components/common/TimePicker.vue index 4190f221..c77757ff 100644 --- a/src/components/common/TimePicker.vue +++ b/src/components/common/TimePicker.vue @@ -8,7 +8,7 @@ :for="inputId" class="visually-hidden" > - Select date + {{ t('calendar.select') }}
@@ -22,6 +22,7 @@ placeholder="DD-MM-YYYY" autocomplete="off" @keydown.enter.prevent="onInputEnter" + @blur="onInputEnter" />
event @@ -47,7 +48,7 @@ :id="hintId" class="visually-hidden" > - Type a date in format DD-MM-YYYY or pick from the calendar. + {{ t('calendar.explanation') }}

-
-
-import { ref, computed, defineProps, defineEmits, watch } from 'vue'; +