import React from "react";
import { cn } from "@ui/cn";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { SchedulerStatus } from "@common/elements/SchedulerStatus";
import { FunctionCalls } from "@common/features/health/components/FunctionCalls";
import { FailureRate } from "@common/features/health/components/FailureRate";
import { CacheHitRate } from "@common/features/health/components/CacheHitRate";
import { ExceptionReporting } from "@common/features/health/components/ExceptionReporting";
import { LogStreams } from "@common/features/health/components/LogStreams";
import { LastDeployed } from "@common/features/health/components/LastDeployed";
import { DeploymentPageTitle } from "@common/elements/DeploymentPageTitle";
import { PageContent } from "@common/elements/PageContent";
import { useConcurrencyStatus } from "@common/features/health/components/ConcurrencyStatus";
import { useGlobalLocalStorage } from "@common/lib/useGlobalLocalStorage";
import { HealthCard } from "@common/elements/HealthCard";
import { ChartForFunctionRate } from "@common/features/health/components/ChartForFunctionRate";
export function HealthView({
header,
PageWrapper,
PagesWrapper,
flags,
}: {
header: JSX.Element;
PageWrapper: React.FC<{ children: React.ReactNode }>;
PagesWrapper: React.FC<{ children: React.ReactNode }>;
flags?: {
healthPageFunctionCallsChart?: boolean;
};
}) {
const {
closedDescription: concurrencyClosedDescription,
lag,
running,
queued,
} = useConcurrencyStatus(flags?.healthPageFunctionCallsChart ?? false);
return (
<PageContent>
<DeploymentPageTitle title="Health" />
<div className="flex max-h-full max-w-full flex-col overflow-hidden pt-2">
{header}
<PagesWrapper>
<PageWrapper>
<div>
<DisclosureSection id="summary" title="Summary" defaultOpen>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<LastDeployed />
<ExceptionReporting />
<LogStreams />
</div>
</DisclosureSection>
<DisclosureSection
id="function-calls"
title="Functions"
defaultOpen
closedDescription={
<span className="text-xs text-content-secondary">
{flags?.healthPageFunctionCallsChart
? "3 charts"
: "2 charts"}
</span>
}
>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{flags?.healthPageFunctionCallsChart && <FunctionCalls />}
<FailureRate />
<CacheHitRate />
</div>
</DisclosureSection>
<DisclosureSection
id="concurrency"
title="Concurrency"
defaultOpen={false}
closedDescription={concurrencyClosedDescription}
>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<SchedulerStatus lag={lag} />
<HealthCard
title="Running Functions"
tip="The maximum number of concurrently running functions in a given minute. This includes system functions used to power the Convex Dashboard."
>
<ChartForFunctionRate
chartData={running}
kind="functionConcurrency"
/>
</HealthCard>
<HealthCard
title="Queued Functions"
tip="The maximum number of functions waiting to be ran in a given minute. Functions are queued when the concurrency limit has been reached. If a function is queued for too long, it will discarded."
>
<ChartForFunctionRate
chartData={queued}
kind="functionConcurrency"
/>
</HealthCard>
</div>
</DisclosureSection>
</div>
</PageWrapper>
</PagesWrapper>
</div>
</PageContent>
);
}
export function DisclosureSection({
id,
title,
defaultOpen = true,
children,
closedDescription,
}: {
id: string;
title: string;
defaultOpen?: boolean;
children: React.ReactNode;
closedDescription?: React.ReactNode;
}) {
const storageKey = `health-disclosure-${id}`;
const [isOpen, setIsOpen] = useGlobalLocalStorage(storageKey, defaultOpen);
return (
<div>
<h4
className={cn(
"sticky top-0 z-10 bg-background-primary",
!isOpen && "border-b",
)}
>
{/* eslint-disable-next-line react/forbid-elements -- Using native button for disclosure heading to avoid Button component styling */}
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="flex w-full flex-wrap items-center gap-2 py-3 text-left font-normal"
>
<ChevronDownIcon
className={cn(
"h-4 w-4 text-content-secondary transition-transform",
isOpen && "rotate-180",
)}
/>
<span className="font-semibold text-content-primary">{title} </span>
{!isOpen && <span className="font-normal">{closedDescription}</span>}
</button>
</h4>
{isOpen && <div>{children}</div>}
</div>
);
}