From 197c5a438caa411de35903ab5447387e5c21d8c0 Mon Sep 17 00:00:00 2001 From: Jake Bayliss Date: Fri, 23 Jan 2026 09:30:09 +1100 Subject: [PATCH 1/9] Adding ISR status badge to Rule page --- .cache/gatsby-source-git/categories | 1 + app/[filename]/ServerRulePage.tsx | 14 ++-- components/ui/isr-status-badge.tsx | 111 ++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 6 deletions(-) create mode 160000 .cache/gatsby-source-git/categories create mode 100644 components/ui/isr-status-badge.tsx diff --git a/.cache/gatsby-source-git/categories b/.cache/gatsby-source-git/categories new file mode 160000 index 000000000..a74c4062c --- /dev/null +++ b/.cache/gatsby-source-git/categories @@ -0,0 +1 @@ +Subproject commit a74c4062cc3eb40c01e0e0ad0d0740b453c99f94 diff --git a/app/[filename]/ServerRulePage.tsx b/app/[filename]/ServerRulePage.tsx index 4e5c8ecde..86fc23c2b 100644 --- a/app/[filename]/ServerRulePage.tsx +++ b/app/[filename]/ServerRulePage.tsx @@ -1,6 +1,7 @@ import Image from "next/image"; import { tinaField } from "tinacms/dist/react"; import { TinaMarkdown } from "tinacms/dist/rich-text"; +import type { BrokenReferences } from "@/app/[filename]/page"; import ArchivedReasonContent from "@/components/ArchivedReasonContent"; import AuthorsCard from "@/components/AuthorsCard"; import Breadcrumbs from "@/components/Breadcrumbs"; @@ -14,7 +15,7 @@ import RuleActionButtons from "@/components/RuleActionButtons"; import { YouTubeShorts } from "@/components/shared/Youtube"; import { getMarkdownComponentMapping } from "@/components/tina-markdown/markdown-component-mapping"; import { Card } from "@/components/ui/card"; -import type { BrokenReferences } from "@/app/[filename]/page"; +import IsrStatusBadge from "@/components/ui/isr-status-badge"; export interface ServerRulePageProps { rule: any; @@ -41,9 +42,7 @@ export default function ServerRulePage({ serverRulePageProps, tinaProps }: Serve <> - {brokenReferences?.detected && ( - - )} + {brokenReferences?.detected && }
@@ -80,8 +79,11 @@ export default function ServerRulePage({ serverRulePageProps, tinaProps }: Serve {rule?.title} -
- +
+
+ + +
diff --git a/components/ui/isr-status-badge.tsx b/components/ui/isr-status-badge.tsx new file mode 100644 index 000000000..35574ab1d --- /dev/null +++ b/components/ui/isr-status-badge.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { usePathname } from "next/navigation"; +import React from "react"; +import { cn } from "@/lib/utils"; +import Tooltip from "../tooltip/tooltip"; + +type NextCacheState = "HIT" | "STALE" | "MISS" | "unknown"; + +function normalizeCacheState(value: string | null): NextCacheState { + if (!value) return "unknown"; + const upper = value.toUpperCase(); + if (upper === "HIT" || upper === "STALE" || upper === "MISS") return upper; + return "unknown"; +} + +function getCacheHeaderValue(headers: Headers): { name: string; value: string | null } { + // Primary header emitted by Next.js ISR (varies by platform/version). + const next = headers.get("x-nextjs-cache"); + if (next) return { name: "x-nextjs-cache", value: next }; + + return { name: "x-nextjs-cache", value: null }; +} + +const copy: Record = { + HIT: { label: "Live", detail: "cached" }, + STALE: { label: "Stale", detail: "revalidating…" }, + MISS: { label: "Generating", detail: "first render" }, + unknown: { label: "Unknown" }, +}; + +const tooltipCopy: Record = { + HIT: "Live", + STALE: "Stale - Refresh for live updates", + MISS: "Generating - Refresh for live updates", + unknown: "Unknown", +}; + +function getStyle(state: NextCacheState, isLoading: boolean) { + if (isLoading) return ""; + + switch (state) { + case "HIT": + return "bg-green-500"; + case "STALE": + return "bg-amber-300"; + case "MISS": + return "bg-sky-300"; + default: + return "bg-zinc-300"; + } +} + +export default function IsrStatusBadge() { + const enabled = process.env.NEXT_PUBLIC_SHOW_ISR_BADGE === "true"; + const pathname = usePathname(); + + const [cacheState, setCacheState] = React.useState("unknown"); + const [isLoading, setIsLoading] = React.useState(true); + + React.useEffect(() => { + if (!enabled) return; + + let cancelled = false; + + // Hide the dot on route change until we get the first response for the new page. + setIsLoading(true); + + const check = async () => { + try { + const url = window.location.href; + + // Prefer HEAD (cheapest). Some setups may not support it, so fall back to GET. + let res = await fetch(url, { method: "HEAD", cache: "no-store", redirect: "follow" }); + if (res.status === 405 || res.status === 501) { + res = await fetch(url, { method: "GET", cache: "no-store", redirect: "follow" }); + } + + const header = getCacheHeaderValue(res.headers); + const state = normalizeCacheState(header.value); + + if (!cancelled) { + setCacheState(state); + setIsLoading(false); + } + } catch { + if (!cancelled) { + setCacheState("unknown"); + setIsLoading(false); + } + } + }; + + check(); + return () => { + cancelled = true; + }; + }, [enabled, pathname]); + + if (!enabled) return null; + + if (isLoading) { + return - {" "} + {" "} + {` ${lastUpdateInRelativeTime}.`} From 0bb6590641fe527eaedcf24d7f49e93423bb8cd4 Mon Sep 17 00:00:00 2001 From: "Jake Bayliss [SSW]" <32888060+jakebayliss@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:04:57 +1100 Subject: [PATCH 3/9] Update components/last-updated-by/index.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- components/last-updated-by/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/last-updated-by/index.tsx b/components/last-updated-by/index.tsx index 001c6fa12..1eea0817e 100644 --- a/components/last-updated-by/index.tsx +++ b/components/last-updated-by/index.tsx @@ -49,7 +49,7 @@ export default function GitHubMetadata({ owner = "tinacms", repo = "tina.io", pa console.error("Error fetching GitHub metadata:", err); setData(null); } finally { - //setLoading(false); + setLoading(false); } }; From ead5e2ed2811d8dbf569662ba46f34c258e2d0c9 Mon Sep 17 00:00:00 2001 From: "Jake Bayliss [SSW]" <32888060+jakebayliss@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:05:40 +1100 Subject: [PATCH 4/9] Update components/ui/isr-status-badge.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- components/ui/isr-status-badge.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/ui/isr-status-badge.tsx b/components/ui/isr-status-badge.tsx index 35574ab1d..c4e6ad0fd 100644 --- a/components/ui/isr-status-badge.tsx +++ b/components/ui/isr-status-badge.tsx @@ -37,8 +37,6 @@ const tooltipCopy: Record = { }; function getStyle(state: NextCacheState, isLoading: boolean) { - if (isLoading) return ""; - switch (state) { case "HIT": return "bg-green-500"; From df1e6a63b42b8cb70592f1e01f4a8245f8de6c52 Mon Sep 17 00:00:00 2001 From: Jake Bayliss Date: Thu, 29 Jan 2026 12:48:56 +1100 Subject: [PATCH 5/9] Add flag in github variables for ISR status --- .github/workflows/build-artifacts.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-artifacts.yml b/.github/workflows/build-artifacts.yml index 3d38f7522..5e0c9ff4d 100644 --- a/.github/workflows/build-artifacts.yml +++ b/.github/workflows/build-artifacts.yml @@ -380,6 +380,7 @@ jobs: NEXT_PUBLIC_BASE_PATH=${{ vars.NEXT_PUBLIC_BASE_PATH }} NEXT_PUBLIC_GTM_CONTAINER_ID=${{ secrets.NEXT_PUBLIC_GTM_CONTAINER_ID }} NEXT_PUBLIC_APPLICATIONINSIGHTS_CONNECTION_STRING=${{ secrets.NEXT_PUBLIC_APPLICATIONINSIGHTS_CONNECTION_STRING }} + NEXT_PUBLIC_SHOW_ISR_BADGE=${{ vars.NEXT_PUBLIC_SHOW_ISR_BADGE }} GH_APP_ID=${{ vars.GH_APP_ID }} GH_APP_PRIVATE_KEY="${{ secrets.GH_APP_PRIVATE_KEY }}" GITHUB_APP_INSTALLATION_ID=${{ secrets.GITHUB_APP_INSTALLATION_ID }} From a5bf752a76a4116bcd3e126630ebe4a236e4830d Mon Sep 17 00:00:00 2001 From: Jake Bayliss Date: Thu, 29 Jan 2026 16:51:38 +1100 Subject: [PATCH 6/9] Remove github var to flag feature --- .github/workflows/build-artifacts.yml | 1 - components/ui/isr-status-badge.tsx | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/build-artifacts.yml b/.github/workflows/build-artifacts.yml index 5e0c9ff4d..3d38f7522 100644 --- a/.github/workflows/build-artifacts.yml +++ b/.github/workflows/build-artifacts.yml @@ -380,7 +380,6 @@ jobs: NEXT_PUBLIC_BASE_PATH=${{ vars.NEXT_PUBLIC_BASE_PATH }} NEXT_PUBLIC_GTM_CONTAINER_ID=${{ secrets.NEXT_PUBLIC_GTM_CONTAINER_ID }} NEXT_PUBLIC_APPLICATIONINSIGHTS_CONNECTION_STRING=${{ secrets.NEXT_PUBLIC_APPLICATIONINSIGHTS_CONNECTION_STRING }} - NEXT_PUBLIC_SHOW_ISR_BADGE=${{ vars.NEXT_PUBLIC_SHOW_ISR_BADGE }} GH_APP_ID=${{ vars.GH_APP_ID }} GH_APP_PRIVATE_KEY="${{ secrets.GH_APP_PRIVATE_KEY }}" GITHUB_APP_INSTALLATION_ID=${{ secrets.GITHUB_APP_INSTALLATION_ID }} diff --git a/components/ui/isr-status-badge.tsx b/components/ui/isr-status-badge.tsx index c4e6ad0fd..0407d6bac 100644 --- a/components/ui/isr-status-badge.tsx +++ b/components/ui/isr-status-badge.tsx @@ -50,15 +50,12 @@ function getStyle(state: NextCacheState, isLoading: boolean) { } export default function IsrStatusBadge() { - const enabled = process.env.NEXT_PUBLIC_SHOW_ISR_BADGE === "true"; const pathname = usePathname(); const [cacheState, setCacheState] = React.useState("unknown"); const [isLoading, setIsLoading] = React.useState(true); React.useEffect(() => { - if (!enabled) return; - let cancelled = false; // Hide the dot on route change until we get the first response for the new page. @@ -93,9 +90,7 @@ export default function IsrStatusBadge() { return () => { cancelled = true; }; - }, [enabled, pathname]); - - if (!enabled) return null; + }, [pathname]); if (isLoading) { return