@bcrs-shared-components/effective-date-time 1.1.52 → 1.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EffectiveDateTime.stories.ts +27 -27
- package/EffectiveDateTime.vue +525 -525
- package/LICENSE +201 -201
- package/index.ts +1 -1
- package/package.json +6 -6
package/EffectiveDateTime.vue
CHANGED
|
@@ -1,525 +1,525 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card
|
|
3
|
-
id="effective-date-time-box"
|
|
4
|
-
flat
|
|
5
|
-
>
|
|
6
|
-
<v-radio-group
|
|
7
|
-
v-model="effectiveDateType"
|
|
8
|
-
column
|
|
9
|
-
class="pt-0 mt-0"
|
|
10
|
-
>
|
|
11
|
-
<v-radio
|
|
12
|
-
label="Immediate (date and time of filing)"
|
|
13
|
-
:value="EffectiveDateTypes.IMMEDIATE"
|
|
14
|
-
/>
|
|
15
|
-
<v-radio
|
|
16
|
-
label="A date and time in the future"
|
|
17
|
-
:value="EffectiveDateTypes.FUTURE_EFFECTIVE"
|
|
18
|
-
/>
|
|
19
|
-
</v-radio-group>
|
|
20
|
-
|
|
21
|
-
<v-form
|
|
22
|
-
ref="form"
|
|
23
|
-
class="date-time-selectors"
|
|
24
|
-
>
|
|
25
|
-
<DatePicker
|
|
26
|
-
ref="datePickerRef"
|
|
27
|
-
title="Date"
|
|
28
|
-
nudge-right="40"
|
|
29
|
-
:inputRules="dateRules"
|
|
30
|
-
:disablePicker="effectiveDateType !== EffectiveDateTypes.FUTURE_EFFECTIVE"
|
|
31
|
-
:minDate="dateToYyyyMmDd(minDate)"
|
|
32
|
-
:maxDate="dateToYyyyMmDd(maxDate)"
|
|
33
|
-
@emitDate="dateText = $event"
|
|
34
|
-
@emitCancel="dateText = ''"
|
|
35
|
-
/>
|
|
36
|
-
|
|
37
|
-
<v-row>
|
|
38
|
-
<v-col
|
|
39
|
-
cols="12"
|
|
40
|
-
sm="6"
|
|
41
|
-
md="3"
|
|
42
|
-
>
|
|
43
|
-
<v-combobox
|
|
44
|
-
id="hour-selector"
|
|
45
|
-
ref="hourSelector"
|
|
46
|
-
v-model="selectHour"
|
|
47
|
-
filled
|
|
48
|
-
class="mr-1"
|
|
49
|
-
label="Hour"
|
|
50
|
-
:items="hours"
|
|
51
|
-
:disabled="!isFutureEffective"
|
|
52
|
-
:rules="hourRules"
|
|
53
|
-
/>
|
|
54
|
-
</v-col>
|
|
55
|
-
<span
|
|
56
|
-
class="time-colon"
|
|
57
|
-
:class="{ 'disabled': !isFutureEffective }"
|
|
58
|
-
>:</span>
|
|
59
|
-
<v-col
|
|
60
|
-
cols="12"
|
|
61
|
-
sm="6"
|
|
62
|
-
md="3"
|
|
63
|
-
>
|
|
64
|
-
<v-combobox
|
|
65
|
-
id="minute-selector"
|
|
66
|
-
ref="minuteSelector"
|
|
67
|
-
v-model="selectMinute"
|
|
68
|
-
filled
|
|
69
|
-
class="ml-1"
|
|
70
|
-
label="Minute"
|
|
71
|
-
:items="minutes"
|
|
72
|
-
:disabled="!isFutureEffective"
|
|
73
|
-
:rules="minuteRules"
|
|
74
|
-
/>
|
|
75
|
-
</v-col>
|
|
76
|
-
<v-col
|
|
77
|
-
cols="12"
|
|
78
|
-
sm="6"
|
|
79
|
-
md="3"
|
|
80
|
-
>
|
|
81
|
-
<v-select
|
|
82
|
-
id="period-selector"
|
|
83
|
-
v-model="selectPeriod"
|
|
84
|
-
filled
|
|
85
|
-
:items="timePeriod"
|
|
86
|
-
:disabled="!isFutureEffective"
|
|
87
|
-
/>
|
|
88
|
-
</v-col>
|
|
89
|
-
<v-col
|
|
90
|
-
cols="12"
|
|
91
|
-
sm="6"
|
|
92
|
-
md="3"
|
|
93
|
-
class="label-col"
|
|
94
|
-
>
|
|
95
|
-
<span
|
|
96
|
-
class="time-zone-label"
|
|
97
|
-
:class="{ 'disabled': !isFutureEffective }"
|
|
98
|
-
>Pacific time</span>
|
|
99
|
-
</v-col>
|
|
100
|
-
</v-row>
|
|
101
|
-
|
|
102
|
-
<!-- display validation alert only after date and time have been entered -->
|
|
103
|
-
<v-row v-if="isFutureEffective && dateText && (selectHour.length > 0) && (selectMinute.length > 0)">
|
|
104
|
-
<v-col class="validation-alert">
|
|
105
|
-
<p
|
|
106
|
-
v-if="isUnderTime"
|
|
107
|
-
class="validation-alert-msg"
|
|
108
|
-
>
|
|
109
|
-
The time must be at least {{ dateToPacificTime(minDate) }} for the selected date
|
|
110
|
-
</p>
|
|
111
|
-
<p
|
|
112
|
-
v-if="isOverTime"
|
|
113
|
-
class="validation-alert-msg"
|
|
114
|
-
>
|
|
115
|
-
The time must be at most {{ dateToPacificTime(maxDate) }} for the selected date
|
|
116
|
-
</p>
|
|
117
|
-
</v-col>
|
|
118
|
-
</v-row>
|
|
119
|
-
</v-form>
|
|
120
|
-
</v-card>
|
|
121
|
-
</template>
|
|
122
|
-
|
|
123
|
-
<script lang="ts">
|
|
124
|
-
import Vue from 'vue'
|
|
125
|
-
import { Component, Emit, Mixins, Prop, Watch } from 'vue-property-decorator'
|
|
126
|
-
import { DatePicker } from '@bcrs-shared-components/date-picker'
|
|
127
|
-
import { DateMixin } from '@/mixins' // NB: local mixin (StoryBook can't find it otherwise)
|
|
128
|
-
import { EffectiveDateTypes } from '@bcrs-shared-components/enums'
|
|
129
|
-
import { EffectiveDateTimeIF, FormFieldType, FormIF } from '@bcrs-shared-components/interfaces'
|
|
130
|
-
|
|
131
|
-
enum PeriodTypes {
|
|
132
|
-
AM = 'am',
|
|
133
|
-
PM = 'pm'
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
@Component({
|
|
137
|
-
components: {
|
|
138
|
-
DatePicker
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
export default class EffectiveDateTime extends Mixins(DateMixin) {
|
|
142
|
-
readonly MIN_DIFF_MINUTES = 3
|
|
143
|
-
readonly MAX_DIFF_DAYS = 10
|
|
144
|
-
|
|
145
|
-
// Add element types to refs
|
|
146
|
-
$refs!: {
|
|
147
|
-
form: FormIF,
|
|
148
|
-
datePickerRef: any, // should be DatePicker but TS complains
|
|
149
|
-
hourSelector: FormFieldType, // used in unit tests
|
|
150
|
-
minuteSelector: FormFieldType // used in unit tests
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** Whether to parse the initial effective date-time into the controls. */
|
|
154
|
-
@Prop({ default: false }) readonly parseInitial!: boolean
|
|
155
|
-
|
|
156
|
-
/** Current JS date, expected to be passed in periodically. */
|
|
157
|
-
@Prop() readonly currentJsDate!: Date
|
|
158
|
-
|
|
159
|
-
/** Effective Date Time object, for initial config. */
|
|
160
|
-
@Prop() readonly effectiveDateTime!: EffectiveDateTimeIF
|
|
161
|
-
|
|
162
|
-
/** Whether to perform validation. */
|
|
163
|
-
@Prop() readonly isAppValidate!: boolean
|
|
164
|
-
|
|
165
|
-
// Declaration for template
|
|
166
|
-
readonly EffectiveDateTypes = EffectiveDateTypes
|
|
167
|
-
|
|
168
|
-
/** Whether Is Immediate is selected. */
|
|
169
|
-
private isImmediate = false
|
|
170
|
-
|
|
171
|
-
/** Whether Is Future Effective is selected. */
|
|
172
|
-
private isFutureEffective = false
|
|
173
|
-
|
|
174
|
-
/** The minimum date that can be entered (ie, now + 3 minutes). */
|
|
175
|
-
private minDate: Date = null
|
|
176
|
-
|
|
177
|
-
/** The maximum date that can be entered (ie, 10 days from now). */
|
|
178
|
-
private maxDate: Date = null
|
|
179
|
-
|
|
180
|
-
// V-model values
|
|
181
|
-
private effectiveDateType: EffectiveDateTypes = null
|
|
182
|
-
private datePicker = ''
|
|
183
|
-
private dateText = ''
|
|
184
|
-
private selectHour: string[] = []
|
|
185
|
-
private selectMinute: string[] = []
|
|
186
|
-
private selectPeriod = PeriodTypes.AM
|
|
187
|
-
|
|
188
|
-
// Combobox items
|
|
189
|
-
private hours = [...Array(12).keys()].map(num => (num + 1).toString())
|
|
190
|
-
private minutes = [...Array(60).keys()].map(num => num.toString().padStart(2, '0'))
|
|
191
|
-
private timePeriod = [PeriodTypes.AM, PeriodTypes.PM]
|
|
192
|
-
|
|
193
|
-
/** Validations rules for date text field. */
|
|
194
|
-
get dateRules (): Array<(v) => boolean | string> {
|
|
195
|
-
// only apply rules when Future Effective is selected
|
|
196
|
-
if (this.isFutureEffective && this.isAppValidate) {
|
|
197
|
-
const minDateStr = this.dateToPacificDate(this.minDate, true)
|
|
198
|
-
const maxDateStr = this.dateToPacificDate(this.maxDate, true)
|
|
199
|
-
return [
|
|
200
|
-
(v: string) => !!v || 'Select date',
|
|
201
|
-
(v: string) => this.isValidDateRange(v) || `Date must be between ${minDateStr} and ${maxDateStr}`
|
|
202
|
-
]
|
|
203
|
-
}
|
|
204
|
-
return []
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* True if date is >= the minimum (ie, today) and <= the maximum (ie, the 10th day).
|
|
209
|
-
* This is used for Vue form validation (in Date Rules above).
|
|
210
|
-
*/
|
|
211
|
-
private isValidDateRange (v: string): boolean {
|
|
212
|
-
let date = new Date(v)
|
|
213
|
-
// only compare year/month/day (ignore time)
|
|
214
|
-
date = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
|
|
215
|
-
const minDay = new Date(this.minDate.getFullYear(), this.minDate.getMonth(), this.minDate.getDate())
|
|
216
|
-
const maxDay = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth(), this.maxDate.getDate())
|
|
217
|
-
return (date >= minDay && date <= maxDay)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Validations rules for hour selector. */
|
|
221
|
-
get hourRules (): Array<(v) => boolean | string> {
|
|
222
|
-
// only apply rules when Future Effective is selected
|
|
223
|
-
if (this.isFutureEffective && this.isAppValidate) {
|
|
224
|
-
return [
|
|
225
|
-
(v: string[]) => (v.length > 0) || 'Select hour',
|
|
226
|
-
(v: string) => (/^([1-9]|1[012])$/.test(v)) || ''
|
|
227
|
-
]
|
|
228
|
-
}
|
|
229
|
-
return []
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** Validations rules for minute selector. */
|
|
233
|
-
get minuteRules (): Array<(v) => boolean | string> {
|
|
234
|
-
// only apply rules when Future Effective is selected
|
|
235
|
-
if (this.isFutureEffective && this.isAppValidate) {
|
|
236
|
-
return [
|
|
237
|
-
(v: string[]) => (v.length > 0) || 'Select minute',
|
|
238
|
-
(v: string) => (/^([0-5]?[0-9])$/.test(v)) || ''
|
|
239
|
-
]
|
|
240
|
-
}
|
|
241
|
-
return []
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* True if time is under the minimum (ie, for today).
|
|
246
|
-
* This is a non-form validation - it needs to be checked for overall component validity.
|
|
247
|
-
*/
|
|
248
|
-
get isUnderTime (): boolean {
|
|
249
|
-
if (this.effectiveDateTime.effectiveDate) {
|
|
250
|
-
const date = new Date(this.effectiveDateTime.effectiveDate)
|
|
251
|
-
// use max seconds and milliseconds for comparison
|
|
252
|
-
date.setSeconds(59, 999)
|
|
253
|
-
return (date.getTime() < this.minDate.getTime())
|
|
254
|
-
}
|
|
255
|
-
return false
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* True if time is over the maximum (ie, for 10th day).
|
|
260
|
-
* This is a non-form validation - it needs to be checked for overall component validity.
|
|
261
|
-
*/
|
|
262
|
-
get isOverTime (): boolean {
|
|
263
|
-
if (this.effectiveDateTime.effectiveDate) {
|
|
264
|
-
const date = new Date(this.effectiveDateTime.effectiveDate)
|
|
265
|
-
// use min seconds and milliseconds for comparison
|
|
266
|
-
date.setSeconds(0, 0)
|
|
267
|
-
return (date.getTime() > this.maxDate.getTime())
|
|
268
|
-
}
|
|
269
|
-
return false
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** Called when component is mounted. */
|
|
273
|
-
mounted (): void {
|
|
274
|
-
if (this.parseInitial) this.parseInitialEffectiveDateTime()
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** Parses initial Effective Date Time and sets state. */
|
|
278
|
-
private parseInitialEffectiveDateTime (): void {
|
|
279
|
-
// set the chosen effective date option
|
|
280
|
-
this.isFutureEffective = this.effectiveDateTime.isFutureEffective
|
|
281
|
-
if (this.isFutureEffective === true) {
|
|
282
|
-
this.effectiveDateType = EffectiveDateTypes.FUTURE_EFFECTIVE
|
|
283
|
-
} else if (this.isFutureEffective === false) {
|
|
284
|
-
this.effectiveDateType = EffectiveDateTypes.IMMEDIATE
|
|
285
|
-
} else {
|
|
286
|
-
this.effectiveDateType = null
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// try to create Date object
|
|
290
|
-
const effectiveDate = this.effectiveDateTime.effectiveDate
|
|
291
|
-
const date = effectiveDate && new Date(effectiveDate)
|
|
292
|
-
|
|
293
|
-
if (date) {
|
|
294
|
-
// set model properties
|
|
295
|
-
let hour = date.getHours()
|
|
296
|
-
const minute = date.getMinutes()
|
|
297
|
-
const period = hour < 12 ? PeriodTypes.AM : PeriodTypes.PM
|
|
298
|
-
|
|
299
|
-
// convert 24h -> 12h and 0h -> 12h
|
|
300
|
-
if (hour > 12) {
|
|
301
|
-
hour -= 12
|
|
302
|
-
} else if (hour === 0) {
|
|
303
|
-
hour = 12
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// set model values
|
|
307
|
-
this.dateText = this.dateToYyyyMmDd(date)
|
|
308
|
-
this.selectHour = [hour.toString()]
|
|
309
|
-
this.selectMinute = [minute.toString().padStart(2, '0')]
|
|
310
|
-
this.selectPeriod = period
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Constructs the effective date and updates the parent. */
|
|
315
|
-
private async constructAndUpdate (): Promise<void> {
|
|
316
|
-
// wait for form to update itself before checking validity
|
|
317
|
-
await Vue.nextTick()
|
|
318
|
-
|
|
319
|
-
const isDateValid = this.$refs.datePickerRef.validateForm()
|
|
320
|
-
const isTimeValid = this.$refs.form.validate()
|
|
321
|
-
if (isDateValid && isTimeValid && !!this.selectHour.length && !!this.selectMinute.length) {
|
|
322
|
-
const year = +this.dateText.slice(0, 4)
|
|
323
|
-
const month = (+this.dateText.slice(5, 7) - 1) // zero-relative
|
|
324
|
-
const date = +this.dateText.slice(8, 10)
|
|
325
|
-
let hours = +this.selectHour
|
|
326
|
-
const minutes = +this.selectMinute
|
|
327
|
-
|
|
328
|
-
// convert 12 am -> 0
|
|
329
|
-
if (this.selectPeriod === PeriodTypes.AM && +this.selectHour === 12) {
|
|
330
|
-
hours = 0
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// convert 1-11 pm -> 13-23
|
|
334
|
-
if (this.selectPeriod === PeriodTypes.PM && +this.selectHour !== 12) {
|
|
335
|
-
hours += 12
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// construct date in UTC using parameters in Pacific time
|
|
339
|
-
const dateTime = this.createUtcDate(year, month, date, hours, minutes)
|
|
340
|
-
|
|
341
|
-
// Set Effective Date
|
|
342
|
-
this.emitEffectiveDate(dateTime)
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// update validity every time
|
|
346
|
-
this.emitValid()
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
@Watch('currentJsDate', { immediate: true })
|
|
350
|
-
onCurrentJsDateChanged (val: Date) {
|
|
351
|
-
// safety check (val may be null)
|
|
352
|
-
if (val) {
|
|
353
|
-
// set new min date
|
|
354
|
-
const minDate = new Date()
|
|
355
|
-
// add 3 minutes
|
|
356
|
-
minDate.setTime(val.getTime() + this.MIN_DIFF_MINUTES * 60 * 1000)
|
|
357
|
-
this.minDate = minDate
|
|
358
|
-
|
|
359
|
-
// set new max date
|
|
360
|
-
const maxDate = new Date()
|
|
361
|
-
// add 10 days
|
|
362
|
-
maxDate.setTime(val.getTime() + this.MAX_DIFF_DAYS * 24 * 60 * 60 * 1000)
|
|
363
|
-
this.maxDate = maxDate
|
|
364
|
-
|
|
365
|
-
// check if form is still valid
|
|
366
|
-
this.emitValid()
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
@Watch('datePicker')
|
|
371
|
-
onDatePickerChanged (val: string): void {
|
|
372
|
-
this.dateText = val
|
|
373
|
-
// the watcher for dateText will fire next
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
@Watch('dateText')
|
|
377
|
-
onDateTextChanged (val: string): void {
|
|
378
|
-
if (this.isFutureEffective) {
|
|
379
|
-
this.constructAndUpdate()
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
@Watch('selectHour')
|
|
384
|
-
onSelectHourChanged (val: string): void {
|
|
385
|
-
if (this.isFutureEffective) {
|
|
386
|
-
this.constructAndUpdate()
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
@Watch('selectMinute')
|
|
391
|
-
onSelectMinuteChanged (val: string): void {
|
|
392
|
-
if (this.isFutureEffective) {
|
|
393
|
-
this.constructAndUpdate()
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
@Watch('selectPeriod')
|
|
398
|
-
onSelectPeriodChanged (val: string): void {
|
|
399
|
-
if (this.isFutureEffective) {
|
|
400
|
-
this.constructAndUpdate()
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
@Watch('effectiveDateType')
|
|
405
|
-
onEffectiveDateTypeChanged (val: EffectiveDateTypes): void {
|
|
406
|
-
this.isImmediate = (val === EffectiveDateTypes.IMMEDIATE)
|
|
407
|
-
this.isFutureEffective = (val === EffectiveDateTypes.FUTURE_EFFECTIVE)
|
|
408
|
-
|
|
409
|
-
// if we changed to IMMEDIATE then clear the model values (otherwise retain them)
|
|
410
|
-
if (this.isImmediate) {
|
|
411
|
-
this.datePicker = ''
|
|
412
|
-
this.dateText = ''
|
|
413
|
-
this.selectHour = []
|
|
414
|
-
this.selectMinute = []
|
|
415
|
-
this.selectPeriod = PeriodTypes.AM
|
|
416
|
-
this.$refs.datePickerRef.clearDate()
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// update the parent
|
|
420
|
-
this.emitIsFutureEffective(this.isFutureEffective)
|
|
421
|
-
this.emitEffectiveDate(null)
|
|
422
|
-
this.emitValid()
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
@Emit('isFutureEffective')
|
|
426
|
-
private emitIsFutureEffective (val: boolean): void {}
|
|
427
|
-
|
|
428
|
-
@Emit('effectiveDate')
|
|
429
|
-
private emitEffectiveDate (val: Date): void {}
|
|
430
|
-
|
|
431
|
-
@Emit('valid')
|
|
432
|
-
private async emitValid (): Promise<boolean> {
|
|
433
|
-
// localized dateText check for future effective selections
|
|
434
|
-
const validDateText = this.isFutureEffective ? !!this.dateText : true
|
|
435
|
-
|
|
436
|
-
// wait for form to update itself before checking validity
|
|
437
|
-
await Vue.nextTick()
|
|
438
|
-
const isDateValid = this.$refs.datePickerRef.validateForm()
|
|
439
|
-
const isTimeValid = this.$refs.form.validate()
|
|
440
|
-
return this.isImmediate || (!!this.effectiveDateType &&
|
|
441
|
-
isDateValid && isTimeValid &&
|
|
442
|
-
!!this.selectHour.length && !!this.selectMinute.length &&
|
|
443
|
-
!this.isUnderTime &&
|
|
444
|
-
!this.isOverTime &&
|
|
445
|
-
validDateText
|
|
446
|
-
)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
</script>
|
|
450
|
-
|
|
451
|
-
<style lang="scss" scoped>
|
|
452
|
-
@import '@/assets/styles/theme.scss';
|
|
453
|
-
|
|
454
|
-
#effective-date-time-box {
|
|
455
|
-
padding: 2rem 2rem 0.5rem;
|
|
456
|
-
line-height: 1.2rem;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
:deep(.v-label) {
|
|
460
|
-
color: $gray7;
|
|
461
|
-
font-weight: normal;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
.v-radio {
|
|
465
|
-
padding-bottom: .5rem;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
.date-time-selectors {
|
|
469
|
-
margin-left: 2rem;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.time-colon {
|
|
473
|
-
margin-left: -4px;
|
|
474
|
-
margin-right: -4px;
|
|
475
|
-
padding-top: 2rem;
|
|
476
|
-
font-size: 25px;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
@media (max-width: 768px) {
|
|
480
|
-
.time-colon {
|
|
481
|
-
display: none;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
.label-col {
|
|
486
|
-
position: relative;
|
|
487
|
-
align-self: center;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.time-zone-label {
|
|
491
|
-
position: absolute;
|
|
492
|
-
top: -10px;
|
|
493
|
-
color: $gray7;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
.disabled {
|
|
497
|
-
color: $gray6;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
.validation-alert {
|
|
501
|
-
position: relative;
|
|
502
|
-
|
|
503
|
-
.validation-alert-msg {
|
|
504
|
-
line-height: 12px;
|
|
505
|
-
position: absolute;
|
|
506
|
-
top: -2rem;
|
|
507
|
-
padding: 0 12px;
|
|
508
|
-
font-size: 12px;
|
|
509
|
-
font-weight: 500;
|
|
510
|
-
color: $BCgovInputError !important;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
:deep() {
|
|
515
|
-
.v-icon.v-icon.v-icon--disabled {
|
|
516
|
-
color: $app-blue !important;
|
|
517
|
-
}
|
|
518
|
-
.v-input--is-disabled {
|
|
519
|
-
opacity: 0.4;
|
|
520
|
-
}
|
|
521
|
-
.v-input--is-disabled .v-input__control > .v-input__slot:before {
|
|
522
|
-
border-image: none;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<v-card
|
|
3
|
+
id="effective-date-time-box"
|
|
4
|
+
flat
|
|
5
|
+
>
|
|
6
|
+
<v-radio-group
|
|
7
|
+
v-model="effectiveDateType"
|
|
8
|
+
column
|
|
9
|
+
class="pt-0 mt-0"
|
|
10
|
+
>
|
|
11
|
+
<v-radio
|
|
12
|
+
label="Immediate (date and time of filing)"
|
|
13
|
+
:value="EffectiveDateTypes.IMMEDIATE"
|
|
14
|
+
/>
|
|
15
|
+
<v-radio
|
|
16
|
+
label="A date and time in the future"
|
|
17
|
+
:value="EffectiveDateTypes.FUTURE_EFFECTIVE"
|
|
18
|
+
/>
|
|
19
|
+
</v-radio-group>
|
|
20
|
+
|
|
21
|
+
<v-form
|
|
22
|
+
ref="form"
|
|
23
|
+
class="date-time-selectors"
|
|
24
|
+
>
|
|
25
|
+
<DatePicker
|
|
26
|
+
ref="datePickerRef"
|
|
27
|
+
title="Date"
|
|
28
|
+
nudge-right="40"
|
|
29
|
+
:inputRules="dateRules"
|
|
30
|
+
:disablePicker="effectiveDateType !== EffectiveDateTypes.FUTURE_EFFECTIVE"
|
|
31
|
+
:minDate="dateToYyyyMmDd(minDate)"
|
|
32
|
+
:maxDate="dateToYyyyMmDd(maxDate)"
|
|
33
|
+
@emitDate="dateText = $event"
|
|
34
|
+
@emitCancel="dateText = ''"
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
<v-row>
|
|
38
|
+
<v-col
|
|
39
|
+
cols="12"
|
|
40
|
+
sm="6"
|
|
41
|
+
md="3"
|
|
42
|
+
>
|
|
43
|
+
<v-combobox
|
|
44
|
+
id="hour-selector"
|
|
45
|
+
ref="hourSelector"
|
|
46
|
+
v-model="selectHour"
|
|
47
|
+
filled
|
|
48
|
+
class="mr-1"
|
|
49
|
+
label="Hour"
|
|
50
|
+
:items="hours"
|
|
51
|
+
:disabled="!isFutureEffective"
|
|
52
|
+
:rules="hourRules"
|
|
53
|
+
/>
|
|
54
|
+
</v-col>
|
|
55
|
+
<span
|
|
56
|
+
class="time-colon"
|
|
57
|
+
:class="{ 'disabled': !isFutureEffective }"
|
|
58
|
+
>:</span>
|
|
59
|
+
<v-col
|
|
60
|
+
cols="12"
|
|
61
|
+
sm="6"
|
|
62
|
+
md="3"
|
|
63
|
+
>
|
|
64
|
+
<v-combobox
|
|
65
|
+
id="minute-selector"
|
|
66
|
+
ref="minuteSelector"
|
|
67
|
+
v-model="selectMinute"
|
|
68
|
+
filled
|
|
69
|
+
class="ml-1"
|
|
70
|
+
label="Minute"
|
|
71
|
+
:items="minutes"
|
|
72
|
+
:disabled="!isFutureEffective"
|
|
73
|
+
:rules="minuteRules"
|
|
74
|
+
/>
|
|
75
|
+
</v-col>
|
|
76
|
+
<v-col
|
|
77
|
+
cols="12"
|
|
78
|
+
sm="6"
|
|
79
|
+
md="3"
|
|
80
|
+
>
|
|
81
|
+
<v-select
|
|
82
|
+
id="period-selector"
|
|
83
|
+
v-model="selectPeriod"
|
|
84
|
+
filled
|
|
85
|
+
:items="timePeriod"
|
|
86
|
+
:disabled="!isFutureEffective"
|
|
87
|
+
/>
|
|
88
|
+
</v-col>
|
|
89
|
+
<v-col
|
|
90
|
+
cols="12"
|
|
91
|
+
sm="6"
|
|
92
|
+
md="3"
|
|
93
|
+
class="label-col"
|
|
94
|
+
>
|
|
95
|
+
<span
|
|
96
|
+
class="time-zone-label"
|
|
97
|
+
:class="{ 'disabled': !isFutureEffective }"
|
|
98
|
+
>Pacific time</span>
|
|
99
|
+
</v-col>
|
|
100
|
+
</v-row>
|
|
101
|
+
|
|
102
|
+
<!-- display validation alert only after date and time have been entered -->
|
|
103
|
+
<v-row v-if="isFutureEffective && dateText && (selectHour.length > 0) && (selectMinute.length > 0)">
|
|
104
|
+
<v-col class="validation-alert">
|
|
105
|
+
<p
|
|
106
|
+
v-if="isUnderTime"
|
|
107
|
+
class="validation-alert-msg"
|
|
108
|
+
>
|
|
109
|
+
The time must be at least {{ dateToPacificTime(minDate) }} for the selected date
|
|
110
|
+
</p>
|
|
111
|
+
<p
|
|
112
|
+
v-if="isOverTime"
|
|
113
|
+
class="validation-alert-msg"
|
|
114
|
+
>
|
|
115
|
+
The time must be at most {{ dateToPacificTime(maxDate) }} for the selected date
|
|
116
|
+
</p>
|
|
117
|
+
</v-col>
|
|
118
|
+
</v-row>
|
|
119
|
+
</v-form>
|
|
120
|
+
</v-card>
|
|
121
|
+
</template>
|
|
122
|
+
|
|
123
|
+
<script lang="ts">
|
|
124
|
+
import Vue from 'vue'
|
|
125
|
+
import { Component, Emit, Mixins, Prop, Watch } from 'vue-property-decorator'
|
|
126
|
+
import { DatePicker } from '@bcrs-shared-components/date-picker'
|
|
127
|
+
import { DateMixin } from '@/mixins' // NB: local mixin (StoryBook can't find it otherwise)
|
|
128
|
+
import { EffectiveDateTypes } from '@bcrs-shared-components/enums'
|
|
129
|
+
import { EffectiveDateTimeIF, FormFieldType, FormIF } from '@bcrs-shared-components/interfaces'
|
|
130
|
+
|
|
131
|
+
enum PeriodTypes {
|
|
132
|
+
AM = 'am',
|
|
133
|
+
PM = 'pm'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Component({
|
|
137
|
+
components: {
|
|
138
|
+
DatePicker
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
export default class EffectiveDateTime extends Mixins(DateMixin) {
|
|
142
|
+
readonly MIN_DIFF_MINUTES = 3
|
|
143
|
+
readonly MAX_DIFF_DAYS = 10
|
|
144
|
+
|
|
145
|
+
// Add element types to refs
|
|
146
|
+
$refs!: {
|
|
147
|
+
form: FormIF,
|
|
148
|
+
datePickerRef: any, // should be DatePicker but TS complains
|
|
149
|
+
hourSelector: FormFieldType, // used in unit tests
|
|
150
|
+
minuteSelector: FormFieldType // used in unit tests
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Whether to parse the initial effective date-time into the controls. */
|
|
154
|
+
@Prop({ default: false }) readonly parseInitial!: boolean
|
|
155
|
+
|
|
156
|
+
/** Current JS date, expected to be passed in periodically. */
|
|
157
|
+
@Prop() readonly currentJsDate!: Date
|
|
158
|
+
|
|
159
|
+
/** Effective Date Time object, for initial config. */
|
|
160
|
+
@Prop() readonly effectiveDateTime!: EffectiveDateTimeIF
|
|
161
|
+
|
|
162
|
+
/** Whether to perform validation. */
|
|
163
|
+
@Prop() readonly isAppValidate!: boolean
|
|
164
|
+
|
|
165
|
+
// Declaration for template
|
|
166
|
+
readonly EffectiveDateTypes = EffectiveDateTypes
|
|
167
|
+
|
|
168
|
+
/** Whether Is Immediate is selected. */
|
|
169
|
+
private isImmediate = false
|
|
170
|
+
|
|
171
|
+
/** Whether Is Future Effective is selected. */
|
|
172
|
+
private isFutureEffective = false
|
|
173
|
+
|
|
174
|
+
/** The minimum date that can be entered (ie, now + 3 minutes). */
|
|
175
|
+
private minDate: Date = null
|
|
176
|
+
|
|
177
|
+
/** The maximum date that can be entered (ie, 10 days from now). */
|
|
178
|
+
private maxDate: Date = null
|
|
179
|
+
|
|
180
|
+
// V-model values
|
|
181
|
+
private effectiveDateType: EffectiveDateTypes = null
|
|
182
|
+
private datePicker = ''
|
|
183
|
+
private dateText = ''
|
|
184
|
+
private selectHour: string[] = []
|
|
185
|
+
private selectMinute: string[] = []
|
|
186
|
+
private selectPeriod = PeriodTypes.AM
|
|
187
|
+
|
|
188
|
+
// Combobox items
|
|
189
|
+
private hours = [...Array(12).keys()].map(num => (num + 1).toString())
|
|
190
|
+
private minutes = [...Array(60).keys()].map(num => num.toString().padStart(2, '0'))
|
|
191
|
+
private timePeriod = [PeriodTypes.AM, PeriodTypes.PM]
|
|
192
|
+
|
|
193
|
+
/** Validations rules for date text field. */
|
|
194
|
+
get dateRules (): Array<(v) => boolean | string> {
|
|
195
|
+
// only apply rules when Future Effective is selected
|
|
196
|
+
if (this.isFutureEffective && this.isAppValidate) {
|
|
197
|
+
const minDateStr = this.dateToPacificDate(this.minDate, true)
|
|
198
|
+
const maxDateStr = this.dateToPacificDate(this.maxDate, true)
|
|
199
|
+
return [
|
|
200
|
+
(v: string) => !!v || 'Select date',
|
|
201
|
+
(v: string) => this.isValidDateRange(v) || `Date must be between ${minDateStr} and ${maxDateStr}`
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
return []
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* True if date is >= the minimum (ie, today) and <= the maximum (ie, the 10th day).
|
|
209
|
+
* This is used for Vue form validation (in Date Rules above).
|
|
210
|
+
*/
|
|
211
|
+
private isValidDateRange (v: string): boolean {
|
|
212
|
+
let date = new Date(v)
|
|
213
|
+
// only compare year/month/day (ignore time)
|
|
214
|
+
date = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
|
|
215
|
+
const minDay = new Date(this.minDate.getFullYear(), this.minDate.getMonth(), this.minDate.getDate())
|
|
216
|
+
const maxDay = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth(), this.maxDate.getDate())
|
|
217
|
+
return (date >= minDay && date <= maxDay)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Validations rules for hour selector. */
|
|
221
|
+
get hourRules (): Array<(v) => boolean | string> {
|
|
222
|
+
// only apply rules when Future Effective is selected
|
|
223
|
+
if (this.isFutureEffective && this.isAppValidate) {
|
|
224
|
+
return [
|
|
225
|
+
(v: string[]) => (v.length > 0) || 'Select hour',
|
|
226
|
+
(v: string) => (/^([1-9]|1[012])$/.test(v)) || ''
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
return []
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Validations rules for minute selector. */
|
|
233
|
+
get minuteRules (): Array<(v) => boolean | string> {
|
|
234
|
+
// only apply rules when Future Effective is selected
|
|
235
|
+
if (this.isFutureEffective && this.isAppValidate) {
|
|
236
|
+
return [
|
|
237
|
+
(v: string[]) => (v.length > 0) || 'Select minute',
|
|
238
|
+
(v: string) => (/^([0-5]?[0-9])$/.test(v)) || ''
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
return []
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* True if time is under the minimum (ie, for today).
|
|
246
|
+
* This is a non-form validation - it needs to be checked for overall component validity.
|
|
247
|
+
*/
|
|
248
|
+
get isUnderTime (): boolean {
|
|
249
|
+
if (this.effectiveDateTime.effectiveDate) {
|
|
250
|
+
const date = new Date(this.effectiveDateTime.effectiveDate)
|
|
251
|
+
// use max seconds and milliseconds for comparison
|
|
252
|
+
date.setSeconds(59, 999)
|
|
253
|
+
return (date.getTime() < this.minDate.getTime())
|
|
254
|
+
}
|
|
255
|
+
return false
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* True if time is over the maximum (ie, for 10th day).
|
|
260
|
+
* This is a non-form validation - it needs to be checked for overall component validity.
|
|
261
|
+
*/
|
|
262
|
+
get isOverTime (): boolean {
|
|
263
|
+
if (this.effectiveDateTime.effectiveDate) {
|
|
264
|
+
const date = new Date(this.effectiveDateTime.effectiveDate)
|
|
265
|
+
// use min seconds and milliseconds for comparison
|
|
266
|
+
date.setSeconds(0, 0)
|
|
267
|
+
return (date.getTime() > this.maxDate.getTime())
|
|
268
|
+
}
|
|
269
|
+
return false
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Called when component is mounted. */
|
|
273
|
+
mounted (): void {
|
|
274
|
+
if (this.parseInitial) this.parseInitialEffectiveDateTime()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Parses initial Effective Date Time and sets state. */
|
|
278
|
+
private parseInitialEffectiveDateTime (): void {
|
|
279
|
+
// set the chosen effective date option
|
|
280
|
+
this.isFutureEffective = this.effectiveDateTime.isFutureEffective
|
|
281
|
+
if (this.isFutureEffective === true) {
|
|
282
|
+
this.effectiveDateType = EffectiveDateTypes.FUTURE_EFFECTIVE
|
|
283
|
+
} else if (this.isFutureEffective === false) {
|
|
284
|
+
this.effectiveDateType = EffectiveDateTypes.IMMEDIATE
|
|
285
|
+
} else {
|
|
286
|
+
this.effectiveDateType = null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// try to create Date object
|
|
290
|
+
const effectiveDate = this.effectiveDateTime.effectiveDate
|
|
291
|
+
const date = effectiveDate && new Date(effectiveDate)
|
|
292
|
+
|
|
293
|
+
if (date) {
|
|
294
|
+
// set model properties
|
|
295
|
+
let hour = date.getHours()
|
|
296
|
+
const minute = date.getMinutes()
|
|
297
|
+
const period = hour < 12 ? PeriodTypes.AM : PeriodTypes.PM
|
|
298
|
+
|
|
299
|
+
// convert 24h -> 12h and 0h -> 12h
|
|
300
|
+
if (hour > 12) {
|
|
301
|
+
hour -= 12
|
|
302
|
+
} else if (hour === 0) {
|
|
303
|
+
hour = 12
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// set model values
|
|
307
|
+
this.dateText = this.dateToYyyyMmDd(date)
|
|
308
|
+
this.selectHour = [hour.toString()]
|
|
309
|
+
this.selectMinute = [minute.toString().padStart(2, '0')]
|
|
310
|
+
this.selectPeriod = period
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Constructs the effective date and updates the parent. */
|
|
315
|
+
private async constructAndUpdate (): Promise<void> {
|
|
316
|
+
// wait for form to update itself before checking validity
|
|
317
|
+
await Vue.nextTick()
|
|
318
|
+
|
|
319
|
+
const isDateValid = this.$refs.datePickerRef.validateForm()
|
|
320
|
+
const isTimeValid = this.$refs.form.validate()
|
|
321
|
+
if (isDateValid && isTimeValid && !!this.selectHour.length && !!this.selectMinute.length) {
|
|
322
|
+
const year = +this.dateText.slice(0, 4)
|
|
323
|
+
const month = (+this.dateText.slice(5, 7) - 1) // zero-relative
|
|
324
|
+
const date = +this.dateText.slice(8, 10)
|
|
325
|
+
let hours = +this.selectHour
|
|
326
|
+
const minutes = +this.selectMinute
|
|
327
|
+
|
|
328
|
+
// convert 12 am -> 0
|
|
329
|
+
if (this.selectPeriod === PeriodTypes.AM && +this.selectHour === 12) {
|
|
330
|
+
hours = 0
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// convert 1-11 pm -> 13-23
|
|
334
|
+
if (this.selectPeriod === PeriodTypes.PM && +this.selectHour !== 12) {
|
|
335
|
+
hours += 12
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// construct date in UTC using parameters in Pacific time
|
|
339
|
+
const dateTime = this.createUtcDate(year, month, date, hours, minutes)
|
|
340
|
+
|
|
341
|
+
// Set Effective Date
|
|
342
|
+
this.emitEffectiveDate(dateTime)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// update validity every time
|
|
346
|
+
this.emitValid()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@Watch('currentJsDate', { immediate: true })
|
|
350
|
+
onCurrentJsDateChanged (val: Date) {
|
|
351
|
+
// safety check (val may be null)
|
|
352
|
+
if (val) {
|
|
353
|
+
// set new min date
|
|
354
|
+
const minDate = new Date()
|
|
355
|
+
// add 3 minutes
|
|
356
|
+
minDate.setTime(val.getTime() + this.MIN_DIFF_MINUTES * 60 * 1000)
|
|
357
|
+
this.minDate = minDate
|
|
358
|
+
|
|
359
|
+
// set new max date
|
|
360
|
+
const maxDate = new Date()
|
|
361
|
+
// add 10 days
|
|
362
|
+
maxDate.setTime(val.getTime() + this.MAX_DIFF_DAYS * 24 * 60 * 60 * 1000)
|
|
363
|
+
this.maxDate = maxDate
|
|
364
|
+
|
|
365
|
+
// check if form is still valid
|
|
366
|
+
this.emitValid()
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@Watch('datePicker')
|
|
371
|
+
onDatePickerChanged (val: string): void {
|
|
372
|
+
this.dateText = val
|
|
373
|
+
// the watcher for dateText will fire next
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
@Watch('dateText')
|
|
377
|
+
onDateTextChanged (val: string): void {
|
|
378
|
+
if (this.isFutureEffective) {
|
|
379
|
+
this.constructAndUpdate()
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@Watch('selectHour')
|
|
384
|
+
onSelectHourChanged (val: string): void {
|
|
385
|
+
if (this.isFutureEffective) {
|
|
386
|
+
this.constructAndUpdate()
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@Watch('selectMinute')
|
|
391
|
+
onSelectMinuteChanged (val: string): void {
|
|
392
|
+
if (this.isFutureEffective) {
|
|
393
|
+
this.constructAndUpdate()
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
@Watch('selectPeriod')
|
|
398
|
+
onSelectPeriodChanged (val: string): void {
|
|
399
|
+
if (this.isFutureEffective) {
|
|
400
|
+
this.constructAndUpdate()
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
@Watch('effectiveDateType')
|
|
405
|
+
onEffectiveDateTypeChanged (val: EffectiveDateTypes): void {
|
|
406
|
+
this.isImmediate = (val === EffectiveDateTypes.IMMEDIATE)
|
|
407
|
+
this.isFutureEffective = (val === EffectiveDateTypes.FUTURE_EFFECTIVE)
|
|
408
|
+
|
|
409
|
+
// if we changed to IMMEDIATE then clear the model values (otherwise retain them)
|
|
410
|
+
if (this.isImmediate) {
|
|
411
|
+
this.datePicker = ''
|
|
412
|
+
this.dateText = ''
|
|
413
|
+
this.selectHour = []
|
|
414
|
+
this.selectMinute = []
|
|
415
|
+
this.selectPeriod = PeriodTypes.AM
|
|
416
|
+
this.$refs.datePickerRef.clearDate()
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// update the parent
|
|
420
|
+
this.emitIsFutureEffective(this.isFutureEffective)
|
|
421
|
+
this.emitEffectiveDate(null)
|
|
422
|
+
this.emitValid()
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@Emit('isFutureEffective')
|
|
426
|
+
private emitIsFutureEffective (val: boolean): void {}
|
|
427
|
+
|
|
428
|
+
@Emit('effectiveDate')
|
|
429
|
+
private emitEffectiveDate (val: Date): void {}
|
|
430
|
+
|
|
431
|
+
@Emit('valid')
|
|
432
|
+
private async emitValid (): Promise<boolean> {
|
|
433
|
+
// localized dateText check for future effective selections
|
|
434
|
+
const validDateText = this.isFutureEffective ? !!this.dateText : true
|
|
435
|
+
|
|
436
|
+
// wait for form to update itself before checking validity
|
|
437
|
+
await Vue.nextTick()
|
|
438
|
+
const isDateValid = this.$refs.datePickerRef.validateForm()
|
|
439
|
+
const isTimeValid = this.$refs.form.validate()
|
|
440
|
+
return this.isImmediate || (!!this.effectiveDateType &&
|
|
441
|
+
isDateValid && isTimeValid &&
|
|
442
|
+
!!this.selectHour.length && !!this.selectMinute.length &&
|
|
443
|
+
!this.isUnderTime &&
|
|
444
|
+
!this.isOverTime &&
|
|
445
|
+
validDateText
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
</script>
|
|
450
|
+
|
|
451
|
+
<style lang="scss" scoped>
|
|
452
|
+
@import '@/assets/styles/theme.scss';
|
|
453
|
+
|
|
454
|
+
#effective-date-time-box {
|
|
455
|
+
padding: 2rem 2rem 0.5rem;
|
|
456
|
+
line-height: 1.2rem;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
:deep(.v-label) {
|
|
460
|
+
color: $gray7;
|
|
461
|
+
font-weight: normal;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.v-radio {
|
|
465
|
+
padding-bottom: .5rem;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.date-time-selectors {
|
|
469
|
+
margin-left: 2rem;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.time-colon {
|
|
473
|
+
margin-left: -4px;
|
|
474
|
+
margin-right: -4px;
|
|
475
|
+
padding-top: 2rem;
|
|
476
|
+
font-size: 25px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
@media (max-width: 768px) {
|
|
480
|
+
.time-colon {
|
|
481
|
+
display: none;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.label-col {
|
|
486
|
+
position: relative;
|
|
487
|
+
align-self: center;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.time-zone-label {
|
|
491
|
+
position: absolute;
|
|
492
|
+
top: -10px;
|
|
493
|
+
color: $gray7;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.disabled {
|
|
497
|
+
color: $gray6;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.validation-alert {
|
|
501
|
+
position: relative;
|
|
502
|
+
|
|
503
|
+
.validation-alert-msg {
|
|
504
|
+
line-height: 12px;
|
|
505
|
+
position: absolute;
|
|
506
|
+
top: -2rem;
|
|
507
|
+
padding: 0 12px;
|
|
508
|
+
font-size: 12px;
|
|
509
|
+
font-weight: 500;
|
|
510
|
+
color: $BCgovInputError !important;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
:deep() {
|
|
515
|
+
.v-icon.v-icon.v-icon--disabled {
|
|
516
|
+
color: $app-blue !important;
|
|
517
|
+
}
|
|
518
|
+
.v-input--is-disabled {
|
|
519
|
+
opacity: 0.4;
|
|
520
|
+
}
|
|
521
|
+
.v-input--is-disabled .v-input__control > .v-input__slot:before {
|
|
522
|
+
border-image: none;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
</style>
|