mirror of
https://github.com/coaidev/coai.git
synced 2025-05-22 22:40:14 +09:00
136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card.tsx";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useMemo, useState } from "react";
|
|
import { useEffectAsync } from "@/utils/hook.ts";
|
|
import {
|
|
Logger,
|
|
listLoggers,
|
|
downloadLogger,
|
|
deleteLogger,
|
|
getLoggerConsole,
|
|
} from "@/admin/api/logger.ts";
|
|
import { getSizeUnit } from "@/utils/base.ts";
|
|
import { Download, RotateCcw, Terminal, Trash } from "lucide-react";
|
|
import { toastState } from "@/admin/utils.ts";
|
|
import { useToast } from "@/components/ui/use-toast.ts";
|
|
import Paragraph from "@/components/Paragraph.tsx";
|
|
import { Label } from "@/components/ui/label.tsx";
|
|
import { NumberInput } from "@/components/ui/number-input.tsx";
|
|
import { Button } from "@/components/ui/button.tsx";
|
|
|
|
type LoggerItemProps = Logger & {
|
|
onUpdate: () => void;
|
|
};
|
|
function LoggerItem({ path, size, onUpdate }: LoggerItemProps) {
|
|
const { t } = useTranslation();
|
|
const { toast } = useToast();
|
|
const loggerSize = useMemo(() => getSizeUnit(size), [size]);
|
|
|
|
return (
|
|
<div className={`logger-item`}>
|
|
<div className={`logger-item-title`}>{path}</div>
|
|
<div className={`grow`} />
|
|
<div className={`logger-item-size`}>{loggerSize}</div>
|
|
<div
|
|
className={`logger-item-action`}
|
|
onClick={async () => downloadLogger(path)}
|
|
>
|
|
<Download className={`w-3 h-3`} />
|
|
</div>
|
|
<div className={`logger-item-action`}>
|
|
<Trash
|
|
className={`w-3 h-3 text-red-600`}
|
|
onClick={async () => {
|
|
const resp = await deleteLogger(path);
|
|
if (resp) onUpdate();
|
|
toastState(toast, t, resp, true);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LoggerList() {
|
|
const [data, setData] = useState<Logger[]>([]);
|
|
|
|
const sync = async () => setData(await listLoggers());
|
|
|
|
useEffectAsync(async () => {
|
|
await sync();
|
|
}, []);
|
|
|
|
return (
|
|
<div className={`logger-list`}>
|
|
{data.map((logger, i) => (
|
|
<LoggerItem {...logger} key={i} onUpdate={sync} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LoggerConsole() {
|
|
const { t } = useTranslation();
|
|
const [data, setData] = useState<string>("");
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
const [length, setLength] = useState<number>(100);
|
|
|
|
const sync = async () => {
|
|
if (loading) return;
|
|
setLoading(true);
|
|
setData(await getLoggerConsole(length));
|
|
setLoading(false);
|
|
};
|
|
useEffectAsync(sync, []);
|
|
|
|
return (
|
|
<Paragraph
|
|
title={t("admin.logger.console")}
|
|
className={`logger-container mb-2`}
|
|
isCollapsed={true}
|
|
>
|
|
<div className={`logger-toolbar`}>
|
|
<Label>{t("admin.logger.consoleLength")}</Label>
|
|
<NumberInput
|
|
value={length}
|
|
onValueChange={setLength}
|
|
min={1}
|
|
max={1000}
|
|
/>
|
|
<div className={`grow`} />
|
|
<Button onClick={sync} variant={`outline`} size={`icon`}>
|
|
<RotateCcw className={`w-4 h-4`} />
|
|
</Button>
|
|
</div>
|
|
<div className={`logger-console`}>
|
|
<Terminal className={`w-4 h-4 console-icon`} />
|
|
<pre className={`thin-scrollbar`}>{data}</pre>
|
|
</div>
|
|
</Paragraph>
|
|
);
|
|
}
|
|
|
|
function Logger() {
|
|
const { t } = useTranslation();
|
|
return (
|
|
<div className={`logger`}>
|
|
<Card className={`admin-card logger-card`}>
|
|
<CardHeader className={`select-none`}>
|
|
<CardTitle>{t("admin.logger.title")}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<LoggerConsole />
|
|
<LoggerList />
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default Logger;
|