From eb1d82d38069d7354bc1dc71e6b1e0ecf3accf61 Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Mon, 17 Jul 2023 10:39:26 +0530 Subject: [PATCH 01/24] Implemented: router and view to handle saved mappings(#192) --- src/locales/en.json | 1 + src/router/index.ts | 7 +++ src/views/Exim.vue | 10 ++++ src/views/SavedMappings.vue | 109 ++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 src/views/SavedMappings.vue diff --git a/src/locales/en.json b/src/locales/en.json index 08d53391..f9d753f7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -118,6 +118,7 @@ "Retry Generate Label": "Retry Generate Label", "Same Day": "Same Day", "Save": "Save", + "Saved mappings": "Saved mappings", "Search time zones": "Search time zones", "Select": "Select", "Select all": "Select all", diff --git a/src/router/index.ts b/src/router/index.ts index 05eded5b..be9f4c12 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -9,6 +9,7 @@ import store from '@/store' import Exim from "@/views/Exim.vue" import UploadImportOrders from "@/views/UploadImportOrders.vue" import DownloadPackedOrders from "@/views/DownloadPackedOrders.vue" +import SavedMappings from "@/views/SavedMappings.vue" const authGuard = (to: any, from: any, next: any) => { if (store.getters['user/isAuthenticated']) { @@ -73,6 +74,12 @@ const routes: Array = [ component: DownloadPackedOrders, beforeEnter: authGuard }, + { + path: "/saved-mappings", + name: "SavedMappings", + component: SavedMappings, + beforeEnter: authGuard + }, { path: "/settings", name: "Settings", diff --git a/src/views/Exim.vue b/src/views/Exim.vue index d39db33a..ae47348f 100644 --- a/src/views/Exim.vue +++ b/src/views/Exim.vue @@ -49,6 +49,16 @@ + + + + + + {{ $t('Saved mappings') }} + + + + diff --git a/src/views/SavedMappings.vue b/src/views/SavedMappings.vue new file mode 100644 index 00000000..7b12c6d8 --- /dev/null +++ b/src/views/SavedMappings.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file From 2668458762fe3646917901663c159fab4951fe5f Mon Sep 17 00:00:00 2001 From: Yash Maheshwari Date: Mon, 17 Jul 2023 14:22:12 +0530 Subject: [PATCH 02/24] Added: support to create new mapping and apply already created mappings(#192) --- .env.example | 3 +- src/components/CreateMappingModal.vue | 138 ++++++++++++++++++ src/components/Menu.vue | 9 +- src/locales/en.json | 1 + src/services/UserService.ts | 38 ++++- src/store/modules/user/UserState.ts | 7 + src/store/modules/user/actions.ts | 170 +++++++++++++++++++++++ src/store/modules/user/getters.ts | 10 ++ src/store/modules/user/index.ts | 7 + src/store/modules/user/mutation-types.ts | 5 +- src/store/modules/user/mutations.ts | 12 ++ src/views/UploadImportOrders.vue | 54 ++++++- 12 files changed, 448 insertions(+), 6 deletions(-) create mode 100644 src/components/CreateMappingModal.vue diff --git a/.env.example b/.env.example index 72552f03..c1f11853 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,5 @@ VUE_APP_BASE_URL= VUE_APP_PERMISSION_ID= VUE_APP_ALIAS={} VUE_APP_DEFAULT_LOG_LEVEL="error" -VUE_APP_UPLD_IMP_ORD={"orderId": { "label": "Order ID", "required": true }, "facilityId": { "label": "Facility ID", "required": true },"trackingCode": { "label": "Tracking Code", "required": true }} +VUE_APP_MAPPING_TYPES={"IMPORD": "IMP_ORD_MAPPING_PREF"} +VUE_APP_MAPPING_IMPORD={"orderId": { "label": "Order ID", "required": true }, "facilityId": { "label": "Facility ID", "required": true },"trackingCode": { "label": "Tracking Code", "required": true }} diff --git a/src/components/CreateMappingModal.vue b/src/components/CreateMappingModal.vue new file mode 100644 index 00000000..dd837f81 --- /dev/null +++ b/src/components/CreateMappingModal.vue @@ -0,0 +1,138 @@ + + + \ No newline at end of file diff --git a/src/components/Menu.vue b/src/components/Menu.vue index 7151108e..728c8dbd 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -39,7 +39,7 @@ import { } from "@ionic/vue"; import { computed, defineComponent } from "vue"; import { mapGetters } from "vuex"; -import { mailUnreadOutline, mailOpenOutline, checkmarkDoneOutline, settingsOutline, swapVerticalOutline } from "ionicons/icons"; +import { bookmarkOutline, mailUnreadOutline, mailOpenOutline, checkmarkDoneOutline, settingsOutline, swapVerticalOutline } from "ionicons/icons"; import { useStore } from "@/store"; import { useRouter } from "vue-router"; @@ -93,6 +93,12 @@ export default defineComponent({ mdIcon: swapVerticalOutline, childRoutes: ["/download-packed-orders", "/upload-import-orders"] // defined child routes as to enable the correct menu when we are on a route that is not listed in the menu }, + { + title: "Saved Mappings", + url: "/saved-mappings", + iosIcon: bookmarkOutline, + mdIcon: bookmarkOutline, + }, { title: "Settings", url: "/settings", @@ -107,6 +113,7 @@ export default defineComponent({ }) return { + bookmarkOutline, selectedIndex, appPages, mailUnreadOutline, diff --git a/src/locales/en.json b/src/locales/en.json index f9d753f7..d4573dab 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -80,6 +80,7 @@ "Order updated successfully": "Order updated successfully", "orders": "orders", "Out of stock": "Out of stock", + "New mapping": "New mapping", "Next day": "Next day", "No packed shipments to ship for these orders": "No packed shipments to ship for these orders", "package": "package", diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 1c51d0f1..83c2bd1b 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -118,11 +118,46 @@ const setUserPreference = async (payload: any): Promise => { }); } +const createFieldMapping = async (payload: any): Promise => { + return api({ + url: "/service/createDataManagerMapping", + method: "POST", + data: payload + }); +} + +const updateFieldMapping = async (payload: any): Promise => { + return api({ + url: "/service/updateDataManagerMapping", + method: "POST", + data: payload + }); +} + +const deleteFieldMapping = async (payload: any): Promise => { + return api({ + url: "/service/deleteDataManagerMapping", + method: "POST", + data: payload + }); +} + +const getFieldMappings = async (payload: any): Promise => { + return api({ + url: "/performFind", + method: "POST", + data: payload + }); +} + export const UserService = { + createFieldMapping, + deleteFieldMapping, login, getAvailableTimeZones, getEComStores, getFacilityDetails, + getFieldMappings, getInProgressOrdersCount, getOutstandingOrdersCount, getProfile, @@ -132,5 +167,6 @@ export const UserService = { setUserPreference, setUserTimeZone, checkPermission, - updateFacility + updateFacility, + updateFieldMapping } \ No newline at end of file diff --git a/src/store/modules/user/UserState.ts b/src/store/modules/user/UserState.ts index 32a39702..252bd62b 100644 --- a/src/store/modules/user/UserState.ts +++ b/src/store/modules/user/UserState.ts @@ -8,4 +8,11 @@ export default interface UserState { printShippingLabel: boolean, printPackingSlip: boolean } + fieldMappings: object | null; + currentMapping: { + id: string; + mappingType: string; + name: string; + value: object; + }; } \ No newline at end of file diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 76f1d114..7e29bf21 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -107,6 +107,7 @@ const actions: ActionTree = { const currentFacility = resp.data.facilities.length > 0 ? resp.data.facilities[0] : {}; resp.data.stores = await dispatch('getEComStores', { facilityId: currentFacility.facilityId }) + dispatch('getFieldMappings') commit(types.USER_INFO_UPDATED, resp.data); commit(types.USER_CURRENT_FACILITY_UPDATED, currentFacility); } catch(err) { @@ -193,6 +194,175 @@ const actions: ActionTree = { setUserPreference({ commit }, payload){ commit(types.USER_PREFERENCE_UPDATED, payload) }, + + async getFieldMappings({ commit }) { + let fieldMappings = {} as any; + try { + const payload = { + "inputFields": { + "mappingPrefTypeEnumId": Object.values(JSON.parse(process.env.VUE_APP_MAPPING_TYPES as string)), + "mappingPrefTypeEnumId_op": "in" + }, + "fieldList": ["mappingPrefName", "mappingPrefId", "mappingPrefValue", "mappingPrefTypeEnumId"], + "filterByDate": "Y", + "viewSize": 20, // considered a user won't have more than 20 saved mappings + "entityName": "DataManagerMapping" + } + + const mappingTypes = JSON.parse(process.env.VUE_APP_MAPPING_TYPES as string) + + // This is needed as it would easy to get app name to categorize mappings + const mappingTypesFlip = Object.keys(mappingTypes).reduce((mappingTypesFlip: any, mappingType) => { + // Updating fieldMpaaings here in case the API fails + fieldMappings[mappingType] = {}; + mappingTypesFlip[mappingTypes[mappingType]] = mappingType; + return mappingTypesFlip; + }, {}); + + const resp = await UserService.getFieldMappings(payload); + if(resp.status == 200 && !hasError(resp) && resp.data.count > 0) { + // updating the structure for mappings so as to directly store it in state + fieldMappings = resp.data.docs.reduce((mappings: any, fieldMapping: any) => { + const mappingType = mappingTypesFlip[fieldMapping.mappingPrefTypeEnumId] + const mapping = mappings[mappingType]; + + mapping[fieldMapping.mappingPrefId] = { + name: fieldMapping.mappingPrefName, + value: JSON.parse(fieldMapping.mappingPrefValue) + } + + fieldMappings[mappingType] = mapping; + return mappings; + }, fieldMappings) + + } else { + logger.error('error', 'No field mapping preference found') + } + } catch(err) { + logger.error('error', err) + } + commit(types.USER_FIELD_MAPPINGS_UPDATED, fieldMappings) + }, + + async createFieldMapping({ commit }, payload) { + try { + + const mappingTypes = JSON.parse(process.env.VUE_APP_MAPPING_TYPES as string) + const mappingPrefTypeEnumId = mappingTypes[payload.mappingType]; + + const params = { + mappingPrefId: payload.id, + mappingPrefName: payload.name, + mappingPrefValue: JSON.stringify(payload.value), + mappingPrefTypeEnumId + } + + const resp = await UserService.createFieldMapping(params); + + if(resp.status == 200 && !hasError(resp)) { + + // using id coming from server, as the random generated id sent in payload is not set as mapping id + // and an auto generated mapping from server is set as id + const fieldMapping = { + id: resp.data.mappingPrefId, + name: payload.name, + value: payload.value, + type: payload.mappingType + } + + commit(types.USER_FIELD_MAPPING_CREATED, fieldMapping) + showToast(translate('This CSV mapping has been saved.')) + } else { + logger.error('error', 'Failed to save CSV mapping.') + showToast(translate('Failed to save CSV mapping.')) + } + } catch(err) { + logger.error('error', err) + showToast(translate('Failed to save CSV mapping.')) + } + }, + + async updateFieldMapping({ commit, state }, payload) { + try { + + const mappingTypes = JSON.parse(process.env.VUE_APP_MAPPING_TYPES as string) + const mappingPrefTypeEnumId = mappingTypes[payload.mappingType]; + + const params = { + mappingPrefId: payload.id, + mappingPrefName: payload.name, + mappingPrefValue: JSON.stringify(payload.value), + mappingPrefTypeEnumId + } + + const resp = await UserService.updateFieldMapping(params); + + if(resp.status == 200 && !hasError(resp)) { + const mappings = JSON.parse(JSON.stringify(state.fieldMappings)) + + mappings[payload.mappingType][payload.id] = { + name: payload.name, + value: payload.value + } + + commit(types.USER_FIELD_MAPPINGS_UPDATED, mappings) + showToast(translate('Changes to the CSV mapping has been saved.')) + } else { + logger.error('error', 'Failed to update CSV mapping.') + showToast(translate('Failed to update CSV mapping.')) + } + } catch(err) { + logger.error('error', err) + showToast(translate('Failed to update CSV mapping.')) + } + }, + + async deleteFieldMapping({ commit, state }, payload) { + try { + const resp = await UserService.deleteFieldMapping({ + 'mappingPrefId': payload.id + }); + + if(resp.status == 200 && !hasError(resp)) { + + const mappings = JSON.parse(JSON.stringify(state.fieldMappings)) + delete mappings[payload.mappingType][payload.id] + + commit(types.USER_FIELD_MAPPINGS_UPDATED, mappings) + commit(types.USER_CURRENT_FIELD_MAPPING_UPDATED, { + id: '', + mappingType: '', + name: '', + value: {} + }) + showToast(translate('This CSV mapping has been deleted.')) + } else { + logger.error('error', 'Failed to delete CSV mapping.') + showToast(translate('Failed to delete CSV mapping.')) + } + } catch(err) { + logger.error('error', err) + showToast(translate('Failed to delete CSV mapping.')) + } + }, + + async updateCurrentMapping({ commit, state }, payload) { + const currentMapping = { + id: payload.id, + mappingType: payload.mappingType, + ...(state.fieldMappings as any)[payload.mappingType][payload.id] + } + commit(types.USER_CURRENT_FIELD_MAPPING_UPDATED, currentMapping) + }, + + async clearCurrentMapping({ commit }) { + commit(types.USER_CURRENT_FIELD_MAPPING_UPDATED, { + id: '', + mappingType: '', + name: '', + value: {} + }) + } } 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 c997073c..7cb0c49a 100644 --- a/src/store/modules/user/getters.ts +++ b/src/store/modules/user/getters.ts @@ -27,6 +27,16 @@ const getters: GetterTree = { }, getUserPreference(state) { return state.preference + }, + getFieldMappings: (state) => (type?: string) => { + if (type) { + const fieldMapping = (state.fieldMappings as any)[type]; + return fieldMapping ? fieldMapping : {} + } + return state.fieldMappings; + }, + getCurrentMapping(state) { + return JSON.parse(JSON.stringify(state.currentMapping)) } } 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 0b22fe9b..61f1f3d9 100644 --- a/src/store/modules/user/index.ts +++ b/src/store/modules/user/index.ts @@ -16,6 +16,13 @@ const userModule: Module = { preference: { printShippingLabel: false, printPackingSlip: false + }, + fieldMappings: {}, + currentMapping: { + id: '', + mappingType: '', + name: '', + value: {} } }, getters, diff --git a/src/store/modules/user/mutation-types.ts b/src/store/modules/user/mutation-types.ts index a791e337..5b72068c 100644 --- a/src/store/modules/user/mutation-types.ts +++ b/src/store/modules/user/mutation-types.ts @@ -5,4 +5,7 @@ export const USER_INFO_UPDATED = SN_USER + '/INFO_UPDATED' export const USER_CURRENT_FACILITY_UPDATED = SN_USER + '/CURRENT_FACILITY_UPDATED' export const USER_INSTANCE_URL_UPDATED = SN_USER + '/INSTANCE_URL_UPDATED' export const USER_CURRENT_ECOM_STORE_UPDATED = SN_USER + '/CURRENT_ECOM_STORE_UPDATED' -export const USER_PREFERENCE_UPDATED = SN_USER + '/PREFERENCE_UPDATED' \ No newline at end of file +export const USER_PREFERENCE_UPDATED = SN_USER + '/PREFERENCE_UPDATED' +export const USER_CURRENT_FIELD_MAPPING_UPDATED = SN_USER + '/_CURRENT_FIELD_MAPPING_UPDATED' +export const USER_FIELD_MAPPINGS_UPDATED = SN_USER + '/FIELD_MAPPINGS_UPDATED' +export const USER_FIELD_MAPPING_CREATED = SN_USER + '/FIELD_MAPPING_CREATED' \ No newline at end of file diff --git a/src/store/modules/user/mutations.ts b/src/store/modules/user/mutations.ts index 1fa4dcb0..5260af0e 100644 --- a/src/store/modules/user/mutations.ts +++ b/src/store/modules/user/mutations.ts @@ -27,5 +27,17 @@ const mutations: MutationTree = { [types.USER_PREFERENCE_UPDATED] (state, payload) { state.preference = {...state.preference, ...payload}; }, + [types.USER_FIELD_MAPPINGS_UPDATED] (state, payload) { + state.fieldMappings = payload; + }, + [types.USER_CURRENT_FIELD_MAPPING_UPDATED] (state, payload) { + state.currentMapping = payload + }, + [types.USER_FIELD_MAPPING_CREATED] (state, payload) { + (state.fieldMappings as any)[payload.type][payload.id] = { + name: payload.name, + value: payload.value + }; + } } export default mutations; \ No newline at end of file diff --git a/src/views/UploadImportOrders.vue b/src/views/UploadImportOrders.vue index 2bd23435..ad236ade 100644 --- a/src/views/UploadImportOrders.vue +++ b/src/views/UploadImportOrders.vue @@ -16,6 +16,19 @@ + + {{ $t("Saved mappings") }} +
+ + + {{ $t("New mapping") }} + + + {{ mapping.name }} + +
+
+ {{ $t("Select the column for the following information in the uploaded CSV.") }} @@ -39,16 +52,21 @@ diff --git a/src/views/SavedMappings.vue b/src/views/SavedMappings.vue index 7b12c6d8..ac0c12c4 100644 --- a/src/views/SavedMappings.vue +++ b/src/views/SavedMappings.vue @@ -6,12 +6,37 @@ {{ $t("Saved mappings") }} + + +
+
+

{{ $t("There are no saved CSV mappings to show. Create a new mapping from a file upload screen")}}

+
+
+ + {{ $t("Import Orders") }} + + {{ mapping.name }} + + +
+ + +
+