diff --git a/src/frontend/components/ErrorDataTable.tsx b/src/frontend/components/ErrorDataTable.tsx index cd0c2f7..69b80b3 100644 --- a/src/frontend/components/ErrorDataTable.tsx +++ b/src/frontend/components/ErrorDataTable.tsx @@ -18,11 +18,15 @@ import { Tooltip, } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' -import { useQuery } from '@tanstack/react-query' import { Link } from '@tanstack/react-router' import dayjs from 'dayjs' -import { useState } from 'react' +import { forwardRef, useImperativeHandle, useState } from 'react' import { TbBug, TbExternalLink, TbHistory, TbMessageReport } from 'react-icons/tb' +import useSWR from 'swr' + +export interface ErrorDataTableHandle { + refresh: () => void +} export interface ErrorDataTableProps { appId?: string @@ -45,15 +49,20 @@ const STATUS_LABEL: Record = { CLOSED: 'Closed', } -export function ErrorDataTable({ appId }: ErrorDataTableProps) { +const fetcher = (url: string) => fetch(url).then((r) => r.json()) + +export const ErrorDataTable = forwardRef( + function ErrorDataTable({ appId }, ref) { const [opened, { open, close }] = useDisclosure(false) const [selectedError, setSelectedError] = useState(null) const [showStackTrace, setShowStackTrace] = useState(false) - const { data: bugsData, isLoading } = useQuery({ - queryKey: ['bugs', appId], - queryFn: () => fetch(`/api/bugs?app=${appId || 'all'}&limit=10`).then((r) => r.json()), - }) + const { data: bugsData, isLoading, mutate } = useSWR( + `/api/bugs?app=${appId || 'all'}&limit=10`, + fetcher + ) + + useImperativeHandle(ref, () => ({ refresh: mutate })) const bugs = bugsData?.data || [] @@ -257,4 +266,4 @@ export function ErrorDataTable({ appId }: ErrorDataTableProps) { ) -} +}) diff --git a/src/frontend/routes/apps.$appId.index.tsx b/src/frontend/routes/apps.$appId.index.tsx index 609e3ee..0a45859 100644 --- a/src/frontend/routes/apps.$appId.index.tsx +++ b/src/frontend/routes/apps.$appId.index.tsx @@ -1,6 +1,5 @@ -import { useQuery } from '@tanstack/react-query' import { VillageActivityLineChart, VillageComparisonBarChart } from '@/frontend/components/DashboardCharts' -import { ErrorDataTable } from '@/frontend/components/ErrorDataTable' +import { ErrorDataTable, type ErrorDataTableHandle } from '@/frontend/components/ErrorDataTable' import { SummaryCard } from '@/frontend/components/SummaryCard' import { useSession } from '@/frontend/hooks/useAuth' import { @@ -21,7 +20,7 @@ import { import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { TbActivity, TbAlertTriangle, @@ -45,6 +44,7 @@ function AppOverviewPage() { 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('') @@ -56,28 +56,31 @@ function AppOverviewPage() { 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 { data: appData, isLoading: appLoading } = useQuery({ - queryKey: ['apps', appId], - queryFn: () => fetch(`/api/apps/${appId}`).then((r) => r.json()), - }) + 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 (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') + 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') } - }, [grid, versionModalOpened]) + }, [versionModalOpened]) const handleRefresh = () => { mutateGrid() mutateDaily() mutateComparison() + errorTableRef.current?.refresh() } const handleSaveVersion = async () => { @@ -214,8 +217,8 @@ function AppOverviewPage() { value={gridLoading ? '...' : (grid?.activity?.today?.toLocaleString() ?? '0')} icon={TbActivity} color="teal" - trend={grid?.activity?.increase - ? { value: `${grid.activity.increase}%`, positive: grid.activity.increase > 0 } + trend={grid?.activity?.increase != null && Number(grid.activity.increase) !== 0 + ? { value: `${grid.activity.increase}%`, positive: Number(grid.activity.increase) > 0 } : undefined} /> @@ -254,7 +257,7 @@ function AppOverviewPage() { - + )