import { useEffect, useMemo, useState } from "react"; import { Navigate, useLocation } from "react-router"; import { useSearchParams } from "react-router-dom"; import { useGleanClick } from "../telemetry/glean-context"; import { OBSERVATORY } from "../telemetry/constants"; import Container from "../ui/atoms/container"; import ObservatoryCSP from "./results/csp"; import { ERROR_MAP, FeedbackLink, useResult, useUpdateResult as useRescanTrigger, } from "./utils"; import { ObservatoryLayout } from "./layout"; import { Progress } from "./progress"; import { ObservatoryDocsNav } from "./docs"; import { ObservatoryCookies } from "./results/cookies"; import { ObservatoryHeaders } from "./results/headers"; import { ObservatoryHistory } from "./results/history"; import { ObservatoryRating } from "./results/rating"; import { ObservatoryTests } from "./results/tests"; import ObservatoryBenchmark from "./results/benchmark"; import "./results.scss"; import { OBSERVATORY_TITLE, OBSERVATORY_TITLE_FULL, } from "../../../libs/constants"; import { SidebarContainer } from "../document/organisms/sidebar"; export default function ObservatoryResults() { const { pathname, search } = useLocation(); const [searchParams] = useSearchParams(); const host = searchParams.get("host"); const { data: result, isLoading, error } = useResult(host!); // Used for rescanning the current host const { trigger, isMutating, error: updateError, } = useRescanTrigger(host || ""); const gleanClick = useGleanClick(); document.title = `Scan results for ${host} | ${OBSERVATORY_TITLE_FULL}`; const combinedError = error || updateError; useEffect(() => { if (combinedError && !isMutating) { gleanClick( `${OBSERVATORY}: error -> ${ERROR_MAP[combinedError.name] || combinedError.message}` ); } }, [combinedError, isMutating, gleanClick]); const hasData = host && result && !isLoading && !isMutating; return host ? (

{OBSERVATORY_TITLE} Report{" "}

{hasData && !combinedError ? ( ) : isLoading ? (
) : isMutating ? (
) : (
Error:{" "} {ERROR_MAP[combinedError.name] || combinedError.message}
Observatory Home
)}
{hasData && !combinedError && (
)}
) : ( ); } function ObservatoryScanResults({ result, host }) { const tabs = useMemo(() => { return [ { label: "Scoring", key: "scoring", element: , }, { label: "CSP analysis", key: "csp", element: , }, { label: "Cookies", key: "cookies", element: , }, { label: "Raw server headers", key: "headers", element: , }, { label: "Scan history", key: "history", element: , }, { label: "Benchmark comparison", key: "benchmark", element: , }, ]; }, [result]); const defaultTabKey = tabs[0].key!; const initialTabKey = window.location.hash.replace("#", "") || defaultTabKey; const initialTab = tabs.findIndex((tab) => tab.key === initialTabKey); const [selectedTab, setSelectedTab] = useState( initialTab === -1 ? 0 : initialTab ); useEffect(() => { const handleHashChange = () => { const tabIndex = tabs.findIndex( (tab) => tab.key === window.location.hash.replace("#", "") ); setSelectedTab(tabIndex === -1 ? 0 : tabIndex); }; window.addEventListener("hashchange", handleHashChange); return () => { window.removeEventListener("hashchange", handleHashChange); }; }); const gleanClick = useGleanClick(); useEffect(() => { const hash = tabs[selectedTab]?.key || defaultTabKey; window.history.replaceState( "", "", window.location.pathname + window.location.search + (hash !== defaultTabKey ? "#" + hash : "") ); }, [tabs, selectedTab, defaultTabKey]); return (

Scan results

    {tabs.map((t, i) => { return (
  1. { gleanClick(`${OBSERVATORY}: tab -> ${t.key}`); setSelectedTab(i); }} />
    {t.element}
  2. ); })}
); }