feat: add responsive behavior to charts and improve layout in Analysis component
This commit is contained in:
parent
f82b56d540
commit
ed2cb2e53d
@ -14,7 +14,7 @@ import {
|
|||||||
VisTooltip,
|
VisTooltip,
|
||||||
VisXYContainer,
|
VisXYContainer,
|
||||||
} from '@unovis/vue';
|
} from '@unovis/vue';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import StatCard from '@/components/card/StatCard.vue';
|
import StatCard from '@/components/card/StatCard.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@ -367,6 +367,21 @@ const peakHour = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const windowWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 1024);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const onResize = () => { windowWidth.value = window.innerWidth; };
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
onBeforeUnmount(() => window.removeEventListener('resize', onResize));
|
||||||
|
});
|
||||||
|
|
||||||
|
const busyHourTickValues = computed(() => {
|
||||||
|
const all = props.busyHours.map((_, i) => i);
|
||||||
|
if (windowWidth.value < 640) return all.filter((_, i) => i % 4 === 0);
|
||||||
|
if (windowWidth.value < 1024) return all.filter((_, i) => i % 2 === 0);
|
||||||
|
return all;
|
||||||
|
});
|
||||||
|
|
||||||
// Top 5 Chart configs
|
// Top 5 Chart configs
|
||||||
type SupplierData = { name: string; amount: number };
|
type SupplierData = { name: string; amount: number };
|
||||||
type CustomerData = { name: string; amount: number };
|
type CustomerData = { name: string; amount: number };
|
||||||
@ -710,7 +725,7 @@ watch([startDate, endDate], () => {
|
|||||||
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||||
<CardTitle>Pendapatan</CardTitle>
|
<CardTitle>Pendapatan</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex flex-col sm:flex-row">
|
||||||
<div v-for="chart in visibleRevenueCharts" :key="chart"
|
<div v-for="chart in visibleRevenueCharts" :key="chart"
|
||||||
class="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l sm:border-t-0 sm:border-l sm:px-8 sm:py-6">
|
class="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l sm:border-t-0 sm:border-l sm:px-8 sm:py-6">
|
||||||
<span class="text-xs text-muted-foreground">
|
<span class="text-xs text-muted-foreground">
|
||||||
@ -765,7 +780,7 @@ watch([startDate, endDate], () => {
|
|||||||
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||||
<CardTitle>Pengeluaran</CardTitle>
|
<CardTitle>Pengeluaran</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex flex-col sm:flex-row">
|
||||||
<div v-for="chart in visibleExpenseCharts" :key="chart"
|
<div v-for="chart in visibleExpenseCharts" :key="chart"
|
||||||
class="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l sm:border-t-0 sm:border-l sm:px-8 sm:py-6">
|
class="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l sm:border-t-0 sm:border-l sm:px-8 sm:py-6">
|
||||||
<span class="text-xs text-muted-foreground">
|
<span class="text-xs text-muted-foreground">
|
||||||
@ -843,7 +858,7 @@ watch([startDate, endDate], () => {
|
|||||||
:color="busyHoursChartConfig.orders.color" :bar-padding="0.1" :rounded-corners="4" />
|
:color="busyHoursChartConfig.orders.color" :bar-padding="0.1" :rounded-corners="4" />
|
||||||
<VisAxis type="x" :x="(_d: BusyHourData, i: number) => i" :tick-line="false"
|
<VisAxis type="x" :x="(_d: BusyHourData, i: number) => i" :tick-line="false"
|
||||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) => busyHours[d]?.hour ?? ''
|
:domain-line="false" :grid-line="false" :tick-format="(d: number) => busyHours[d]?.hour ?? ''
|
||||||
" :tick-values="busyHours.map((_, i) => i)" />
|
" :tick-values="busyHourTickValues" />
|
||||||
<VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="(d: number) => Math.round(d).toString()
|
<VisAxis type="y" :num-ticks="4" :tick-line="false" :domain-line="false" :tick-format="(d: number) => Math.round(d).toString()
|
||||||
" />
|
" />
|
||||||
<VisTooltip :triggers="{ [barSelector]: busyHoursTooltip }" class-name="custom-tooltip" />
|
<VisTooltip :triggers="{ [barSelector]: busyHoursTooltip }" class-name="custom-tooltip" />
|
||||||
@ -906,7 +921,7 @@ watch([startDate, endDate], () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Top 5 Charts -->
|
<!-- Top 5 Charts -->
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
<Card v-if="can('analysis.top_suppliers')">
|
<Card v-if="can('analysis.top_suppliers')">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle class="text-base">Top 5 Supplier</CardTitle>
|
<CardTitle class="text-base">Top 5 Supplier</CardTitle>
|
||||||
@ -914,7 +929,7 @@ watch([startDate, endDate], () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ChartContainer v-if="supplierBarData.length > 0" :config="supplierChartConfig"
|
<ChartContainer v-if="supplierBarData.length > 0" :config="supplierChartConfig"
|
||||||
class="min-h-[250px] w-full">
|
class="aspect-auto h-[250px] w-full">
|
||||||
<VisXYContainer :data="supplierBarData" :y-domain="[0, undefined]">
|
<VisXYContainer :data="supplierBarData" :y-domain="[0, undefined]">
|
||||||
<VisGroupedBar :x="(_d: SupplierData, i: number) => i"
|
<VisGroupedBar :x="(_d: SupplierData, i: number) => i"
|
||||||
:y="(d: SupplierData) => d.amount" :color="supplierChartConfig.amount.color"
|
:y="(d: SupplierData) => d.amount" :color="supplierChartConfig.amount.color"
|
||||||
@ -945,7 +960,7 @@ watch([startDate, endDate], () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ChartContainer v-if="customerBarData.length > 0" :config="customerChartConfig"
|
<ChartContainer v-if="customerBarData.length > 0" :config="customerChartConfig"
|
||||||
class="min-h-[250px] w-full">
|
class="aspect-auto h-[250px] w-full">
|
||||||
<VisXYContainer :data="customerBarData" :y-domain="[0, undefined]">
|
<VisXYContainer :data="customerBarData" :y-domain="[0, undefined]">
|
||||||
<VisGroupedBar :x="(_d: CustomerData, i: number) => i"
|
<VisGroupedBar :x="(_d: CustomerData, i: number) => i"
|
||||||
:y="(d: CustomerData) => d.amount" :color="customerChartConfig.amount.color"
|
:y="(d: CustomerData) => d.amount" :color="customerChartConfig.amount.color"
|
||||||
@ -976,7 +991,7 @@ watch([startDate, endDate], () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ChartContainer v-if="productBarData.length > 0" :config="productChartConfig"
|
<ChartContainer v-if="productBarData.length > 0" :config="productChartConfig"
|
||||||
class="min-h-[250px] w-full">
|
class="aspect-auto h-[250px] w-full">
|
||||||
<VisXYContainer :data="productBarData" :y-domain="[0, undefined]">
|
<VisXYContainer :data="productBarData" :y-domain="[0, undefined]">
|
||||||
<VisGroupedBar :x="(_d: ProductData, i: number) => i" :y="(d: ProductData) => d.qty"
|
<VisGroupedBar :x="(_d: ProductData, i: number) => i" :y="(d: ProductData) => d.qty"
|
||||||
:color="productChartConfig.qty.color" :rounded-corners="4" bar-padding="0.1" />
|
:color="productChartConfig.qty.color" :rounded-corners="4" bar-padding="0.1" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user