+
+
+ {{ pref.description }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/locales/en.json b/src/locales/en.json
index 1b431448b..f22b82b84 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -4,6 +4,7 @@
"An email notification will be sent to that their order is ready for pickup. This order will also be moved to the packed orders tab.": "An email notification will be sent to { customerName } that their order is ready for pickup.{ space } This order will also be moved to the packed orders tab.",
"An email notification will be sent to that their order is ready for pickup.": "An email notification will be sent to { customerName } that their order is ready for pickup.",
"Are you sure you want to change the time zone to?": "Are you sure you want to change the time zone to?",
+ "Are you sure you want to update the notification preferences?": "Are you sure you want to update the notification preferences?",
"Arrived": "Arrived",
"Authenticating": "Authenticating",
"Assign Pickers": "Assign Pickers",
@@ -56,14 +57,21 @@
"Logout": "Logout",
"Mismatch": "Mismatch",
"More": "More",
+ "New notification received.": "New notification received.",
"No inventory details found": "No inventory details found",
"Not in stock": "Not in stock",
"Not in Stock": "Not in Stock",
"No products found": "No products found",
"No reason": "No reason",
+ "No notifications to show": "No notifications to show",
"No picker assigned.": "No picker assigned.",
"No picker found": "No picker found",
"No time zone found": "No time zone found",
+ "Notifications": "Notifications",
+ "Notification Preference": "Notification Preference",
+ "Notification preferences not found.": "Notification preferences not found.",
+ "Notification preferences updated.": "Notification preferences updated.",
+ "Notification preferences not updated. Please try again.": "Notification preferences not updated. Please try again.",
"Open": "Open",
"OMS": "OMS",
"OMS instance": "OMS instance",
@@ -144,6 +152,7 @@
"Warehouse": "Warehouse",
"Worn Display": "Worn Display",
"This order will be removed from your dashboard. This action cannot be undone.": "This order will be removed from your dashboard.{ space } This action cannot be undone.",
+ "Update notification preferences": "Update notification preferences",
"View shipping orders along with pickup orders.": "View shipping orders along with pickup orders.",
"You do not have permission to access this page": "You do not have permission to access this page",
"Zipcode": "Zipcode"
diff --git a/src/locales/es.json b/src/locales/es.json
index 0e865887e..24fc5c9f4 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -4,6 +4,7 @@
"An email notification will be sent to that their order is ready for pickup. This order will also be moved to the packed orders tab.": "Se enviará una notificación por correo electrónico a {customerName} de que su pedido está listo para recoger.{ space } Este pedido también se moverá a la pestaña de pedidos empacados.",
"An email notification will be sent to that their order is ready for pickup.": "Se enviará una notificación por correo electrónico a {customerName} de que su pedido está listo para recoger.",
"Are you sure you want to change the time zone to?": "¿Estás seguro de que quieres cambiar la zona horaria a?",
+ "Are you sure you want to update the notification preferences?": "Are you sure you want to update the notification preferences?",
"Arrived": "Llegó",
"Authenticating": "Authenticating",
"Assign Pickers": "Asignar recolectores",
@@ -56,13 +57,20 @@
"Logout": "Cerrar sesión",
"Mismatch": "Desajuste",
"More": "Más",
+ "New notification received.": "New notification received.",
"No inventory details found": "No se encontraron detalles de inventario",
"Not in stock": "No disponible en el inventario",
"No products found": "No se encontraron productos",
"No reason": "Sin razón",
+ "No notifications to show": "No notifications to show",
"No picker assigned.": "No picker assigned.",
"No picker found": "No se encontró recolector",
"No time zone found": "No se encontró zona horaria",
+ "Notifications": "Notifications",
+ "Notification Preference": "Notification Preference",
+ "Notification preferences not found.": "Notification preferences not found.",
+ "Notification preferences updated.": "Notification preferences updated.",
+ "Notification preferences not updated. Please try again.": "Notification preferences not updated. Please try again.",
"Open": "Abierto",
"OMS": "OMS",
"OMS instance": "Instancia de OMS",
@@ -143,6 +151,7 @@
"Warehouse": "Almacén",
"Worn Display": "Pantalla desgastada",
"This order will be removed from your dashboard. This action cannot be undone.": "Este pedido será eliminado de tu panel de control.{ space } Esta acción no se puede deshacer.",
+ "Update notification preferences": "Update notification preferences",
"View shipping orders along with pickup orders.": "Ver órdenes de envío junto con órdenes de recogida.",
"You do not have permission to access this page": "No tienes permiso para acceder a esta página",
"Zipcode": "Código postal",
diff --git a/src/locales/ja.json b/src/locales/ja.json
index ee350f413..86643f852 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -4,6 +4,7 @@
"An email notification will be sent to that their order is ready for pickup. This order will also be moved to the packed orders tab.": "{ customerName }様宛に注文の受け取り準備が完了したことをお知らするメールが送信されます。{ space } この注文は「梱包済み注文」タブに移動されます.",
"An email notification will be sent to that their order is ready for pickup.": "{ customerName }様宛に注文の受け取り準備が完了したことをお知らするメールが送信されます。",
"Are you sure you want to change the time zone to?": "タイムゾーンを変更してもよろしいですか?",
+ "Are you sure you want to update the notification preferences?": "Are you sure you want to update the notification preferences?",
"Arrived": "到着",
"Authenticating": "Authenticating",
"Assign Pickers": "受け取り人の割当",
@@ -56,13 +57,20 @@
"Logout": "ログアウト",
"Mismatch": "不一致",
"More": "More",
+ "New notification received.": "New notification received.",
"No inventory details found": "在庫の詳細が見つかりません",
"Not in Stock": "在庫切れ",
"No products found": "商品が見つかりません",
"No reason": "理由なし",
+ "No notifications to show": "No notifications to show",
"No picker assigned.": "No picker assigned.",
"No picker found": "受取人が見つかりません",
"No time zone found": "タイムゾーンが見つかりません",
+ "Notifications": "Notifications",
+ "Notification Preference": "Notification Preference",
+ "Notification preferences not found.": "Notification preferences not found.",
+ "Notification preferences updated.": "Notification preferences updated.",
+ "Notification preferences not updated. Please try again.": "Notification preferences not updated. Please try again.",
"Open": "オープン",
"OMS": "OMS",
"OMS instance": "OMSインスタンス",
@@ -143,6 +151,7 @@
"Warehouse": "倉庫",
"Worn Display": "すり切れたディスプレイ",
"This order will be removed from your dashboard. This action cannot be undone.": "この注文はダッシュボードから削除されます。{ space } この操作は元に戻せません。",
+ "Update notification preferences": "Update notification preferences",
"View shipping orders along with pickup orders.": "店舗受取の注文と一緒に配送注文を表示します",
"You do not have permission to access this page": "このページにアクセスする権限がありません",
"Zipcode": "郵便番号"
diff --git a/src/main.ts b/src/main.ts
index 9b076d297..524854075 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -27,15 +27,19 @@ import './theme/variables.css';
import "@hotwax/apps-theme";
import store from './store'
+
import permissionPlugin from '@/authorization';
import permissionRules from '@/authorization/Rules';
import permissionActions from '@/authorization/Actions';
+
import { dxpComponents } from '@hotwax/dxp-components'
-import { login, logout, loader } from './user-utils';
import localeMessages from './locales';
-import { getConfig, initialise } from './adapter';
-import { getUserPreference, setUserPreference } from '@/adapter';
+import { getConfig, initialise, getUserPreference, setUserPreference } from '@/adapter';
+import { login, logout, loader } from '@/utils/user';
+import { addNotification, storeClientRegistrationToken } from '@/utils/firebase';
+console.log("Firease Config: " + process.env.VUE_APP_FIREBASE_CONFIG);
+console.log("Firebase VAPID key: " + process.env.VUE_APP_FIREBASE_VAPID_KEY);
const app = createApp(App)
.use(IonicVue, {
mode: 'md'
@@ -47,16 +51,20 @@ const app = createApp(App)
actions: permissionActions
})
.use(dxpComponents, {
+ addNotification,
+ appLoginUrl: process.env.VUE_APP_LOGIN_URL as string,
+ appFirebaseConfig: JSON.parse(process.env.VUE_APP_FIREBASE_CONFIG),
+ appFirebaseVapidKey: process.env.VUE_APP_FIREBASE_VAPID_KEY,
defaultImgUrl: require("@/assets/images/defaultImage.png"),
+ getConfig,
+ initialise,
+ loader,
login,
logout,
- loader,
- appLoginUrl: process.env.VUE_APP_LOGIN_URL as string,
localeMessages,
- getConfig,
- initialise,
getUserPreference,
- setUserPreference
+ setUserPreference,
+ storeClientRegistrationToken,
});
// Filters are removed in Vue 3 and global filter introduced https://v3.vuejs.org/guide/migration/filters.html#global-filters
diff --git a/src/router/index.ts b/src/router/index.ts
index b15567e1a..6e996e2ad 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -5,6 +5,7 @@ import Tabs from '@/views/Tabs.vue'
import OrderDetail from '@/views/OrderDetail.vue'
import ProductDetail from '@/views/ProductDetail.vue'
import ShipToStoreOrders from '@/views/ShipToStoreOrders.vue'
+import Notifications from '@/views/Notifications.vue'
import Shopify from '@/views/Shopify.vue'
import { hasPermission } from '@/authorization';
@@ -13,7 +14,7 @@ import { translate } from '@hotwax/dxp-components'
import 'vue-router'
import { Login, useAuthStore } from '@hotwax/dxp-components';
-import { loader } from '@/user-utils';
+import { loader } from '@/utils/user';
// Defining types for the meta values
declare module 'vue-router' {
@@ -110,6 +111,12 @@ const routes: Array = [
component: ShipToStoreOrders,
beforeEnter: authGuard,
},
+ {
+ path: '/notifications',
+ name: "Notifications",
+ component: Notifications,
+ beforeEnter: authGuard,
+ },
{
path: '/shopify',
name: 'Shopify',
diff --git a/src/store/modules/user/UserState.ts b/src/store/modules/user/UserState.ts
index 3b584a803..b3f5869ca 100644
--- a/src/store/modules/user/UserState.ts
+++ b/src/store/modules/user/UserState.ts
@@ -6,4 +6,7 @@ export default interface UserState {
preference: any;
permissions: any;
currentEComStore: any;
+ notifications: any;
+ notificationPrefs: any;
+ firebaseDeviceId: string;
}
\ No newline at end of file
diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts
index 429fb5fd0..f1c1f6064 100644
--- a/src/store/modules/user/actions.ts
+++ b/src/store/modules/user/actions.ts
@@ -1,18 +1,23 @@
import { UserService } from '@/services/UserService'
import { ActionTree } from 'vuex'
import RootState from '@/store/RootState'
+import store from '@/store';
import UserState from './UserState'
import * as types from './mutation-types'
import { showToast } from '@/utils'
-import { Settings } from 'luxon';
import {
getUserPreference,
hasError,
resetConfig,
setUserPreference,
updateInstanceUrl,
- updateToken
+ updateToken,
+ getNotificationEnumIds,
+ getNotificationUserPrefTypeIds,
+ storeClientRegistrationToken,
+ getUserFacilities
} from '@/adapter'
+import { DateTime, Settings } from 'luxon';
import {
getServerPermissionsFromRules,
prepareAppPermissions,
@@ -20,6 +25,7 @@ import {
setPermissions
} from '@/authorization'
import { useAuthStore, translate } from '@hotwax/dxp-components'
+import { generateDeviceId, generateTopicName } from '@/utils/firebase'
const actions: ActionTree = {
@@ -60,6 +66,12 @@ const actions: ActionTree = {
const userProfile = await UserService.getUserProfile(token);
+ //fetching user facilities
+ const isAdminUser = appPermissions.some((appPermission: any) => appPermission?.action === "APP_STOREFULFILLMENT_ADMIN" );
+ const baseURL = store.getters['user/getBaseUrl'];
+ const facilities = await getUserFacilities(token, baseURL, userProfile?.partyId, "PICKUP", isAdminUser);
+ userProfile.facilities = facilities;
+
// removing duplicate records as a single user can be associated with a facility by multiple roles.
userProfile.facilities.reduce((uniqueFacilities: any, facility: any, index: number) => {
if(uniqueFacilities.includes(facility.facilityId)) userProfile.facilities.splice(index, 1);
@@ -102,6 +114,7 @@ const actions: ActionTree = {
const authStore = useAuthStore()
// TODO add any other tasks if need
dispatch("product/clearProducts", null, { root: true })
+ dispatch('clearNotificationState')
commit(types.USER_END_SESSION)
resetPermissions();
resetConfig();
@@ -153,6 +166,58 @@ const actions: ActionTree = {
'userPrefTypeId': 'BOPIS_PREFERENCE',
'userPrefValue': JSON.stringify(state.preference)
});
+ },
+
+ addNotification({ state, commit }, payload) {
+ const notifications = JSON.parse(JSON.stringify(state.notifications))
+ notifications.push({ ...payload.notification, time: DateTime.now().toMillis() })
+ if (payload.isForeground) {
+ showToast(translate("New notification received."));
+ }
+ commit(types.USER_NOTIFICATIONS_UPDATED, notifications)
+ },
+
+ async fetchNotificationPreferences({ commit, state }) {
+ let resp = {} as any
+ const oms = state.instanceUrl
+ const facilityId = (state.currentFacility as any).facilityId
+ let notificationPreferences = [], enumerationResp = [], userPrefIds = [] as any
+ try {
+ resp = await getNotificationEnumIds(process.env.VUE_APP_NOTIF_ENUM_TYPE_ID)
+ enumerationResp = resp.docs
+ resp = await getNotificationUserPrefTypeIds(process.env.VUE_APP_NOTIF_APP_ID)
+ userPrefIds = resp.docs.map((userPref: any) => userPref.userPrefTypeId)
+ } catch (error) {
+ console.error(error)
+ } finally {
+ // checking enumerationResp as we want to show disbaled prefs if only getNotificationEnumIds returns
+ // data and getNotificationUserPrefTypeIds fails or returns empty response (all disbaled)
+ if (enumerationResp.length) {
+ notificationPreferences = enumerationResp.reduce((notifactionPref: any, pref: any) => {
+ const userPrefTypeIdToSearch = generateTopicName(oms, facilityId, pref.enumId)
+ notifactionPref.push({ ...pref, isEnabled: userPrefIds.includes(userPrefTypeIdToSearch) })
+ return notifactionPref
+ }, [])
+ }
+ commit(types.USER_NOTIFICATIONS_PREFERENCES_UPDATED, notificationPreferences)
+ }
+ },
+
+ async storeClientRegistrationToken({ commit }, registrationToken) {
+ const firebaseDeviceId = generateDeviceId()
+ commit(types.USER_FIREBASE_DEVICEID_UPDATED, firebaseDeviceId)
+
+ try {
+ await storeClientRegistrationToken(registrationToken, firebaseDeviceId, process.env.VUE_APP_NOTIF_APP_ID)
+ } catch (error) {
+ console.error(error)
+ }
+ },
+
+ clearNotificationState({ commit }) {
+ commit(types.USER_NOTIFICATIONS_UPDATED, [])
+ commit(types.USER_NOTIFICATIONS_PREFERENCES_UPDATED, [])
+ commit(types.USER_FIREBASE_DEVICEID_UPDATED, '')
}
}
export default actions;
\ No newline at end of file
diff --git a/src/store/modules/user/getters.ts b/src/store/modules/user/getters.ts
index 945ddccba..e9adb4b73 100644
--- a/src/store/modules/user/getters.ts
+++ b/src/store/modules/user/getters.ts
@@ -44,6 +44,15 @@ const getters: GetterTree = {
},
getCurrentEComStore(state) {
return state.currentEComStore;
+ },
+ getNotifications(state) {
+ return state.notifications.sort((a: any, b: any) => b.time - a.time)
+ },
+ getNotificationPrefs(state) {
+ return state.notificationPrefs
+ },
+ getFirebaseDeviceId(state) {
+ return state.firebaseDeviceId
}
}
export default getters;
\ No newline at end of file
diff --git a/src/store/modules/user/index.ts b/src/store/modules/user/index.ts
index 120410e07..02c5e893f 100644
--- a/src/store/modules/user/index.ts
+++ b/src/store/modules/user/index.ts
@@ -19,6 +19,9 @@ const userModule: Module = {
},
currentEComStore: {},
permissions: [],
+ notifications: [],
+ notificationPrefs: [],
+ firebaseDeviceId: ''
},
getters,
actions,
diff --git a/src/store/modules/user/mutation-types.ts b/src/store/modules/user/mutation-types.ts
index 086c92905..2793ddfc4 100644
--- a/src/store/modules/user/mutation-types.ts
+++ b/src/store/modules/user/mutation-types.ts
@@ -6,4 +6,7 @@ export const USER_CURRENT_FACILITY_UPDATED = SN_USER + '/CURRENT_FACILITY_UPDATE
export const USER_INSTANCE_URL_UPDATED = SN_USER + '/INSTANCE_URL_UPDATED'
export const USER_PREFERENCE_UPDATED = SN_USER + '/PREFERENCE_UPDATED'
export const USER_CURRENT_ECOM_STORE_UPDATED = SN_USER + '/CURRENT_ECOM_STORE_UPDATED'
-export const USER_PERMISSIONS_UPDATED = SN_USER + '/PERMISSIONS_UPDATED'
\ No newline at end of file
+export const USER_PERMISSIONS_UPDATED = SN_USER + '/PERMISSIONS_UPDATED'
+export const USER_NOTIFICATIONS_UPDATED = SN_USER + '/NOTIFICATIONS_UPDATED'
+export const USER_NOTIFICATIONS_PREFERENCES_UPDATED = SN_USER + '/NOTIFICATIONS_PREFERENCES_UPDATED'
+export const USER_FIREBASE_DEVICEID_UPDATED = SN_USER + '/FIREBASE_DEVICEID_UPDATED'
\ No newline at end of file
diff --git a/src/store/modules/user/mutations.ts b/src/store/modules/user/mutations.ts
index 65bf74be7..bdcf4067c 100644
--- a/src/store/modules/user/mutations.ts
+++ b/src/store/modules/user/mutations.ts
@@ -29,6 +29,15 @@ const mutations: MutationTree = {
},
[types.USER_PERMISSIONS_UPDATED] (state, payload) {
state.permissions = payload
+ },
+ [types.USER_NOTIFICATIONS_UPDATED] (state, payload) {
+ state.notifications = payload
+ },
+ [types.USER_NOTIFICATIONS_PREFERENCES_UPDATED] (state, payload) {
+ state.notificationPrefs = payload
+ },
+ [types.USER_FIREBASE_DEVICEID_UPDATED] (state, payload) {
+ state.firebaseDeviceId = payload
}
}
export default mutations;
\ No newline at end of file
diff --git a/src/utils/firebase.ts b/src/utils/firebase.ts
new file mode 100644
index 000000000..bf903a7b2
--- /dev/null
+++ b/src/utils/firebase.ts
@@ -0,0 +1,19 @@
+import { DateTime } from "luxon"
+import store from '@/store'
+
+const storeClientRegistrationToken = async (registrationToken: string) => store.dispatch('user/storeClientRegistrationToken', registrationToken);
+
+const addNotification = async (notification: any) => store.dispatch('user/addNotification', notification);
+
+// device ID:
+const generateDeviceId = () => (DateTime.now().toFormat('ddMMyy') + String(DateTime.now().toMillis()).slice(-6))
+
+// topic name: oms-facilityId-enumId(enumCode)
+const generateTopicName = (oms: string, facilityId: string, enumId: string) => `${oms}-${facilityId}-${enumId}`
+
+export {
+ addNotification,
+ generateTopicName,
+ generateDeviceId,
+ storeClientRegistrationToken
+}
\ No newline at end of file
diff --git a/src/user-utils/index.ts b/src/utils/user.ts
similarity index 93%
rename from src/user-utils/index.ts
rename to src/utils/user.ts
index 5711bfd41..faf98e48b 100644
--- a/src/user-utils/index.ts
+++ b/src/utils/user.ts
@@ -1,4 +1,4 @@
-import { translate } from '@hotwax/dxp-components'
+import { translate } from '@/i18n'
import store from '@/store'
import { loadingController } from '@ionic/vue'
diff --git a/src/views/Notifications.vue b/src/views/Notifications.vue
new file mode 100644
index 000000000..0c12c61f3
--- /dev/null
+++ b/src/views/Notifications.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+ {{ $t("Notifications") }}
+
+
+
+
+
+
+
+
+
+