Openstatus www.openstatus.dev

chore(status-page): set search-params for tab navigation (#1429)

* chore: search params tab

* fix: default regions

authored by

Maximilian Kaske and committed by
GitHub
3bc57a5a 84cd9a95

+56 -7
+10 -3
apps/status-page/src/app/(status-page)/[domain]/(public)/events/(list)/page.tsx
··· 24 24 import { useQuery } from "@tanstack/react-query"; 25 25 import Link from "next/link"; 26 26 import { useParams } from "next/navigation"; 27 - 28 - // TODO: include ?filter=maintenance/reports 27 + import { useQueryStates } from "nuqs"; 28 + import { searchParamsParsers } from "./search-params"; 29 29 30 30 export default function Page() { 31 + const [{ tab }, setSearchParams] = useQueryStates(searchParamsParsers); 31 32 const { domain } = useParams<{ domain: string }>(); 32 33 const trpc = useTRPC(); 33 34 const { data: page } = useQuery( ··· 39 40 const { statusReports, maintenances } = page; 40 41 41 42 return ( 42 - <Tabs defaultValue="reports" className="gap-4"> 43 + <Tabs 44 + defaultValue={tab} 45 + onValueChange={(value) => 46 + setSearchParams({ tab: value as "reports" | "maintenances" }) 47 + } 48 + className="gap-4" 49 + > 43 50 <TabsList> 44 51 <TabsTrigger value="reports">Reports</TabsTrigger> 45 52 <TabsTrigger value="maintenances">Maintenances</TabsTrigger>
+7
apps/status-page/src/app/(status-page)/[domain]/(public)/events/(list)/search-params.ts
··· 1 + import { createSearchParamsCache, parseAsStringEnum } from "nuqs/server"; 2 + 3 + export const searchParamsParsers = { 4 + tab: parseAsStringEnum(["reports", "maintenances"]).withDefault("reports"), 5 + }; 6 + 7 + export const searchParamsCache = createSearchParamsCache(searchParamsParsers);
+10 -1
apps/status-page/src/app/(status-page)/[domain]/(public)/monitors/[id]/page.tsx
··· 47 47 import { useQuery } from "@tanstack/react-query"; 48 48 import { TrendingUp } from "lucide-react"; 49 49 import { useParams } from "next/navigation"; 50 + import { useQueryStates } from "nuqs"; 50 51 import { useMemo } from "react"; 52 + import { searchParamsParsers } from "./search-params"; 51 53 52 54 export default function Page() { 55 + const [{ tab }, setSearchParams] = useQueryStates(searchParamsParsers); 53 56 const trpc = useTRPC(); 54 57 const { id, domain } = useParams<{ id: string; domain: string }>(); 55 58 const { data: page } = useQuery( ··· 209 212 <ButtonBack href="./" /> 210 213 <ButtonCopyLink /> 211 214 </div> 212 - <StatusMonitorTabs defaultValue="global"> 215 + <StatusMonitorTabs 216 + defaultValue={tab} 217 + onValueChange={(value) => 218 + setSearchParams({ tab: value as "global" | "region" | "uptime" }) 219 + } 220 + > 213 221 <StatusMonitorTabsList className="grid grid-cols-3"> 214 222 <StatusMonitorTabsTrigger value="global"> 215 223 <StatusMonitorTabsTriggerLabel> ··· 303 311 <ChartLineRegions 304 312 className="h-[250px]" 305 313 data={regionLatencyData} 314 + defaultRegions={tempMonitor?.regions} 306 315 /> 307 316 )} 308 317 </StatusChartContent>
+7
apps/status-page/src/app/(status-page)/[domain]/(public)/monitors/[id]/search-params.ts
··· 1 + import { createSearchParamsCache, parseAsStringEnum } from "nuqs/server"; 2 + 3 + export const searchParamsParsers = { 4 + tab: parseAsStringEnum(["global", "region", "uptime"]).withDefault("global"), 5 + }; 6 + 7 + export const searchParamsCache = createSearchParamsCache(searchParamsParsers);
+20 -1
apps/status-page/src/components/chart/chart-line-regions.tsx
··· 20 20 import { regions } from "@/data/regions"; 21 21 import { formatMilliseconds } from "@/lib/formatter"; 22 22 import { cn } from "@/lib/utils"; 23 + import type { MonitorRegion } from "@openstatus/db/src/schema"; 23 24 import { useState } from "react"; 24 25 import { ChartLegendBadge } from "./chart-legend-badge"; 25 26 import { ChartTooltipNumber } from "./chart-tooltip-number"; ··· 67 68 ) satisfies ChartConfig; 68 69 } 69 70 71 + function getChartConfigDefault(regions: MonitorRegion[]) { 72 + return regions.reduce( 73 + (acc, region, index) => { 74 + acc[region] = { 75 + label: region, 76 + color: `var(--rainbow-${((index + 5) % 17) + 1})`, 77 + }; 78 + return acc; 79 + }, 80 + {} as Record<string, { label: string; color: string }>, 81 + ) satisfies ChartConfig; 82 + } 83 + 70 84 export function ChartLineRegions({ 71 85 className, 72 86 data, 87 + defaultRegions, 73 88 }: { 74 89 className?: string; 75 90 data: { 76 91 timestamp: string; 77 92 [key: string]: string | number | null; 78 93 }[]; 94 + defaultRegions?: MonitorRegion[]; 79 95 }) { 80 - const chartConfig = getChartConfig(data); 96 + const chartConfig = 97 + data.length > 0 98 + ? getChartConfig(data) 99 + : getChartConfigDefault(defaultRegions ?? []); 81 100 const [activeSeries, setActiveSeries] = useState< 82 101 Array<keyof typeof chartConfig> 83 102 >(Object.keys(chartConfig).slice(0, 2));
+2 -2
apps/status-page/src/components/status-page/status-monitor-tabs.tsx
··· 52 52 return ( 53 53 <div 54 54 className={cn( 55 - "flex flex-row flex-wrap items-center gap-1 text-left text-muted-foreground text-xs", 55 + "flex min-h-5 flex-row flex-wrap items-center gap-1 text-left text-muted-foreground text-xs", 56 56 className, 57 57 )} 58 58 {...props} ··· 64 64 className, 65 65 ...props 66 66 }: React.ComponentProps<typeof Skeleton>) { 67 - return <Skeleton className={cn("h-4 w-24", className)} {...props} />; 67 + return <Skeleton className={cn("h-5 w-24", className)} {...props} />; 68 68 } 69 69 70 70 export function StatusMonitorTabsContent({