upd: connected api monitoring

Deskripsi:
- update version

No Issues
This commit is contained in:
2026-04-09 16:58:02 +08:00
parent ba74539542
commit c63b8cd385
5 changed files with 83 additions and 7 deletions

BIN
bun.lockb Executable file

Binary file not shown.

View File

@@ -27,6 +27,7 @@
"@mantine/charts": "^9.0.0", "@mantine/charts": "^9.0.0",
"@mantine/core": "^8.3.18", "@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18", "@mantine/hooks": "^8.3.18",
"@mantine/notifications": "^8.3.18",
"@prisma/client": "6", "@prisma/client": "6",
"@tanstack/react-query": "^5.95.2", "@tanstack/react-query": "^5.95.2",
"@tanstack/react-router": "^1.168.10", "@tanstack/react-router": "^1.168.10",

View File

@@ -1,5 +1,7 @@
import { ColorSchemeScript, MantineProvider, createTheme } from '@mantine/core' import { ColorSchemeScript, MantineProvider, createTheme } from '@mantine/core'
import '@mantine/core/styles.css' import '@mantine/core/styles.css'
import '@mantine/notifications/styles.css'
import { Notifications } from '@mantine/notifications'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createRouter, RouterProvider } from '@tanstack/react-router' import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen' import { routeTree } from './routeTree.gen'
@@ -61,6 +63,7 @@ export function App() {
<> <>
<ColorSchemeScript defaultColorScheme="auto" /> <ColorSchemeScript defaultColorScheme="auto" />
<MantineProvider theme={theme} defaultColorScheme="auto"> <MantineProvider theme={theme} defaultColorScheme="auto">
<Notifications />
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<RouterProvider router={router} /> <RouterProvider router={router} />
</QueryClientProvider> </QueryClientProvider>

View File

@@ -16,4 +16,5 @@ export const API_URLS = {
getGridOverview: () => `${API_BASE_URL}/api/monitoring/grid-overview`, getGridOverview: () => `${API_BASE_URL}/api/monitoring/grid-overview`,
getDailyActivity: () => `${API_BASE_URL}/api/monitoring/daily-activity`, getDailyActivity: () => `${API_BASE_URL}/api/monitoring/daily-activity`,
getComparisonActivity: () => `${API_BASE_URL}/api/monitoring/comparison-activity`, getComparisonActivity: () => `${API_BASE_URL}/api/monitoring/comparison-activity`,
postVersionUpdate: () => `${API_BASE_URL}/api/monitoring/version-update`,
} }

View File

@@ -1,3 +1,5 @@
import { useEffect, useState } from 'react'
import { notifications } from '@mantine/notifications'
import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts' import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts'
import { ErrorDataTable } from '@/frontend/components/ErrorDataTable' import { ErrorDataTable } from '@/frontend/components/ErrorDataTable'
import { SummaryCard } from '@/frontend/components/SummaryCard' import { SummaryCard } from '@/frontend/components/SummaryCard'
@@ -40,6 +42,13 @@ function AppOverviewPage() {
const isDesaPlus = appId === 'desa-plus' const isDesaPlus = appId === 'desa-plus'
const [versionModalOpened, { open: openVersionModal, close: closeVersionModal }] = useDisclosure(false) const [versionModalOpened, { open: openVersionModal, close: closeVersionModal }] = useDisclosure(false)
// Form State
const [latestVersion, setLatestVersion] = useState('')
const [minVersion, setMinVersion] = useState('')
const [messageUpdate, setMessageUpdate] = useState('')
const [maintenance, setMaintenance] = useState(false)
const [isSaving, setIsSaving] = useState(false)
// Data Fetching // Data Fetching
const { data: gridRes, isLoading: gridLoading, mutate: mutateGrid } = useSWR(isDesaPlus ? API_URLS.getGridOverview() : null, fetcher) 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: dailyRes, isLoading: dailyLoading, mutate: mutateDaily } = useSWR(isDesaPlus ? API_URLS.getDailyActivity() : null, fetcher)
@@ -49,31 +58,93 @@ function AppOverviewPage() {
const dailyData = dailyRes?.data || [] const dailyData = dailyRes?.data || []
const comparisonData = comparisonRes?.data || [] const comparisonData = comparisonRes?.data || []
// Initialize form when data loads or modal opens
useEffect(() => {
if (grid?.version && versionModalOpened) {
setLatestVersion(grid.version.mobile_latest_version || '')
setMinVersion(grid.version.mobile_minimum_version || '')
setMessageUpdate(grid.version.mobile_message_update || '')
setMaintenance(grid.version.mobile_maintenance === 'true')
}
}, [grid, versionModalOpened])
const handleRefresh = () => { const handleRefresh = () => {
mutateGrid() mutateGrid()
mutateDaily() mutateDaily()
mutateComparison() mutateComparison()
} }
const handleSaveVersion = async () => {
setIsSaving(true)
try {
const response = await fetch(API_URLS.postVersionUpdate(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mobile_latest_version: latestVersion,
mobile_minimum_version: minVersion,
mobile_maintenance: maintenance,
mobile_message_update: messageUpdate,
}),
})
if (response.ok) {
notifications.show({
title: 'Update Successful',
message: 'Application version information has been updated.',
color: 'teal',
})
mutateGrid()
closeVersionModal()
} else {
notifications.show({
title: 'Update Failed',
message: 'Failed to update version information. Please check your data.',
color: 'red',
})
}
} catch (error) {
notifications.show({
title: 'Network Error',
message: 'Could not connect to the server. Please try again later.',
color: 'red',
})
} finally {
setIsSaving(false)
}
}
return ( return (
<> <>
<Modal opened={versionModalOpened} onClose={closeVersionModal} title="Update Version Information" radius="md"> <Modal opened={versionModalOpened} onClose={closeVersionModal} title="Update Version Information" radius="md">
<Stack gap="md"> <Stack gap="md">
<TextInput label="Active Version" defaultValue={grid?.version?.mobile_latest_version || 'v1.2.0'} /> <TextInput
<TextInput label="Minimum Version" defaultValue={grid?.version?.mobile_minimum_version || 'v1.0.0'} /> label="Active Version"
placeholder="e.g. 2.0.5"
value={latestVersion}
onChange={(e) => setLatestVersion(e.currentTarget.value)}
/>
<TextInput
label="Minimum Version"
placeholder="e.g. 2.0.0"
value={minVersion}
onChange={(e) => setMinVersion(e.currentTarget.value)}
/>
<Textarea <Textarea
label="Update Message" label="Update Message"
placeholder="Enter release notes or update message..." placeholder="Enter release notes or update message..."
defaultValue={grid?.version?.mobile_message_update || ''} value={messageUpdate}
onChange={(e) => setMessageUpdate(e.currentTarget.value)}
minRows={3} minRows={3}
autosize autosize
/> />
<Switch <Switch
label="Maintenance Mode" label="Maintenance Mode"
description="Enable to put the app in maintenance mode for users." description="Enable to put the app in maintenance mode for users."
defaultChecked={grid?.version?.mobile_maintenance === 'true'} checked={maintenance}
onChange={(e) => setMaintenance(e.currentTarget.checked)}
/> />
<Button fullWidth onClick={closeVersionModal}>Save Changes</Button> <Button fullWidth onClick={handleSaveVersion} loading={isSaving}>Save Changes</Button>
</Stack> </Stack>
</Modal> </Modal>
@@ -84,11 +155,11 @@ function AppOverviewPage() {
<Text size="sm" c="dimmed">Detailed metrics for {isDesaPlus ? 'Desa+' : appId}</Text> <Text size="sm" c="dimmed">Detailed metrics for {isDesaPlus ? 'Desa+' : appId}</Text>
</Stack> </Stack>
<Group gap="md"> {/* <Group gap="md">
<ActionIcon variant="light" color="brand-blue" size="lg" radius="md" onClick={handleRefresh}> <ActionIcon variant="light" color="brand-blue" size="lg" radius="md" onClick={handleRefresh}>
<TbRefresh size={20} /> <TbRefresh size={20} />
</ActionIcon> </ActionIcon>
</Group> </Group> */}
</Group> </Group>
<SimpleGrid cols={{ base: 1, sm: 2, lg: 4 }} spacing="lg"> <SimpleGrid cols={{ base: 1, sm: 2, lg: 4 }} spacing="lg">