diff --git a/package.json b/package.json
index 7b8112a5..9be8cd09 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "desa-darmasaba",
- "version": "0.1.40",
+ "version": "0.1.41",
"private": true,
"scripts": {
"dev": "next dev",
diff --git a/src/app/admin/(dashboard)/_state/ekonomi/umkm/umkm.ts b/src/app/admin/(dashboard)/_state/ekonomi/umkm/umkm.ts
index 2f8f23a1..0f785224 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/umkm/umkm.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/umkm/umkm.ts
@@ -493,23 +493,31 @@ export const umkmState = proxy({
summary: { data: null as any, loading: false },
topProduk: { data: [] as any[], loading: false },
detail: { data: [] as any[], loading: false },
- async loadAll(periode = "") {
+ mode: "month" as "week" | "month",
+ async loadAll(periode = "", mode?: "week" | "month", kategoriId = "", umkmId = "") {
const p = periode || `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`;
+ const m = mode ?? this.mode;
+ const modeParam = m === "week" ? "&mode=week" : "";
+ const detailFilter = [
+ kategoriId ? `&kategoriId=${kategoriId}` : "",
+ umkmId ? `&umkmId=${umkmId}` : "",
+ ].join("");
this.kpi.loading = true;
this.summary.loading = true;
this.topProduk.loading = true;
this.detail.loading = true;
try {
const [kpi, sum, top, det] = await Promise.all([
- fetch(`/api/ekonomi/umkm/dashboard/kpi?periode=${p}`).then(r => r.json()),
- fetch(`/api/ekonomi/umkm/dashboard/ringkasan-penjualan?periode=${p}`).then(r => r.json()),
- fetch(`/api/ekonomi/umkm/dashboard/top-produk?periode=${p}`).then(r => r.json()),
- fetch(`/api/ekonomi/umkm/dashboard/detail-penjualan?periode=${p}`).then(r => r.json())
+ fetch(`/api/ekonomi/umkm/dashboard/kpi?periode=${p}${modeParam}`).then(r => r.json()),
+ fetch(`/api/ekonomi/umkm/dashboard/ringkasan-penjualan?periode=${p}${modeParam}`).then(r => r.json()),
+ fetch(`/api/ekonomi/umkm/dashboard/top-produk?periode=${p}${modeParam}`).then(r => r.json()),
+ fetch(`/api/ekonomi/umkm/dashboard/detail-penjualan?periode=${p}${modeParam}${detailFilter}`).then(r => r.json()),
]);
if (kpi.success) this.kpi.data = kpi.data;
if (sum.success) this.summary.data = sum.data;
if (top.success) this.topProduk.data = top.data;
if (det.success) this.detail.data = det.data;
+ this.mode = m;
} catch (e) { console.error(e); } finally {
this.kpi.loading = false;
this.summary.loading = false;
diff --git a/src/app/admin/(dashboard)/ekonomi/umkm/dashboard/page.tsx b/src/app/admin/(dashboard)/ekonomi/umkm/dashboard/page.tsx
index 702c6c7e..240dc3d0 100644
--- a/src/app/admin/(dashboard)/ekonomi/umkm/dashboard/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/umkm/dashboard/page.tsx
@@ -1,10 +1,12 @@
'use client'
import colors from '@/con/colors';
import {
+ Badge,
Box,
Card,
Grid,
Group,
+ Select,
SimpleGrid,
Skeleton,
Stack,
@@ -16,10 +18,11 @@ import {
TableTr,
Text,
Title,
- Badge
+ SegmentedControl,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowUpRight, IconArrowDownRight, IconMinus } from '@tabler/icons-react';
+import { useState, useEffect } from 'react';
import { useProxy } from 'valtio/utils';
import { Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import umkmState from '../../../_state/ekonomi/umkm/umkm';
@@ -27,10 +30,25 @@ import umkmState from '../../../_state/ekonomi/umkm/umkm';
function UmkmDashboard() {
const state = useProxy(umkmState.dashboard);
+ const [kategoriId, setKategoriId] = useState("");
+ const [umkmId, setUmkmId] = useState("");
+ const [kategoriList, setKategoriList] = useState<{ value: string; label: string }[]>([]);
+ const [umkmList, setUmkmList] = useState<{ value: string; label: string }[]>([]);
+
useShallowEffect(() => {
state.loadAll();
+ fetch('/api/ekonomi/kategoriproduk/find-many-all').then(r => r.json()).then(res => {
+ if (res.success) setKategoriList(res.data.map((k: any) => ({ value: k.id, label: k.nama })));
+ });
+ fetch('/api/ekonomi/umkm/find-many-all').then(r => r.json()).then(res => {
+ if (res.success) setUmkmList(res.data.map((u: any) => ({ value: u.id, label: u.nama })));
+ });
}, []);
+ useEffect(() => {
+ if (state.kpi.data) state.loadAll("", state.mode, kategoriId, umkmId);
+ }, [kategoriId, umkmId]);
+
if (state.kpi.loading || !state.kpi.data) {
return ;
}
@@ -42,15 +60,31 @@ function UmkmDashboard() {
return (
+
+ Update Penjualan Produk
+ state.loadAll("", v as "week" | "month", kategoriId, umkmId)}
+ data={[
+ { label: 'Minggu ini', value: 'week' },
+ { label: 'Bulan ini', value: 'month' },
+ ]}
+ />
+
+
-
+
+
-
-
@@ -59,7 +93,7 @@ function UmkmDashboard() {
Grafik Penjualan per Produk
- ({
+ ({
name: item.namaProduk,
penjualan: item.penjualanBulanIni
}))}>
@@ -76,16 +110,21 @@ function UmkmDashboard() {
-
- Top 3 Produk
+
+ Top 3 Produk Terlaris
- {topProduk.map((item, i) => (
-
+ {topProduk.map((item: any, i: number) => (
+
- {item.namaProduk}
+ {item.namaProduk}
{item.namaUmkm}
+
+ Rp {item.totalPenjualan.toLocaleString()} ยท {item.jumlahTerjual} terjual
+
- Rp {item.totalPenjualan.toLocaleString()}
+ = 0 ? 'teal' : 'red'} variant="light" size="sm">
+ {item.growth >= 0 ? '+' : ''}{item.growth}%
+
))}
@@ -94,24 +133,62 @@ function UmkmDashboard() {
- Detail Penjualan & Stok
+
+ Detail Penjualan Produk
+
+
+
Produk
- Penjualan
+ Penjualan Bulan Ini
+ Bulan Lalu
Trend
+ Volume
Stok
Status
- {detail.map((item, i) => (
+ {detail.map((item: any, i: number) => (
{item.namaProduk}
Rp {item.penjualanBulanIni.toLocaleString()}
- {renderTrend(item.trend)}
+ Rp {item.penjualanBulanLalu.toLocaleString()}
+
+
+ {renderTrend(item.trend)}
+ {item.trendPersen !== 0 && (
+
+ {item.trendPersen > 0 ? '+' : ''}{item.trendPersen}%
+
+ )}
+
+
+ {item.volume}
{item.stok}