Created
January 26, 2025 08:38
-
-
Save nodahikaru/6cd1b4046d17cb7ffb83dd93f1732d81 to your computer and use it in GitHub Desktop.
Explorer Chart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup> | |
import { computed, watch, ref, reactive, defineAsyncComponent, onBeforeUnmount } from 'vue' | |
import { useDashboardStore } from '@/stores/dashboard' | |
import { useTrends } from '@/use/useTrends' | |
import event from '@/plugins/event' | |
import { useDisplay } from 'vuetify' | |
import VueApexCharts from 'vue3-apexcharts' | |
const Correlations = defineAsyncComponent(() => import('@/components/Trends/Correlations.vue')) | |
const RotateScreenPrompt = defineAsyncComponent(() => import('@/components/RotateScreenPrompt.vue')) | |
const { mdAndDown } = useDisplay() | |
const promptRotate = ref(false) | |
const dashboardStore = useDashboardStore() | |
const { nonComparableOptions, comparableOptions, chartMonths, getExplorerData } = useTrends() | |
const loading = ref(false) | |
const facilityIds = computed(() => dashboardStore.getFacilityIds) | |
const groupId = computed(() => dashboardStore.getGroupId) | |
const dashboardKpis = computed(() => dashboardStore.getDashboardKpis) | |
const lkpi = computed(() => dashboardStore.getlkpi) | |
const rkpi = computed(() => dashboardStore.getrkpi) | |
const lkpiObj = computed(() => { | |
if (lkpi.value && dashboardKpis.value.length) { | |
return dashboardKpis.value.find(k => k.chart_id == lkpi.value) | |
} | |
return null | |
}) | |
const rkpiObj = computed(() => { | |
if (rkpi.value && dashboardKpis.value.length) { | |
return dashboardKpis.value.find(k => k.chart_id == rkpi.value) | |
} | |
return null | |
}) | |
let abortController = null | |
let activeCallId = 0 | |
let corr = reactive({}) | |
const chartKey = ref(0) | |
const chartOptions = ref({ | |
chart: {}, | |
grid: { | |
show: false | |
}, | |
xaxis: { | |
axisBorder: { | |
show: false, | |
}, | |
axisTicks: { | |
show: false | |
}, | |
}, | |
yaxis: { | |
show: false | |
} | |
}) | |
const chartSeries = ref([]) | |
const showCorrelation = computed(() => lkpi.value && rkpi.value) | |
const updateSeries = ({ charts }) => { | |
let final = [] | |
const { leading, compare } = charts | |
const comparable = Object.keys(compare).length > 0 | |
final.push({ | |
name: comparable ? lkpiObj.value.title : 'This Year', | |
type: 'column', | |
key: lkpiObj.value.value, | |
data: chartMonths.value.map(m => ({ | |
x: m, | |
y: leading[m].value, | |
...leading[m] | |
})) | |
}) | |
if (comparable) { | |
final.push({ | |
name: rkpiObj.value.title, | |
type: 'column', | |
key: rkpiObj.value.value, | |
data: chartMonths.value.map(m => ({ | |
x: m, | |
y: compare[m].value, | |
...compare[m] | |
})) | |
}) | |
} else { | |
final.push({ | |
name: '% Last Year', | |
type: 'line', | |
key: lkpiObj.value.value, | |
data: chartMonths.value.map(m => ({ | |
x: m, | |
y: leading[m].delta_pct, | |
...leading[m] | |
})) | |
}) | |
} | |
chartSeries.value = final | |
scaling(comparable) | |
} | |
const scaling = (comparable) => { | |
chartOptions.value = comparable ? comparableOptions : nonComparableOptions | |
const lData = chartSeries.value[0].data.map(d => { | |
if (d.format == 'string') return parseInt(d.y.split(d.split_value)[d.split_value_index]) | |
return d.y ? parseFloat(d.y) : null | |
}).filter(k => k != null) | |
const rData = chartSeries.value[1].data.map(d => { | |
if (d.format == 'string') return parseInt(d.y.split(d.split_value)[d.split_value_index]) | |
return d.y ? parseFloat(d.y) : null | |
}).filter(k => k != null) | |
const maxFactor = comparable ? 1.05 : 1 | |
const minFactor = 0.95 | |
const lMin = Math.min(...lData) * minFactor | |
const lMax = Math.max(...lData) * maxFactor | |
const rMin = Math.min(...rData) * minFactor | |
const rMax = Math.max(...rData) * maxFactor | |
const min = Math.min(lMin, rMax) * minFactor | |
if (comparable) { | |
chartOptions.value = { | |
...chartOptions.value, | |
yaxis: [ | |
{ | |
show: false, | |
max: lMax, | |
min: lMin == 0 ? min : lMin, | |
}, | |
{ | |
show: false, | |
max: rMax, | |
min: rMin == 0 ? min : rMin | |
} | |
] | |
} | |
} else { | |
chartOptions.value = { | |
...chartOptions.value, | |
yaxis: [ | |
{ | |
show: false, | |
max: lMax, | |
min: lMin, | |
}, | |
{ | |
show: false, | |
} | |
] | |
} | |
} | |
chartKey.value++ | |
} | |
const loadData = async () => { | |
const callId = ++activeCallId | |
abort() | |
loading.value = true | |
try { | |
abortController = new AbortController() | |
const signal = abortController.signal | |
const params = { | |
leading_dash: lkpiObj.value?.dashboard_id, | |
leading_chart: lkpiObj.value?.chart_id, | |
dashboard_id: rkpiObj.value ? rkpiObj.value.dashboard_id : null, | |
chart_id: rkpiObj.value ? rkpiObj.value.chart_id : null, | |
months: chartMonths.value.join(','), | |
} | |
const data = await getExplorerData(signal, params) | |
if (callId === activeCallId) { | |
const { charts, correlations } = data | |
if (correlations) corr = correlations | |
if (charts) updateSeries({ charts }) | |
} | |
} catch (e) { | |
if (e.message !== 'canceled' && callId === activeCallId) { | |
event.error(e) | |
} | |
} finally { | |
if (callId === activeCallId) { | |
loading.value = false | |
abortController = null | |
} | |
} | |
} | |
const abort = () => { | |
if (loading.value && abortController) { | |
try { | |
abortController.abort() | |
console.log(`Cancelled pending API call`) | |
} catch(e) { | |
console.error(`Error cancelling pending API Call`, e) | |
} | |
} | |
} | |
watch([lkpi, rkpi], ([ln, rn]) => { | |
if (ln || rn) loadData() | |
}, { immediate: true }) | |
watch([facilityIds, groupId], () => { | |
loadData() | |
}) | |
onBeforeUnmount(() => { | |
abort() | |
}) | |
</script> | |
<template lang="pug"> | |
div.px-2 | |
v-container.w-100(fluid) | |
rotate-screen-prompt.mb-4(v-if="mdAndDown", v-model="promptRotate") | |
v-card.mt-2.mx-auto(flat, :loading="loading", width="100%") | |
v-card-text | |
.d-flex.justify-end.pr-2.position-absolute.right-0 | |
Correlations(v-if="showCorrelation" :loading="loading" :correlations="corr") | |
vue-apex-charts(:key="chartKey", height="560" :options="chartOptions", :series="chartSeries") | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment