89 lines
3.3 KiB
JavaScript
89 lines
3.3 KiB
JavaScript
import { useEffect, useRef } from "react";
|
|
import { Moon, Plus, RefreshCcw, Route, Search, Server, Sun } from "lucide-react";
|
|
import Button from "./ui/Button";
|
|
|
|
export default function Header({
|
|
query,
|
|
onQueryChange,
|
|
theme,
|
|
onToggleTheme,
|
|
onAddServer,
|
|
onAddService,
|
|
onRefresh,
|
|
loading,
|
|
searchEnabled = true,
|
|
}) {
|
|
const searchRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
function onKey(event) {
|
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
event.preventDefault();
|
|
searchRef.current?.focus();
|
|
}
|
|
}
|
|
window.addEventListener("keydown", onKey);
|
|
return () => window.removeEventListener("keydown", onKey);
|
|
}, []);
|
|
|
|
return (
|
|
<header className="sticky top-0 z-40 border-b border-line bg-bg">
|
|
<div className="mx-auto flex max-w-7xl flex-wrap items-center gap-3 px-4 py-3.5 sm:px-6 lg:px-8">
|
|
<div className="flex items-center gap-3">
|
|
<span className="flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500 to-indigo-600 text-white shadow-lg shadow-cyan-500/30">
|
|
<Route size={20} />
|
|
</span>
|
|
<div className="leading-tight">
|
|
<p className="text-[11px] font-bold uppercase tracking-[0.18em] text-accent">MTC</p>
|
|
<h1 className="text-[15px] font-bold text-ink">Reverse Proxy Manager</h1>
|
|
</div>
|
|
</div>
|
|
|
|
{searchEnabled && (
|
|
<div className="order-last w-full sm:order-none sm:mx-auto sm:max-w-md sm:flex-1">
|
|
<div className="group relative">
|
|
<Search size={15} className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-ink-muted" />
|
|
<input
|
|
ref={searchRef}
|
|
value={query}
|
|
onChange={(event) => onQueryChange(event.target.value)}
|
|
placeholder="Search servers, IPs, endpoints…"
|
|
className="input pl-9! pr-14!"
|
|
type="search"
|
|
/>
|
|
<kbd className="mono pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 rounded border border-line bg-surface-2 px-1.5 py-0.5 text-[10px] font-medium text-ink-muted">
|
|
Ctrl K
|
|
</kbd>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
aria-label="Toggle theme"
|
|
className="rounded-lg border border-line bg-surface-2 p-2 text-ink-soft transition hover:border-line-strong hover:text-ink"
|
|
onClick={onToggleTheme}
|
|
type="button"
|
|
>
|
|
{theme === "dark" ? <Sun size={16} /> : <Moon size={16} />}
|
|
</button>
|
|
<button
|
|
aria-label="Refresh"
|
|
className="rounded-lg border border-line bg-surface-2 p-2 text-ink-soft transition hover:border-line-strong hover:text-ink"
|
|
onClick={onRefresh}
|
|
type="button"
|
|
>
|
|
<RefreshCcw size={16} className={loading ? "animate-spin" : ""} />
|
|
</button>
|
|
<Button variant="secondary" icon={Plus} onClick={onAddService} className="hidden md:inline-flex">
|
|
Service
|
|
</Button>
|
|
<Button variant="primary" icon={Server} onClick={onAddServer}>
|
|
<span className="hidden sm:inline">Add </span>Server
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|