Skip to content

Commit

Permalink
Merge pull request #729 from R-Sourabh/#680-data-manager-log
Browse files Browse the repository at this point in the history
Implemented: Updated the DataManager Logs page to be dynamic in Job Manager(#680)
  • Loading branch information
ymaheshwari1 authored Nov 13, 2024
2 parents 3974ff7 + 08be04e commit ad5ba07
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 50 deletions.
76 changes: 76 additions & 0 deletions src/components/DownloadLogsFilePopover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<ion-content>
<ion-list>
<ion-list-header>{{ dataManagerLog.logId }}</ion-list-header>
<ion-item button @click="downloadFile('logFile')">
{{ translate('Log file') }}
</ion-item>
<ion-item button @click="downloadFile('uploadedFile')">
{{ translate('Uploaded file') }}
</ion-item>
<ion-item button :disabled="!dataManagerLog?.errorRecordContentId" lines="none" @click="downloadFile('failedRecords')">
{{ translate('Failed records') }}
</ion-item>
</ion-list>
</ion-content>
</template>

<script lang="ts">
import {
IonContent,
IonItem,
IonList,
IonListHeader,
popoverController
} from "@ionic/vue";
import { defineComponent } from "vue";
import { translate } from "@hotwax/dxp-components";
import { JobService } from "@/services/JobService";
import { saveDataFile, showToast } from '@/utils';
import logger from "@/logger";
export default defineComponent({
name: "DownloadLogsFilePopover",
components: {
IonContent,
IonItem,
IonList,
IonListHeader
},
props: ["dataManagerLog"],
methods: {
async downloadFile(type: string) {
let dataResource = {} as any;
if (type === 'logFile') {
dataResource.dataResourceId = this.dataManagerLog.logFileDataResourceId
dataResource.name = this.dataManagerLog.logFileContentName
} else if (type === 'uploadedFile') {
dataResource.name = this.dataManagerLog.contentName
dataResource.dataResourceId = this.dataManagerLog.dataResourceId
} else if (type === 'failedRecords') {
dataResource.dataResourceId = this.dataManagerLog.errorRecordDataResourceId
dataResource.name = this.dataManagerLog.errorRecordContentName
}
if (dataResource.dataResourceId) {
try {
const response = await JobService.fetchFileData({
dataResourceId: dataResource.dataResourceId
});
saveDataFile(response.data, dataResource.name);
} catch (error) {
showToast(translate('Error downloading file'))
logger.error(error)
}
}
popoverController.dismiss();
}
},
setup() {
return {
translate
}
}
});
</script>
137 changes: 97 additions & 40 deletions src/components/JobConfiguration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
<ion-button fill="outline" slot="end" v-if="isRefreshRequired" @click="refreshCurrentJob">
<ion-icon :icon="refreshOutline" slot="icon-only" />
</ion-button>
<ion-badge slot="end" color="dark" v-if="currentJob?.runTime && currentJob.statusId !== 'SERVICE_DRAFT' && !isRefreshRequired">{{ translate("running") }} {{ timeTillJob(currentJob.runTime) }}</ion-badge>
<ion-badge slot="end" color="dark" v-if="currentJob.cancelDateTime || currentJob.finishDateTime">{{ currentJob.statusId == "SERVICE_CANCELLED" || currentJob.statusId == "SERVICE_CRASHED" ? timeTillJob(currentJob.cancelDateTime) : timeTillJob(currentJob.finishDateTime) }}</ion-badge>
<ion-badge slot="end" color="dark" v-else-if="currentJob?.runTime && currentJob.statusId !== 'SERVICE_DRAFT' && !isRefreshRequired">{{ translate("running") }} {{ timeTillJob(currentJob.runTime) }}</ion-badge>
</ion-item>

<ion-list>
Expand All @@ -19,33 +20,45 @@

<ion-item>
<ion-icon slot="start" :icon="timeOutline" />
<ion-select interface="popover" :placeholder="translate('Select')" :value="runTime" @ionChange="updateRunTime($event)">
<div slot="label" class="ion-text-wrap">{{ translate("Run time") }}</div>
<ion-select-option v-for="runTime in runTimes" :key="runTime.value" :value="runTime.value">{{ translate(runTime.label) }}</ion-select-option>
</ion-select>
<!-- TODO: display a button when we are not having a runtime and open the datetime component
on click of that button
Currently, when mapping the same datetime component for label and button so it's not working so for
now commented the button and added a fallback string -->
<!-- <ion-button id="open-run-time-modal" size="small" fill="outline" color="medium" v-show="!currentJob?.runTime">{{ translate("Select run time") }}</ion-button> -->
<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>
<template v-if="historyJobConfig">
<ion-label class="ion-text-wrap">{{ translate("Run time") }}</ion-label>
<ion-label slot="end">{{ currentJob.runTime ? getTime(currentJob.runTime) : '' }}</ion-label>
</template>
<template v-else>
<ion-select interface="popover" :placeholder="translate('Select')" :value="runTime" @ionChange="updateRunTime($event)">
<div slot="label" class="ion-text-wrap">{{ translate("Run time") }}</div>
<ion-select-option v-for="runTime in runTimes" :key="runTime.value" :value="runTime.value">{{ translate(runTime.label) }}</ion-select-option>
</ion-select>
<!-- TODO: display a button when we are not having a runtime and open the datetime component
on click of that button
Currently, when mapping the same datetime component for label and button so it's not working so for
now commented the button and added a fallback string -->
<!-- <ion-button id="open-run-time-modal" size="small" fill="outline" color="medium" v-show="!currentJob?.runTime">{{ translate("Select run time") }}</ion-button> -->
<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>
</template>
</ion-item>

<ion-item>
<ion-icon slot="start" :icon="timerOutline" />
<ion-select :value="jobStatus" :interface-options="{ header: translate('Frequency') }" interface="popover" :placeholder="translate('Disabled')" @ionChange="jobStatus = $event.detail.value" @ionDismiss="jobStatus == 'CUSTOM' && setCustomFrequency()">
<div slot="label" class="ion-text-wrap">{{ translate("Schedule") }}</div>
<ion-select-option v-for="freq in frequencyOptions" :key="freq.id" :value="freq.id">{{ freq.description }}</ion-select-option>
</ion-select>
<template v-if="historyJobConfig">
<ion-label class="ion-text-wrap">{{ translate("Schedule") }}</ion-label>
<ion-label slot="end">{{ currentJob.tempExprId ? temporalExpr(currentJob.tempExprId)?.description : "🙃" }}</ion-label>
</template>
<template v-else>
<ion-select :value="jobStatus" :interface-options="{ header: translate('Frequency') }" interface="popover" :placeholder="translate('Disabled')" @ionChange="jobStatus = $event.detail.value" @ionDismiss="jobStatus == 'CUSTOM' && setCustomFrequency()">
<div slot="label" class="ion-text-wrap">{{ translate("Schedule") }}</div>
<ion-select-option v-for="freq in frequencyOptions" :key="freq.id" :value="freq.id">{{ freq.description }}</ion-select-option>
</ion-select>
</template>
</ion-item>

<ion-item lines="none">
Expand Down Expand Up @@ -76,43 +89,67 @@
</ion-item> -->
</ion-list>

<div class="actions desktop-only">
<div class="actions desktop-only" :disabled="historyJobConfig">
<div>
<ion-button size="small" fill="outline" color="medium" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || currentJob.statusId === 'SERVICE_DRAFT' || isRefreshRequired" @click="skipJob(currentJob)">{{ translate("Skip once") }}</ion-button>
<ion-button size="small" fill="outline" color="danger" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || currentJob.statusId === 'SERVICE_DRAFT' || isRefreshRequired" @click="cancelJob(currentJob)">{{ translate("Disable") }}</ion-button>
<ion-button size="small" fill="outline" color="medium" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || currentJob.statusId === 'SERVICE_DRAFT' || isRefreshRequired || historyJobConfig" @click="skipJob(currentJob)">{{ translate("Skip once") }}</ion-button>
<ion-button size="small" fill="outline" color="danger" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || currentJob.statusId === 'SERVICE_DRAFT' || isRefreshRequired || historyJobConfig" @click="cancelJob(currentJob)">{{ translate("Disable") }}</ion-button>
</div>
<div>
<ion-button :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || isRequiredParametersMissing || isRefreshRequired" size="small" fill="outline" @click="saveChanges()">{{ translate("Save changes") }}</ion-button>
<ion-button :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || isRequiredParametersMissing || isRefreshRequired || historyJobConfig" size="small" fill="outline" @click="saveChanges()">{{ translate("Save changes") }}</ion-button>
</div>
</div>

<div class=" actions mobile-only">
<ion-button size="small" expand="block" fill="outline" color="medium" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || status === 'SERVICE_DRAFT' || isRefreshRequired" @click="skipJob(currentJob)">{{ translate("Skip once") }}</ion-button>
<ion-button size="small" expand="block" fill="outline" color="danger" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || status === 'SERVICE_DRAFT' || isRefreshRequired" @click="cancelJob(currentJob)">{{ translate("Disable") }}</ion-button>
<ion-button :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || isRequiredParametersMissing || isRefreshRequired" expand="block" @click="saveChanges()">{{ translate("Save changes") }}</ion-button>
<ion-button size="small" expand="block" fill="outline" color="medium" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || status === 'SERVICE_DRAFT' || isRefreshRequired || historyJobConfig" @click="skipJob(currentJob)">{{ translate("Skip once") }}</ion-button>
<ion-button size="small" expand="block" fill="outline" color="danger" :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || status === 'SERVICE_DRAFT' || isRefreshRequired || historyJobConfig" @click="cancelJob(currentJob)">{{ translate("Disable") }}</ion-button>
<ion-button :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || isRequiredParametersMissing || isRefreshRequired || historyJobConfig" expand="block" @click="saveChanges()">{{ translate("Save changes") }}</ion-button>
</div>
</section>
<div class="more-actions">
<ion-item @click="viewJobHistory(currentJob)" button>
<ion-icon slot="start" :icon="timeOutline" />
{{ translate("History") }}
</ion-item>
<ion-item :disabled="!hasPermission(Actions.APP_JOB_UPDATE)" @click="runNow(currentJob)" button>
<ion-item :disabled="!hasPermission(Actions.APP_JOB_UPDATE) || historyJobConfig" @click="runNow(currentJob)" button>
<ion-icon slot="start" :icon="flashOutline" />
{{ translate("Run now") }}
</ion-item>
<ion-item @click="copyJobInformation(currentJob)" button>
<ion-icon slot="start" :icon="copyOutline" />
{{ translate("Copy details") }}
</ion-item>
<ion-item @click="updatePinnedJobs(currentJob?.systemJobEnumId)" button>
<ion-item :disabled="historyJobConfig" @click="updatePinnedJobs(currentJob?.systemJobEnumId)" button>
<ion-icon slot="start" :icon="pinOutline" />
<ion-checkbox :checked="pinnedJobs && pinnedJobs.includes(currentJob.systemJobEnumId)">
<ion-label>{{ translate("Pin job") }}</ion-label>
</ion-checkbox>
</ion-item>
</div>

<!-- Import logs -->
<section v-if="historyJobConfig && currentJob.runtimeData?.configId && getDataManagerLogs?.length">
<ion-item lines="none">
<h1>{{ translate('Import logs') }}</h1>
<ion-button slot="end" fill="clear" @click="openImportLogsDetails()">{{ translate('View details') }}</ion-button>
</ion-item>
<ion-progress-bar :value="(getProcessedFileCount() - getErrorFileCount()) / getDataManagerLogs.length"></ion-progress-bar>
<ion-list>
<ion-item>
<ion-icon slot="start" :icon="fileTrayFullOutline" />
{{ translate('Files received') }}
<ion-label slot="end">{{ getDataManagerLogs.length }}</ion-label>
</ion-item>
<ion-item>
<ion-icon slot="start" :icon="codeWorkingOutline" />
{{ translate('Files processed') }}
<ion-label slot="end">{{ getProcessedFileCount() }}</ion-label>
</ion-item>
<ion-item lines="none">
<ion-icon slot="start" :icon="warningOutline" />
{{ translate('Files with errors') }}
<ion-label slot="end">{{ getErrorFileCount() }}</ion-label>
</ion-item>
</ion-list>
</section>
</template>

<script lang="ts">
Expand All @@ -129,6 +166,7 @@ import {
IonLabel,
IonList,
IonModal,
IonProgressBar,
IonRow,
IonSelect,
IonSelectOption,
Expand All @@ -138,15 +176,18 @@ import {
import {
addOutline,
calendarClearOutline,
codeWorkingOutline,
flashOutline,
fileTrayFullOutline,
listCircleOutline,
copyOutline,
timeOutline,
timerOutline,
syncOutline,
personCircleOutline,
pinOutline,
refreshOutline
refreshOutline,
warningOutline
} from "ionicons/icons";
import JobHistoryModal from '@/components/JobHistoryModal.vue'
import { Plugins } from '@capacitor/core';
Expand All @@ -173,6 +214,7 @@ export default defineComponent({
IonLabel,
IonList,
IonModal,
IonProgressBar,
IonRow,
IonSelect,
IonSelectOption,
Expand Down Expand Up @@ -205,7 +247,7 @@ export default defineComponent({
this.generateRunTimes(this.runTime)
this.generateFrequencyOptions(this.jobStatus)
},
props: ["isBrokerJob", "status", "type"],
props: ["isBrokerJob", "status", "type", "historyJobConfig"],
computed: {
...mapGetters({
pinnedJobs: 'user/getPinnedJobs',
Expand All @@ -215,6 +257,8 @@ export default defineComponent({
currentEComStore: 'user/getCurrentEComStore',
currentJob: 'job/getCurrentJob',
pendingJobs: 'job/getPendingJobs',
getDataManagerLogs: 'job/getDataManagerLogs',
temporalExpr: 'job/getTemporalExpr'
}),
isRequiredParametersMissing() {
return this.customRequiredParameters.some((parameter: any) => !parameter.value?.trim())
Expand All @@ -225,6 +269,16 @@ export default defineComponent({
}
},
methods: {
getProcessedFileCount() {
return this.getDataManagerLogs?.filter((log: any) => log.statusId === "SERVICE_FINISHED").length
},
getErrorFileCount() {
return this.getDataManagerLogs?.filter((log: any) => log.errorRecordContentId !== null).length
},
openImportLogsDetails() {
const jobId = this.currentJob.jobId
this.router.push({ name: 'DataManagerLogDetails', params: { jobId } })
},
getDateTime(time: any) {
return DateTime.fromMillis(time).toISO()
},
Expand Down Expand Up @@ -525,10 +579,12 @@ export default defineComponent({
Actions,
addOutline,
calendarClearOutline,
codeWorkingOutline,
copyOutline,
DateTime,
listCircleOutline,
flashOutline,
fileTrayFullOutline,
hasPermission,
isCustomRunTime,
getNowTimestamp,
Expand All @@ -540,17 +596,18 @@ export default defineComponent({
personCircleOutline,
pinOutline,
refreshOutline,
translate
translate,
warningOutline
};
}
});
</script>

<style scoped>
ion-list {
margin: 0 0 var(--spacer-base);
section {
margin-top: var(--spacer-sm);
margin-bottom: var(--spacer-sm);
}
.actions > ion-button {
margin: var(--spacer-sm);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/JobParameterModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</ion-buttons>
<ion-title>{{ translate('Custom Parameters') }}</ion-title>
<ion-buttons slot="end">
<ion-button color="primary" :disabled="currentJob.statusId === 'SERVICE_PENDING'" @click="save()">{{ translate('Save') }}</ion-button>
<ion-button color="primary" :disabled="currentJob.statusId !== 'SERVICE_DRAFT'" @click="save()">{{ translate('Save') }}</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
Expand Down
Loading

0 comments on commit ad5ba07

Please sign in to comment.