upd: connected api monitoring
Deskripsi: - overview page No Issues
This commit is contained in:
@@ -11,25 +11,12 @@ import {
|
||||
import { LineChart, BarChart } from '@mantine/charts'
|
||||
import { TbTimeline, TbChartBar, TbArrowUpRight } from 'react-icons/tb'
|
||||
|
||||
const activityData = [
|
||||
{ date: 'Mar 26', logs: 1200 },
|
||||
{ date: 'Mar 27', logs: 1900 },
|
||||
{ date: 'Mar 28', logs: 1540 },
|
||||
{ date: 'Mar 29', logs: 2400 },
|
||||
{ date: 'Mar 30', logs: 2100 },
|
||||
{ date: 'Mar 31', logs: 3200 },
|
||||
{ date: 'Apr 01', logs: 3800 },
|
||||
]
|
||||
interface ChartProps {
|
||||
data?: any[]
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const villageComparisonData = [
|
||||
{ village: 'Sukatani', activity: 4500 },
|
||||
{ village: 'Sukamaju', activity: 3800 },
|
||||
{ village: 'Bojong Gede', activity: 3200 },
|
||||
{ village: 'Beji', activity: 2800 },
|
||||
{ village: 'Tapos', activity: 2400 },
|
||||
]
|
||||
|
||||
export function VillageActivityLineChart() {
|
||||
export function VillageActivityLineChart({ data = [], isLoading }: ChartProps) {
|
||||
const theme = useMantineTheme()
|
||||
|
||||
return (
|
||||
@@ -46,14 +33,14 @@ export function VillageActivityLineChart() {
|
||||
</Box>
|
||||
</Group>
|
||||
<Badge variant="light" color="blue" size="sm" rightSection={<TbArrowUpRight size={12} />}>
|
||||
Growing
|
||||
{isLoading ? '...' : 'Live'}
|
||||
</Badge>
|
||||
</Group>
|
||||
|
||||
<Box h={300} mt="lg">
|
||||
<LineChart
|
||||
h={300}
|
||||
data={activityData}
|
||||
data={data}
|
||||
dataKey="date"
|
||||
series={[{ name: 'logs', color: '#2563EB' }]}
|
||||
curveType="monotone"
|
||||
@@ -76,7 +63,7 @@ export function VillageActivityLineChart() {
|
||||
)
|
||||
}
|
||||
|
||||
export function VillageComparisonBarChart() {
|
||||
export function VillageComparisonBarChart({ data = [], isLoading }: ChartProps) {
|
||||
const theme = useMantineTheme()
|
||||
|
||||
return (
|
||||
@@ -89,7 +76,7 @@ export function VillageComparisonBarChart() {
|
||||
</ThemeIcon>
|
||||
<Box>
|
||||
<Text fw={700} size="sm">USAGE COMPARISON BETWEEN VILLAGES</Text>
|
||||
<Text size="xs" c="dimmed">Top 5 most active village deployments</Text>
|
||||
<Text size="xs" c="dimmed">Most active village deployments</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
</Group>
|
||||
@@ -97,7 +84,7 @@ export function VillageComparisonBarChart() {
|
||||
<Box h={300} mt="lg">
|
||||
<BarChart
|
||||
h={300}
|
||||
data={villageComparisonData}
|
||||
data={data}
|
||||
dataKey="village"
|
||||
series={[{ name: 'activity', color: 'indigo.6' }]}
|
||||
withTooltip
|
||||
@@ -112,7 +99,6 @@ export function VillageComparisonBarChart() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Custom SVG Gradient definitions for Premium SaaS look */}
|
||||
<defs>
|
||||
<linearGradient id="barGradient" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#2563EB" stopOpacity={1} />
|
||||
|
||||
@@ -13,4 +13,7 @@ export const API_URLS = {
|
||||
`${API_BASE_URL}/api/monitoring/user?page=${page}&search=${encodeURIComponent(search)}`,
|
||||
getLogsAllVillages: (page: number, search: string) =>
|
||||
`${API_BASE_URL}/api/monitoring/log-all-villages?page=${page}&search=${encodeURIComponent(search)}`,
|
||||
getGridOverview: () => `${API_BASE_URL}/api/monitoring/grid-overview`,
|
||||
getDailyActivity: () => `${API_BASE_URL}/api/monitoring/daily-activity`,
|
||||
getComparisonActivity: () => `${API_BASE_URL}/api/monitoring/comparison-activity`,
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ import {
|
||||
TextInput,
|
||||
Switch,
|
||||
Badge,
|
||||
Textarea
|
||||
Textarea,
|
||||
Skeleton
|
||||
} from '@mantine/core'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { createFileRoute, useParams, useNavigate } from '@tanstack/react-router'
|
||||
import useSWR from 'swr'
|
||||
import {
|
||||
TbActivity,
|
||||
TbAlertTriangle,
|
||||
@@ -24,72 +26,75 @@ import {
|
||||
TbRefresh,
|
||||
TbVersions
|
||||
} from 'react-icons/tb'
|
||||
import { API_URLS } from '../config/api'
|
||||
|
||||
export const Route = createFileRoute('/apps/$appId/')({
|
||||
component: AppOverviewPage,
|
||||
})
|
||||
|
||||
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
||||
|
||||
function AppOverviewPage() {
|
||||
const { appId } = useParams({ from: '/apps/$appId/' })
|
||||
const navigate = useNavigate()
|
||||
const isDesaPlus = appId === 'desa-plus'
|
||||
const [versionModalOpened, { open: openVersionModal, close: closeVersionModal }] = useDisclosure(false)
|
||||
|
||||
// Data Fetching
|
||||
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() : null, fetcher)
|
||||
const { data: comparisonRes, isLoading: comparisonLoading, mutate: mutateComparison } = useSWR(isDesaPlus ? API_URLS.getComparisonActivity() : null, fetcher)
|
||||
|
||||
const grid = gridRes?.data
|
||||
const dailyData = dailyRes?.data || []
|
||||
const comparisonData = comparisonRes?.data || []
|
||||
|
||||
const handleRefresh = () => {
|
||||
mutateGrid()
|
||||
mutateDaily()
|
||||
mutateComparison()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={versionModalOpened} onClose={closeVersionModal} title="Update Version Information" radius="md">
|
||||
<Stack gap="md">
|
||||
<TextInput label="Active Version" defaultValue="v1.2.0" />
|
||||
<TextInput label="Minimum Version" defaultValue="v1.0.0" />
|
||||
<TextInput label="Active Version" defaultValue={grid?.version?.mobile_latest_version || 'v1.2.0'} />
|
||||
<TextInput label="Minimum Version" defaultValue={grid?.version?.mobile_minimum_version || 'v1.0.0'} />
|
||||
<Textarea
|
||||
label="Update Message"
|
||||
placeholder="Enter release notes or update message..."
|
||||
defaultValue={grid?.version?.mobile_message_update || ''}
|
||||
minRows={3}
|
||||
autosize
|
||||
/>
|
||||
<Switch label="Maintenance Mode" description="Enable to put the app in maintenance mode for users." />
|
||||
<Switch
|
||||
label="Maintenance Mode"
|
||||
description="Enable to put the app in maintenance mode for users."
|
||||
defaultChecked={grid?.version?.mobile_maintenance === 'true'}
|
||||
/>
|
||||
<Button fullWidth onClick={closeVersionModal}>Save Changes</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
||||
<Stack gap="xl">
|
||||
{/* 🔝 HEADER SECTION */}
|
||||
{/* <Paper withBorder p="lg" radius="2xl" className="glass"> */}
|
||||
<Group justify="space-between">
|
||||
<Stack gap={0}>
|
||||
<Title order={3}>Overview</Title>
|
||||
<Text size="sm" c="dimmed">Last updated: Just now</Text>
|
||||
<Text size="sm" c="dimmed">Detailed metrics for {isDesaPlus ? 'Desa+' : appId}</Text>
|
||||
</Stack>
|
||||
|
||||
<Group gap="md">
|
||||
{/* <Select
|
||||
placeholder="Date Range"
|
||||
data={['Today', '7 Days', '30 Days']}
|
||||
defaultValue="Today"
|
||||
leftSection={<TbCalendar size={16} />}
|
||||
radius="md"
|
||||
w={140}
|
||||
/> */}
|
||||
<ActionIcon variant="light" color="brand-blue" size="lg" radius="md">
|
||||
<ActionIcon variant="light" color="brand-blue" size="lg" radius="md" onClick={handleRefresh}>
|
||||
<TbRefresh size={20} />
|
||||
</ActionIcon>
|
||||
{/* <Button
|
||||
variant="gradient"
|
||||
gradient={{ from: '#2563EB', to: '#7C3AED' }}
|
||||
radius="md"
|
||||
leftSection={<TbFilter size={18} />}
|
||||
>
|
||||
Add Filter
|
||||
</Button> */}
|
||||
</Group>
|
||||
</Group>
|
||||
{/* </Paper> */}
|
||||
|
||||
{/* 📊 1. SUMMARY CARDS */}
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, lg: 4 }} spacing="lg">
|
||||
<SummaryCard
|
||||
title="Active Version"
|
||||
value="v1.2.0"
|
||||
value={gridLoading ? '...' : (grid?.version?.mobile_latest_version || 'N/A')}
|
||||
icon={TbVersions}
|
||||
color="brand-blue"
|
||||
onClick={openVersionModal}
|
||||
@@ -97,33 +102,38 @@ function AppOverviewPage() {
|
||||
<Group justify="space-between" mt="md">
|
||||
<Stack gap={0}>
|
||||
<Text size="xs" c="dimmed">Min. Version</Text>
|
||||
<Text size="sm" fw={600}>v1.0.0</Text>
|
||||
<Text size="sm" fw={600}>{grid?.version?.mobile_minimum_version || '-'}</Text>
|
||||
</Stack>
|
||||
<Stack gap={0} align="flex-end">
|
||||
<Text size="xs" c="dimmed">Maintenance</Text>
|
||||
<Badge size="sm" color="gray" variant="light">False</Badge>
|
||||
<Badge size="sm" color={grid?.version?.mobile_maintenance === 'true' ? 'red' : 'gray'} variant="light">
|
||||
{grid?.version?.mobile_maintenance?.toUpperCase() || 'FALSE'}
|
||||
</Badge>
|
||||
</Stack>
|
||||
</Group>
|
||||
</SummaryCard>
|
||||
|
||||
<SummaryCard
|
||||
title="Total Activity Today"
|
||||
value="3,842"
|
||||
value={gridLoading ? '...' : (grid?.activity?.today?.toLocaleString() || '0')}
|
||||
icon={TbActivity}
|
||||
color="teal"
|
||||
trend={{ value: '14.2%', positive: true }}
|
||||
trend={grid?.activity?.increase ? { value: `${grid.activity.increase}%`, positive: grid.activity.increase > 0 } : undefined}
|
||||
/>
|
||||
|
||||
<SummaryCard
|
||||
title="Total Villages Active"
|
||||
value="138"
|
||||
value={gridLoading ? '...' : (grid?.village?.active || '0')}
|
||||
icon={TbBuildingCommunity}
|
||||
color="indigo"
|
||||
onClick={() => navigate({ to: `/apps/${appId}/villages` })}
|
||||
>
|
||||
<Group justify="space-between" mt="md">
|
||||
<Text size="xs" c="dimmed">Nonactive Villages</Text>
|
||||
<Badge size="sm" color="red" variant="light">24</Badge>
|
||||
<Badge size="sm" color="red" variant="light">{grid?.village?.inactive || 0}</Badge>
|
||||
</Group>
|
||||
</SummaryCard>
|
||||
|
||||
<SummaryCard
|
||||
title="Errors Today"
|
||||
value="12"
|
||||
@@ -134,13 +144,11 @@ function AppOverviewPage() {
|
||||
/>
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 📈 📊 2 & 3. CHARTS GRID */}
|
||||
<SimpleGrid cols={{ base: 1, lg: 2 }} spacing="lg">
|
||||
<VillageActivityLineChart />
|
||||
<VillageComparisonBarChart />
|
||||
<VillageActivityLineChart data={dailyData} isLoading={dailyLoading} />
|
||||
<VillageComparisonBarChart data={comparisonData} isLoading={comparisonLoading} />
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 🐞 4. LATEST ERROR REPORTS */}
|
||||
<ErrorDataTable />
|
||||
</Stack>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user