102 lines
2.7 KiB
TypeScript
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>
|
|
);
|
|
}
|