1<template> 2 <b-container fluid="xl"> 3 <page-title /> 4 <b-row> 5 <b-col md="8" xl="6"> 6 <alert variant="info" class="mb-4"> 7 <span> 8 {{ $t('pageDateTime.alert.message') }} 9 <b-link to="/profile-settings"> 10 {{ $t('pageDateTime.alert.link') }}</b-link 11 > 12 </span> 13 </alert> 14 </b-col> 15 </b-row> 16 <page-section> 17 <b-row> 18 <b-col lg="3"> 19 <dl> 20 <dt>{{ $t('pageDateTime.form.date') }}</dt> 21 <dd v-if="bmcTime">{{ bmcTime | formatDate }}</dd> 22 <dd v-else>--</dd> 23 </dl> 24 </b-col> 25 <b-col lg="3"> 26 <dl> 27 <dt>{{ $t('pageDateTime.form.time.label') }}</dt> 28 <dd v-if="bmcTime">{{ bmcTime | formatTime }}</dd> 29 <dd v-else>--</dd> 30 </dl> 31 </b-col> 32 </b-row> 33 </page-section> 34 <page-section :section-title="$t('pageDateTime.configureSettings')"> 35 <b-form novalidate @submit.prevent="submitForm"> 36 <b-form-group 37 label="Configure date and time" 38 :disabled="loading" 39 label-sr-only 40 > 41 <b-form-radio 42 v-model="form.configurationSelected" 43 value="manual" 44 data-test-id="dateTime-radio-configureManual" 45 > 46 {{ $t('pageDateTime.form.manual') }} 47 </b-form-radio> 48 <b-row class="mt-3 ml-3"> 49 <b-col sm="6" lg="4" xl="3"> 50 <b-form-group 51 :label="$t('pageDateTime.form.date')" 52 label-for="input-manual-date" 53 > 54 <b-form-text id="date-format-help">YYYY-MM-DD</b-form-text> 55 <b-input-group> 56 <b-form-input 57 id="input-manual-date" 58 v-model="form.manual.date" 59 :state="getValidationState($v.form.manual.date)" 60 :disabled="ntpOptionSelected" 61 data-test-id="dateTime-input-manualDate" 62 class="form-control-with-button" 63 @blur="$v.form.manual.date.$touch()" 64 /> 65 <b-form-invalid-feedback role="alert"> 66 <div v-if="!$v.form.manual.date.pattern"> 67 {{ $t('global.form.invalidFormat') }} 68 </div> 69 <div v-if="!$v.form.manual.date.required"> 70 {{ $t('global.form.fieldRequired') }} 71 </div> 72 </b-form-invalid-feedback> 73 <b-form-datepicker 74 v-model="form.manual.date" 75 class="btn-datepicker btn-icon-only" 76 button-only 77 right 78 :hide-header="true" 79 :locale="locale" 80 :label-help=" 81 $t('global.calendar.useCursorKeysToNavigateCalendarDates') 82 " 83 :title="$t('global.calendar.selectDate')" 84 :disabled="ntpOptionSelected" 85 button-variant="link" 86 aria-controls="input-manual-date" 87 > 88 <template #button-content> 89 <icon-calendar /> 90 <span class="sr-only"> 91 {{ $t('global.calendar.selectDate') }} 92 </span> 93 </template> 94 </b-form-datepicker> 95 </b-input-group> 96 </b-form-group> 97 </b-col> 98 <b-col sm="6" lg="4" xl="3"> 99 <b-form-group 100 :label="$t('pageDateTime.form.time.timezone', { timezone })" 101 label-for="input-manual-time" 102 > 103 <b-form-text id="time-format-help">HH:MM</b-form-text> 104 <b-input-group> 105 <b-form-input 106 id="input-manual-time" 107 v-model="form.manual.time" 108 :state="getValidationState($v.form.manual.time)" 109 :disabled="ntpOptionSelected" 110 data-test-id="dateTime-input-manualTime" 111 @blur="$v.form.manual.time.$touch()" 112 /> 113 <b-form-invalid-feedback role="alert"> 114 <div v-if="!$v.form.manual.time.pattern"> 115 {{ $t('global.form.invalidFormat') }} 116 </div> 117 <div v-if="!$v.form.manual.time.required"> 118 {{ $t('global.form.fieldRequired') }} 119 </div> 120 </b-form-invalid-feedback> 121 </b-input-group> 122 </b-form-group> 123 </b-col> 124 </b-row> 125 <b-form-radio 126 v-model="form.configurationSelected" 127 value="ntp" 128 data-test-id="dateTime-radio-configureNTP" 129 > 130 NTP 131 </b-form-radio> 132 <b-row class="mt-3 ml-3"> 133 <b-col sm="6" lg="4" xl="3"> 134 <b-form-group 135 :label="$t('pageDateTime.form.ntpServers.server1')" 136 label-for="input-ntp-1" 137 > 138 <b-input-group> 139 <b-form-input 140 id="input-ntp-1" 141 v-model="form.ntp.firstAddress" 142 :state="getValidationState($v.form.ntp.firstAddress)" 143 :disabled="manualOptionSelected" 144 data-test-id="dateTime-input-ntpServer1" 145 @blur="$v.form.ntp.firstAddress.$touch()" 146 /> 147 <b-form-invalid-feedback role="alert"> 148 <div v-if="!$v.form.ntp.firstAddress.required"> 149 {{ $t('global.form.fieldRequired') }} 150 </div> 151 </b-form-invalid-feedback> 152 </b-input-group> 153 </b-form-group> 154 </b-col> 155 <b-col sm="6" lg="4" xl="3"> 156 <b-form-group 157 :label="$t('pageDateTime.form.ntpServers.server2')" 158 label-for="input-ntp-2" 159 > 160 <b-input-group> 161 <b-form-input 162 id="input-ntp-2" 163 v-model="form.ntp.secondAddress" 164 :disabled="manualOptionSelected" 165 data-test-id="dateTime-input-ntpServer2" 166 /> 167 </b-input-group> 168 </b-form-group> 169 </b-col> 170 <b-col sm="6" lg="4" xl="3"> 171 <b-form-group 172 :label="$t('pageDateTime.form.ntpServers.server3')" 173 label-for="input-ntp-3" 174 > 175 <b-input-group> 176 <b-form-input 177 id="input-ntp-3" 178 v-model="form.ntp.thirdAddress" 179 :disabled="manualOptionSelected" 180 data-test-id="dateTime-input-ntpServer3" 181 /> 182 </b-input-group> 183 </b-form-group> 184 </b-col> 185 </b-row> 186 <b-button 187 variant="primary" 188 type="submit" 189 data-test-id="dateTime-button-saveSettings" 190 > 191 {{ $t('global.action.saveSettings') }} 192 </b-button> 193 </b-form-group> 194 </b-form> 195 </page-section> 196 </b-container> 197</template> 198 199<script> 200import Alert from '@/components/Global/Alert'; 201import IconCalendar from '@carbon/icons-vue/es/calendar/20'; 202import PageTitle from '@/components/Global/PageTitle'; 203import PageSection from '@/components/Global/PageSection'; 204 205import BVToastMixin from '@/components/Mixins/BVToastMixin'; 206import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; 207import LocalTimezoneLabelMixin from '@/components/Mixins/LocalTimezoneLabelMixin'; 208import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; 209 210import { mapState } from 'vuex'; 211import { requiredIf, helpers } from 'vuelidate/lib/validators'; 212 213const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/; 214const isoTimeRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; 215 216export default { 217 name: 'DateTime', 218 components: { Alert, IconCalendar, PageTitle, PageSection }, 219 mixins: [ 220 BVToastMixin, 221 LoadingBarMixin, 222 LocalTimezoneLabelMixin, 223 VuelidateMixin, 224 ], 225 beforeRouteLeave(to, from, next) { 226 this.hideLoader(); 227 next(); 228 }, 229 data() { 230 return { 231 locale: this.$store.getters['global/languagePreference'], 232 form: { 233 configurationSelected: 'manual', 234 manual: { 235 date: '', 236 time: '', 237 }, 238 ntp: { firstAddress: '', secondAddress: '', thirdAddress: '' }, 239 }, 240 loading, 241 }; 242 }, 243 validations() { 244 return { 245 form: { 246 manual: { 247 date: { 248 required: requiredIf(function () { 249 return this.form.configurationSelected === 'manual'; 250 }), 251 pattern: helpers.regex('pattern', isoDateRegex), 252 }, 253 time: { 254 required: requiredIf(function () { 255 return this.form.configurationSelected === 'manual'; 256 }), 257 pattern: helpers.regex('pattern', isoTimeRegex), 258 }, 259 }, 260 ntp: { 261 firstAddress: { 262 required: requiredIf(function () { 263 return this.form.configurationSelected === 'ntp'; 264 }), 265 }, 266 }, 267 }, 268 }; 269 }, 270 computed: { 271 ...mapState('dateTime', ['ntpServers', 'isNtpProtocolEnabled']), 272 bmcTime() { 273 return this.$store.getters['global/bmcTime']; 274 }, 275 ntpOptionSelected() { 276 return this.form.configurationSelected === 'ntp'; 277 }, 278 manualOptionSelected() { 279 return this.form.configurationSelected === 'manual'; 280 }, 281 isUtcDisplay() { 282 return this.$store.getters['global/isUtcDisplay']; 283 }, 284 timezone() { 285 if (this.isUtcDisplay) { 286 return 'UTC'; 287 } 288 return this.localOffset(); 289 }, 290 }, 291 watch: { 292 ntpServers() { 293 this.setNtpValues(); 294 }, 295 manualDate() { 296 this.emitChange(); 297 }, 298 bmcTime() { 299 this.form.manual.date = this.$options.filters.formatDate( 300 this.$store.getters['global/bmcTime'] 301 ); 302 this.form.manual.time = this.$options.filters 303 .formatTime(this.$store.getters['global/bmcTime']) 304 .slice(0, 5); 305 }, 306 }, 307 created() { 308 this.startLoader(); 309 this.setNtpValues(); 310 Promise.all([ 311 this.$store.dispatch('global/getBmcTime'), 312 this.$store.dispatch('dateTime/getNtpData'), 313 ]).finally(() => this.endLoader()); 314 }, 315 methods: { 316 emitChange() { 317 if (this.$v.$invalid) return; 318 this.$v.$reset(); //reset to re-validate on blur 319 this.$emit('change', { 320 manualDate: this.manualDate ? new Date(this.manualDate) : null, 321 }); 322 }, 323 setNtpValues() { 324 this.form.configurationSelected = this.isNtpProtocolEnabled 325 ? 'ntp' 326 : 'manual'; 327 [ 328 this.form.ntp.firstAddress = '', 329 this.form.ntp.secondAddress = '', 330 this.form.ntp.thirdAddress = '', 331 ] = [this.ntpServers[0], this.ntpServers[1], this.ntpServers[2]]; 332 }, 333 submitForm() { 334 this.$v.$touch(); 335 if (this.$v.$invalid) return; 336 this.startLoader(); 337 338 let dateTimeForm = {}; 339 let isNTPEnabled = this.form.configurationSelected === 'ntp'; 340 341 if (!isNTPEnabled) { 342 const isUtcDisplay = this.$store.getters['global/isUtcDisplay']; 343 let date; 344 345 dateTimeForm.ntpProtocolEnabled = false; 346 347 if (isUtcDisplay) { 348 // Create UTC Date 349 date = this.getUtcDate(this.form.manual.date, this.form.manual.time); 350 } else { 351 // Create local Date 352 date = new Date(`${this.form.manual.date} ${this.form.manual.time}`); 353 } 354 355 dateTimeForm.updatedDateTime = date.toISOString(); 356 } else { 357 dateTimeForm.ntpProtocolEnabled = true; 358 359 const ntpArray = [ 360 this.form.ntp.firstAddress, 361 this.form.ntp.secondAddress, 362 this.form.ntp.thirdAddress, 363 ]; 364 365 // Filter the ntpArray to remove empty strings, 366 // per Redfish spec there should be no empty strings or null on the ntp array. 367 const ntpArrayFiltered = ntpArray.filter((x) => x); 368 369 dateTimeForm.ntpServersArray = [...ntpArrayFiltered]; 370 371 [this.ntpServers[0], this.ntpServers[1], this.ntpServers[2]] = [ 372 ...dateTimeForm.ntpServersArray, 373 ]; 374 375 this.setNtpValues(); 376 } 377 378 this.$store 379 .dispatch('dateTime/updateDateTime', dateTimeForm) 380 .then((success) => { 381 this.successToast(success); 382 if (!isNTPEnabled) return; 383 // Shift address up if second address is empty 384 // to avoid refreshing after delay when updating NTP 385 if (!this.form.ntp.secondAddress && this.form.ntp.thirdAddres) { 386 this.form.ntp.secondAddress = this.form.ntp.thirdAddres; 387 this.form.ntp.thirdAddress = ''; 388 } 389 }) 390 .then(() => { 391 this.$store.dispatch('global/getBmcTime'); 392 }) 393 .catch(({ message }) => this.errorToast(message)) 394 .finally(() => { 395 this.$v.form.$reset(); 396 this.endLoader(); 397 }); 398 }, 399 getUtcDate(date, time) { 400 // Split user input string values to create 401 // a UTC Date object 402 const datesArray = date.split('-'); 403 const timeArray = time.split(':'); 404 let utcDate = Date.UTC( 405 datesArray[0], // User input year 406 //UTC expects zero-index month value 0-11 (January-December) 407 //for reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC#Parameters 408 parseInt(datesArray[1]) - 1, // User input month 409 datesArray[2], // User input day 410 timeArray[0], // User input hour 411 timeArray[1] // User input minute 412 ); 413 return new Date(utcDate); 414 }, 415 }, 416}; 417</script> 418