Skip to content

Instantly share code, notes, and snippets.

@nodahikaru
Created January 26, 2025 08:38
Show Gist options
  • Save nodahikaru/6cd1b4046d17cb7ffb83dd93f1732d81 to your computer and use it in GitHub Desktop.
Save nodahikaru/6cd1b4046d17cb7ffb83dd93f1732d81 to your computer and use it in GitHub Desktop.
Explorer Chart
<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