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