From 23b8f7229a7b3da188604078c4295f3e47b47fb1 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 9 Dec 2024 17:57:50 +0100 Subject: [PATCH] [NOT READY] fix dates + charts + ui --- .../components/dashboard/ActionableChart.vue | 114 +++++++++++++----- dashboard/components/dashboard/CountCard.vue | 9 +- .../components/dashboard/CountCardOld.vue | 40 ------ .../components/dashboard/EmbedChartCard.vue | 22 +++- dashboard/components/dashboard/TopCards.vue | 18 +-- .../components/dialog/CreateSnapshot.vue | 8 +- .../composables/snapshots/BaseSnapshots.ts | 74 ++++++------ dashboard/composables/useCustomFetch.ts | 4 +- dashboard/composables/useSnapshot.ts | 6 +- dashboard/pages/index.vue | 8 +- dashboard/server/api/timeline/events.ts | 4 +- .../server/api/timeline/events_stacked.ts | 5 +- dashboard/server/api/timeline/sessions.ts | 4 +- .../server/api/timeline/sessions_duration.ts | 2 +- dashboard/server/api/timeline/visits.ts | 4 +- dashboard/server/services/TimelineService.ts | 13 +- dashboard/server/utils/getRequestData.ts | 10 +- shared/services/DateService.ts | 18 ++- 18 files changed, 211 insertions(+), 152 deletions(-) delete mode 100644 dashboard/components/dashboard/CountCardOld.vue diff --git a/dashboard/components/dashboard/ActionableChart.vue b/dashboard/components/dashboard/ActionableChart.vue index a6bb901..1a4e489 100644 --- a/dashboard/components/dashboard/ActionableChart.vue +++ b/dashboard/components/dashboard/ActionableChart.vue @@ -6,6 +6,23 @@ import { useLineChart, LineChart } from 'vue-chart-3'; const errorData = ref<{ errored: boolean, text: string }>({ errored: false, text: '' }) + +function createGradient(startColor: string) { + const c = document.createElement('canvas'); + const ctx = c.getContext("2d"); + let gradient: any = `${startColor}22`; + if (ctx) { + gradient = ctx.createLinearGradient(0, 25, 0, 300); + gradient.addColorStop(0, `${startColor}99`); + gradient.addColorStop(0.35, `${startColor}66`); + gradient.addColorStop(1, `${startColor}22`); + } else { + console.warn('Cannot get context for gradient'); + } + + return gradient; +} + const chartOptions = ref>({ responsive: true, maintainAspectRatio: false, @@ -68,12 +85,32 @@ const chartData = ref>({ borderColor: '#5655d7', borderWidth: 4, fill: true, - tension: 0.45, + tension: 0.35, pointRadius: 0, pointHoverRadius: 10, hoverBackgroundColor: '#5655d7', hoverBorderColor: 'white', hoverBorderWidth: 2, + segment: { + borderColor(ctx, options) { + const todayIndex = visitsData.data.value?.todayIndex; + if (!todayIndex || todayIndex == -1) return '#5655d7'; + if (ctx.p1DataIndex >= todayIndex) return '#5655d700'; + return '#5655d7' + }, + borderDash(ctx, options) { + const todayIndex = visitsData.data.value?.todayIndex; + if (!todayIndex || todayIndex == -1) return undefined; + if (ctx.p1DataIndex == todayIndex - 1) return [3, 5]; + return undefined; + }, + backgroundColor(ctx, options) { + const todayIndex = visitsData.data.value?.todayIndex; + if (!todayIndex || todayIndex == -1) return createGradient('#5655d7'); + if (ctx.p1DataIndex >= todayIndex) return '#5655d700'; + return createGradient('#5655d7'); + }, + }, }, { label: 'Unique sessions', @@ -85,18 +122,20 @@ const chartData = ref>({ hoverBorderColor: '#4abde8', hoverBorderWidth: 2, type: 'bar', + // barThickness: 20, + borderSkipped: ['bottom'] }, { label: 'Events', data: [], backgroundColor: ['#fbbf24'], - borderColor: '#fbbf24', borderWidth: 2, hoverBackgroundColor: '#fbbf24', hoverBorderColor: '#fbbf24', hoverBorderWidth: 2, type: 'bubble', - stack: 'combined' + stack: 'combined', + borderColor: ["#fbbf24"] }, ], }); @@ -109,6 +148,17 @@ function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'li const { chart, tooltip } = context; const tooltipEl = externalTooltipElement.value; + const currentIndex = tooltip.dataPoints[0].parsed.x; + + const todayIndex = visitsData.data.value?.todayIndex; + if (todayIndex && todayIndex >= 0) { + if (currentIndex > todayIndex - 1) { + if (!tooltipEl) return; + return tooltipEl.style.opacity = '0'; + } + } + + currentTooltipData.value.visits = (tooltip.dataPoints.find(e => e.datasetIndex == 0)?.raw) as number; currentTooltipData.value.sessions = (tooltip.dataPoints.find(e => e.datasetIndex == 1)?.raw) as number; currentTooltipData.value.events = ((tooltip.dataPoints.find(e => e.datasetIndex == 2)?.raw) as any)?.r2 as number; @@ -142,14 +192,19 @@ const selectLabels: { label: string, value: Slice }[] = [ { label: 'Month', value: 'month' }, ]; -const selectLablesAvailable = computed<{ label: string, value: Slice, disabled: boolean }[]>(() => { +const selectLabelsAvailable = computed<{ label: string, value: Slice, disabled: boolean }[]>(() => { return selectLabels.map(e => { return { ...e, disabled: !DateService.canUseSliceFromDays(snapshotDuration.value, e.value)[0] } }); }) const selectedSlice = computed(() => { - return selectLablesAvailable.value[selectedLabelIndex.value].value + const targetValue = selectLabelsAvailable.value[selectedLabelIndex.value]; + if (!targetValue) return 'day'; + if (targetValue.disabled) { + selectedLabelIndex.value = selectLabelsAvailable.value.findIndex(e => !e.disabled); + } + return selectLabelsAvailable.value[selectedLabelIndex.value].value }); const selectedLabelIndex = ref(1); @@ -158,13 +213,12 @@ const allDatesFull = ref([]); function transformResponse(input: { _id: string, count: number }[]) { const data = input.map(e => e.count); - - console.log('RESPONSE', input); const labels = input.map(e => DateService.getChartLabelFromISO(e._id, new Date().getTimezoneOffset(), selectedSlice.value)); - console.log('LABELS', input); - if (input.length > 0) allDatesFull.value = input.map(e => e._id.toString()); - return { data, labels } + + const todayIndex = input.findIndex(e => new Date(e._id).getTime() > (Date.now() - new Date().getTimezoneOffset() * 1000 * 60)); + + return { data, labels, todayIndex } } function onResponseError(e: any) { @@ -200,21 +254,7 @@ watch(readyToDisplay, () => { }) -function createGradient(startColor: string) { - const c = document.createElement('canvas'); - const ctx = c.getContext("2d"); - let gradient: any = `${startColor}22`; - if (ctx) { - gradient = ctx.createLinearGradient(0, 25, 0, 300); - gradient.addColorStop(0, `${startColor}99`); - gradient.addColorStop(0.35, `${startColor}66`); - gradient.addColorStop(1, `${startColor}22`); - } else { - console.warn('Cannot get context for gradient'); - } - return gradient; -} function onDataReady() { if (!visitsData.data.value) return; @@ -228,9 +268,10 @@ function onDataReady() { chartData.value.datasets[0].data = visitsData.data.value.data; chartData.value.datasets[1].data = sessionsData.data.value.data; + chartData.value.datasets[2].data = eventsData.data.value.data.map(e => { - const rValue = 25 / maxEventSize * e; - return { x: 0, y: maxChartY + 70, r: isNaN(rValue) ? 0 : rValue, r2: e } + const rValue = 20 / maxEventSize * e; + return { x: 0, y: maxChartY + 20, r: isNaN(rValue) ? 0 : rValue, r2: e } }); @@ -238,6 +279,22 @@ function onDataReady() { chartData.value.datasets[1].backgroundColor = [createGradient('#4abde8')]; chartData.value.datasets[2].backgroundColor = [createGradient('#fbbf24')]; + + (chartData.value.datasets[1] as any).borderSkipped = sessionsData.data.value.data.map((e, i) => { + const todayIndex = eventsData.data.value?.todayIndex || 0; + if (i == todayIndex - 1) return true; + return 'bottom'; + }); + + chartData.value.datasets[2].borderColor = eventsData.data.value.data.map((e, i) => { + const todayIndex = eventsData.data.value?.todayIndex || 0; + if (i == todayIndex - 1) return '#fbbf2400'; + return '#fbbf24'; + }); + + + + updateChart(); } @@ -251,7 +308,8 @@ const currentTooltipData = ref<{ visits: number, events: number, sessions: numbe const tooltipNameIndex = ['visits', 'sessions', 'events']; function onLegendChange(dataset: any, index: number, checked: any) { - dataset.hidden = !checked; + const newValue = !checked; + dataset.hidden = newValue; } const legendColors = ref(['#5655d7', '#4abde8', '#fbbf24']) @@ -268,7 +326,7 @@ const legendClasses = ref([ diff --git a/dashboard/components/dashboard/CountCard.vue b/dashboard/components/dashboard/CountCard.vue index d395edd..1f13a29 100644 --- a/dashboard/components/dashboard/CountCard.vue +++ b/dashboard/components/dashboard/CountCard.vue @@ -10,7 +10,8 @@ const props = defineProps<{ data?: number[], labels?: string[], ready?: boolean, - slow?: boolean + slow?: boolean, + todayIndex: number }>(); const { snapshotDuration } = useSnapshot() @@ -34,9 +35,9 @@ const uTooltipText = computed(() => {
-
+
{{ value }} -
+
{{ avg }}
{{ text }}
@@ -58,7 +59,7 @@ const uTooltipText = computed(() => {
-
diff --git a/dashboard/components/dashboard/CountCardOld.vue b/dashboard/components/dashboard/CountCardOld.vue deleted file mode 100644 index d7c9ed7..0000000 --- a/dashboard/components/dashboard/CountCardOld.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - \ No newline at end of file diff --git a/dashboard/components/dashboard/EmbedChartCard.vue b/dashboard/components/dashboard/EmbedChartCard.vue index c3f50db..ce6b204 100644 --- a/dashboard/components/dashboard/EmbedChartCard.vue +++ b/dashboard/components/dashboard/EmbedChartCard.vue @@ -7,8 +7,10 @@ const props = defineProps<{ data: any[], labels: string[] color: string, + todayIndex: number }>(); + const chartOptions = ref>({ responsive: true, maintainAspectRatio: false, @@ -48,10 +50,22 @@ const chartData = ref>({ data: props.data, backgroundColor: [props.color + '77'], borderColor: props.color, - borderWidth: 4, - fill: true, - tension: 0.45, - pointRadius: 0 + borderWidth: 2, + fill: false, + tension: 0.35, + pointRadius: 0, + segment: { + borderColor(ctx, options) { + if (!props.todayIndex || props.todayIndex == -1) return props.color; + if (ctx.p1DataIndex >= props.todayIndex) return props.color + '00'; + return props.color; + }, + borderDash(ctx, options) { + if (!props.todayIndex || props.todayIndex == -1) return undefined; + if (ctx.p1DataIndex == props.todayIndex -1) return [2, 4]; + return undefined; + }, + }, }, ], }); diff --git a/dashboard/components/dashboard/TopCards.vue b/dashboard/components/dashboard/TopCards.vue index 62984d4..c8b77ff 100644 --- a/dashboard/components/dashboard/TopCards.vue +++ b/dashboard/components/dashboard/TopCards.vue @@ -8,8 +8,7 @@ const { snapshot, safeSnapshotDates, snapshotDuration } = useSnapshot() const chartSlice = computed(() => { if (snapshotDuration.value <= 3) return 'hour' as Slice; - if (snapshotDuration.value <= 3 * 7) return 'day' as Slice; - if (snapshotDuration.value <= 3 * 30) return 'week' as Slice; + if (snapshotDuration.value <= 32) return 'day' as Slice; return 'month' as Slice; }); @@ -31,7 +30,7 @@ function transformResponse(input: { _id: string, count: number }[]) { const trend = Math.max(Math.min(diffPercent, 99), -99); - return { data, labels, trend } + return { data, labels, trend, input } } @@ -92,6 +91,11 @@ const avgSessionDuration = computed(() => { return `${hours > 0 ? hours + 'h ' : ''}${minutes}m ${seconds.toFixed()}s` }); +const todayIndex = computed(()=>{ + if (!visitsData.data.value) return -1; + return visitsData.data.value.input.findIndex(e => new Date(e._id).getTime() > (Date.now() - new Date().getTimezoneOffset() * 1000 * 60)); +}) + @@ -99,26 +103,26 @@ const avgSessionDuration = computed(() => {