import React from "react"; import { __, sprintf } from "@wordpress/i18n"; import { useQuery } from "@tanstack/react-query"; import { getApps, getConnection, getHealth } from "../utils/api"; import { formatDate, relativeTime, toMillis } from "../utils/format"; import { APPS_KEY } from "./ConnectedApps"; import { CONNECTION_KEY } from "./ConnectionConfig"; import { HEALTH_KEY } from "./ConnectionHealth"; import { IconClock, IconCode, IconGrid, IconPlug } from "./Icons"; /** * The four KPI tiles (ADR-062). * * Every value is read from a payload the page already fetches — the same three * React Query keys the sections below use, so this row costs no extra request: * an identical key and query function is served from the shared cache. * * **No sparklines and no delta chips.** The approved mock-up draws both, and * they are decoration over data that does not exist: the server keeps one * `last_used` stamp per credential and no history whatsoever, so a trend line * here could only be invented. * * @since 4.9.0 */ /** * One tile. The accent arrives as custom properties so the stylesheet decides * what happens to it in dark mode, the way the client cards do (ADR-054). */ const Tile = ({ label, Icon, accent, accentDark, value, valueSm, note }) => (
{label}
{value} {note && {note}}
); /** * The analytics skeleton for a tile that is still waiting on its query * (`KpiSkeleton` in Pro's `analytics/components/common/ui.js`), so the row * arrives at its final height in one step instead of growing under the reader. */ const TileSkeleton = () => (
); export default function KpiRow({ status }) { const connection = useQuery({ queryKey: [CONNECTION_KEY], queryFn: getConnection }); const health = useQuery({ queryKey: [HEALTH_KEY], queryFn: getHealth }); const apps = useQuery({ queryKey: [APPS_KEY], queryFn: getApps }); const connectedAt = connection.data?.connected_at || null; const toolCount = health.data?.abilities?.betterdocs; const appRows = Array.isArray(apps.data?.oauth_apps) ? apps.data.oauth_apps : []; // The newest stamp anything holds: the connection token's own last use, and // each OAuth app's. There is no other record of activity on this server. // Compared as milliseconds, but carried forward in the shape it arrived in — // `relativeTime` does its own conversion, and handing it a value already in // milliseconds would multiply it by a thousand a second time. const lastActivity = [connection.data?.last_used, ...appRows.map((app) => app.last_used)] .map((value) => ({ value, ms: toMillis(value) })) .filter((stamp) => stamp.ms !== null) .reduce((newest, stamp) => (newest === null || stamp.ms > newest.ms ? stamp : newest), null); return (
{connection.isLoading ? ( ) : ( )} {health.isLoading || typeof toolCount !== "number" ? ( ) : ( )} {apps.isLoading ? ( ) : ( )} {connection.isLoading || apps.isLoading ? ( ) : ( )}
); }