feat: enhance permissions and layout in Analysis component for better role-based display
This commit is contained in:
parent
b7f8bf7e0f
commit
1e8fd5b595
@ -132,7 +132,8 @@ public function permissions(): array
|
||||
Permission::ANALYSIS_EXPENSE,
|
||||
Permission::ANALYSIS_BUSY_HOURS,
|
||||
Permission::ANALYSIS_PROFIT_ORDERS,
|
||||
Permission::ANALYSIS_TOP_SUPPLIERS,
|
||||
Permission::ANALYSIS_PROFIT_HPP,
|
||||
Permission::ANALYSIS_PROFIT_GROSS,
|
||||
Permission::ANALYSIS_TOP_CUSTOMERS,
|
||||
Permission::ANALYSIS_TOP_PRODUCTS,
|
||||
Permission::ANALYSIS_MARKETING_SALES,
|
||||
@ -332,6 +333,7 @@ public function permissions(): array
|
||||
Permission::ANALYSIS_REVENUE,
|
||||
Permission::ANALYSIS_PROFIT_ORDERS,
|
||||
Permission::ANALYSIS_TOP_PRODUCTS,
|
||||
Permission::ANALYSIS_TOP_CUSTOMERS,
|
||||
|
||||
Permission::ATTENDANCES_VIEW,
|
||||
Permission::ATTENDANCES_CREATE,
|
||||
|
||||
@ -382,6 +382,49 @@ const busyHourTickValues = computed(() => {
|
||||
return all;
|
||||
});
|
||||
|
||||
const sectionOrder = computed(() => {
|
||||
if (hasAnyRole(['owner', 'developer'])) {
|
||||
return {
|
||||
statCards: 1,
|
||||
revenue: 5,
|
||||
expense: 6,
|
||||
profitGross: 7,
|
||||
totalOrder: 8,
|
||||
marketingSales: 9,
|
||||
topSuppliers: 10,
|
||||
topProducts: 11,
|
||||
topCustomers: 12,
|
||||
busyHours: 13,
|
||||
};
|
||||
}
|
||||
|
||||
if (hasAnyRole(['admin_toko', 'direktur'])) {
|
||||
return {
|
||||
statCards: 1,
|
||||
revenue: 4,
|
||||
expense: 5,
|
||||
profitGross: 6,
|
||||
totalOrder: 7,
|
||||
marketingSales: 8,
|
||||
topProducts: 9,
|
||||
topCustomers: 10,
|
||||
busyHours: 11,
|
||||
};
|
||||
}
|
||||
|
||||
if (hasRole('marketing')) {
|
||||
return {
|
||||
statCards: 1,
|
||||
revenue: 2,
|
||||
totalOrder: 3,
|
||||
topProducts: 4,
|
||||
topCustomers: 5,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
// Top 5 Chart configs
|
||||
type SupplierData = { name: string; amount: number };
|
||||
type CustomerData = { name: string; amount: number };
|
||||
@ -609,8 +652,8 @@ watch([startDate, endDate], () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kehadiran & Kas Toko -->
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<!-- Stat Cards -->
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4" :style="{ order: sectionOrder.statCards ?? 99 }">
|
||||
<StatCard v-if="can('analysis.attendance') && isManager" title="Kehadiran" :icon="UserCheck"
|
||||
main-label="Total Karyawan" :main-value="attendance.total_employees"
|
||||
:sub-label="attendance.percentage + '% hadir'" :items="[
|
||||
@ -720,7 +763,7 @@ watch([startDate, endDate], () => {
|
||||
</div>
|
||||
|
||||
<!-- Revenue Chart -->
|
||||
<Card v-if="can('analysis.revenue')" class="py-4 sm:py-0">
|
||||
<Card v-if="can('analysis.revenue')" class="py-4 sm:py-0" :style="{ order: sectionOrder.revenue ?? 99 }">
|
||||
<CardHeader class="flex flex-col items-stretch border-b p-0! sm:flex-row">
|
||||
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||
<CardTitle>Pendapatan</CardTitle>
|
||||
@ -775,7 +818,7 @@ watch([startDate, endDate], () => {
|
||||
</Card>
|
||||
|
||||
<!-- Expense Chart -->
|
||||
<Card v-if="can('analysis.expense')" class="py-4 sm:py-0">
|
||||
<Card v-if="can('analysis.expense')" class="py-4 sm:py-0" :style="{ order: sectionOrder.expense ?? 99 }">
|
||||
<CardHeader class="flex flex-col items-stretch border-b p-0! sm:flex-row">
|
||||
<div class="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||
<CardTitle>Pengeluaran</CardTitle>
|
||||
@ -830,191 +873,57 @@ watch([startDate, endDate], () => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Busy Hours Chart -->
|
||||
<Card v-if="can('analysis.busy_hours')">
|
||||
<CardHeader>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Jam Sibuk Toko</CardTitle>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Jam Tersibuk
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-primary">
|
||||
{{ peakHour.hour }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ peakHour.orders }} pesanan
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="busyHours.length > 0" :config="busyHoursChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="busyHours" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: BusyHourData, i: number) => i" :y="(d: BusyHourData) => d.orders"
|
||||
:color="busyHoursChartConfig.orders.color" :bar-padding="0.1" :rounded-corners="4" />
|
||||
<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 ?? ''
|
||||
" :tick-values="busyHourTickValues" />
|
||||
<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" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data pesanan
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Profit Metrics Cards -->
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<StatCard v-if="can('analysis.profit_orders')" title="Total Order" :icon="ShoppingCart"
|
||||
main-label="Pesanan Selesai" :main-value="profitMetrics.total_orders.toLocaleString('id-ID')
|
||||
" :items="[
|
||||
<!-- Laba Kotor -->
|
||||
<StatCard v-if="
|
||||
can('analysis.profit_gross') ||
|
||||
can('analysis.profit_hpp')
|
||||
" title="Laba Kotor" :icon="TrendingUp" main-label="Laba Kotor" :main-value="'Rp' + formatRupiah(profitMetrics.gross_profit)
|
||||
" :items="[
|
||||
{
|
||||
label: 'Pendapatan',
|
||||
value:
|
||||
'Rp' +
|
||||
formatRupiah(revenueSummary.total_revenue),
|
||||
},
|
||||
{
|
||||
label: 'HPP',
|
||||
value: 'Rp' + formatRupiah(profitMetrics.hpp),
|
||||
},
|
||||
...(!hasRole('cashier') ? [
|
||||
{
|
||||
label: 'Produk Terjual',
|
||||
value: profitMetrics.total_products_sold.toLocaleString(
|
||||
'id-ID',
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Item/Transaksi',
|
||||
value: profitMetrics.items_per_transaction,
|
||||
},
|
||||
{
|
||||
label: 'AOV',
|
||||
value: 'Rp' + formatRupiah(profitMetrics.aov),
|
||||
},
|
||||
]" />
|
||||
|
||||
<StatCard v-if="
|
||||
can('analysis.profit_gross') ||
|
||||
can('analysis.profit_hpp')
|
||||
" title="Laba Kotor" :icon="TrendingUp" main-label="Laba Kotor" :main-value="'Rp' + formatRupiah(profitMetrics.gross_profit)
|
||||
" :items="[
|
||||
{
|
||||
label: 'Pendapatan',
|
||||
label: 'Laba Bersih',
|
||||
value:
|
||||
'Rp' +
|
||||
formatRupiah(revenueSummary.total_revenue),
|
||||
formatRupiah(profitMetrics.net_profit) +
|
||||
' (' +
|
||||
profitMetrics.profit_margin +
|
||||
'%)',
|
||||
},
|
||||
{
|
||||
label: 'HPP',
|
||||
value: 'Rp' + formatRupiah(profitMetrics.hpp),
|
||||
},
|
||||
...(!hasRole('cashier') ? [
|
||||
{
|
||||
label: 'Laba Bersih',
|
||||
value:
|
||||
'Rp' +
|
||||
formatRupiah(profitMetrics.net_profit) +
|
||||
' (' +
|
||||
profitMetrics.profit_margin +
|
||||
'%)',
|
||||
},
|
||||
] : []),
|
||||
]" />
|
||||
</div>
|
||||
] : []),
|
||||
]" :style="{ order: sectionOrder.profitGross ?? 99 }" />
|
||||
|
||||
<!-- Top 5 Charts -->
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card v-if="can('analysis.top_suppliers')">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Supplier</CardTitle>
|
||||
<CardDescription>Berdasarkan total harga pembelian</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="supplierBarData.length > 0" :config="supplierChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="supplierBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: SupplierData, i: number) => i"
|
||||
:y="(d: SupplierData) => d.amount" :color="supplierChartConfig.amount.color"
|
||||
:rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: SupplierData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
supplierBarData[d]?.name ?? ''
|
||||
" :tick-values="supplierBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" :tick-format="(d: number) =>
|
||||
'Rp' + formatRupiahShort(d)
|
||||
" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: supplierTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data supplier
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card v-if="can('analysis.top_customers')">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Pelanggan</CardTitle>
|
||||
<CardDescription>Berdasarkan total nilai pesanan</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="customerBarData.length > 0" :config="customerChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="customerBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: CustomerData, i: number) => i"
|
||||
:y="(d: CustomerData) => d.amount" :color="customerChartConfig.amount.color"
|
||||
:rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: CustomerData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
customerBarData[d]?.name ?? ''
|
||||
" :tick-values="customerBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" :tick-format="(d: number) =>
|
||||
'Rp' + formatRupiahShort(d)
|
||||
" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: customerTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data pelanggan
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card v-if="can('analysis.top_products')">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Produk</CardTitle>
|
||||
<CardDescription>Berdasarkan jumlah terjual</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="productBarData.length > 0" :config="productChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="productBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: ProductData, i: number) => i" :y="(d: ProductData) => d.qty"
|
||||
:color="productChartConfig.qty.color" :rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: ProductData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
productBarData[d]?.name ?? ''
|
||||
" :tick-values="productBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: productTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data produk
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<!-- Total Order -->
|
||||
<StatCard v-if="can('analysis.profit_orders')" title="Total Order" :icon="ShoppingCart"
|
||||
main-label="Pesanan Selesai" :main-value="profitMetrics.total_orders.toLocaleString('id-ID')
|
||||
" :items="[
|
||||
{
|
||||
label: 'Produk Terjual',
|
||||
value: profitMetrics.total_products_sold.toLocaleString(
|
||||
'id-ID',
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Item/Transaksi',
|
||||
value: profitMetrics.items_per_transaction,
|
||||
},
|
||||
{
|
||||
label: 'Rata-rata/Transaksi',
|
||||
value: 'Rp' + formatRupiah(profitMetrics.aov),
|
||||
},
|
||||
]" :style="{ order: sectionOrder.totalOrder ?? 99 }" />
|
||||
|
||||
<!-- Marketing Sales Table -->
|
||||
<Card v-if="can('analysis.marketing_sales')">
|
||||
<Card v-if="can('analysis.marketing_sales')" :style="{ order: sectionOrder.marketingSales ?? 99 }">
|
||||
<CardHeader>
|
||||
<CardTitle>Penjualan Marketing</CardTitle>
|
||||
<CardDescription>Rekap penjualan per marketing</CardDescription>
|
||||
@ -1092,6 +1001,139 @@ watch([startDate, endDate], () => {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Top 5 Supplier -->
|
||||
<Card v-if="can('analysis.top_suppliers')" :style="{ order: sectionOrder.topSuppliers ?? 99 }">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Supplier</CardTitle>
|
||||
<CardDescription>Berdasarkan total harga pembelian</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="supplierBarData.length > 0" :config="supplierChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="supplierBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: SupplierData, i: number) => i"
|
||||
:y="(d: SupplierData) => d.amount" :color="supplierChartConfig.amount.color"
|
||||
:rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: SupplierData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
supplierBarData[d]?.name ?? ''
|
||||
" :tick-values="supplierBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" :tick-format="(d: number) =>
|
||||
'Rp' + formatRupiahShort(d)
|
||||
" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: supplierTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data supplier
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Top 5 Produk -->
|
||||
<Card v-if="can('analysis.top_products')" :style="{ order: sectionOrder.topProducts ?? 99 }">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Produk</CardTitle>
|
||||
<CardDescription>Berdasarkan jumlah terjual</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="productBarData.length > 0" :config="productChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="productBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: ProductData, i: number) => i" :y="(d: ProductData) => d.qty"
|
||||
:color="productChartConfig.qty.color" :rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: ProductData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
productBarData[d]?.name ?? ''
|
||||
" :tick-values="productBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: productTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data produk
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Top 5 Pelanggan -->
|
||||
<Card v-if="can('analysis.top_customers')" :style="{ order: sectionOrder.topCustomers ?? 99 }">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-base">Top 5 Pelanggan</CardTitle>
|
||||
<CardDescription>Berdasarkan total nilai pesanan</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="customerBarData.length > 0" :config="customerChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="customerBarData" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: CustomerData, i: number) => i"
|
||||
:y="(d: CustomerData) => d.amount" :color="customerChartConfig.amount.color"
|
||||
:rounded-corners="4" bar-padding="0.1" />
|
||||
<VisAxis type="x" :x="(_d: CustomerData, i: number) => i" :tick-line="false"
|
||||
:domain-line="false" :grid-line="false" :tick-format="(d: number) =>
|
||||
customerBarData[d]?.name ?? ''
|
||||
" :tick-values="customerBarData.map((_, i) => i)
|
||||
" />
|
||||
<VisAxis type="y" :num-ticks="3" :tick-line="false" :domain-line="false" :tick-format="(d: number) =>
|
||||
'Rp' + formatRupiahShort(d)
|
||||
" />
|
||||
<VisTooltip :triggers="{
|
||||
[barSelector]: customerTooltip,
|
||||
}" class-name="custom-tooltip" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data pelanggan
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Busy Hours Chart -->
|
||||
<Card v-if="can('analysis.busy_hours')" :style="{ order: sectionOrder.busyHours ?? 99 }">
|
||||
<CardHeader>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Jam Sibuk Toko</CardTitle>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Jam Tersibuk
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-primary">
|
||||
{{ peakHour.hour }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ peakHour.orders }} pesanan
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer v-if="busyHours.length > 0" :config="busyHoursChartConfig"
|
||||
class="aspect-auto h-[250px] w-full">
|
||||
<VisXYContainer :data="busyHours" :y-domain="[0, undefined]">
|
||||
<VisGroupedBar :x="(_d: BusyHourData, i: number) => i" :y="(d: BusyHourData) => d.orders"
|
||||
:color="busyHoursChartConfig.orders.color" :bar-padding="0.1" :rounded-corners="4" />
|
||||
<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 ?? ''
|
||||
" :tick-values="busyHourTickValues" />
|
||||
<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" />
|
||||
</VisXYContainer>
|
||||
</ChartContainer>
|
||||
<div v-else class="flex h-[250px] items-center justify-center text-muted-foreground">
|
||||
Belum ada data pesanan
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user