1<template>
2  <b-row class="mb-2">
3    <b-col class="d-sm-flex">
4      <b-form-group
5        :label="$t('global.table.fromDate')"
6        label-for="input-from-date"
7        class="mr-3 my-0 w-100"
8      >
9        <b-input-group>
10          <b-form-input
11            id="input-from-date"
12            v-model="fromDate"
13            placeholder="YYYY-MM-DD"
14            :state="getValidationState(v$.fromDate)"
15            class="form-control-with-button mb-3 mb-md-0"
16            @blur="v$.fromDate.$touch()"
17          />
18          <b-form-invalid-feedback role="alert">
19            <template v-if="v$.fromDate.pattern.$invalid">
20              {{ $t('global.form.invalidFormat') }}
21            </template>
22            <template v-if="v$.fromDate.maxDate.$invalid">
23              {{ $t('global.form.dateMustBeBefore', { date: toDate }) }}
24            </template>
25          </b-form-invalid-feedback>
26          <b-form-datepicker
27            v-model="fromDate"
28            class="btn-datepicker btn-icon-only"
29            button-only
30            right
31            :max="toDate"
32            :hide-header="true"
33            :locale="locale"
34            :label-help="
35              $t('global.calendar.useCursorKeysToNavigateCalendarDates')
36            "
37            :title="$t('global.calendar.selectDate')"
38            button-variant="link"
39            aria-controls="input-from-date"
40          >
41            <template #button-content>
42              <icon-calendar />
43              <span class="sr-only">
44                {{ $t('global.calendar.selectDate') }}
45              </span>
46            </template>
47          </b-form-datepicker>
48        </b-input-group>
49      </b-form-group>
50      <b-form-group
51        :label="$t('global.table.toDate')"
52        label-for="input-to-date"
53        class="my-0 w-100"
54      >
55        <b-input-group>
56          <b-form-input
57            id="input-to-date"
58            v-model="toDate"
59            placeholder="YYYY-MM-DD"
60            :state="getValidationState(v$.toDate)"
61            class="form-control-with-button"
62            @blur="v$.toDate.$touch()"
63          />
64          <b-form-invalid-feedback role="alert">
65            <template v-if="v$.toDate.pattern.$invalid">
66              {{ $t('global.form.invalidFormat') }}
67            </template>
68            <template v-if="v$.toDate.minDate.$invalid">
69              {{ $t('global.form.dateMustBeAfter', { date: fromDate }) }}
70            </template>
71          </b-form-invalid-feedback>
72          <b-form-datepicker
73            v-model="toDate"
74            class="btn-datepicker btn-icon-only"
75            button-only
76            right
77            :min="fromDate"
78            :hide-header="true"
79            :locale="locale"
80            :label-help="
81              $t('global.calendar.useCursorKeysToNavigateCalendarDates')
82            "
83            :title="$t('global.calendar.selectDate')"
84            button-variant="link"
85            aria-controls="input-to-date"
86          >
87            <template #button-content>
88              <icon-calendar />
89              <span class="sr-only">
90                {{ $t('global.calendar.selectDate') }}
91              </span>
92            </template>
93          </b-form-datepicker>
94        </b-input-group>
95      </b-form-group>
96    </b-col>
97  </b-row>
98</template>
99
100<script>
101import IconCalendar from '@carbon/icons-vue/es/calendar/20';
102import { helpers } from 'vuelidate/lib/validators';
103import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
104import { useVuelidate } from '@vuelidate/core';
105import { useI18n } from 'vue-i18n';
106
107const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/;
108
109export default {
110  components: { IconCalendar },
111  mixins: [VuelidateMixin],
112  setup() {
113    return {
114      v$: useVuelidate(),
115    };
116  },
117  data() {
118    return {
119      $t: useI18n().t,
120      fromDate: '',
121      toDate: '',
122      offsetToDate: '',
123      locale: this.$store.getters['global/languagePreference'],
124    };
125  },
126  validations() {
127    return {
128      fromDate: {
129        pattern: helpers.regex('pattern', isoDateRegex),
130        maxDate: (value) => {
131          if (!this.toDate) return true;
132          const date = new Date(value);
133          const maxDate = new Date(this.toDate);
134          if (date.getTime() > maxDate.getTime()) return false;
135          return true;
136        },
137      },
138      toDate: {
139        pattern: helpers.regex('pattern', isoDateRegex),
140        minDate: (value) => {
141          if (!this.fromDate) return true;
142          const date = new Date(value);
143          const minDate = new Date(this.fromDate);
144          if (date.getTime() < minDate.getTime()) return false;
145          return true;
146        },
147      },
148    };
149  },
150  watch: {
151    fromDate() {
152      this.emitChange();
153    },
154    toDate(newVal) {
155      // Offset the end date to end of day to make sure all
156      // entries from selected end date are included in filter
157      this.offsetToDate = new Date(newVal).setUTCHours(23, 59, 59, 999);
158      this.emitChange();
159    },
160  },
161  methods: {
162    emitChange() {
163      if (this.v$.$invalid) return;
164      this.v$.$reset(); //reset to re-validate on blur
165      this.$emit('change', {
166        fromDate: this.fromDate ? new Date(this.fromDate) : null,
167        toDate: this.toDate ? new Date(this.offsetToDate) : null,
168      });
169    },
170  },
171};
172</script>
173