Skip to content

Commit

Permalink
Merge pull request #320 from R-Sourabh/#228-facility-selector
Browse files Browse the repository at this point in the history
Implemented: centralized facility selector(#228)
  • Loading branch information
ymaheshwari1 authored Oct 17, 2024
2 parents 1d09f6c + 0eb2f1f commit 76fce68
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 6 deletions.
162 changes: 162 additions & 0 deletions src/components/DxpFacilitySwitcher.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<template>
<ion-card>
<ion-card-header>
<ion-card-title>
{{ $t('Facility') }}
</ion-card-title>
</ion-card-header>
<ion-card-content>
{{ $t('Specify which facility you want to operate from. Order, inventory and other configuration data will be specific to the facility you select.') }}
</ion-card-content>
<ion-item lines="none">
<ion-label>
{{ currentFacility.facilityName }}
<p>{{ currentFacility.facilityId }}</p>
</ion-label>
<ion-button id="open-facility-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="facilityModal" trigger="open-facility-modal" @didPresent="loadFacilities()" @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 Facility") }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-toolbar>
<ion-searchbar @ionFocus="selectSearchBarText($event)" :placeholder="$t('Search facilities')" v-model="queryString" @keyup.enter="queryString = $event.target.value; findFacility()" @keydown="preventSpecialCharacters($event)"/>
</ion-toolbar>
<ion-content>
<ion-radio-group v-model="selectedFacilityId">
<ion-list>
<!-- Loading state -->
<div class="empty-state" v-if="isLoading">
<ion-item lines="none">
<ion-spinner color="secondary" name="crescent" slot="start" />
{{ $t("Fetching facilities") }}
</ion-item>
</div>
<!-- Empty state -->
<div class="empty-state" v-else-if="!filteredFacilities.length">
<p>{{ $t("No facilities found") }}</p>
</div>
<div v-else>
<ion-item v-for="facility in filteredFacilities" :key="facility.facilityId">
<ion-radio label-placement="end" justify="start" :value="facility.facilityId">
<ion-label>
{{ facility.facilityName }}
<p>{{ facility.facilityId }}</p>
</ion-label>
</ion-radio>
</ion-item>
</div>
</ion-list>
</ion-radio-group>

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button :disabled="selectedFacilityId === currentFacility.facilityId" @click="updateFacility">
<ion-icon :icon="saveOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-modal>
</template>

<script setup lang="ts">
import {
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardHeader,
IonCardTitle,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonItem,
IonLabel,
IonList,
IonModal,
IonRadio,
IonRadioGroup,
IonSearchbar,
IonSpinner,
IonTitle,
IonToolbar
} from '@ionic/vue';
import { closeOutline, saveOutline } from "ionicons/icons";
import { useUserStore } from 'src/store/user';
import { computed, ref } from 'vue';
const userStore = useUserStore();
const facilities = computed(() => userStore.getFacilites)
const currentFacility = computed(() => userStore.getCurrentFacility)
const facilityModal = ref()
const queryString = ref('')
const isLoading = ref(true);
const filteredFacilities = ref([])
const selectedFacilityId = ref(currentFacility.value.facilityId)
const emit = defineEmits(["updateFacility"])
const closeModal = () => {
facilityModal.value.$el.dismiss(null, 'cancel');
}
function loadFacilities() {
filteredFacilities.value = facilities.value;
isLoading.value = false;
}
const findFacility = () => {
isLoading.value = true
const searchedString = queryString.value.trim().toLowerCase();
if(searchedString) {
filteredFacilities.value = facilities.value.filter((facility: any) =>
facility.facilityName.toLowerCase().includes(searchedString) ||
facility.facilityId.toLowerCase().includes(searchedString)
);
} else {
filteredFacilities.value = facilities.value;
}
isLoading.value = false
}
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();
}
async function updateFacility() {
const selectedFacility = facilities.value.find((facility: any) => facility.facilityId === selectedFacilityId.value)
await userStore.setFacilityPreference(selectedFacility)
emit('updateFacility', selectedFacility);
closeModal();
}
function clearSearch() {
queryString.value = ''
filteredFacilities.value = []
isLoading.value = true
}
</script>

<style scoped>
ion-content {
--padding-bottom: 80px;
}
</style>
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import '@ionic/vue/css/flex-utils.css';
import '@ionic/vue/css/display.css';

export { default as DxpAppVersionInfo } from './DxpAppVersionInfo.vue';
export { default as DxpFacilitySwitcher } from './DxpFacilitySwitcher.vue'
export { default as DxpGitBookSearch } from './DxpGitBookSearch.vue';
export { default as DxpImage } from './DxpImage.vue';
export { default as DxpLanguageSwitcher } from './DxpLanguageSwitcher.vue';
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ declare var process: any;
import { createPinia } from "pinia";
import { useProductIdentificationStore } from "./store/productIdentification";
import { useAuthStore } from "./store/auth";
import { DxpAppVersionInfo, DxpGitBookSearch, DxpImage, DxpLanguageSwitcher, DxpLogin, DxpMenuFooterNavigation, DxpOmsInstanceNavigator, DxpProductIdentifier, DxpShopifyImg, DxpTimeZoneSwitcher, DxpUserProfile } from "./components";
import { DxpAppVersionInfo, DxpFacilitySwitcher, DxpGitBookSearch, DxpImage, DxpLanguageSwitcher, DxpLogin, DxpMenuFooterNavigation, DxpOmsInstanceNavigator, DxpProductIdentifier, DxpShopifyImg, DxpTimeZoneSwitcher, DxpUserProfile } from "./components";
import { goToOms, getProductIdentificationValue } from "./utils";
import { initialiseFirebaseApp } from "./utils/firebase"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
Expand All @@ -25,6 +25,7 @@ let loginContext = {} as any
let shopifyImgContext = {} as any
let appContext = {} as any
let productIdentificationContext = {} as any
let facilityContext = {} as any
let notificationContext = {} as any
let gitBookContext = {} as any
let userContext = {} as any
Expand Down Expand Up @@ -67,6 +68,7 @@ export let dxpComponents = {
})

app.component('DxpAppVersionInfo', DxpAppVersionInfo)
app.component('DxpFacilitySwitcher', DxpFacilitySwitcher)
app.component('DxpGitBookSearch', DxpGitBookSearch)
app.component('DxpImage', DxpImage)
app.component('DxpLanguageSwitcher', DxpLanguageSwitcher)
Expand Down Expand Up @@ -96,6 +98,10 @@ export let dxpComponents = {

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

facilityContext.getUserFacilities = options.getUserFacilities
facilityContext.setUserPreference = options.setUserPreference
facilityContext.getUserPreference = options.getUserPreference

notificationContext.addNotification = options.addNotification
notificationContext.appFirebaseConfig = options.appFirebaseConfig
Expand Down Expand Up @@ -138,6 +144,7 @@ export {
loginContext,
notificationContext,
productIdentificationContext,
facilityContext,
shopifyImgContext,
translate,
useAuthStore,
Expand Down
4 changes: 4 additions & 0 deletions src/store/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export const useAuthStore = defineStore('userAuth', {
getters: {
getToken: (state) => state.token,
getOms: (state) => state.oms,
getBaseUrl: (state) => {
let baseURL = state.oms
return baseURL.startsWith('http') ? baseURL.includes('/api') ? baseURL : `${baseURL}/api/` : `https://${baseURL}.hotwax.io/api/`;
},
isAuthenticated: (state) => {
let isTokenExpired = false
if (state.token.expiration) {
Expand Down
59 changes: 54 additions & 5 deletions src/store/user.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { defineStore } from "pinia";
import { appContext, i18n, translate, userContext } from "../../src";
import { hasError } from "@hotwax/oms-api";
import { i18n, translate, userContext, useAuthStore } from "../../src";
import { DateTime } from "luxon";
import { showToast } from "src/utils";
import { facilityContext } from "../index";

declare let process: any;

Expand All @@ -12,14 +12,18 @@ export const useUserStore = defineStore('user', {
localeOptions: process.env.VUE_APP_LOCALES ? JSON.parse(process.env.VUE_APP_LOCALES) : { "en-US": "English" },
locale: 'en-US',
currentTimeZoneId: '',
timeZones: []
timeZones: [],
facilities: [],
currentFacility: {} as any
}
},
getters: {
getLocale: (state) => state.locale,
getLocaleOptions: (state) => state.localeOptions,
getTimeZones: (state) => state.timeZones,
getCurrentTimeZone: (state) => state.currentTimeZoneId
getCurrentTimeZone: (state) => state.currentTimeZoneId,
getFacilites: (state) => state.facilities,
getCurrentFacility: (state) => state.currentFacility
},
actions: {
async setLocale(locale: string) {
Expand Down Expand Up @@ -74,7 +78,52 @@ export const useUserStore = defineStore('user', {
},
updateTimeZone(tzId: string) {
this.currentTimeZoneId = tzId
}
},

async getUserFacilities(partyId: any, facilityGroupId: any, isAdminUser: boolean) {
const authStore = useAuthStore();

try {
const response = await facilityContext.getUserFacilities(authStore.getToken.value, authStore.getBaseUrl, partyId, facilityGroupId, isAdminUser);
this.facilities = response;
} catch (error) {
console.error(error);
}
return this.facilities
},

async setFacilityPreference(payload: any) {

try {
await facilityContext.setUserPreference({
userPrefTypeId: 'SELECTED_FACILITY',
userPrefValue: payload.facilityId
})
} catch (error) {
console.error('error', error)
}
this.currentFacility = payload;
},

async getFacilityPreference(userPrefTypeId: any) {
const authStore = useAuthStore();

if (!this.facilities.length) {
return;
}
let preferredFacility = this.facilities[0];

try {
let preferredFacilityId = await facilityContext.getUserPreference(authStore.getToken.value, authStore.getBaseUrl, userPrefTypeId);
if(preferredFacilityId) {
const facility = this.facilities.find((facility: any) => facility.facilityId === preferredFacilityId);
facility && (preferredFacility = facility)
}
} catch (error) {
console.error(error);
}
this.currentFacility = preferredFacility;
},
},
persist: true
})

0 comments on commit 76fce68

Please sign in to comment.