1307 lines
42 KiB
Vue
1307 lines
42 KiB
Vue
<template>
|
|
<main class="h-screen-main relative">
|
|
<div class="h-full relative bg-neutral-50">
|
|
<div class="flex justify-start items-start p-4 h-full">
|
|
<!-- tag -->
|
|
<aside class="border-r border-neutral-300 pr-4 mr-4 h-full sticky top-0 self-start">
|
|
<section class="px-2 space-y-2 w-56 h-full">
|
|
<div>
|
|
<p class="h1">{{ i18next.t("Compare.timeUsage") }}</p>
|
|
<ul class="list-disc list-inside text-sm leading-5 pl-3">
|
|
<li v-for="(item, index) in timeUsageData" :key="index" :class="{active: isActive === item.tagId}"
|
|
@click="handleClick(item.tagId)" class="cursor-pointer hover:text-primary">
|
|
{{ item.label }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<p class="h1">{{ i18next.t("Compare.frequency") }}</p>
|
|
<ul class="list-disc list-inside text-sm leading-5 pl-3">
|
|
<li v-for="(item, index) in frequencyData" :key="index" :class="{active: isActive === item.tagId}"
|
|
@click="handleClick(item.tagId)" class="cursor-pointer hover:text-primary">
|
|
{{ item.label }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
</aside>
|
|
<!-- graph -->
|
|
<article class="w-full h-full pr-2 mr-2 overflow-x-hidden overflow-y-auto scrollbar scroll-smooth">
|
|
<section>
|
|
<p class="h1 px-4 border-b border-neutral-900"><span class="material-symbols-outlined mr-2 align-middle">
|
|
{{ i18next.t("Compare.schedule") }}
|
|
</span>
|
|
{{ i18next.t("Compare.timeUsage") }}
|
|
</p>
|
|
<ul class="list-disc list-inside px-4 pl-7 text-sm">
|
|
<li id="cycleTime" class="scroll-smooth">
|
|
<span class="inline-block py-4">{{ i18next.t("Compare.cycleEfficiency") }}</span>
|
|
<ul>
|
|
<li class="bg-neutral-10 mb-4 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgCycleTime.title }}
|
|
</span>
|
|
<Chart type="line" :data="avgCycleTimeData" :options="avgCycleTimeOptions" class="h-96" />
|
|
</li>
|
|
<li class="bg-neutral-10 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgCycleEfficiency.title }}
|
|
<span class="material-symbols-outlined align-middle ml-2 cursor-pointer !text-base"
|
|
v-tooltip.bottom="tooltip.avgCycleEfficiency">
|
|
{{ i18next.t("Compare.info") }}
|
|
</span>
|
|
</span>
|
|
<Chart type="bar" :data="avgCycleEfficiencyData" :options="avgCycleEfficiencyOptions" class="h-96" />
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li id="processingTime">
|
|
<span class="inline-block py-4 text-sm">{{ i18next.t("Compare.ProcessingTime") }}</span>
|
|
<ul>
|
|
<li class="bg-neutral-10 mb-4 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgProcessTime.title }}
|
|
</span>
|
|
<Chart type="line" :data="avgProcessTimeData" :options="avgProcessTimeOptions" class="h-96" />
|
|
</li>
|
|
<li class="bg-neutral-10 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgProcessTimeByTask.title }}
|
|
</span>
|
|
<Chart type="bar" :data="avgProcessTimeByTaskData" :options="avgProcessTimeByTaskOptions"
|
|
class="h-[500px]"
|
|
:style="{ height: avgProcessTimeByTaskHeight }"/>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li id="waitingTime">
|
|
<span class="inline-block py-4 text-sm">{{ i18next.t("Compare.WaitingTime") }}</span>
|
|
<ul>
|
|
<li class="bg-neutral-10 mb-4 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgWaitingTime.title }}
|
|
<span class="material-symbols-outlined align-middle ml-2 cursor-pointer !text-base"
|
|
v-tooltip.bottom="tooltip.avgWaitingTime">
|
|
info
|
|
</span></span>
|
|
<Chart type="line" :data="avgWaitingTimeData" :options="avgWaitingTimeOptions" class="h-96" />
|
|
</li>
|
|
<li class="bg-neutral-10 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.avgWaitingTimeByEdge.title }}
|
|
<span class="material-symbols-outlined align-middle ml-2 cursor-pointer !text-base"
|
|
v-tooltip.bottom="tooltip.avgWaitingTimeByEdge">{{ i18next.t("Compare.info") }}</span></span>
|
|
<div>
|
|
<Chart v-if="avgWaitingTimeByEdgeData !== null" type="bar" :data="avgWaitingTimeByEdgeData"
|
|
:options="avgWaitingTimeByEdgeOptions" :style="{ height: avgWaitingTimeByEdgeHeight }" class="h-[500px]" />
|
|
<div v-else class="h-96 bg-neutral-100 m-4 flex">
|
|
<p class="h2 text-danger m-auto">{{ i18next.t("Compare.NoWaitingTime") }}</p>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<p class="h1 px-4 border-b border-neutral-900 mt-2"><span class="material-symbols-outlined mr-2 align-middle">
|
|
{{i18next.t("Compare.moving")}}
|
|
</span>
|
|
{{ i18next.t("Compare.frequency") }}
|
|
</p>
|
|
<ul class="list-disc list-inside px-4 pl-7 text-sm">
|
|
<li id="cases">
|
|
<span class="inline-block py-4">{{ i18next.t("Compare.NumberOfCases") }}</span>
|
|
<ul>
|
|
<li class="bg-neutral-10 mb-4 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">{{ contentData.freq.title }}</span>
|
|
<FreqChart v-if="compareDashboardData" :chartData="compareDashboardData?.freq?.cases" :content="contentData?.freq" yUnit="count"
|
|
pageName='Compare'
|
|
/>
|
|
</li>
|
|
<li class="bg-neutral-10 p-1 border border-neutral-300 rounded">
|
|
<span class="block font-bold text-sm leading-loose text-center my-2">
|
|
{{ contentData.casesByTask.title }}
|
|
</span>
|
|
<Chart type="bar" :data="casesByTaskData" :options="casesByTaskOptions" :style="{ height: casesByTaskHeight }"
|
|
class="h-[500px]"/>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
</article>
|
|
<!-- Sidebar: State -->
|
|
<div class="bg-transparent z-10">
|
|
<ul class="flex flex-col justify-center items-center">
|
|
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9
|
|
cursor-pointer bg-neutral-50 drop-shadow hover:border-primary" @click="sidebarState = !sidebarState"
|
|
:class="{'border-primary': sidebarState}" id="compareState">
|
|
<span class="material-symbols-outlined !text-2xl text-neutral-500 hover:text-primary p-1.5"
|
|
:class="[sidebarState ? 'text-primary' : 'text-neutral-500']">
|
|
info
|
|
</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<SidebarStates v-model:visible="sidebarState"></SidebarStates>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</template>
|
|
<script setup>
|
|
// The Lucia project.
|
|
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
|
// Authors:
|
|
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
|
/**
|
|
* @module views/Compare/Dashboard/Compare
|
|
* Performance comparison dashboard with side-by-side
|
|
* charts comparing two datasets.
|
|
*/
|
|
|
|
import { ref, onMounted } from 'vue';
|
|
import { useRoute } from 'vue-router';
|
|
import { storeToRefs } from 'pinia';
|
|
import moment from 'moment';
|
|
import { useLoadingStore } from '@/stores/loading';
|
|
import { useCompareStore } from '@/stores/compare';
|
|
import SidebarStates from '@/components/Compare/SidebarStates.vue';
|
|
import { setLineChartData } from '@/module/setChartData.js';
|
|
import { simpleTimeLabel, followTimeLabel,
|
|
setTimeStringFormatBaseOnTimeDifference,
|
|
mapTimestampToAxisTicksByFormat,
|
|
getStepSizeOfYTicks,
|
|
getYTicksByIndex,
|
|
} from '@/module/timeLabel.js';
|
|
import i18next from '@/i18n/i18n';
|
|
import {
|
|
knownLayoutChartOption,
|
|
knownScaleLineChartOptions,
|
|
knownScaleHorizontalChartOptions,
|
|
knownScaleBarChartOptions,
|
|
GRID_COLOR,
|
|
} from "@/constants/constants.js";
|
|
import FreqChart from '../../Discover/Performance/FreqChart.vue';
|
|
|
|
const route = useRoute();
|
|
|
|
// Stores
|
|
const loadingStore = useLoadingStore();
|
|
const compareStore = useCompareStore();
|
|
const { isLoading } = storeToRefs(loadingStore);
|
|
const { compareDashboardData } = storeToRefs(compareStore);
|
|
|
|
// Data
|
|
const timeUsageData = [
|
|
{tagId: '#cycleTime', label: i18next.t("Compare.cycleEfficiency")},
|
|
{tagId: '#processingTime', label: i18next.t("Compare.ProcessingTime")},
|
|
{tagId: '#waitingTime', label: i18next.t("Compare.WaitingTime")},
|
|
];
|
|
const frequencyData = [
|
|
{tagId: '#cases', label: i18next.t("Compare.NumberOfCases")},
|
|
];
|
|
const contentData = {
|
|
avgCycleTime: {title: i18next.t("Compare.avgCycleTimeTitle"), x: i18next.t("Compare.labelDateX"),
|
|
y: i18next.t("Compare.avgCycleTimeY")},
|
|
avgCycleEfficiency: {title: i18next.t("Compare.avgCycleEfficiencyTitle"), x: i18next.t("Compare.labelDateX"),
|
|
y: i18next.t("Compare.avgCycleEfficiencyY")},
|
|
avgProcessTime: {title: i18next.t("Compare.avgProcessTimeTitle"), x: i18next.t("Compare.labelDateX"),
|
|
y: i18next.t("Compare.avgProcessTimeY")},
|
|
avgProcessTimeByTask: {title: i18next.t("Compare.avgProcessTimeByTaskTitle"),
|
|
x: i18next.t("Compare.avgProcessTimeByTaskX"), y: i18next.t("Compare.labelActivityY")},
|
|
avgWaitingTime: {title: i18next.t("Compare.avgWaitingTimeTitle"), x: i18next.t("Compare.labelDateX"),
|
|
y: i18next.t("Compare.avgWaitingTimeY")},
|
|
avgWaitingTimeByEdge: {title: i18next.t("Compare.avgWaitingTimeByEdgeTitle"),
|
|
x: i18next.t("Compare.avgWaitingTimeByEdgeX"),
|
|
y: i18next.t("Compare.avgWaitingTimeByEdgeY")},
|
|
freq: {title: i18next.t("Compare.freqTitle"), x: i18next.t("Compare.labelDateX"), y: i18next.t("Compare.freqY")},
|
|
casesByTask: {title: i18next.t("Compare.casesByTaskTitle"), x: i18next.t("Compare.casesByTaskX"),
|
|
y: i18next.t("Compare.labelActivityY")},
|
|
};
|
|
const tooltip = {
|
|
avgCycleEfficiency: {
|
|
value: i18next.t("Compare.avgCycleEfficiency"),
|
|
class: '!max-w-[212px] !text-[10px] !opacity-80',
|
|
autoHide: false,
|
|
},
|
|
avgWaitingTime: {
|
|
value: i18next.t("Compare.avgWaitingTime"),
|
|
class: '!max-w-[212px] !text-[10px] !opacity-80',
|
|
autoHide: false,
|
|
},
|
|
avgWaitingTimeByEdge: {
|
|
value: i18next.t("Compare.avgWaitingTimeByEdge"),
|
|
class: '!max-w-[212px] !text-[10px] !opacity-80',
|
|
autoHide: false,
|
|
},
|
|
};
|
|
|
|
const isActive = ref(null);
|
|
const avgCycleTimeData = ref(null);
|
|
const avgCycleTimeOptions = ref(null);
|
|
const avgCycleEfficiencyData = ref(null);
|
|
const avgCycleEfficiencyOptions = ref(null);
|
|
const avgProcessTimeData = ref(null);
|
|
const avgProcessTimeOptions = ref(null);
|
|
const avgProcessTimeByTaskData = ref(null);
|
|
const avgProcessTimeByTaskOptions = ref(null);
|
|
const avgWaitingTimeData = ref(null);
|
|
const avgWaitingTimeOptions = ref(null);
|
|
const avgWaitingTimeByEdgeData = ref(null);
|
|
const avgWaitingTimeByEdgeOptions = ref(null);
|
|
const freqData = ref(null);
|
|
const freqOptions = ref(null);
|
|
const casesByTaskData = ref(null);
|
|
const casesByTaskOptions = ref(null);
|
|
const horizontalBarHeight = 500;
|
|
const avgProcessTimeByTaskHeight = ref(500);
|
|
const avgWaitingTimeByEdgeHeight = ref(500);
|
|
const casesByTaskHeight = ref(500);
|
|
const colorPrimary = '#0099FF';
|
|
const colorSecondary = '#FFAA44';
|
|
const sidebarState = ref(false);
|
|
|
|
// Methods
|
|
/**
|
|
* Scrolls to the selected chart section.
|
|
* @param {string} tagId - The anchor tag ID to navigate to.
|
|
*/
|
|
function handleClick(tagId) {
|
|
isActive.value = tagId;
|
|
|
|
if (isSafeTagId(tagId)) {
|
|
window.location.href = tagId;
|
|
} else {
|
|
console.warn("Unsafe tagId: ", tagId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates that a tag ID is safe for navigation.
|
|
* @param {string} tagId - The tag ID to validate.
|
|
* @returns {boolean} True if the tag ID is safe.
|
|
*/
|
|
function isSafeTagId(tagId) {
|
|
const pattern = /^#?[a-zA-Z0-9]*$/;
|
|
return pattern.test(tagId);
|
|
}
|
|
|
|
/**
|
|
* Generates evenly-spaced X-axis label timestamps.
|
|
* @param {object} valueData - Object with min and max date strings.
|
|
* @returns {Array<number>} Array of timestamp values.
|
|
*/
|
|
function setXLabelsData(valueData) {
|
|
const min = new Date(valueData.min).getTime();
|
|
const max = new Date(valueData.max).getTime();
|
|
const numPoints = 12;
|
|
const step = (max - min) / (numPoints - 1);
|
|
const data = [];
|
|
for(let i = 0; i< numPoints; i++) {
|
|
const x = min + i * step;
|
|
data.push(x);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Calculates the height for a horizontal bar chart based on bar count.
|
|
* @param {object} chartData - The chart data with x_axis labels.
|
|
* @returns {string} The CSS height string.
|
|
*/
|
|
function getHorizontalBarHeight(chartData) {
|
|
const totalBars = chartData.x_axis.labels.length;
|
|
let hBarHeight = horizontalBarHeight;
|
|
|
|
if(totalBars > 10){
|
|
hBarHeight = (totalBars - 10) * 16 * 2 + horizontalBarHeight
|
|
};
|
|
|
|
return hBarHeight + 'px'
|
|
}
|
|
|
|
/**
|
|
* Builds a line chart configuration for Chart.js.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {string} yUnit - 'date' or 'count'.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getLineChart(chartData, content, yUnit) {
|
|
let datasetsPrimary;
|
|
let datasetsSecondary;
|
|
const minX = chartData.x_axis.min;
|
|
const maxX = chartData.x_axis.max;
|
|
let xLabelData;
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
const getMoment = (time)=> {
|
|
return moment(time).format('YYYY/M/D hh:mm:ss')
|
|
};
|
|
const getSimpleTimeLabel = simpleTimeLabel;
|
|
|
|
switch (yUnit) {
|
|
case 'date':
|
|
datasetsPrimary = setLineChartData(chartData.data[0].data, chartData.x_axis.max, chartData.x_axis.min, false,
|
|
chartData.y_axis.max, chartData.y_axis.min);
|
|
datasetsSecondary = setLineChartData(chartData.data[1].data, chartData.x_axis.max, chartData.x_axis.min, false,
|
|
chartData.y_axis.max, chartData.y_axis.min);
|
|
xLabelData = setXLabelsData(chartData.x_axis);
|
|
break;
|
|
case 'count':
|
|
datasetsPrimary = chartData.data[0].data;
|
|
datasetsSecondary = chartData.data[1].data;
|
|
xLabelData = chartData.data[0].data.map(item => new Date(item.x).getTime());
|
|
break;
|
|
}
|
|
|
|
const formatToSet = setTimeStringFormatBaseOnTimeDifference(minX, maxX);
|
|
const ticksOfXAxis = mapTimestampToAxisTicksByFormat(xLabelData, formatToSet);
|
|
const customizedScaleOption = getCustomizedScaleOption(
|
|
knownScaleLineChartOptions, {
|
|
customizeOptions: {
|
|
content, ticksOfXAxis,
|
|
}
|
|
});
|
|
primeVueSetData = {
|
|
labels: xLabelData,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: datasetsPrimary,
|
|
fill: false,
|
|
tension: 0,
|
|
borderColor: colorPrimary,
|
|
pointBackgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: datasetsSecondary,
|
|
fill: false,
|
|
tension: 0,
|
|
borderColor: colorSecondary,
|
|
pointBackgroundColor: colorSecondary,
|
|
}
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 16,
|
|
left: 8,
|
|
right: 8,
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
mode: 'index',
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {
|
|
title: function(context) {
|
|
return `${content.x}: ${getMoment(context[0].parsed.x)}`;
|
|
},
|
|
},
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: customizedScaleOption,
|
|
};
|
|
switch (yUnit) {
|
|
case 'date':
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = getSimpleTimeLabel(context.parsed.y, 2);
|
|
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
primeVueSetOption.scales.x.min = minX;
|
|
primeVueSetOption.scales.x.max = maxX;
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const {resultStepSize: YTickStepSize, unitToUse} = getStepSizeOfYTicks(ticks[ticks.length - 1].value, ticks.length);
|
|
return getYTicksByIndex(YTickStepSize, index, unitToUse);
|
|
}
|
|
break;
|
|
case 'count':
|
|
primeVueSetOption.scales.y.ticks.precision = 0;
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = context.parsed.y;
|
|
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
break;
|
|
}
|
|
|
|
return [primeVueSetData, primeVueSetOption];
|
|
}
|
|
|
|
/**
|
|
* Builds a bar chart configuration for Chart.js.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {string} caller - Identifier for chart-specific options.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getBarChart(chartData, content, caller) {
|
|
const getMoment = (time)=> moment(time).format('YYYY/MM/DD');
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
|
|
const datasetsPrimary = chartData.data[0].data.map(value => {
|
|
return {
|
|
x: getMoment(value.x),
|
|
y: value.y === null ? null : value.y * 100
|
|
}
|
|
});
|
|
const xDataPrimary = datasetsPrimary.map(i => i.x);
|
|
const yDataPrimary = datasetsPrimary.map(i => i.y);
|
|
const datasetsSecondary = chartData.data[1].data.map(value => {
|
|
return {
|
|
x: getMoment(value.x),
|
|
y: value.y === null ? null : value.y * 100
|
|
}
|
|
});
|
|
const yDataSecondary = datasetsSecondary.map(i => i.y);
|
|
|
|
primeVueSetData = {
|
|
labels: xDataPrimary,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: yDataPrimary,
|
|
backgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: yDataSecondary,
|
|
backgroundColor: colorSecondary,
|
|
},
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: knownLayoutChartOption,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: 'index',
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {
|
|
title: function(context) {
|
|
return `${content.x}: ${context[0].label}`;
|
|
},
|
|
label: function(context) {
|
|
const value = `${(context.parsed.y * 10).toFixed(2)}%`;
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
}
|
|
},
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: customizeScaleChartOptionTitleByContent(knownScaleBarChartOptions, content),
|
|
};
|
|
|
|
primeVueSetOption.scales = {
|
|
...primeVueSetOption.scales,
|
|
x: {
|
|
ticks: {
|
|
callback: function(value, index, ticks) {
|
|
return moment(xDataPrimary[index]).format('YYYY/MM');
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
if(caller === "Cycle Eff") {
|
|
primeVueSetOption.scales.y.reverse = true;
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
return 10 * index;
|
|
};
|
|
primeVueSetOption.scales.y.grid = {
|
|
color: GRID_COLOR,
|
|
};
|
|
primeVueSetOption.scales.x.grid = {
|
|
display: false,
|
|
};
|
|
}
|
|
|
|
return [primeVueSetData, primeVueSetOption]
|
|
}
|
|
|
|
/**
|
|
* Builds a horizontal bar chart configuration for Chart.js.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {boolean} isSingle - Whether labels are single values.
|
|
* @param {string} xUnit - 'date' or 'count'.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getHorizontalBarChart(chartData, content, isSingle, xUnit) {
|
|
const maxY = chartData.y_axis.max;
|
|
const getSimpleTimeLabel = simpleTimeLabel;
|
|
const getFollowTimeLabel = followTimeLabel;
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
|
|
const datasetsPrimary = chartData.data[0].data;
|
|
const datasetsSecondary = chartData.data[1].data;
|
|
datasetsPrimary.sort((a, b) => b.y - a.y);
|
|
datasetsSecondary.sort((a, b) => {
|
|
const indexA = datasetsPrimary.findIndex(item => item.x === a.x);
|
|
const indexB = datasetsPrimary.findIndex(item => item.x === b.x);
|
|
return indexA - indexB;
|
|
});
|
|
|
|
const xLabelData = datasetsPrimary.map(item => item.x);
|
|
const yDataPrimary = datasetsPrimary.map(item => item.y);
|
|
const yDataSecondary = datasetsSecondary.map(item => item.y);
|
|
|
|
primeVueSetData = {
|
|
labels: xLabelData,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: yDataPrimary,
|
|
backgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: yDataSecondary,
|
|
backgroundColor: colorSecondary,
|
|
},
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
indexAxis: 'y',
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 16,
|
|
left: 8,
|
|
right: 8,
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
mode: 'index',
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {}
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: customizeScaleChartOptionTitleByContent(knownScaleHorizontalChartOptions, null),
|
|
};
|
|
switch (xUnit) {
|
|
case 'date':
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = context.parsed.x === null ? "n/a" : getSimpleTimeLabel(context.parsed.x, 2);
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
primeVueSetOption.scales.x.ticks.callback = function (value, index, ticks) {
|
|
return getFollowTimeLabel(value, maxY, 1)
|
|
};
|
|
break;
|
|
case 'count':
|
|
default:
|
|
primeVueSetOption.scales.x.ticks.precision = 0;
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = context.parsed.x === null ? "n/a" : context.parsed.x;
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
break;
|
|
}
|
|
if(isSingle) {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label}`;
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
if(label) {
|
|
return label.length > 21 ? `${label.substring(0, 18)}...` : label
|
|
};
|
|
}
|
|
|
|
}else {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label.replace(',', ' - ')}`
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
let labelStart = label[0];
|
|
let labelEnd = label[1];
|
|
|
|
labelStart = labelStart.length > 10 ? `${labelStart.substring(0,7)}...` : labelStart;
|
|
labelEnd = labelEnd.length > 10 ? `${labelEnd.substring(0,7)}...` : labelEnd;
|
|
return labelStart + " - " + labelEnd
|
|
};
|
|
}
|
|
|
|
return [primeVueSetData, primeVueSetOption]
|
|
}
|
|
|
|
/**
|
|
* Customizes Chart.js scale options with content and tick data.
|
|
* @param {object} whichScaleObj - The base scale options object.
|
|
* @param {object} options - Options containing content and ticksOfXAxis.
|
|
* @returns {object} The customized scale options.
|
|
*/
|
|
function getCustomizedScaleOption(whichScaleObj, {customizeOptions: {
|
|
content,
|
|
ticksOfXAxis,
|
|
},
|
|
}) {
|
|
let resultScaleObj;
|
|
resultScaleObj = customizeScaleChartOptionTitleByContent(whichScaleObj, content);
|
|
resultScaleObj = customizeScaleChartOptionTicks(resultScaleObj, ticksOfXAxis);
|
|
return resultScaleObj;
|
|
}
|
|
|
|
/**
|
|
* Sets axis titles on a scale options object.
|
|
* @param {object} whichScaleObj - The base scale options.
|
|
* @param {object} content - Object with x and y axis title text.
|
|
* @returns {object} The scale options with updated titles.
|
|
*/
|
|
function customizeScaleChartOptionTitleByContent(whichScaleObj, content){
|
|
if (!content) {
|
|
return whichScaleObj;
|
|
}
|
|
|
|
return {
|
|
...whichScaleObj,
|
|
x: {
|
|
...whichScaleObj.x,
|
|
title: {
|
|
...whichScaleObj.x.title,
|
|
text: content.x
|
|
}
|
|
},
|
|
y: {
|
|
...whichScaleObj.y,
|
|
title: {
|
|
...whichScaleObj.y.title,
|
|
text: content.y
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sets custom tick callbacks on a scale options object.
|
|
* @param {object} scaleObjectToAlter - The scale options to modify.
|
|
* @param {Array} ticksOfXAxis - The formatted tick labels.
|
|
* @returns {object} The scale options with custom ticks.
|
|
*/
|
|
function customizeScaleChartOptionTicks(scaleObjectToAlter, ticksOfXAxis) {
|
|
return {
|
|
...scaleObjectToAlter,
|
|
x: {
|
|
...scaleObjectToAlter.x,
|
|
ticks: {
|
|
...scaleObjectToAlter.x.ticks,
|
|
callback: function(value, index) {
|
|
return ticksOfXAxis[index];
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Builds an alternative line chart configuration with inline scales.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {string} yUnit - 'date' or 'count'.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getLineChart0(chartData, content, yUnit) {
|
|
let datasetsPrimary;
|
|
let datasetsSecondary;
|
|
const minX = chartData.x_axis.min;
|
|
const maxX = chartData.x_axis.max;
|
|
let xLabelData;
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
const getMoment = (time)=> {
|
|
return moment(time).format('YYYY/M/D hh:mm:ss')
|
|
};
|
|
const getSimpleTimeLabel = simpleTimeLabel;
|
|
|
|
switch (yUnit) {
|
|
case 'date':
|
|
datasetsPrimary = setLineChartData(chartData.data[0].data, chartData.x_axis.max, chartData.x_axis.min, false,
|
|
chartData.y_axis.max, chartData.y_axis.min);
|
|
datasetsSecondary = setLineChartData(chartData.data[1].data, chartData.x_axis.max, chartData.x_axis.min, false,
|
|
chartData.y_axis.max, chartData.y_axis.min);
|
|
xLabelData = setXLabelsData(chartData.x_axis);
|
|
break;
|
|
case 'count':
|
|
datasetsPrimary = chartData.data[0].data;
|
|
datasetsSecondary = chartData.data[1].data;
|
|
xLabelData = chartData.data[0].data.map(item => new Date(item.x).getTime());
|
|
break;
|
|
}
|
|
|
|
const formatToSet = setTimeStringFormatBaseOnTimeDifference(minX, maxX);
|
|
const ticksOfXAxis = mapTimestampToAxisTicksByFormat(xLabelData, formatToSet);
|
|
|
|
primeVueSetData = {
|
|
labels: xLabelData,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: datasetsPrimary,
|
|
fill: false,
|
|
tension: 0,
|
|
borderColor: colorPrimary,
|
|
pointBackgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: datasetsSecondary,
|
|
fill: false,
|
|
tension: 0,
|
|
borderColor: colorSecondary,
|
|
pointBackgroundColor: colorSecondary,
|
|
}
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 16,
|
|
left: 8,
|
|
right: 8,
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
mode: 'index',
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {
|
|
title: function(context) {
|
|
return `${content.x}: ${getMoment(context[0].parsed.x)}`;
|
|
},
|
|
label:function(context) {
|
|
return `${content.y}: ${getSimpleTimeLabel(context.parsed.y, 2)}`;
|
|
},
|
|
},
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
min: minX,
|
|
max: maxX,
|
|
type: 'time',
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.x,
|
|
},
|
|
time: {
|
|
displayFormats: {
|
|
second: 'h:mm:ss',
|
|
minute: 'M/d h:mm',
|
|
hour: 'M/d h:mm',
|
|
day: 'M/d h',
|
|
month: 'y/M/d',
|
|
},
|
|
round: true
|
|
},
|
|
ticks: {
|
|
padding: 8,
|
|
display: true,
|
|
maxRotation: 0,
|
|
color: '#64748b',
|
|
source: 'labels',
|
|
callback: function(value, index) {
|
|
return ticksOfXAxis[index];
|
|
},
|
|
},
|
|
border: {
|
|
color: '#64748b',
|
|
},
|
|
},
|
|
y: {
|
|
beginAtZero: true,
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.y
|
|
},
|
|
grid: {
|
|
color: '#64748b',
|
|
},
|
|
border: {
|
|
display: false,
|
|
},
|
|
ticks: {
|
|
|
|
color: '#64748b',
|
|
padding: 8,
|
|
callback: function (value, index, ticks) {
|
|
const {resultStepSize, unitToUse} = getStepSizeOfYTicks(ticks[ticks.length - 1].value, ticks.length);
|
|
return getYTicksByIndex(resultStepSize, index, unitToUse);
|
|
},
|
|
}
|
|
},
|
|
},
|
|
};
|
|
|
|
return [primeVueSetData, primeVueSetOption];
|
|
}
|
|
|
|
/**
|
|
* Builds a horizontal bar chart for cases-by-task data.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {boolean} isSingle - Whether labels are single values.
|
|
* @param {string} [xUnit='count'] - The x-axis unit.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getCaseByTaskHorizontalBarChart(chartData, content, isSingle, xUnit = 'count') {
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
|
|
const datasetsPrimary = chartData.data[0].data;
|
|
const datasetsSecondary = chartData.data[1].data;
|
|
datasetsPrimary.sort((a, b) => b.y - a.y);
|
|
datasetsSecondary.sort((a, b) => {
|
|
const indexA = datasetsPrimary.findIndex(item => item.x === a.x);
|
|
const indexB = datasetsPrimary.findIndex(item => item.x === b.x);
|
|
return indexA - indexB;
|
|
});
|
|
|
|
const xLabelData = datasetsPrimary.map(item => item.x);
|
|
const yDataPrimary = datasetsPrimary.map(item => item.y);
|
|
const yDataSecondary = datasetsSecondary.map(item => item.y);
|
|
|
|
primeVueSetData = {
|
|
labels: xLabelData,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: yDataPrimary,
|
|
backgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: yDataSecondary,
|
|
backgroundColor: colorSecondary,
|
|
},
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
indexAxis: 'y',
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 16,
|
|
left: 8,
|
|
right: 8,
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
mode: 'index',
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {}
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.x,
|
|
},
|
|
ticks: {
|
|
display: true,
|
|
maxRotation: 0,
|
|
color: '#64748b',
|
|
callback: function(value, index, ticks) {
|
|
return value;
|
|
}
|
|
},
|
|
grid: {
|
|
color: '#64748b',
|
|
tickLength: 0,
|
|
},
|
|
border: {
|
|
display:false,
|
|
},
|
|
},
|
|
y: {
|
|
beginAtZero: true,
|
|
type: 'category',
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.y,
|
|
},
|
|
ticks:{
|
|
color: '#64748b',
|
|
padding: 8,
|
|
},
|
|
grid: {
|
|
display:false,
|
|
color: '#64748b',
|
|
},
|
|
border: {
|
|
display: false,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
switch (xUnit) {
|
|
case 'dummy':
|
|
case 'count':
|
|
default:
|
|
primeVueSetOption.scales.x.ticks.precision = 0;
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = context.parsed.x === null ? "n/a" : context.parsed.x;
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
break;
|
|
}
|
|
if(isSingle) {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label}`;
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
if(label) {
|
|
return label.length > 21 ? `${label.substring(0, 18)}...` : label
|
|
};
|
|
}
|
|
|
|
}else {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label.replace(',', ' - ')}`
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
let labelStart = label[0];
|
|
let labelEnd = label[1];
|
|
|
|
labelStart = labelStart.length > 10 ? `${labelStart.substring(0,7)}...` : labelStart;
|
|
labelEnd = labelEnd.length > 10 ? `${labelEnd.substring(0,7)}...` : labelEnd;
|
|
return labelStart + " - " + labelEnd
|
|
};
|
|
}
|
|
|
|
return [primeVueSetData, primeVueSetOption]
|
|
}
|
|
|
|
/**
|
|
* Builds a horizontal bar chart for average process time data.
|
|
* @param {object} chartData - The chart data from the API.
|
|
* @param {object} content - The axis label content.
|
|
* @param {boolean} isSingle - Whether labels are single values.
|
|
* @param {string} [xUnit='date'] - The x-axis unit.
|
|
* @returns {Array} [chartData, chartOptions] tuple.
|
|
*/
|
|
function getAvgProcessTimeHorizontalBarChart(chartData, content, isSingle, xUnit="date") {
|
|
const maxY = chartData.y_axis.max;
|
|
const getSimpleTimeLabel = simpleTimeLabel;
|
|
const getFollowTimeLabel = followTimeLabel;
|
|
const labelPrimary = chartData.data[0].label;
|
|
const labelSecondary = chartData.data[1].label;
|
|
let primeVueSetData = {};
|
|
let primeVueSetOption = {};
|
|
|
|
const datasetsPrimary = chartData.data[0].data;
|
|
const datasetsSecondary = chartData.data[1].data;
|
|
datasetsPrimary.sort((a, b) => b.y - a.y);
|
|
datasetsSecondary.sort((a, b) => {
|
|
const indexA = datasetsPrimary.findIndex(item => item.x === a.x);
|
|
const indexB = datasetsPrimary.findIndex(item => item.x === b.x);
|
|
return indexA - indexB;
|
|
});
|
|
|
|
const xLabelData = datasetsPrimary.map(item => item.x);
|
|
const yDataPrimary = datasetsPrimary.map(item => item.y);
|
|
const yDataSecondary = datasetsSecondary.map(item => item.y);
|
|
|
|
primeVueSetData = {
|
|
labels: xLabelData,
|
|
datasets: [
|
|
{
|
|
label: labelPrimary,
|
|
data: yDataPrimary,
|
|
backgroundColor: colorPrimary,
|
|
},
|
|
{
|
|
label: labelSecondary,
|
|
data: yDataSecondary,
|
|
backgroundColor: colorSecondary,
|
|
},
|
|
]
|
|
};
|
|
primeVueSetOption = {
|
|
indexAxis: 'y',
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 16,
|
|
left: 8,
|
|
right: 8,
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: false,
|
|
tooltip: {
|
|
mode: 'index',
|
|
titleFont: {weight: 'normal'},
|
|
callbacks: {}
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.x,
|
|
},
|
|
ticks: {
|
|
display: true,
|
|
maxRotation: 0,
|
|
color: '#64748b',
|
|
},
|
|
grid: {
|
|
color: '#64748b',
|
|
tickLength: 0,
|
|
},
|
|
border: {
|
|
display:false,
|
|
},
|
|
},
|
|
y: {
|
|
beginAtZero: true,
|
|
type: 'category',
|
|
title: {
|
|
display: true,
|
|
color: '#334155',
|
|
font: {
|
|
size: 12,
|
|
lineHeight: 2
|
|
},
|
|
text: content.y
|
|
},
|
|
ticks:{
|
|
color: '#64748b',
|
|
padding: 8,
|
|
},
|
|
grid: {
|
|
display:false,
|
|
color: '#64748b',
|
|
},
|
|
border: {
|
|
display: false,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
switch (xUnit) {
|
|
case 'dummy':
|
|
case 'date':
|
|
default:
|
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
|
const value = context.parsed.x === null ? "n/a" : getSimpleTimeLabel(context.parsed.x, 2);
|
|
switch (context.datasetIndex) {
|
|
case 0:
|
|
return `${labelPrimary}: ${value}`;
|
|
case 1:
|
|
return `${labelSecondary}: ${value}`;
|
|
}
|
|
};
|
|
primeVueSetOption.scales.x.ticks.callback = function (value, index, ticks) {
|
|
return getFollowTimeLabel(value, maxY, 1)
|
|
};
|
|
break;
|
|
}
|
|
if(isSingle) {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label}`;
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
if(label) {
|
|
return label.length > 21 ? `${label.substring(0, 18)}...` : label
|
|
};
|
|
}
|
|
|
|
}else {
|
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
|
return `${content.y}: ${context[0].label.replace(',', ' - ')}`
|
|
};
|
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
|
const label = xLabelData[index];
|
|
let labelStart = label[0];
|
|
let labelEnd = label[1];
|
|
|
|
labelStart = labelStart.length > 10 ? `${labelStart.substring(0,7)}...` : labelStart;
|
|
labelEnd = labelEnd.length > 10 ? `${labelEnd.substring(0,7)}...` : labelEnd;
|
|
return labelStart + " - " + labelEnd
|
|
};
|
|
}
|
|
|
|
return [primeVueSetData, primeVueSetOption]
|
|
}
|
|
|
|
// Created logic
|
|
(async () => {
|
|
isLoading.value = true;
|
|
try {
|
|
const routeParams = route.params;
|
|
const primaryType = routeParams.primaryType;
|
|
const secondaryType = routeParams.secondaryType;
|
|
const primaryId = routeParams.primaryId;
|
|
const secondaryId = routeParams.secondaryId;
|
|
const typeMap = {
|
|
'log': 'log_id',
|
|
'filter': 'filter_id'
|
|
};
|
|
const primaryTypeParam = typeMap[primaryType];
|
|
const secondaryTypeParam = typeMap[secondaryType];
|
|
const queryParams = [
|
|
{ [primaryTypeParam]: primaryId },
|
|
{ [secondaryTypeParam]: secondaryId }
|
|
];
|
|
|
|
// Fetch Compare Data
|
|
await compareStore.getCompare(queryParams);
|
|
avgProcessTimeByTaskHeight.value = getHorizontalBarHeight(compareDashboardData.value.time.avg_process_time_by_task);
|
|
if(compareDashboardData.value.time.avg_waiting_time_by_edge !== null) {
|
|
avgWaitingTimeByEdgeHeight.value = getHorizontalBarHeight(compareDashboardData.value.time.avg_waiting_time_by_edge);
|
|
};
|
|
casesByTaskHeight.value = getHorizontalBarHeight(compareDashboardData.value.freq.cases_by_task);
|
|
// create chart
|
|
[avgCycleTimeData.value, avgCycleTimeOptions.value] = getLineChart(
|
|
compareDashboardData.value.time.avg_cycle_time, contentData.avgCycleTime, 'date');
|
|
[avgCycleEfficiencyData.value, avgCycleEfficiencyOptions.value] = getBarChart(
|
|
compareDashboardData.value.time.avg_cycle_efficiency, contentData.avgCycleEfficiency, "Cycle Eff");
|
|
[avgProcessTimeData.value, avgProcessTimeOptions.value] = getLineChart(
|
|
compareDashboardData.value.time.avg_process_time, contentData.avgProcessTime, 'date');
|
|
[avgProcessTimeByTaskData.value, avgProcessTimeByTaskOptions.value] = getAvgProcessTimeHorizontalBarChart(
|
|
compareDashboardData.value.time.avg_process_time_by_task,
|
|
contentData.avgProcessTimeByTask, true, 'date');
|
|
[avgWaitingTimeData.value, avgWaitingTimeOptions.value] = getLineChart(
|
|
compareDashboardData.value.time.avg_waiting_time, contentData.avgWaitingTime, 'date');
|
|
if(compareDashboardData.value.time.avg_waiting_time_by_edge !== null) {
|
|
[avgWaitingTimeByEdgeData.value, avgWaitingTimeByEdgeOptions.value] = getHorizontalBarChart(
|
|
compareDashboardData.value.time.avg_waiting_time_by_edge,
|
|
contentData.avgWaitingTimeByEdge, false, 'date');
|
|
} else {
|
|
[avgWaitingTimeByEdgeData.value, avgWaitingTimeByEdgeOptions.value] = [null, null]
|
|
}
|
|
[freqData.value, freqOptions.value] = getLineChart(
|
|
compareDashboardData.value.freq.cases, contentData.freq, 'count');
|
|
[casesByTaskData.value, casesByTaskOptions.value] = getCaseByTaskHorizontalBarChart(
|
|
compareDashboardData.value.freq.cases_by_task, contentData.casesByTask, true, 'count');
|
|
} catch (error) {
|
|
console.error('Failed to initialize compare dashboard:', error);
|
|
}
|
|
})();
|
|
|
|
// Mounted
|
|
onMounted(() => {
|
|
isLoading.value = false;
|
|
});
|
|
</script>
|
|
<style scoped>
|
|
@reference "../../../assets/tailwind.css";
|
|
.active {
|
|
@apply text-primary
|
|
}
|
|
</style>
|