import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts' import { ErrorDataTable, type ErrorDataTableHandle } from '@/frontend/components/ErrorDataTable' import { SummaryCard } from '@/frontend/components/SummaryCard' import { useSession } from '@/frontend/hooks/useAuth' import { ActionIcon, Badge, Button, Group, Modal, SimpleGrid, Stack, Switch, Text, Textarea, TextInput, Title, Tooltip, } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' import { useEffect, useRef, useState } from 'react' import { TbActivity, TbAlertTriangle, TbBuildingCommunity, TbRefresh, TbVersions, } from 'react-icons/tb' import useSWR from 'swr' 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) const { data: session } = useSession() const isDeveloper = session?.user?.role === 'DEVELOPER' const errorTableRef = useRef(null) const [latestVersion, setLatestVersion] = useState('') const [minVersion, setMinVersion] = useState('') const [messageUpdate, setMessageUpdate] = useState('') const [maintenance, setMaintenance] = useState(false) const [isSaving, setIsSaving] = useState(false) const [dailyRange, setDailyRange] = useState<7 | 30 | 90>(7) const [comparisonRange, setComparisonRange] = useState<7 | 30 | 90>(7) 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 grid = gridRes?.data const dailyData = dailyRes?.data || [] const comparisonData = comparisonRes?.data || [] // Ref so the modal-sync effect always reads current grid without re-running on every background refetch const gridRef = useRef(grid) gridRef.current = grid useEffect(() => { if (versionModalOpened && gridRef.current?.version) { const v = gridRef.current.version setLatestVersion(v.mobile_latest_version || '') setMinVersion(v.mobile_minimum_version || '') setMessageUpdate(v.mobile_message_update || '') setMaintenance(v.mobile_maintenance === 'true') } }, [versionModalOpened]) const handleRefresh = () => { mutateGrid() mutateDaily() mutateComparison() errorTableRef.current?.refresh() } 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) { await fetch(API_URLS.createLog(), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'UPDATE', message: `Updated version info: latest=${latestVersion}, min=${minVersion}, maintenance=${maintenance}` }), }).catch(console.error) notifications.show({ title: 'Updated', message: 'Application version information has been saved.', color: 'teal' }) mutateGrid() closeVersionModal() } else { notifications.show({ title: 'Failed', message: 'Could not update version info. Please try again.', color: 'red' }) } } catch { notifications.show({ title: 'Network Error', message: 'Could not connect to the server.', color: 'red' }) } finally { setIsSaving(false) } } const maintenanceOn = grid?.version?.mobile_maintenance === 'true' return ( <> Update Version Info} radius="xl" overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} > setLatestVersion(e.currentTarget.value)} /> setMinVersion(e.currentTarget.value)} />