Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: new UI for brokering page (#620) #621

Merged
merged 15 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 185 additions & 64 deletions src/components/BatchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,107 @@
<ion-icon slot="icon-only" :icon="closeOutline" />
</ion-button>
</ion-buttons>
<ion-title>{{ currentBatch?.jobName ? currentBatch?.jobName : $t('New broker run') }}</ion-title>
<ion-title>{{ $t('New broker run') }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-item>
<ion-label position="fixed">{{ $t('Batch name') }}</ion-label>
<ion-label position="fixed">{{ $t('Name') }}</ion-label>
<ion-input :placeholder="currentDateTime = getCurrentDateTime()" v-model="jobName" />
</ion-item>
<ion-item :disabled="currentBatch?.jobId">
<ion-label>{{ $t('Order queue') }}</ion-label>
<ion-select slot="end" interface="popover" :value="this.currentScheduledBatch?.facilityId || batchFacilityId" @ionChange="batchFacilityId = $event['detail'].value">
<ion-item>
<ion-icon slot="start" :icon="ticketOutline" />
<ion-label>{{ $t('Order parking') }}</ion-label>
<ion-select slot="end" interface="popover" :value="batchFacilityId" @ionChange="batchFacilityId = $event['detail'].value; updateCustomParameters()">
<ion-select-option value="_NA_">{{ $t("Brokering queue") }}</ion-select-option>
<ion-select-option value="PRE_ORDER_PARKING">{{ $t("Pre-order parking") }}</ion-select-option>
<ion-select-option value="BACKORDER_PARKING">{{ $t("Back-order parking") }}</ion-select-option>
</ion-select>
</ion-item>
<ion-radio-group>
<ion-item :disabled="currentBatch?.jobId">
<ion-label>{{ $t('New orders') }}</ion-label>
<!-- "this.currentScheduledBatch?.unfillable === false" - Did this because ion-radio is not considering boolean -->
<ion-radio :checked="this.currentScheduledBatch?.unfillable === false" slot="start" @click="unfillableOrder = false" color="secondary"/>
<ion-item>
<ion-icon slot="start" :icon="warningOutline" />
<ion-label>{{ $t('Unfillable orders') }}</ion-label>
<ion-toggle slot="end" :checked="unfillableOrder" @ion-change="unfillableOrder = !unfillableOrder; updateCustomParameters()" />
</ion-item>

<ion-list v-if="customOptionalParameters.length || customRequiredParameters.length">
<ion-item lines="none">
<ion-label>{{ $t('More parameters') }}</ion-label>
</ion-item>

<ion-item-divider v-if="customRequiredParameters.length" color="light">
<ion-label>{{ $t('Required Parameters') }}</ion-label>
</ion-item-divider>

<ion-item :key="index" v-for="(parameter, index) in customRequiredParameters">
<ion-label>{{ parameter.name }}</ion-label>
<ion-input :placeholder="parameter.value ? parameter.value : parameter.name" v-model="parameter.value" />
<ion-note slot="helper">{{ parameter.type }}</ion-note>
</ion-item>
<ion-item :disabled="currentBatch?.jobId">
<ion-label>{{ $t('Unfillable orders') }}</ion-label>
<!-- "this.currentScheduledBatch?.unfillable === false" - Did this because ion-radio is not considering boolean -->
<ion-radio :checked="this.currentScheduledBatch?.unfillable === true" slot="start" @click="unfillableOrder = true" color="secondary"/>

<ion-item-divider v-if="customOptionalParameters.length" color="light">
<ion-label>{{ $t('Optional Parameters') }}</ion-label>
</ion-item-divider>

<ion-item :key="index" v-for="(parameter, index) in customOptionalParameters">
<ion-label>{{ parameter.name }}</ion-label>
<ion-input :placeholder="parameter.value ? parameter.value : parameter.name" v-model="parameter.value"/>
<ion-note slot="helper">{{ parameter.type }}</ion-note>
</ion-item>
</ion-radio-group>
<ion-item>
<ion-label position="fixed">{{ $t("Schedule") }}</ion-label>
<ion-datetime hour-cycle="h12" :value="currentBatch?.runTime ? getDateTime(currentBatch.runTime) : getNowTimestamp()" @ionChange="updateRunTime($event)" presentation="time" size="cover" />
</ion-item>
<ion-fab @click="updateJob()" vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button>
<ion-icon :icon="checkmarkDoneOutline" />
</ion-fab-button>
</ion-fab>
</ion-list>
<ion-list v-else>
<ion-item>
<ion-label>{{ $t('No parameters available') }}</ion-label>
</ion-item>
</ion-list>

<ion-card>
<ion-card-header>
<ion-card-title>{{ $t('Schedule') }}</ion-card-title>
</ion-card-header>

<ion-item>
<ion-icon slot="start" :icon="timeOutline" />
<ion-label>{{ $t('Run time') }}</ion-label>
<ion-select interface="popover" :placeholder="$t('Select')" :value="runTime" @ionChange="updateRunTime($event)">
<ion-select-option v-for="runTime in runTimes" :key="runTime.value" :value="runTime.value">{{ $t(runTime.label) }}</ion-select-option>
</ion-select>

<ion-modal class="date-time-modal" :is-open="isDateTimeModalOpen" @didDismiss="() => isDateTimeModalOpen = false">
<ion-content force-overscroll="false">
<ion-datetime
show-default-buttons
hour-cycle="h23"
:value="runTime ? (isCustomRunTime(runTime) ? getDateTime(runTime) : getDateTime(DateTime.now().toMillis() + runTime)) : getNowTimestamp()"
@ionChange="updateCustomTime($event)"
/>
</ion-content>
</ion-modal>
</ion-item>
<ion-item lines="none">
<ion-icon slot="start" :icon="timerOutline" />
<ion-label>{{ $t('Frequency') }}</ion-label>
<ion-select :value="jobStatus" :interface-options="{ header: $t('Frequency') }" interface="popover" :placeholder="$t('Disabled')" @ionChange="jobStatus = $event.detail.value" @ionDismiss="jobStatus == 'CUSTOM' && setCustomFrequency()">
<ion-select-option v-for="freq in frequencyOptions" :key="freq.id" :value="freq.id">{{ freq.description }}</ion-select-option>
</ion-select>
</ion-item>
</ion-card>
</ion-content>
<ion-fab :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || isRequiredParametersMissing" @click="updateJob()" vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button>
<ion-icon :icon="checkmarkDoneOutline" />
</ion-fab-button>
</ion-fab>
</template>

<script lang="ts">
import {
IonButton,
IonButtons,
IonCard,
IonCardHeader,
IonCardTitle,
IonContent,
IonDatetime,
IonFab,
Expand All @@ -59,27 +115,35 @@ import {
IonIcon,
IonInput,
IonItem,
IonItemDivider,
IonLabel,
IonRadio,
IonRadioGroup,
IonList,
IonModal,
IonNote,
IonSelect,
IonSelectOption,
IonTitle,
IonToggle,
IonToolbar,
modalController
} from '@ionic/vue';
import { defineComponent } from 'vue';
import { closeOutline, checkmarkDoneOutline } from 'ionicons/icons';
import { closeOutline, checkmarkDoneOutline, ticketOutline, timeOutline, timerOutline, warningOutline } from 'ionicons/icons';
import { mapGetters, useStore } from 'vuex';
import { DateTime } from 'luxon';
import { handleDateTimeInput, generateJobCustomParameters, getNowTimestamp, isFutureDate, showToast, hasJobDataError } from '@/utils';
import { handleDateTimeInput, generateAllowedFrequencies, generateAllowedRunTimes, generateJobCustomParameters, generateJobCustomOptions, getNowTimestamp, hasJobDataError, isCustomRunTime, isFutureDate, showToast } from '@/utils';
import { translate } from '@/i18n'
import CustomFrequencyModal from '@/components/CustomFrequencyModal.vue';
import { Actions, hasPermission } from '@/authorization'

export default defineComponent({
name: 'BatchModal',
components: {
IonButton,
IonButtons,
IonCard,
IonCardHeader,
IonCardTitle,
IonContent,
IonDatetime,
IonFab,
Expand All @@ -88,15 +152,17 @@ export default defineComponent({
IonIcon,
IonInput,
IonItem,
IonItemDivider,
IonLabel,
IonRadio,
IonRadioGroup,
IonList,
IonModal,
IonNote,
IonSelect,
IonSelectOption,
IonTitle,
IonToggle,
IonToolbar,
},
props: ["id", "enumId"],
data() {
return {
jobEnums: JSON.parse(process.env?.VUE_APP_BATCH_JOB_ENUMS as string) as any,
Expand All @@ -105,9 +171,13 @@ export default defineComponent({
unfillableOrder: false as boolean,
batchFacilityId: '_NA_' as string,
currentDateTime: '' as string,
jobRunTime: '' as any,
currentScheduledBatch: {} as any,
orders: ""
runTime: '' as any,
runTimes: [] as any,
isDateTimeModalOpen: false,
jobStatus: null,
frequencyOptions: [] as any,
customOptionalParameters: [] as any,
customRequiredParameters: [] as any
}
},
computed: {
Expand All @@ -119,31 +189,46 @@ export default defineComponent({
}),
},
mounted() {
this.getCurrentBatch();
this.generateRunTimes(this.runTime)
this.generateFrequencyOptions(this.jobStatus)
this.updateCustomParameters()
},
methods: {
getCurrentBatch() {
this.currentBatch = this.getJob(this.enumId)?.find((job: any) => job.id === this.id)
this.jobName = this.currentBatch?.jobName;
this.currentScheduledBatch = (this as any).jobEnums[this.currentBatch?.enumId];
},
getDateTime(time: any) {
return DateTime.fromMillis(time).toISO()
},
closeModal() {
modalController.dismiss({ dismissed: true });
},
async updateJob() {
let batchJobEnum = this.enumId;
if (!batchJobEnum) {
const jobEnum: any = Object.values(this.jobEnums)?.find((job: any) => {
return job.unfillable === this.unfillableOrder && job.facilityId === this.batchFacilityId
});
batchJobEnum = jobEnum.id
async generateRunTimes(currentRunTime?: any) {
const runTimes = JSON.parse(JSON.stringify(generateAllowedRunTimes()))
let selectedRunTime
// 0 check for the 'Now' value and '' check for initial render
if (currentRunTime || currentRunTime === 0 ) {
selectedRunTime = runTimes.some((runTime: any) => runTime.value === currentRunTime)
if (!selectedRunTime) runTimes.push({ label: this.getTime(currentRunTime), value: currentRunTime })
}
this.runTime = currentRunTime
this.runTimes = runTimes
},
async generateFrequencyOptions(currentFrequency?: any) {
const frequencyOptions = JSON.parse(JSON.stringify(generateAllowedFrequencies()));
if (hasPermission(Actions.APP_CUSTOM_FREQ_VIEW)) frequencyOptions.push({ "id": "CUSTOM", "description": "Custom"})
if (currentFrequency) {
const selectedFrequency = frequencyOptions.find((frequency: any) => frequency.id === currentFrequency);
if (!selectedFrequency ) {
const frequencies = await this.store.dispatch("job/fetchTemporalExpression", [ currentFrequency ]);
const frequency = frequencies[currentFrequency];
frequency && (frequencyOptions.push({ "id": frequency.tempExprId, "description": frequency.description }))
}
}
this.frequencyOptions = frequencyOptions;
this.jobStatus = currentFrequency;
},
async updateJob() {
const jobEnum: any = Object.values(this.jobEnums)?.find((job: any) => job.unfillable === this.unfillableOrder && job.facilityId === this.batchFacilityId);

const job = this.currentBatch ? this.currentBatch : this.getJob(batchJobEnum)?.find((job: any) => job.status === 'SERVICE_DRAFT');

const job = this.getJob(jobEnum.id)?.find((job: any) => job.status === 'SERVICE_DRAFT');
if (!job) {
showToast(translate('Configuration missing'))
return;
Expand All @@ -152,40 +237,76 @@ export default defineComponent({
// return if job has missing data or error
if (hasJobDataError(job)) return;

if (this.jobRunTime) {
job['runTime'] = this.jobRunTime
}

job['jobStatus'] = 'EVERYDAY';
job['jobName'] = this.jobName || this.currentDateTime;
job.runTime = this.runTime != 0 ? (!isCustomRunTime(this.runTime) ? DateTime.now().toMillis() + this.runTime : this.runTime) : ''

// if job runTime is not a valid date then making runTime as empty
if (job?.runTime && !isFutureDate(job?.runTime)) {
job.runTime = ''
}
if (job?.status === 'SERVICE_DRAFT') {
const jobCustomParameters = generateJobCustomParameters([], [], job.runtimeData)
await this.store.dispatch('job/scheduleService', { job, jobCustomParameters })
} else if (job?.status === 'SERVICE_PENDING') {
await this.store.dispatch('job/updateJob', job)
}

job['jobStatus'] = this.jobStatus ? this.jobStatus : 'HOURLY';
job['jobName'] = this.jobName || this.currentDateTime;

const jobCustomParameters = generateJobCustomParameters(this.customRequiredParameters, this.customOptionalParameters, job.runtimeData)

await this.store.dispatch('job/scheduleService', { job, jobCustomParameters })
this.closeModal()
},
updateRunTime(ev: CustomEvent) {
this.jobRunTime = handleDateTimeInput(ev['detail'].value)
async setCustomFrequency() {
const customFrequencyModal = await modalController.create({
component: CustomFrequencyModal,
});
customFrequencyModal.onDidDismiss()
.then((result) => {
let jobStatus = result.data.frequencyId;
this.generateFrequencyOptions(jobStatus);
});
return customFrequencyModal.present();
},
updateCustomTime(event: CustomEvent) {
const currTime = DateTime.now().toMillis();
const setTime = handleDateTimeInput(event.detail.value);
if (setTime > currTime) this.generateRunTimes(setTime)
else showToast(translate("Provide a future date and time"))
},
updateCustomParameters() {
const jobEnum: any = Object.values(this.jobEnums)?.find((job: any) => job.unfillable === this.unfillableOrder && job.facilityId === this.batchFacilityId);

const job = this.getJob(jobEnum.id)?.find((job: any) => job.status === 'SERVICE_DRAFT');
this.customOptionalParameters = generateJobCustomOptions(job).optionalParameters;
this.customRequiredParameters = generateJobCustomOptions(job).requiredParameters;
},
updateRunTime(event: CustomEvent) {
const value = event.detail.value
if (value != 'CUSTOM') this.generateRunTimes(value)
else this.isDateTimeModalOpen = true
},
getCurrentDateTime() {
return DateTime.now().setZone(this.userProfile.userTimeZone).toLocaleString(DateTime.DATETIME_MED);
},
getTime (time: any) {
return DateTime.fromMillis(time).toLocaleString(DateTime.DATETIME_MED);
},
isRequiredParametersMissing() {
return this.customRequiredParameters.some((parameter: any) => !parameter.value?.trim())
}
},
setup() {
const store = useStore();

return {
checkmarkDoneOutline,
closeOutline,
getNowTimestamp,
hasPermission,
isCustomRunTime,
store,
getNowTimestamp
ticketOutline,
timeOutline,
timerOutline,
warningOutline,
Actions,
DateTime
};
},
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/JobConfiguration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<section>
<ion-item lines="none">
<!-- Adding conditional check for currentJob.jobName as currentJob is undefined when i18n runs $t -->
<h1>{{ currentJob.enumName ? currentJob.enumName : currentJob.jobName ? currentJob.jobName : '' }}</h1>
<h1>{{ isBrokerJob ? currentJob.jobName : currentJob.enumName ? currentJob.enumName : currentJob.jobName ? currentJob.jobName : '' }}</h1>
<ion-badge slot="end" color="dark" v-if="currentJob?.runTime && currentJob.statusId !== 'SERVICE_DRAFT'">{{ $t("running") }} {{ timeTillJob(currentJob.runTime) }}</ion-badge>
</ion-item>

Expand Down Expand Up @@ -199,7 +199,7 @@ export default defineComponent({
this.generateRunTimes(this.runTime)
this.generateFrequencyOptions(this.jobStatus)
},
props: ["status", "type"],
props: ["isBrokerJob", "status", "type"],
computed: {
...mapGetters({
pinnedJobs: 'user/getPinnedJobs',
Expand Down
Loading
Loading