From 59779b588f41f4f1732bdfc33ad49b48b54daf15 Mon Sep 17 00:00:00 2001 From: amansinghbais Date: Thu, 14 Nov 2024 19:30:01 +0530 Subject: [PATCH 01/17] Implemeted: support for showing maarg jobs in the app (#734) --- package-lock.json | 21 + package.json | 2 + src/components/JobHistoryModal.vue | 43 +- src/components/MaargJobConfiguration.vue | 433 +++++++++++++++++++ src/components/MaargJobParameterModal.vue | 134 ++++++ src/components/ScheduleModal.vue | 160 +++++++ src/locales/en.json | 1 + src/services/MaargJobService.ts | 90 ++++ src/services/UserService.ts | 30 +- src/store/RootState.ts | 1 + src/store/index.ts | 2 + src/store/modules/maargJob/MaargJobState.ts | 4 + src/store/modules/maargJob/actions.ts | 89 ++++ src/store/modules/maargJob/getters.ts | 14 + src/store/modules/maargJob/index.ts | 19 + src/store/modules/maargJob/mutation-types.ts | 5 + src/store/modules/maargJob/mutations.ts | 13 + src/store/modules/user/UserState.ts | 4 + src/store/modules/user/actions.ts | 19 +- src/store/modules/user/getters.ts | 7 + src/store/modules/user/index.ts | 4 + src/store/modules/user/mutation-types.ts | 3 +- src/store/modules/user/mutations.ts | 7 + src/utils/index.ts | 62 ++- src/views/Fulfillment.vue | 63 ++- 25 files changed, 1211 insertions(+), 19 deletions(-) create mode 100644 src/components/MaargJobConfiguration.vue create mode 100644 src/components/MaargJobParameterModal.vue create mode 100644 src/components/ScheduleModal.vue create mode 100644 src/services/MaargJobService.ts create mode 100644 src/store/modules/maargJob/MaargJobState.ts create mode 100644 src/store/modules/maargJob/actions.ts create mode 100644 src/store/modules/maargJob/getters.ts create mode 100644 src/store/modules/maargJob/index.ts create mode 100644 src/store/modules/maargJob/mutation-types.ts create mode 100644 src/store/modules/maargJob/mutations.ts diff --git a/package-lock.json b/package-lock.json index e6849f12..23d4d873 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,8 @@ "@types/papaparse": "^5.3.1", "boon-js": "^2.0.3", "core-js": "^3.6.5", + "cron-parser": "^4.9.0", + "cronstrue": "^2.50.0", "file-saver": "^2.0.5", "luxon": "^3.2.0", "mitt": "^2.1.0", @@ -6195,6 +6197,25 @@ "node": ">=8" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cronstrue": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.51.0.tgz", + "integrity": "sha512-7EG9VaZZ5SRbZ7m25dmP6xaS0qe9ay6wywMskFOU/lMDKa+3gZr2oeT5OUfXwRP/Bcj8wxdYJ65AHU70CI3tsw==", + "bin": { + "cronstrue": "bin/cli.js" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, diff --git a/package.json b/package.json index 4b58abf3..169e7958 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "@ionic/vue-router": "^7.6.0", "@types/file-saver": "^2.0.4", "@types/papaparse": "^5.3.1", + "cronstrue": "^2.50.0", + "cron-parser": "^4.9.0", "boon-js": "^2.0.3", "core-js": "^3.6.5", "file-saver": "^2.0.5", diff --git a/src/components/JobHistoryModal.vue b/src/components/JobHistoryModal.vue index 75a796be..016cf98c 100644 --- a/src/components/JobHistoryModal.vue +++ b/src/components/JobHistoryModal.vue @@ -6,7 +6,7 @@ - {{ currentJob?.enumName }} + {{ currentJob?.enumName ? currentJob.enumName : currentJob?.jobName ? currentJob.jobName : currentJob?.description }} @@ -16,7 +16,18 @@
- + + + + {{ job.runTime ? getTime(job.runTime) : "-" }} @@ -49,8 +60,10 @@ import { closeOutline } from 'ionicons/icons'; import { mapGetters, useStore } from 'vuex'; import { DateTime } from 'luxon'; import { JobService } from '@/services/JobService' -import { hasError } from '@/utils'; +import { hasError, timeTillRun } from '@/utils'; import { translate } from '@hotwax/dxp-components'; +import logger from '@/logger'; +import { MaargJobService } from '@/services/MaargJobService'; export default defineComponent({ name: 'JobHistoryModal', @@ -69,10 +82,10 @@ export default defineComponent({ }, data() { return { - jobHistory: [] + jobHistory: [] as any } }, - props: ['currentJob'], + props: ['currentJob', 'isMaargJob'], computed: { ...mapGetters({ getCurrentEComStore:'user/getCurrentEComStore', @@ -119,10 +132,27 @@ export default defineComponent({ } catch(err) { this.$log.error(err); } + }, + async fetchMaargJobHistory() { + try { + const resp = await MaargJobService.fetchMaargJobHistory({ + jobName: this.currentJob.jobName, + pageSize: 200, + orderByField: "startTime DESC" + }); + + if(!hasError(resp)) { + this.jobHistory = resp.data + } else { + throw resp; + } + } catch(error: any) { + logger.error(error); + } } }, mounted() { - this.fetchJobHistory() + this.isMaargJob ? this.fetchMaargJobHistory() : this.fetchJobHistory() }, setup() { const store = useStore(); @@ -130,6 +160,7 @@ export default defineComponent({ return { closeOutline, store, + timeTillRun, translate }; }, diff --git a/src/components/MaargJobConfiguration.vue b/src/components/MaargJobConfiguration.vue new file mode 100644 index 00000000..b8880da8 --- /dev/null +++ b/src/components/MaargJobConfiguration.vue @@ -0,0 +1,433 @@ + + + + + \ No newline at end of file diff --git a/src/components/MaargJobParameterModal.vue b/src/components/MaargJobParameterModal.vue new file mode 100644 index 00000000..ffe71b0c --- /dev/null +++ b/src/components/MaargJobParameterModal.vue @@ -0,0 +1,134 @@ + + + \ No newline at end of file diff --git a/src/components/ScheduleModal.vue b/src/components/ScheduleModal.vue new file mode 100644 index 00000000..52ef0424 --- /dev/null +++ b/src/components/ScheduleModal.vue @@ -0,0 +1,160 @@ + + \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index a2975e89..e68b888a 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -268,6 +268,7 @@ "Skip job": "Skip job", "Skip once": "Skip once", "Skipping will run this job at the next occurrence based on the temporal expression.": "Skipping will run this job at the next occurrence based on the temporal expression.", + "Some of the app functionality will not work due to missing configuration.": "Some of the app functionality will not work due to missing configuration.", "Some jobs have slow frequency type, hence, feasible frequency will be set automatically": "Some jobs have slow frequency type, hence, feasible frequency will be set automatically", "Something went wrong": "Something went wrong", "Something went wrong while getting complete user permissions.": "Something went wrong while getting complete user permissions.", diff --git a/src/services/MaargJobService.ts b/src/services/MaargJobService.ts new file mode 100644 index 00000000..d33f6ac2 --- /dev/null +++ b/src/services/MaargJobService.ts @@ -0,0 +1,90 @@ +import store from '@/store'; +import { api, client } from '@/adapter'; + + +const fetchMaargJobs = async (payload: any): Promise => { + const omsRedirectionInfo = store.getters['user/getOmsRedirectionInfo']; + const baseURL = store.getters['user/getMaargBaseUrl']; + + return client({ + url: "serviceJobs", + method: "GET", + baseURL, + headers: { + "api_key": omsRedirectionInfo.token, + "Content-Type": "application/json" + }, + params: payload + }); +} + +const fetchMaargJobInfo = async (jobName: any): Promise => { + const omsRedirectionInfo = store.getters['user/getOmsRedirectionInfo']; + const baseURL = store.getters['user/getMaargBaseUrl']; + + return client({ + url: `serviceJobs/${jobName}`, + method: "GET", + baseURL, + headers: { + "api_key": omsRedirectionInfo.token, + "Content-Type": "application/json" + } + }); +} + + +const runNow = async (jobName: any): Promise => { + const omsRedirectionInfo = store.getters['user/getOmsRedirectionInfo']; + const baseURL = store.getters['user/getMaargBaseUrl']; + + return client({ + url: `serviceJobs/${jobName}/runNow`, + method: "POST", + baseURL, + headers: { + "api_key": omsRedirectionInfo.token, + "Content-Type": "application/json" + } + }); +} + +const updateMaargJob = async (payload: any): Promise => { + const omsRedirectionInfo = store.getters['user/getOmsRedirectionInfo']; + const baseURL = store.getters['user/getMaargBaseUrl']; + + return client({ + url: `serviceJobs/${payload.jobName}`, + method: "POST", + baseURL, + data: payload, + headers: { + "api_key": omsRedirectionInfo.token, + "Content-Type": "application/json" + } + }); +} + +const fetchMaargJobHistory = async (payload: any): Promise => { + const omsRedirectionInfo = store.getters['user/getOmsRedirectionInfo']; + const baseURL = store.getters['user/getMaargBaseUrl']; + + return client({ + url: `serviceJobs/${payload.jobName}/runs`, + method: "GET", + baseURL, + params: payload, + headers: { + "api_key": omsRedirectionInfo.token, + "Content-Type": "application/json" + } + }); +} + +export const MaargJobService = { + fetchMaargJobs, + fetchMaargJobHistory, + fetchMaargJobInfo, + runNow, + updateMaargJob +} \ No newline at end of file diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 3b02477c..cfd239fc 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -14,6 +14,33 @@ const login = async (username: string, password: string): Promise => { }); } +const moquiLogin = async (omsRedirectionUrl: string, token: string): Promise => { + const baseURL = omsRedirectionUrl.startsWith('http') ? omsRedirectionUrl.includes('/rest/s1/admin') ? omsRedirectionUrl : `${omsRedirectionUrl}/rest/s1/admin/` : `https://${omsRedirectionUrl}.hotwax.io/rest/s1/admin/`; + let api_key = "" + + try { + const resp = await client({ + url: "login", + method: "post", + baseURL, + params: { + token + }, + headers: { + "Content-Type": "application/json" + } + }) as any; + + if(!hasError(resp) && (resp.data.api_key || resp.data.token)) { + api_key = resp.data.api_key || resp.data.token + } else { + throw "Sorry, login failed. Please try again"; + } + } catch(err) { + return Promise.resolve(""); + } + return Promise.resolve(api_key) +} const getShopifyConfig = async (productStoreId: any, token?: any): Promise => { try { @@ -362,5 +389,6 @@ export const UserService = { associatePinnedJobPrefToUser, updatePinnedJobPref, setUserPreference, - getUserPermissions + getUserPermissions, + moquiLogin } \ No newline at end of file diff --git a/src/store/RootState.ts b/src/store/RootState.ts index 99f6acf0..041ee3c4 100644 --- a/src/store/RootState.ts +++ b/src/store/RootState.ts @@ -2,4 +2,5 @@ export default interface RootState { user: any; product: any; util: any; + maargJob: any; } \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index ad7f24d9..bdfbbc3d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -10,6 +10,7 @@ import jobModule from "./modules/job" import utilModule from "./modules/util" import webhookModule from "./modules/webhook" import { setPermissions } from '@/authorization' +import maargJobModule from "./modules/maargJob" // TODO check how to register it from the components only @@ -38,6 +39,7 @@ const store = createStore({ 'product': productModule, 'job': jobModule, 'util': utilModule, + 'maargJob': maargJobModule, 'webhook': webhookModule }, }) diff --git a/src/store/modules/maargJob/MaargJobState.ts b/src/store/modules/maargJob/MaargJobState.ts new file mode 100644 index 00000000..108a55f6 --- /dev/null +++ b/src/store/modules/maargJob/MaargJobState.ts @@ -0,0 +1,4 @@ +export default interface MaargJobState { + maargJobs: any; + currentMaargJob: any; +} \ No newline at end of file diff --git a/src/store/modules/maargJob/actions.ts b/src/store/modules/maargJob/actions.ts new file mode 100644 index 00000000..af60c8eb --- /dev/null +++ b/src/store/modules/maargJob/actions.ts @@ -0,0 +1,89 @@ +import { ActionTree } from 'vuex' +import RootState from '@/store/RootState' +import JobState from './MaargJobState' +import * as types from './mutation-types' +import { isCustomRunTime, generateAllowedFrequencies, hasError, showToast } from '@/utils' +import { translate } from '@hotwax/dxp-components' +import { DateTime } from 'luxon'; +import store from '@/store' +import logger from "@/logger"; +import { MaargJobService } from '@/services/MaargJobService' + +const actions: ActionTree = { + async fetchMaargJobs({ commit }, jobTypeEnumIds){ + const productStoreId = store.getters["user/getCurrentEComStore"]?.productStoreId + + let resp = {} as any; + const maargJobs = {} as any; + + try { + resp = await MaargJobService.fetchMaargJobs({ jobTypeEnumId: jobTypeEnumIds, jobTypeEnumId_op: "in" }) + if(!hasError(resp) && resp.data?.length) { + const jobs = resp.data; + const responses = await Promise.allSettled(jobs.map((job: any) => MaargJobService.fetchMaargJobInfo(job.jobName))) + + responses.map((response: any) => { + if(response.status === "fulfilled") { + const job = response.value.data.jobDetail + const paramValue = {} as any; + + job.serviceJobParameters.map((parameter: any) => { + paramValue[parameter.parameterName] = parameter.parameterValue + }) + job["parameterValues"] = paramValue + + if(Object.keys(paramValue).includes("productStoreIds")) { + if(paramValue["productStoreIds"] === productStoreId) { + maargJobs[job.jobTypeEnumId] = job + } + } else { + maargJobs[job.jobTypeEnumId] = job + } + } + }) + } else { + throw resp; + } + } catch(error: any) { + logger.error(error); + } + + commit(types.MAARGJOB_MAARG_JOBS_UPDATED, maargJobs); + }, + + async updateMaargJob({ commit, state }, jobEnumId) { + const jobs = JSON.parse(JSON.stringify(state.maargJobs)); + const currentJob = jobs[jobEnumId] + + try { + const resp = await MaargJobService.fetchMaargJobInfo(currentJob.jobName); + if(!hasError(resp)) { + const currentJob = resp.data?.jobDetail + + const paramValue = {} as any; + currentJob.serviceJobParameters.map((parameter: any) => { + paramValue[parameter.parameterName] = parameter.parameterValue + }) + currentJob["parameterValues"] = paramValue + + jobs[jobEnumId] = currentJob + commit(types.MAARGJOB_MAARG_JOBS_UPDATED, jobs); + commit(types.MAARGJOB_CURRENT_JOB_UPDATED, currentJob); + } else { + throw resp; + } + } catch(error: any) { + logger.error(error); + } + }, + + async updateCurrentMaargJob({ commit }, payload) { + if(payload?.job) { + commit(types.MAARGJOB_CURRENT_JOB_UPDATED, payload.job); + return payload?.job; + } + + // Todo: refetch job for the mobile view of job details. + } +} +export default actions; diff --git a/src/store/modules/maargJob/getters.ts b/src/store/modules/maargJob/getters.ts new file mode 100644 index 00000000..1e49697c --- /dev/null +++ b/src/store/modules/maargJob/getters.ts @@ -0,0 +1,14 @@ +import { GetterTree } from 'vuex' +import MaargJobState from './MaargJobState' +import RootState from '../../RootState' + +const getters: GetterTree = { + getMaargJob: (state) => (jobTypeEnumId: string): any => { + return state.maargJobs[jobTypeEnumId] ? state.maargJobs[jobTypeEnumId] : {} + }, + getCurrentMaargJob: (state) => { + return state.currentMaargJob + } +} + +export default getters; \ No newline at end of file diff --git a/src/store/modules/maargJob/index.ts b/src/store/modules/maargJob/index.ts new file mode 100644 index 00000000..2f54cb2a --- /dev/null +++ b/src/store/modules/maargJob/index.ts @@ -0,0 +1,19 @@ +import actions from './actions' +import getters from './getters' +import mutations from './mutations' +import { Module } from 'vuex' +import MaargJobState from './MaargJobState' +import RootState from '../../RootState' + +const maargJobModule: Module = { + namespaced: true, + state: { + maargJobs: {}, + currentMaargJob: {} + }, + getters, + actions, + mutations, +} + +export default maargJobModule; \ No newline at end of file diff --git a/src/store/modules/maargJob/mutation-types.ts b/src/store/modules/maargJob/mutation-types.ts new file mode 100644 index 00000000..57a58f6f --- /dev/null +++ b/src/store/modules/maargJob/mutation-types.ts @@ -0,0 +1,5 @@ +export const SN_MAARGJOB = 'maargJob' +export const MAARGJOB_UPDATED_BULK = SN_MAARGJOB + '/UPDATED_BULK' +export const MAARGJOB_MAARG_JOBS_UPDATED = SN_MAARGJOB + '/MAARG_JOBS_UPDATED' +export const MAARGJOB_CURRENT_JOB_UPDATED = SN_MAARGJOB + '/CURRENT_JOB_UPDATED' + diff --git a/src/store/modules/maargJob/mutations.ts b/src/store/modules/maargJob/mutations.ts new file mode 100644 index 00000000..c84a09ad --- /dev/null +++ b/src/store/modules/maargJob/mutations.ts @@ -0,0 +1,13 @@ +import { MutationTree } from 'vuex' +import MaargJobState from './MaargJobState' +import * as types from './mutation-types' + +const mutations: MutationTree = { + [types.MAARGJOB_MAARG_JOBS_UPDATED] (state, payload) { + state.maargJobs = payload + }, + [types.MAARGJOB_CURRENT_JOB_UPDATED] (state, payload) { + state.currentMaargJob = payload + }, +} +export default mutations; \ No newline at end of file diff --git a/src/store/modules/user/UserState.ts b/src/store/modules/user/UserState.ts index 84f86c9c..e933b589 100644 --- a/src/store/modules/user/UserState.ts +++ b/src/store/modules/user/UserState.ts @@ -8,4 +8,8 @@ export default interface UserState { currentShopifyConfig: any; productStoreCategories: any; pwaState: any; + omsRedirectionInfo: { + url: string; + token: string; + } } \ No newline at end of file diff --git a/src/store/modules/user/actions.ts b/src/store/modules/user/actions.ts index 60051585..6a3608b1 100644 --- a/src/store/modules/user/actions.ts +++ b/src/store/modules/user/actions.ts @@ -22,7 +22,7 @@ const actions: ActionTree = { */ async login({ commit, dispatch }, payload) { try { - const { token, oms } = payload + const { token, oms, omsRedirectionUrl } = payload dispatch("setUserInstanceUrl", oms); // Getting the permissions list from server @@ -84,6 +84,19 @@ const actions: ActionTree = { Settings.defaultZone = userProfile.userTimeZone; } + if(omsRedirectionUrl) { + const api_key = await UserService.moquiLogin(omsRedirectionUrl, token) + if(api_key) { + dispatch("setOmsRedirectionInfo", { url: omsRedirectionUrl, token: api_key }) + } else { + showToast(translate("Some of the app functionality will not work due to missing configuration.")) + console.error("Some of the app functionality will not work due to missing configuration."); + } + } else { + showToast(translate("Some of the app functionality will not work due to missing configuration.")) + console.error("Some of the app functionality will not work due to missing configuration.") + } + // TODO user single mutation commit(types.USER_CURRENT_ECOM_STORE_UPDATED, preferredStore); commit(types.USER_INFO_UPDATED, userProfile); @@ -180,6 +193,10 @@ const actions: ActionTree = { updateInstanceUrl(payload) }, + setOmsRedirectionInfo({ commit }, payload) { + commit(types.USER_OMS_REDIRECTION_INFO_UPDATED, payload) + }, + updatePwaState({commit}, payload) { commit(types.USER_PWA_STATE_UPDATED, payload); }, diff --git a/src/store/modules/user/getters.ts b/src/store/modules/user/getters.ts index b0f6419f..e3aa9a49 100644 --- a/src/store/modules/user/getters.ts +++ b/src/store/modules/user/getters.ts @@ -44,6 +44,13 @@ const getters: GetterTree = { }, getPinnedJobs(state) { return state.current ? (state.current as any)['pinnedJobs']?.jobs : [] + }, + getMaargBaseUrl (state) { + const url = state.omsRedirectionInfo.url + return url.startsWith('http') ? url.includes('/rest/s1/admin') ? url : `${url}/rest/s1/admin/` : `https://${url}.hotwax.io/rest/s1/admin/`; + }, + getOmsRedirectionInfo(state) { + return state.omsRedirectionInfo } } 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 90bc9c82..c1e26305 100644 --- a/src/store/modules/user/index.ts +++ b/src/store/modules/user/index.ts @@ -23,6 +23,10 @@ const userModule: Module = { updateExists: false, registration: null, }, + omsRedirectionInfo: { + url: "", + token: "" + } }, getters, actions, diff --git a/src/store/modules/user/mutation-types.ts b/src/store/modules/user/mutation-types.ts index f36325e3..cbbf1ea3 100644 --- a/src/store/modules/user/mutation-types.ts +++ b/src/store/modules/user/mutation-types.ts @@ -8,4 +8,5 @@ export const USER_CURRENT_SHOPIFY_CONFIG_UPDATED = SN_USER + '/SHOPIFY_CONFIG_UP export const USER_CURRENT_ECOM_STORE_UPDATED = SN_USER + '/CURRENT_ECOM_STORE_UPDATED' export const USER_PERMISSIONS_UPDATED = SN_USER + '/PERMISSIONS_UPDATED' export const USER_PWA_STATE_UPDATED = SN_USER + '/PWA_STATE_UPDATED' -export const USER_PRDCT_STR_CATGRS_UPDATED = SN_USER + '/PRDCT_STR_CATGRS_UPDATED' \ No newline at end of file +export const USER_PRDCT_STR_CATGRS_UPDATED = SN_USER + '/PRDCT_STR_CATGRS_UPDATED' +export const USER_OMS_REDIRECTION_INFO_UPDATED = SN_USER + '/OMS_REDIRECTION_INFO_UPDATED' \ No newline at end of file diff --git a/src/store/modules/user/mutations.ts b/src/store/modules/user/mutations.ts index b3003c9e..845b95b0 100644 --- a/src/store/modules/user/mutations.ts +++ b/src/store/modules/user/mutations.ts @@ -17,6 +17,10 @@ const mutations: MutationTree = { state.shopifyConfigs = [] state.permissions = [] state.productStoreCategories = {} + state.omsRedirectionInfo = { + url: "", + token: "" + } }, [types.USER_INFO_UPDATED] (state, payload) { state.current = { ...state.current, ...payload} @@ -43,5 +47,8 @@ const mutations: MutationTree = { [types.USER_PERMISSIONS_UPDATED] (state, payload) { state.permissions = payload }, + [types.USER_OMS_REDIRECTION_INFO_UPDATED](state, payload) { + state.omsRedirectionInfo = payload; + } } export default mutations; diff --git a/src/utils/index.ts b/src/utils/index.ts index 313e99ca..b892d3d7 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,6 +5,7 @@ import { DateTime } from "luxon"; import logger from "@/logger"; import { translate } from "@hotwax/dxp-components"; import { Plugins } from '@capacitor/core'; +import cronstrue from "cronstrue" // TODO Use separate files for specific utilities @@ -235,10 +236,8 @@ const convertValue = (parameter: any) => { try { if(parameter.type === 'Map' || parameter.type === 'List' || parameter.type === 'Object') { return JSON.parse(value) - } else if(parameter.type === 'String' || parameter.type === 'Date' || parameter.type === 'Time') { - return value } else { - return JSON.parse(value); + return value } } catch { logger.error('Unable to parse the defined value', value) @@ -354,6 +353,57 @@ const saveDataFile = async (response: any, fileName: string) => { saveAs(blob, fileName); } +function getDateAndTime(time: any) { + return time ? DateTime.fromMillis(time).toLocaleString({ ...DateTime.DATETIME_MED, hourCycle: "h12" }) : "-"; +} + +function timeTillRun(endTime: any) { + const timeDiff = DateTime.fromMillis(endTime).diff(DateTime.local()); + return DateTime.local().plus(timeDiff).toRelative(); +} + +function getCronString(cronExpression: any) { + try { + return cronstrue.toString(cronExpression) + } catch(e) { + logger.error(e) + return "" + } +} + +const generateMaargJobCustomOptions = (job: any) => { + let inputParameters = job?.serviceInParameters ? JSON.parse(JSON.stringify(job?.serviceInParameters)) : [] + const optionalParameters: Array = []; + const requiredParameters: Array = []; + + // removing some fields that we don't want user to edit, and for which the values will be added programatically + const excludeParameters = ['productStoreIds'] + inputParameters = inputParameters.filter((parameter: any) =>!excludeParameters.includes(parameter.name)) + + inputParameters.map((parameter: any) => { + if(parameter.required === "true") { + requiredParameters.push({ + name: parameter.name, + value: job?.parameterValues && job?.parameterValues[parameter.name] && job?.parameterValues[parameter.name] !== 'null' ? convertToString({ value: job?.parameterValues[parameter.name], type: parameter.type }) : '', // added check for null as we don't want to pass null as a value in the params + type: parameter.type, + default: parameter.default + }) + } else { + optionalParameters.push({ + name: parameter.name, + value: job?.parameterValues && job?.parameterValues[parameter.name] && job?.parameterValues[parameter.name] !== 'null' ? convertToString({ value: job?.parameterValues[parameter.name], type: parameter.type }) : '', // added check for null as we don't want to pass null as a value in the params + type: parameter.type, + default: parameter.default + }) + } + }) + + return { + optionalParameters, + requiredParameters + } +} + export { copyToClipboard, isCustomRunTime, @@ -362,6 +412,9 @@ export { generateAllowedRunTimes, generateJobCustomParameters, generateJobCustomOptions, + generateMaargJobCustomOptions, + getCronString, + getDateAndTime, handleDateTimeInput, hasJobDataError, showToast, @@ -371,5 +424,6 @@ export { JsonToCsvOption, isFutureDate, prepareRuntime, - saveDataFile + saveDataFile, + timeTillRun } diff --git a/src/views/Fulfillment.vue b/src/views/Fulfillment.vue index e1becc8e..28a6a04d 100644 --- a/src/views/Fulfillment.vue +++ b/src/views/Fulfillment.vue @@ -86,11 +86,33 @@

{{ translate("Unfulfilled orders that pass their auto cancelation date will be canceled automatically in HotWax Commerce. They will also be canceled in Shopify if upload for canceled orders is enabled.") }}

+ + + + {{ translate("Feed") }} + + + {{ translate("Generate order items feed") }} + {{ isMaargJobFound('GNRT_FF_ORD_ITM_FEED') ? getMaargJobStatus("GNRT_FF_ORD_ITM_FEED") : translate("Not found") }} + + + {{ translate("Send shopify acknowledge feed") }} + {{ isMaargJobFound('SND_FF_ACK_FEED') ? getMaargJobStatus("SND_FF_ACK_FEED") : translate("Not found") }} + + + + +

{{ translate("Generated order items feed and send fulfillment acknowlegment feed to shopify.") }}

+
+
+
+ -