From 4aa1f07fbb5fce8cbdc67e5dd10b31e5d55d8644 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Sat, 22 Aug 2026 19:17:53 +0200 Subject: [PATCH] feat(web): green welcome accent + session-dismissable dev banner with telemetry Co-Authored-By: Claude Fable 5 --- .../components/ActivelyDevelopedBanner.tsx | 105 ++++++++++++++++++ .../dashboard/components/WelcomeMessage.tsx | 14 +-- .../app/dashboard/page.tsx | 53 +-------- 3 files changed, 115 insertions(+), 57 deletions(-) create mode 100644 src/Web/autopilot-monitor-web/app/dashboard/components/ActivelyDevelopedBanner.tsx diff --git a/src/Web/autopilot-monitor-web/app/dashboard/components/ActivelyDevelopedBanner.tsx b/src/Web/autopilot-monitor-web/app/dashboard/components/ActivelyDevelopedBanner.tsx new file mode 100644 index 00000000..4cca31d6 --- /dev/null +++ b/src/Web/autopilot-monitor-web/app/dashboard/components/ActivelyDevelopedBanner.tsx @@ -0,0 +1,105 @@ +"use client"; + +import { useSyncExternalStore } from "react"; +import { DOCS_URL } from "@/utils/config"; +import { trackEvent } from "@/lib/appInsights"; + +// Session-scoped dismissal: sessionStorage is per-tab, so the banner stays hidden +// across reloads in this tab (service-desk monitor use case) but reappears in every +// new tab — deliberately never a permanent opt-out. Module-level store in the +// themeStore shape: a same-tab sessionStorage write fires no "storage" event, and +// useSyncExternalStore keeps the statically prerendered markup hydration-safe. +const DismissKey = "devBannerDismissed"; +const listeners = new Set<() => void>(); + +const getSnapshot = (): boolean => sessionStorage.getItem(DismissKey) === "1"; +const getServerSnapshot = (): boolean => false; + +function subscribe(onStoreChange: () => void): () => void { + listeners.add(onStoreChange); + return () => listeners.delete(onStoreChange); +} + +function dismiss(): void { + sessionStorage.setItem(DismissKey, "1"); + for (const listener of [...listeners]) listener(); +} + +const linkClassName = + "underline font-medium hover:text-green-600 dark:hover:text-blue-200"; + +export function ActivelyDevelopedBanner() { + const dismissed = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); + if (dismissed) return null; + + const trackLink = (link: string) => trackEvent("dev_banner_link_clicked", { link }); + + return ( +
+ + + +

+ Actively developed.{" "} + Autopilot Monitor recognizes a wide range of deployment scenarios and improves + continuously — your reports directly shape it.{" "} + If something looks off, check the{" "} + trackLink("platform_changelog")} + > + Platform Changelog + {" "} + or{" "} + trackLink("service_announcements")} + > + Service Announcements + + .{" "} + Feedback or bug report?{" "} + trackLink("github_issues")} + > + Open a GitHub issue + + {" "}or message me on{" "} + trackLink("linkedin")} + > + LinkedIn + + . +

+ +
+ ); +} diff --git a/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx b/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx index b3ed6983..be883206 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/components/WelcomeMessage.tsx @@ -6,12 +6,12 @@ export function WelcomeMessage() { return (
{/* Top accent bar */} -
+
{/* Header */}
-
- +
+
@@ -49,10 +49,10 @@ export function WelcomeMessage() { - + diff --git a/src/Web/autopilot-monitor-web/app/dashboard/page.tsx b/src/Web/autopilot-monitor-web/app/dashboard/page.tsx index c9b0b8fe..de25a61f 100644 --- a/src/Web/autopilot-monitor-web/app/dashboard/page.tsx +++ b/src/Web/autopilot-monitor-web/app/dashboard/page.tsx @@ -12,6 +12,7 @@ import { useAuth } from "../../contexts/AuthContext"; import { useNotifications } from "../../contexts/NotificationContext"; import { StatsCard } from "./components/StatsCards"; import { WelcomeMessage } from "./components/WelcomeMessage"; +import { ActivelyDevelopedBanner } from "./components/ActivelyDevelopedBanner"; import { SessionTable } from "./components/SessionTable"; import { TenantFilterBar } from "./components/TenantFilterBar"; import { DeleteConfirmModal, BlockConfirmModal } from "./components/ConfirmationModals"; @@ -29,7 +30,6 @@ import { api } from "@/lib/api"; import { authenticatedFetch } from "@/lib/authenticatedFetch"; import { formatDuration } from "@/lib/formatting"; import { hasTenantReadScope } from "@/lib/tenantScope"; -import { DOCS_URL } from "@/utils/config"; import { TableSkeleton } from "@/components/skeletons/TableSkeleton"; export default function Home() { @@ -345,55 +345,8 @@ function HomeContent() { {/* Main content */}
- {/* Feedback & bug report banner */} -
- - - -

- Actively developed.{" "} - Autopilot Monitor recognizes a wide range of deployment scenarios and improves - continuously — your reports directly shape it.{" "} - If something looks off, check the{" "} - - Platform Changelog - {" "} - or{" "} - - Service Announcements - - .{" "} - Feedback or bug report?{" "} - - Open a GitHub issue - - {" "}or message me on{" "} - - LinkedIn - - . -

-
+ {/* Feedback & bug report banner (session-dismissable, telemetry-instrumented) */} + {serialValidationEnabled === false && (