diff --git a/src/frontend/config/api.ts b/src/frontend/config/api.ts index 76878f0..78b1235 100644 --- a/src/frontend/config/api.ts +++ b/src/frontend/config/api.ts @@ -32,6 +32,7 @@ export const API_URLS = { if (dateTo) params.set('dateTo', dateTo) 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}`, getGridOverview: () => `${DESA_PLUS_PROXY}/api/monitoring/grid-overview`, getDailyActivity: (range: 7 | 30 | 90 = 7) => `${DESA_PLUS_PROXY}/api/monitoring/daily-activity?range=${range}`, getComparisonActivity: (range: 7 | 30 | 90 = 7) => `${DESA_PLUS_PROXY}/api/monitoring/comparison-activity?range=${range}`, diff --git a/src/frontend/routes/apps.$appId.index.tsx b/src/frontend/routes/apps.$appId.index.tsx index e5b3b40..b3d98c3 100644 --- a/src/frontend/routes/apps.$appId.index.tsx +++ b/src/frontend/routes/apps.$appId.index.tsx @@ -4,10 +4,15 @@ import { SummaryCard } from '@/frontend/components/SummaryCard' import { useSession } from '@/frontend/hooks/useAuth' import { ActionIcon, + Anchor, Badge, Button, + Collapse, + Divider, Group, Modal, + Paper, + SegmentedControl, SimpleGrid, Stack, Switch, @@ -25,6 +30,8 @@ import { TbActivity, TbAlertTriangle, TbBuildingCommunity, + TbChevronDown, + TbChevronUp, TbRefresh, TbVersions, } from 'react-icons/tb' @@ -54,12 +61,15 @@ function AppOverviewPage() { const [dailyRange, setDailyRange] = useState<7 | 30 | 90>(7) const [comparisonRange, setComparisonRange] = useState<7 | 30 | 90>(7) + const [staleDays, setStaleDays] = useState<7 | 14 | 30>(7) + const [staleExpanded, { toggle: toggleStale }] = useDisclosure(false) const { data: gridRes, isLoading: gridLoading, mutate: mutateGrid } = useSWR(isDesaPlus ? API_URLS.getGridOverview() : null, fetcher) const { data: dailyRes, isLoading: dailyLoading, mutate: mutateDaily } = useSWR(isDesaPlus ? API_URLS.getDailyActivity(dailyRange) : null, fetcher) const { data: comparisonRes, isLoading: comparisonLoading, mutate: mutateComparison } = useSWR(isDesaPlus ? API_URLS.getComparisonActivity(comparisonRange) : null, fetcher) const { data: appData, isLoading: appLoading } = useSWR(`/api/apps/${appId}`, fetcher) + const { data: staleRes } = useSWR(isDesaPlus ? API_URLS.getStaleVillages(staleDays) : null, fetcher) const grid = gridRes?.data const dailyData = dailyRes?.data || [] @@ -248,6 +258,62 @@ function AppOverviewPage() { /> + {isDesaPlus && staleRes?.data?.count > 0 && ( + + + + + + {staleRes.data.count} desa tidak ada aktivitas dalam {staleDays} hari terakhir + + + + setStaleDays(Number(v) as 7 | 14 | 30)} + data={[ + { label: '7H', value: '7' }, + { label: '14H', value: '14' }, + { label: '30H', value: '30' }, + ]} + /> + + {staleExpanded ? : } + + + + + + + + {staleRes.data.villages.map((v: { id: string; name: string; daysSince: number | null }) => ( + + navigate({ to: `/apps/${appId}/villages/${v.id}` })} + style={{ cursor: 'pointer' }} + > + {v.name} + + + {v.daysSince === null ? 'Belum pernah ada aktivitas' : `${v.daysSince} hari lalu`} + + + ))} + + + + )} + Analytics