feat: tambah peak hours chart di halaman village detail

This commit is contained in:
2026-05-28 14:47:16 +08:00
parent 0e2c97df47
commit 1f18001c86
2 changed files with 76 additions and 1 deletions

View File

@@ -33,6 +33,11 @@ export const API_URLS = {
return `${DESA_PLUS_PROXY}/api/monitoring/log-all-villages?${params}` return `${DESA_PLUS_PROXY}/api/monitoring/log-all-villages?${params}`
}, },
getStaleVillages: (days: 7 | 14 | 30 = 7) => `${DESA_PLUS_PROXY}/api/monitoring/stale-villages?days=${days}`, getStaleVillages: (days: 7 | 14 | 30 = 7) => `${DESA_PLUS_PROXY}/api/monitoring/stale-villages?days=${days}`,
getPeakHours: (idVillage?: string) => {
const params = new URLSearchParams()
if (idVillage) params.set('idVillage', idVillage)
return `${DESA_PLUS_PROXY}/api/monitoring/peak-hours?${params}`
},
getInactiveUsers: (days: 7 | 14 | 30 = 7, idVillage?: string, page = 1) => { getInactiveUsers: (days: 7 | 14 | 30 = 7, idVillage?: string, page = 1) => {
const params = new URLSearchParams({ days: String(days), page: String(page) }) const params = new URLSearchParams({ days: String(days), page: String(page) })
if (idVillage) params.set('idVillage', idVillage) if (idVillage) params.set('idVillage', idVillage)

View File

@@ -1,4 +1,4 @@
import { AreaChart } from '@mantine/charts' import { AreaChart, BarChart } from '@mantine/charts'
import { import {
Badge, Badge,
Box, Box,
@@ -194,6 +194,73 @@ function ActivityChart({ villageId }: { villageId: string }) {
) )
} }
// ── Peak Hours Chart ──────────────────────────────────────────────────────────
function PeakHoursChart({ villageId }: { villageId: string }) {
const { data: response, isLoading } = useSWR(API_URLS.getPeakHours(villageId), fetcher)
const hours: { hour: number; label: string; count: number }[] = response?.data?.hours || []
const peak: { label: string; count: number } | null = response?.data?.peak || null
return (
<Paper withBorder radius="xl" p="lg">
<Group justify="space-between" mb="lg" wrap="wrap" gap="sm">
<Group gap="xs">
<ThemeIcon size={28} radius="md" variant="light" color="violet">
<TbClock size={14} />
</ThemeIcon>
<Stack gap={0}>
<Text fw={700} size="sm">Peak Activity Hours</Text>
<Text size="xs" c="dimmed">
{peak && peak.count > 0
? `Busiest hour: ${peak.label} (${peak.count.toLocaleString()} activities)`
: 'No activity data'}
</Text>
</Stack>
</Group>
</Group>
{isLoading ? (
<Stack h={200} align="center" justify="center">
<Loader type="dots" />
</Stack>
) : (
<BarChart
h={200}
data={hours}
dataKey="label"
series={[{ name: 'count', color: 'violet.5' }]}
withTooltip
withXAxis
withYAxis={false}
tickLine="none"
gridAxis="none"
barProps={{ radius: 4 }}
tooltipProps={{
content: ({ active, payload, label }: any) => {
if (!active || !payload?.length) return null
return (
<div style={{
backgroundColor: '#1A1B1E',
padding: '8px 12px',
borderRadius: '6px',
border: '1px solid #373A40',
boxShadow: '0 4px 12px rgba(0,0,0,0.5)',
whiteSpace: 'nowrap',
}}>
<div style={{ fontSize: '12px', fontWeight: 600, color: '#C1C2C5', marginBottom: '4px' }}>{label}</div>
<div style={{ fontSize: '11px', color: '#9775FA' }}>
Activities: <span style={{ fontWeight: 700 }}>{payload[0].value}</span>
</div>
</div>
)
},
}}
/>
)}
</Paper>
)
}
// ── Recent Activity Logs ────────────────────────────────────────────────────── // ── Recent Activity Logs ──────────────────────────────────────────────────────
function RecentVillageLogs({ villageId }: { villageId: string }) { function RecentVillageLogs({ villageId }: { villageId: string }) {
@@ -563,6 +630,9 @@ function VillageDetailPage() {
{/* ── Activity Chart ── */} {/* ── Activity Chart ── */}
<ActivityChart villageId={villageId} /> <ActivityChart villageId={villageId} />
{/* ── Peak Hours Chart ── */}
<PeakHoursChart villageId={villageId} />
{/* ── Recent Logs + System Info ── */} {/* ── Recent Logs + System Info ── */}
<Grid gutter="md" align="flex-start"> <Grid gutter="md" align="flex-start">
<Grid.Col span={{ base: 12, md: 8 }}> <Grid.Col span={{ base: 12, md: 8 }}>