diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..a4112fe
Binary files /dev/null and b/bun.lockb differ
diff --git a/package.json b/package.json
index 7d357e2..d0c4921 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"@mantine/charts": "^9.0.0",
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
+ "@mantine/notifications": "^8.3.18",
"@prisma/client": "6",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-router": "^1.168.10",
diff --git a/src/frontend/App.tsx b/src/frontend/App.tsx
index 838a693..453a205 100644
--- a/src/frontend/App.tsx
+++ b/src/frontend/App.tsx
@@ -1,5 +1,7 @@
import { ColorSchemeScript, MantineProvider, createTheme } from '@mantine/core'
import '@mantine/core/styles.css'
+import '@mantine/notifications/styles.css'
+import { Notifications } from '@mantine/notifications'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
@@ -61,6 +63,7 @@ export function App() {
<>
+
diff --git a/src/frontend/config/api.ts b/src/frontend/config/api.ts
index 0302546..bfcaf88 100644
--- a/src/frontend/config/api.ts
+++ b/src/frontend/config/api.ts
@@ -16,4 +16,5 @@ export const API_URLS = {
getGridOverview: () => `${API_BASE_URL}/api/monitoring/grid-overview`,
getDailyActivity: () => `${API_BASE_URL}/api/monitoring/daily-activity`,
getComparisonActivity: () => `${API_BASE_URL}/api/monitoring/comparison-activity`,
+ postVersionUpdate: () => `${API_BASE_URL}/api/monitoring/version-update`,
}
diff --git a/src/frontend/routes/apps.$appId.index.tsx b/src/frontend/routes/apps.$appId.index.tsx
index 6c737c8..acb394f 100644
--- a/src/frontend/routes/apps.$appId.index.tsx
+++ b/src/frontend/routes/apps.$appId.index.tsx
@@ -1,3 +1,5 @@
+import { useEffect, useState } from 'react'
+import { notifications } from '@mantine/notifications'
import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts'
import { ErrorDataTable } from '@/frontend/components/ErrorDataTable'
import { SummaryCard } from '@/frontend/components/SummaryCard'
@@ -40,6 +42,13 @@ function AppOverviewPage() {
const isDesaPlus = appId === 'desa-plus'
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
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)
@@ -49,31 +58,93 @@ function AppOverviewPage() {
const dailyData = dailyRes?.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 = () => {
mutateGrid()
mutateDaily()
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 (
<>
-
-
+ setLatestVersion(e.currentTarget.value)}
+ />
+ setMinVersion(e.currentTarget.value)}
+ />
@@ -84,11 +155,11 @@ function AppOverviewPage() {
Detailed metrics for {isDesaPlus ? 'Desa+' : appId}
-
+ {/*
-
+ */}