Skip to content

Commit

Permalink
Implemented: support for time zone switcher component(#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymaheshwari1 committed Mar 22, 2024
1 parent c30b87c commit a6a8888
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 28 deletions.
152 changes: 138 additions & 14 deletions src/components/DxpTimeZoneSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,159 @@
{{ $t('The timezone you select is used to ensure automations you schedule are always accurate to the time you select.') }}
</ion-card-content>
<ion-item lines="none">
<ion-label> {{ userProfile && userProfile.userTimeZone ? userProfile.userTimeZone : '-' }} </ion-label>
<ion-button @click="changeTimeZone()" slot="end" fill="outline" color="dark">{{ $t("Change") }}</ion-button>
<ion-label>{{ currentTimeZoneId }}</ion-label>
<ion-button id="time-zone-modal" slot="end" fill="outline" color="dark">{{ $t("Change") }}</ion-button>
</ion-item>
</ion-card>
<!-- Using inline modal(as recommended by ionic), also using it inline as the component inside modal is not getting mounted when using modalController -->
<ion-modal ref="timeZoneModal" trigger="time-zone-modal" @didPresent="search()" @didDismiss="clearSearch()">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="closeModal">
<ion-icon :icon="closeOutline" />
</ion-button>
</ion-buttons>
<ion-title>{{ $t("Select time zone") }}</ion-title>
</ion-toolbar>
<ion-toolbar>
<ion-searchbar @ionFocus="selectSearchBarText($event)" :placeholder="$t('Search time zones')" v-model="queryString" @keyup.enter="queryString = $event.target.value; findTimeZone()" @keydown="preventSpecialCharacters($event)" />
</ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
<!-- Empty state -->
<div class="empty-state" v-if="isLoading">
<ion-item lines="none">
<ion-spinner color="secondary" name="crescent" slot="start" />
{{ $t("Fetching time zones") }}
</ion-item>
</div>
<div class="empty-state" v-else-if="filteredTimeZones.length === 0">
<p>{{ $t("No time zone found") }}</p>
</div>

<!-- Timezones -->
<div v-else>
<ion-list>
<ion-radio-group value="rd" v-model="timeZoneId">
<ion-item :key="timeZone.id" v-for="timeZone in filteredTimeZones">
<ion-radio label-placement="end" justify="start" :value="timeZone.id">{{ timeZone.label }} ({{ timeZone.id }})</ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
</div>

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button :disabled="!currentTimeZoneId" @click="setUserTimeZone">
<ion-icon :icon="saveOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-modal>
</template>

<script setup lang="ts">
import {
IonButton,
IonButtons,
IonCard,
IonCardHeader,
IonCardTitle,
IonCardContent,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonButton,
modalController
IonList,
IonModal,
IonRadio,
IonRadioGroup,
IonSearchbar,
IonSpinner,
IonTitle,
IonToolbar
} from '@ionic/vue';
import { appContext } from '../index';
import { computed } from 'vue';
import { defineAsyncComponent } from 'vue';
import TimeZoneModal from './TimeZoneModal.vue';
import { closeOutline, saveOutline } from "ionicons/icons";
import { appContext, useUserStore } from '../index';
import { computed, onBeforeMount, ref } from "vue";
const appState = appContext.config.globalProperties.$store;
const userProfile = computed(() => appState.getters['user/getUserProfile'])
const userStore = useUserStore();
const userProfile: any = computed(() => appState.getters['user/getUserProfile'])
const timeZones = computed(() => userStore.getTimeZones)
const currentTimeZoneId = computed(() => userStore.getCurrentTimeZone)
const isLoading = ref(false);
const timeZoneModal = ref();
const queryString = ref('');
const filteredTimeZones = ref([])
const timeZoneId = ref('')
const emit = defineEmits(["timeZoneUpdated"])
const props = defineProps({
persist: {
type: Boolean,
default: false
}
})
const closeModal = () => {
timeZoneModal.value.$el.dismiss(null, 'cancel');
}
onBeforeMount(async () => {
isLoading.value = true;
await userStore.getAvailableTimeZones();
findTimeZone();
if(userProfile.value && userProfile.value.userTimeZone) {
userStore.currentTimeZoneId = userProfile.value.userTimeZone
timeZoneId.value = userProfile.value.userTimeZone
}
isLoading.value = false;
})
async function setUserTimeZone() {
await userStore.setUserTimeZone(timeZoneId.value).then((tzId) => {
emit('timeZoneUpdated', tzId);
}).catch(err => err)
closeModal();
}
function findTimeZone() {
const searchedString = queryString.value.toLowerCase();
filteredTimeZones.value = timeZones.value.filter((timeZone: any) => timeZone.id.toLowerCase().match(searchedString) || timeZone.label.toLowerCase().match(searchedString));
}
async function selectSearchBarText(event: any) {
const element = await event.target.getInputElement()
element.select();
}
function preventSpecialCharacters($event: any) {
// Searching special characters fails the API, hence, they must be omitted
if(/[`!@#$%^&*()_+\-=\\|,.<>?~]/.test($event.key)) $event.preventDefault();
}
function search() {
isLoading.value = true;
findTimeZone();
isLoading.value = false;
}
const changeTimeZone = async () => {
const timeZoneModal = await modalController.create({
component: defineAsyncComponent(() => import('./TimeZoneModal.vue')),
});
return timeZoneModal.present();
// clearing the data explicitely as the modal is mounted due to the component being mounted always
function clearSearch() {
// Do not clear the data on modal dismiss if the user wants to persist the search even after modal close
if(props.persist) {
return;
}
queryString.value = ''
filteredTimeZones.value = []
}
</script>
11 changes: 0 additions & 11 deletions src/components/TimeZoneModal.vue

This file was deleted.

4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export let dxpComponents = {

userContext.setUserLocale = options.setUserLocale

// TimeZone specific api from oms-api package exposed by the app
userContext.setUserTimeZone = options.setUserTimeZone
userContext.getAvailableTimeZones = options.getAvailableTimeZones

productIdentificationContext.getProductIdentificationPref = options.getProductIdentificationPref
productIdentificationContext.setProductIdentificationPref = options.setProductIdentificationPref

Expand Down
46 changes: 43 additions & 3 deletions src/store/user.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { defineStore } from "pinia";
import { i18n, userContext } from "../../src";
import { appContext, i18n, translate, userContext } from "../../src";
import { hasError } from "@hotwax/oms-api";
import { DateTime } from "luxon";
import { showToast } from "src/utils";

declare let process: any;

export const useUserStore = defineStore('user', {
state: () => {
return {
localeOptions: process.env.VUE_APP_LOCALES ? JSON.parse(process.env.VUE_APP_LOCALES) : { "en-US": "English" },
locale: 'en-US'
locale: 'en-US',
currentTimeZoneId: '',
timeZones: []
}
},
getters: {
getLocale: (state) => state.locale,
getLocaleOptions: (state) => state.localeOptions
getLocaleOptions: (state) => state.localeOptions,
getTimeZones: (state) => state.timeZones,
getCurrentTimeZone: (state) => state.currentTimeZoneId
},
actions: {
async setLocale(locale: string) {
Expand All @@ -34,6 +41,39 @@ export const useUserStore = defineStore('user', {
i18n.global.locale.value = newLocale
this.locale = newLocale
}
},
async setUserTimeZone(tzId: string) {
// Do not make any api call if the user clicks the same timeZone again that is already selected
if(this.currentTimeZoneId === tzId) {
return;
}

try {
await userContext.setUserTimeZone({ tzId })
this.currentTimeZoneId = tzId

showToast(translate("Time zone updated successfully"));
return Promise.resolve(tzId)
} catch(err) {
console.error('Error', err)
return Promise.reject('')
}
},
async getAvailableTimeZones() {
// Do not fetch timeZones information, if already available
if(this.timeZones.length) {
return;
}

try {
const resp = await userContext.getAvailableTimeZones()
this.timeZones = resp.filter((timeZone: any) => DateTime.local().setZone(timeZone.id).isValid);
} catch(err) {
console.error('Error', err)
}
},
updateTimeZone(tzId: string) {
this.currentTimeZoneId = tzId
}
},
persist: true
Expand Down

0 comments on commit a6a8888

Please sign in to comment.