Import source: web/src/App
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
+196
@@ -0,0 +1,196 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { AnimatePresence, LayoutGroup, motion } from "motion/react";
|
||||
import { useMonitor } from "./hooks/useMonitor";
|
||||
import { springs, UIProvider, useUI } from "./lib/ui";
|
||||
import type { CheckKind } from "./lib/types";
|
||||
import { fmtNumber } from "./lib/format";
|
||||
import { Header } from "./components/Header";
|
||||
import { Boot } from "./components/Boot";
|
||||
import { TargetCard } from "./components/TargetCard";
|
||||
import { TargetDetail } from "./components/TargetDetail";
|
||||
import { OrbitalOverview } from "./components/OrbitalOverview";
|
||||
import { RadialDay } from "./components/RadialDay";
|
||||
import { IncidentList } from "./components/IncidentList";
|
||||
import { PublicView } from "./components/PublicView";
|
||||
|
||||
const MIN_BOOT_MS = 1700;
|
||||
|
||||
type Filter = "all" | CheckKind;
|
||||
|
||||
function FilterChips({
|
||||
targets,
|
||||
filter,
|
||||
onChange,
|
||||
}: {
|
||||
targets: { type: CheckKind }[];
|
||||
filter: Filter;
|
||||
onChange: (f: Filter) => void;
|
||||
}) {
|
||||
const { t, lang, reduced } = useUI();
|
||||
const kinds = useMemo(() => {
|
||||
const present = new Set(targets.map((x) => x.type));
|
||||
return (["http", "tls", "tcp", "icmp", "docker"] as CheckKind[]).filter((k) =>
|
||||
present.has(k),
|
||||
);
|
||||
}, [targets]);
|
||||
|
||||
if (kinds.length < 2) return null;
|
||||
|
||||
const chip = (value: Filter, label: string, count: number) => {
|
||||
const active = filter === value;
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
onClick={() => onChange(value)}
|
||||
aria-pressed={active}
|
||||
className={`relative px-2.5 py-[3px] rounded-full font-mono text-[9.5px] tracking-[0.12em] uppercase cursor-pointer transition-colors ${
|
||||
active ? "text-ink-950" : "text-paper-faint hover:text-paper-dim"
|
||||
}`}
|
||||
>
|
||||
{active && (
|
||||
<motion.span
|
||||
layoutId="filter-chip"
|
||||
className="absolute inset-0 rounded-full bg-brass"
|
||||
transition={reduced ? { duration: 0 } : springs.snappy}
|
||||
/>
|
||||
)}
|
||||
<span className="relative z-10">
|
||||
{label} <span className="opacity-65 tabular">{fmtNumber(lang, count)}</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1 flex-wrap">
|
||||
{chip("all", t.all, targets.length)}
|
||||
{kinds.map((k) =>
|
||||
chip(k, k, targets.filter((x) => x.type === k).length),
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Shell() {
|
||||
const monitor = useMonitor();
|
||||
const { t, mode, reduced } = useUI();
|
||||
const [selected, setSelected] = useState<string | null>(
|
||||
() => decodeURIComponent(location.hash.slice(1)) || null,
|
||||
);
|
||||
const [filter, setFilter] = useState<Filter>("all");
|
||||
const [bootElapsed, setBootElapsed] = useState(reduced);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setBootElapsed(true), reduced ? 0 : MIN_BOOT_MS);
|
||||
return () => clearTimeout(id);
|
||||
}, [reduced]);
|
||||
|
||||
const booted = monitor.ready && bootElapsed;
|
||||
const selectedTarget = monitor.targets.find((x) => x.id === selected) ?? null;
|
||||
|
||||
// The selected target lives in the URL — deep-linkable, back-button friendly.
|
||||
const select = (id: string | null) => {
|
||||
setSelected(id);
|
||||
history.replaceState(null, "", id ? `#${encodeURIComponent(id)}` : location.pathname);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") select(null);
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, []);
|
||||
|
||||
const visibleTargets =
|
||||
filter === "all"
|
||||
? monitor.targets
|
||||
: monitor.targets.filter((x) => x.type === filter);
|
||||
|
||||
return (
|
||||
<div className="relative z-[1] min-h-screen">
|
||||
<AnimatePresence>{!booted && <Boot />}</AnimatePresence>
|
||||
|
||||
{booted && (
|
||||
<>
|
||||
<Header connected={monitor.connected} targets={monitor.targets} />
|
||||
<div className="rule-fade mx-6 sm:mx-10" />
|
||||
|
||||
{mode === "public" ? (
|
||||
<PublicView monitor={monitor} />
|
||||
) : (
|
||||
<LayoutGroup>
|
||||
<main className="px-6 sm:px-10 pb-16">
|
||||
{/* hero: orbit + day dial */}
|
||||
<section className="grid gap-10 lg:grid-cols-[minmax(0,1.35fr)_minmax(0,1fr)] items-start pt-8">
|
||||
<OrbitalOverview
|
||||
targets={monitor.targets}
|
||||
onSelect={select}
|
||||
/>
|
||||
<div className="flex flex-col gap-8">
|
||||
<RadialDay
|
||||
targets={monitor.targets}
|
||||
dayBuckets={monitor.dayBuckets}
|
||||
/>
|
||||
<IncidentList
|
||||
incidents={monitor.incidents.slice(0, 6)}
|
||||
targets={monitor.targets}
|
||||
onSelect={select}
|
||||
compact
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* targets grid */}
|
||||
<section className="mt-12">
|
||||
<div className="flex items-baseline gap-4 mb-5 flex-wrap">
|
||||
<h2 className="microlabel">{t.targets}</h2>
|
||||
<div className="rule-fade flex-1 min-w-16" />
|
||||
<FilterChips
|
||||
targets={monitor.targets}
|
||||
filter={filter}
|
||||
onChange={setFilter}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(215px,1fr))]">
|
||||
<AnimatePresence mode="popLayout" initial={false}>
|
||||
{visibleTargets.map((target, i) => (
|
||||
<TargetCard
|
||||
key={target.id}
|
||||
target={target}
|
||||
index={i}
|
||||
onSelect={select}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<AnimatePresence>
|
||||
{selectedTarget && (
|
||||
<TargetDetail
|
||||
target={selectedTarget}
|
||||
buckets={monitor.dayBuckets[selectedTarget.id] ?? []}
|
||||
incidents={monitor.incidents.filter(
|
||||
(i) => i.target_id === selectedTarget.id,
|
||||
)}
|
||||
onClose={() => select(null)}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</LayoutGroup>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<UIProvider>
|
||||
<Shell />
|
||||
</UIProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user