wip: charts.js experimental work

This commit is contained in:
Hayden 2022-12-05 12:38:08 -09:00
parent 5bbb969763
commit 9e9bcdde4f
No known key found for this signature in database
GPG key ID: 17CF79474E257545
5 changed files with 307 additions and 2 deletions

View file

@ -0,0 +1,94 @@
<template>
<DoughnutChart
:chart-options="chartOptions"
:chart-data="chartData"
:chart-id="chartId"
:dataset-id-key="datasetIdKey"
:css-classes="cssClasses"
:styles="styles"
:width="width"
:height="height"
/>
</template>
<script lang="ts">
import { Doughnut as DoughnutChart } from "vue-chartjs";
import { Chart as ChartJS, Title, Tooltip, Legend, CategoryScale, LinearScale, ArcElement } from "chart.js";
import { TChartData } from "vue-chartjs/dist/types";
ChartJS.register(Title, Tooltip, Legend, CategoryScale, LinearScale, ArcElement);
export default defineComponent({
name: "BarChart",
components: {
DoughnutChart,
},
props: {
chartId: {
type: String,
default: "bar-chart",
},
datasetIdKey: {
type: String,
default: "label",
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: "",
type: String,
},
styles: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object as () => TChartData<"doughnut", number[], unknown>,
default: () => {
return {
labels: ["Red", "Blue", "Yellow"],
datasets: [
{
label: "My First Dataset",
data: [300, 50, 100],
backgroundColor: ["rgb(255, 99, 132)", "rgb(54, 162, 235)", "rgb(255, 205, 86)"],
hoverOffset: 4,
},
],
};
},
},
},
data() {
return {
chartOptions: {
responsive: false,
// Legend on the left
plugins: {
legend: {
position: "bottom",
},
// Display percentage
// tooltip: {
// callbacks: {
// label: context => {
// const label = context.dataset?.label || "";
// const value = context.parsed.y;
// return `${label}: ${value}%`;
// },
// },
// },
},
},
};
},
});
</script>

View file

@ -0,0 +1,96 @@
<template>
<LineChart
:chart-options="chartOptions"
:chart-data="chartData"
:chart-id="chartId"
:dataset-id-key="datasetIdKey"
:css-classes="cssClasses"
:styles="styles"
:width="width"
:height="height"
/>
</template>
<script lang="ts">
import { Line as LineChart } from "vue-chartjs";
import {
Chart as ChartJS,
PointElement,
Title,
Tooltip,
Legend,
CategoryScale,
LinearScale,
LineElement,
} from "chart.js";
import { TChartData } from "vue-chartjs/dist/types";
ChartJS.register(Title, Tooltip, Legend, CategoryScale, LinearScale, PointElement, LineElement);
export default defineComponent({
name: "BarChart",
components: {
LineChart,
},
props: {
chartId: {
type: String,
default: "bar-chart",
},
datasetIdKey: {
type: String,
default: "label",
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: "",
type: String,
},
styles: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object as () => TChartData<"line", number[], unknown>,
default: () => {
return {
labels: ["January", "February", "March"],
datasets: [{ data: [40, 20, 12] }],
};
},
},
},
data() {
return {
chartOptions: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
display: true,
},
},
elements: {
line: {
borderWidth: 5,
},
point: {
radius: 4,
},
},
},
};
},
});
</script>

View file

@ -0,0 +1,38 @@
import { ComputedRef } from "vue";
type ColorType = "hsla";
export type VarOptions = {
type: ColorType;
transparency?: number;
apply?: (value: string) => string;
};
export function useCssVar(name: string, options?: VarOptions): ComputedRef<string> {
if (!options) {
options = {
type: "hsla",
transparency: 1,
apply: null,
};
}
switch (options.type) {
case "hsla": {
return computed(() => {
if (!document) {
return "";
}
let val = getComputedStyle(document.documentElement).getPropertyValue(name);
val = val.trim().split(" ").join(", ");
if (options.transparency) {
val += `, ${options.transparency}`;
}
return `hsla(${val})`;
});
}
}
}

View file

@ -36,12 +36,14 @@
"@tailwindcss/typography": "^0.5.4",
"@vueuse/nuxt": "^9.1.1",
"autoprefixer": "^10.4.8",
"chart.js": "^4.0.1",
"daisyui": "^2.24.0",
"dompurify": "^2.4.1",
"markdown-it": "^13.0.1",
"pinia": "^2.0.21",
"postcss": "^8.4.16",
"tailwindcss": "^3.1.8",
"vue": "^3.2.38"
"vue": "^3.2.38",
"vue-chartjs": "^4.1.2"
}
}

View file

@ -6,7 +6,6 @@
definePageMeta({
middleware: ["auth"],
});
useHead({
title: "Homebox | Home",
});
@ -87,6 +86,63 @@
eventBus.emit(EventTypes.ClearStores);
}
const { data: timeseries } = useAsyncData(async () => {
const { data } = await api.stats.totalPriceOverTime();
return data;
});
const primary = useCssVar("--p");
const secondary = useCssVar("--s");
const accent = useCssVar("--a");
const neutral = useCssVar("--n");
const base = useCssVar("--b");
const chartData = computed(() => {
let start = timeseries.value?.valueAtStart;
return {
labels: timeseries?.value.entries.map(t => new Date(t.date).toDateString()) || [],
datasets: [
{
label: "Purchase Price",
data:
timeseries.value?.entries.map(t => {
start += t.value;
return start;
}) || [],
backgroundColor: primary.value,
borderColor: primary.value,
},
],
};
});
const { data: donutSeries } = useAsyncData(async () => {
const { data } = await api.stats.locations();
return data;
});
const donutData = computed(() => {
return {
labels: donutSeries.value?.map(l => l.name) || [],
datasets: [
{
label: "Value",
data: donutSeries.value?.map(l => l.total) || [],
backgroundColor: [primary.value, secondary.value, neutral.value],
borderColor: [primary.value, secondary.value, neutral.value],
hoverOffset: 4,
},
],
};
});
const refDonutEl = ref<HTMLDivElement>(null);
const donutElWidth = computed(() => {
return refDonutEl.value?.clientWidth || 0;
});
</script>
<template>
@ -148,6 +204,25 @@
</BaseCard>
</section>
<section v-if="timeseries" class="grid grid-cols-6 gap-6">
<BaseCard class="col-span-4">
<template #title>Total Asset Value {{ fmtCurrency(timeseries.valueAtEnd) }}</template>
<div class="p-6 pt-0">
<ClientOnly>
<ChartLine chart-id="asd" :height="200" :chart-data="chartData" />
</ClientOnly>
</div>
</BaseCard>
<BaseCard class="col-span-2">
<template #title> Asset By Location {{ fmtCurrency(timeseries.valueAtEnd) }}</template>
<div ref="refDonutEl" class="grid place-content-center h-full">
<ClientOnly>
<ChartDonut chart-id="donut" :width="donutElWidth - 50" :height="300" :chart-data="donutData" />
</ClientOnly>
</div>
</BaseCard>
</section>
<section>
<BaseSectionHeader class="mb-5"> Storage Locations </BaseSectionHeader>
<div class="grid grid-cols-1 sm:grid-cols-2 card md:grid-cols-3 gap-4">