Skip to content

Commit

Permalink
Merge pull request #251 from ymaheshwari1/#245
Browse files Browse the repository at this point in the history
Implemented: support to let user define the cron expression(#245)
  • Loading branch information
ymaheshwari1 authored Aug 13, 2024
2 parents 2e327c9 + 4f5353e commit 9df831b
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ VUE_APP_RULE_ENUMS={"QUEUE":{"id":"OIP_QUEUE","code":"facilityId"},"SHIPPING_MET
VUE_APP_RULE_FILTER_ENUMS={"FACILITY_GROUP":{"id":"IIP_FACILITY_GROUP","code":"facilityGroupId"},"PROXIMITY":{"id":"IIP_PROXIMITY","code":"distance"},"BRK_SAFETY_STOCK":{"id":"IIP_BRK_SFTY_STOCK","code":"brokeringSafetyStock"},"MEASUREMENT_SYSTEM":{"id":"IIP_MSMNT_SYSTEM","code":"measurementSystem"},"SPLIT_ITEM_GROUP":{"id":"IIP_SPLIT_ITEM_GROUP","code":"splitOrderItemGroup"}}
VUE_APP_RULE_SORT_ENUMS={"PROXIMITY":{"id":"ISP_PROXIMITY","code":"distance"},"INV_BALANCE":{"id":"ISP_INV_BAL","code":"inventoryForAllocation"},"CUSTOMER_SEQ":{"id":"ISP_CUST_SEQ","code":"facilitySequence"}}
VUE_APP_RULE_ACTION_ENUMS={"RM_AUTO_CANCEL_DATE":{"id":"ORA_RM_CANCEL_DATE","code":"RM_AUTO_CANCEL_DATE"},"AUTO_CANCEL_DAYS":{"id":"ORA_AUTO_CANCEL_DAYS","code":"ADD_AUTO_CANCEL_DATE"},"NEXT_RULE":{"id":"ORA_NEXT_RULE","code":"NEXT_RULE"},"MOVE_TO_QUEUE":{"id":"ORA_MV_TO_QUEUE","code":"MOVE_TO_QUEUE"}}
VUE_APP_CRON_EXPRESSIONS={"Every 5 minutes":"0 */5 * ? * *","Every 15 minutes":"0 */15 * ? * *","Every 15 minutes(8am - 2pm)":"0 */15 8-14 ? * *","Every 30 minutes":"0 */30 * ? * *","Every 30 minutes(8am - 2pm)":"0 */30 8-14 ? * *","Hourly":"0 0 * ? * *","Every six hours":"0 0 */6 ? * *","Every day at midnight":"0 0 0 * * ?"}
VUE_APP_CRON_EXPRESSIONS={"Every 5 minutes":"0 */5 * ? * *","Every 15 minutes":"0 */15 * ? * *","Every 30 minutes":"0 */30 * ? * *","Hourly":"0 0 * ? * *","Every six hours":"0 0 */6 ? * *","Every day at midnight":"0 0 0 * * ?"}
VUE_APP_LOGIN_URL="https://launchpad.hotwax.io/login"
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"axios": "^0.21.1",
"axios-cache-adapter": "^2.7.3",
"core-js": "^3.6.5",
"cron-parser": "^4.9.0",
"cronstrue": "^2.50.0",
"http-status-codes": "^2.1.4",
"luxon": "^2.3.0",
"mitt": "^2.1.0",
Expand Down
141 changes: 141 additions & 0 deletions src/components/ScheduleModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<template>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="closeModal()">
<ion-icon slot="icon-only" :icon="closeOutline" />
</ion-button>
</ion-buttons>
<ion-title>{{ translate("Schedule") }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-list>
<ion-item>
<ion-input label-placement="floating" :label="translate('Expression')" v-model="expression"></ion-input>
</ion-item>
<ion-item>
<ion-icon slot="start" :icon="timerOutline"/>
<ion-label>{{ isExpressionValid && getCronString ? getCronString : "-" }}</ion-label>
</ion-item>
<ion-item>
<ion-icon slot="start" :icon="timeOutline"/>
<ion-label>{{ isExpressionValid && getCronString ? getNextExecutionTime : "Provide a valid cron expression" }}</ion-label>
</ion-item>
</ion-list>

<ion-list-header>{{ translate("Schedule Options") }}</ion-list-header>

<ion-list>
<ion-radio-group v-model="expression">
<ion-item :key="expression" v-for="(expression, description) in cronExpressions">
<ion-radio label-placement="end" justify="start" :value="expression">{{ description }}</ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button @click="saveChanges()" :disabled="!isExpressionUpdated() || !isExpressionValid || getCronString.length <= 0">
<ion-icon :icon="saveOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</template>

<script setup lang="ts">
import { translate } from "@/i18n";
import {
alertController,
IonButton,
IonButtons,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonList,
IonListHeader,
IonRadio,
IonRadioGroup,
IonTitle,
IonToolbar,
modalController,
} from "@ionic/vue";
import { closeOutline, saveOutline, timeOutline, timerOutline } from "ionicons/icons";
import { computed, defineProps, ref } from "vue";
import cronstrue from "cronstrue";
import cronParser from "cron-parser";
import logger from "@/logger";
import { getDateAndTime } from "@/utils";
import store from "@/store";
const props = defineProps({
cronExpression: {
required: true,
type: String
}
})
let expression = ref(props.cronExpression)
const cronExpressions = JSON.parse(process.env?.VUE_APP_CRON_EXPRESSIONS as string)
const userProfile = computed(() => store.getters["user/getUserProfile"])
const isExpressionValid = computed(() => {
try {
cronParser.parseExpression(expression.value, { tz: userProfile.value.timeZone })
return true
} catch(e) {
logger.warn("Invalid expression", e)
return false
}
})
const getCronString = computed(() => {
try {
return cronstrue.toString(expression.value)
} catch(e) {
logger.warn(e)
return ""
}
})
const getNextExecutionTime = computed(() => {
try {
const interval = cronParser.parseExpression(expression.value, { tz: userProfile.value.timeZone })
return getDateAndTime((interval.next() as any)["_date"].ts)
} catch(e) {
logger.error("Invalid expression", e)
return ""
}
})
// Not passing any data on modal close as we are updating the routings on every button click.
function closeModal(expression = "") {
modalController.dismiss({ expression });
}
function isExpressionUpdated() {
return props.cronExpression !== expression.value
}
async function saveChanges() {
const alert = await alertController
.create({
header: translate("Save changes"),
message: translate("Are you sure you want to save these changes?"),
buttons: [{
text: translate("Cancel"),
role: "cancel"
}, {
text: translate("Save"),
handler: () => {
closeModal(expression.value)
}
}]
});
return alert.present();
}
</script>
3 changes: 3 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"Enter a valid value": "Enter a valid value",
"Error getting user profile": "Error getting user profile",
"Execution history": "Execution history",
"Expression": "Expression",
"Failed to clone brokering run": "Failed to clone brokering run",
"Failed to clone rule": "Failed to clone rule",
"Failed to clone the rule": "Failed to clone the rule",
Expand Down Expand Up @@ -93,6 +94,7 @@
"New Run": "New Run",
"New routing created": "New routing created",
"Next rule": "Next rule",
"Next run": "Next run",
"No available history for this group": "No available history for this group",
"No available history for this route": "No available history for this route",
"No archived routings": "No archived routings",
Expand Down Expand Up @@ -144,6 +146,7 @@
"Save changes": "Save changes",
"Save changes before moving to the details page of unarchived route": "Save changes before moving to the details page of unarchived route",
"Schedule": "Schedule",
"Schedule Options": "Schedule Options",
"Scheduler": "Scheduler",
"Search time zones": "Search time zones",
"Select": "Select",
Expand Down
77 changes: 38 additions & 39 deletions src/views/BrokeringRoute.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,21 @@
<ion-icon slot="icon-only" :icon="refreshOutline" />
</ion-button>
</ion-item>
<ion-item>
<ion-icon slot="start" :icon="timeOutline"/>
<ion-label>{{ translate("Run time") }}</ion-label>
<!-- When the group is in draft status, do not display the runTime from the schedule -->
<ion-label slot="end">{{ job.paused === 'N' ? getDateAndTime(job.nextExecutionDateTime) : "-" }}</ion-label>
</ion-item>
<ion-item>
<ion-item detail button @click="openScheduleModal">
<ion-icon slot="start" :icon="timerOutline"/>
<!-- When the group is in draft status or the job is not present, do not display the frequency and just display the label for schedule -->
<ion-label v-if="!job.paused || job.paused === 'Y'">{{ translate("Schedule") }}</ion-label>
<!-- <ion-label v-if="!job.paused || job.paused === 'Y'">{{ translate("Schedule") }}</ion-label>
<ion-label v-if="!job.paused || job.paused === 'Y'" slot="end">{{ "-" }}</ion-label>
<ion-select :disabled="typeof isOmsConnectionExist === 'boolean' && !isOmsConnectionExist" v-else :label="translate('Schedule')" interface="popover" :placeholder="translate('Select')" :value="job.cronExpression" @ionChange="updateCronExpression($event)">
<ion-select-option v-for="(expression, description) in cronExpressions" :key="expression" :value="expression">{{ description }}</ion-select-option>
</ion-select>
</ion-select> -->
<ion-label>{{ getCronString() || job.cronExpression }}</ion-label>
</ion-item>
<ion-item>
<ion-icon slot="start" :icon="timeOutline"/>
<ion-label>{{ translate("Next run") }}</ion-label>
<!-- When the group is in draft status, do not display the runTime from the schedule -->
<ion-label slot="end">{{ job.paused === 'N' ? getDateAndTime(job.nextExecutionDateTime) : "-" }}</ion-label>
</ion-item>
<ion-item :disabled="typeof isOmsConnectionExist === 'boolean' && !isOmsConnectionExist" lines="none" button @click="runNow()">
<ion-icon slot="start" :icon="flashOutline"/>
Expand Down Expand Up @@ -219,6 +220,8 @@ import emitter from "@/event-bus";
import { translate } from "@/i18n";
import GroupHistoryModal from "@/components/GroupHistoryModal.vue"
import RoutingHistoryModal from "@/components/RoutingHistoryModal.vue"
import cronstrue from "cronstrue"
import ScheduleModal from "@/components/ScheduleModal.vue";
const router = useRouter();
const store = useStore();
Expand All @@ -229,7 +232,6 @@ const props = defineProps({
}
})
const cronExpressions = JSON.parse(process.env?.VUE_APP_CRON_EXPRESSIONS as string)
let routingsForReorder = ref([])
let description = ref("")
let isDescUpdating = ref(false)
Expand Down Expand Up @@ -300,9 +302,13 @@ onBeforeRouteLeave(async (to) => {
return;
})
function updateCronExpression(event: CustomEvent) {
job.value.cronExpression = event.detail.value
saveChanges()
function getCronString() {
try {
return cronstrue.toString(job.value.cronExpression)
} catch(e) {
logger.error(e)
return ""
}
}
function initializeOrderRoutings() {
Expand All @@ -313,28 +319,6 @@ async function checkOmsConnectionStatus() {
await store.dispatch("util/checkOmsConnectionStatus")
}
async function saveChanges() {
const alert = await alertController
.create({
header: translate("Save changes"),
message: translate("Are you sure you want to save these changes?"),
buttons: [{
text: translate("Cancel"),
handler: () => {
// If clicking cancel reverting the value for cronExpression to original value, so that user does not gets confused that whether the value is changed or not
job.value.cronExpression = currentRoutingGroup.value["schedule"] ? JSON.parse(JSON.stringify(currentRoutingGroup.value))["schedule"].cronExpression : ''
},
role: "cancel"
}, {
text: translate("Save"),
handler: async () => {
await saveSchedule()
}
}]
});
return alert.present();
}
async function fetchGroupHistory() {
groupHistory.value = []
Expand Down Expand Up @@ -382,10 +366,9 @@ async function saveSchedule() {
const resp = await OrderRoutingService.scheduleBrokering(payload)
if(!hasError(resp)) {
showToast(translate("Job updated"))
await store.dispatch("orderRouting/setCurrentGroup", JSON.parse(JSON.stringify({
...currentRoutingGroup.value,
schedule: job.value
})))
// Fetching the group schedule information again after making changes to the job schedule to fetch the correct nextExecutionTime for job, doing so as we do not get the updated information in POST schedule api call
await store.dispatch("orderRouting/fetchCurrentGroupSchedule", { routingGroupId: props.routingGroupId, currentGroup: currentRoutingGroup.value })
job.value = currentRoutingGroup.value["schedule"] ? JSON.parse(JSON.stringify(currentRoutingGroup.value))["schedule"] : {}
} else {
throw resp.data
}
Expand Down Expand Up @@ -655,6 +638,22 @@ async function openRoutingHistoryModal(orderRoutingId: string, routingName: stri
routingHistoryModal.present();
}
async function openScheduleModal() {
const scheduleModal = await modalController.create({
component: ScheduleModal,
componentProps: { cronExpression: job.value.cronExpression }
})
scheduleModal.onDidDismiss().then(async (result: any) => {
if(result?.data?.expression) {
job.value.cronExpression = result.data.expression
await saveSchedule()
}
})
scheduleModal.present();
}
async function updateOrderRouting(routing: Route, fieldToUpdate: string, value: string) {
orderRoutings.value.map((route: any) => {
if(route.orderRoutingId === routing.orderRoutingId) {
Expand Down

0 comments on commit 9df831b

Please sign in to comment.