Files
ProxyWarden/src/ui/LogDock.tsx
T

102 lines
2.7 KiB
TypeScript

import { Button } from "./Button";
export interface LogDockEntry {
id: string;
kind: "success" | "error" | "info";
title: string;
text: string;
at: number;
}
export interface LogDockProps {
entries: LogDockEntry[];
activeEntry: LogDockEntry | null;
open: boolean;
onToggle: () => void;
formatTime: (timestamp: number) => string;
}
function isNativePreviewError(entry: LogDockEntry | null) {
if (!entry) return false;
return (
entry.text.includes("reading 'invoke'") ||
entry.text.includes("undefined (reading")
);
}
function displayEntry(entry: LogDockEntry | null) {
if (!entry) return null;
if (!isNativePreviewError(entry)) return entry;
return {
...entry,
title: "Desktop-команды недоступны",
text: "Запусти клиент через Tauri, чтобы управлять службами и применять конфиг.",
};
}
export function LogDock({
entries,
activeEntry,
open,
onToggle,
formatTime,
}: LogDockProps) {
const current = displayEntry(activeEntry);
return (
<footer
className={`log-dock ${current?.kind ?? "idle"}`}
aria-live="polite"
>
<div className={`log-current ${current ? "visible" : "hidden"}`}>
{current ? (
<>
<strong>{current.title}</strong>
<span>{current.text}</span>
</>
) : (
<span className="log-muted">Журнал событий</span>
)}
</div>
<Button
type="button"
variant="neutral"
size="sm"
className="log-toggle"
onClick={onToggle}
>
{open ? "Скрыть" : "Посмотреть"}{" "}
<span className="log-count">{entries.length}</span>
</Button>
{open ? (
<div className="log-history">
{entries.length ? (
entries.map((entry) => {
const friendly = displayEntry(entry);
return (
<div className={`log-history-row ${entry.kind}`} key={entry.id}>
<time>{formatTime(entry.at)}</time>
<div>
<strong>{friendly?.title ?? entry.title}</strong>
<span>{friendly?.text ?? entry.text}</span>
{isNativePreviewError(entry) ? (
<span className="log-raw-detail">
Детали: {entry.text}
</span>
) : null}
</div>
</div>
);
})
) : (
<div className="log-history-row">
<time>--:--:--</time>
<span>Событий пока нет.</span>
</div>
)}
</div>
) : null}
</footer>
);
}