mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-04 15:36:45 +08:00
feat(web): consolidate operation log settings into a collection dialog
Rename the traceability surface to "operation log", move its entry from the sidebar into the settings dialog, and collect the capture level and the retention policy in one dialog. The retention fields are shown inline instead of behind a disclosure; edits stay a draft until Save, and Reset restores the persisted policy. Copy updated across the eight locales.
This commit is contained in:
@@ -1871,6 +1871,9 @@ export default function HomeSidebar({
|
||||
const canViewStorageAnalysis =
|
||||
currentWorkspace?.workspace.source !== 'cloud_projection' &&
|
||||
currentWorkspace?.permissions.includes('audit.view');
|
||||
// The operation log is an audit surface: owner / admin only.
|
||||
const canViewOperationTrace =
|
||||
currentWorkspace?.permissions.includes('audit.view') ?? false;
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [settingsSection, setSettingsSection] =
|
||||
useState<SettingsSection>('models');
|
||||
@@ -2221,19 +2224,8 @@ export default function HomeSidebar({
|
||||
<span>{t('models.title')}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
{/* Operation traceability is an audit surface: only roles holding
|
||||
audit.view (owner / admin) get a direct sidebar entry. */}
|
||||
{currentWorkspace?.permissions.includes('audit.view') && (
|
||||
<SidebarMenuItem data-sidebar-guide="operation-trace">
|
||||
<SidebarMenuButton
|
||||
onClick={() => openSettings('operationTrace')}
|
||||
tooltip={t('operationTrace.title')}
|
||||
>
|
||||
<History className="size-4 text-blue-500" />
|
||||
<span>{t('operationTrace.title')}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)}
|
||||
{/* Operation log entry moved to the account menu: it is an audit
|
||||
surface, not a primary navigation target. */}
|
||||
</SidebarMenu>
|
||||
|
||||
{/* API-key management is available only to authorized Workspace roles. */}
|
||||
@@ -2358,6 +2350,17 @@ export default function HomeSidebar({
|
||||
{t('storageAnalysis.title')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canViewOperationTrace && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
openSettings('operationTrace');
|
||||
}}
|
||||
>
|
||||
<History />
|
||||
{t('operationTrace.title')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
|
||||
@@ -105,13 +105,6 @@ export default function SettingsDialog({
|
||||
description: t('workspace.description'),
|
||||
icon: <UsersRound className="size-4" />,
|
||||
},
|
||||
{
|
||||
id: 'operationTrace',
|
||||
label: t('settingsDialog.nav.operationTrace'),
|
||||
title: t('operationTrace.title'),
|
||||
description: t('operationTrace.description'),
|
||||
icon: <History className="size-4" />,
|
||||
},
|
||||
{
|
||||
id: 'models',
|
||||
label: t('settingsDialog.nav.models'),
|
||||
@@ -140,6 +133,13 @@ export default function SettingsDialog({
|
||||
description: t('account.settingsDescription'),
|
||||
icon: <Settings className="size-4" />,
|
||||
},
|
||||
{
|
||||
id: 'operationTrace',
|
||||
label: t('settingsDialog.nav.operationTrace'),
|
||||
title: t('operationTrace.title'),
|
||||
description: t('operationTrace.description'),
|
||||
icon: <History className="size-4" />,
|
||||
},
|
||||
];
|
||||
const permissions = currentWorkspace?.permissions ?? [];
|
||||
const canManageApiKeys = permissions.includes('api_key.manage');
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Loader2, SlidersHorizontal, Timer } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import type {
|
||||
OperationGovernance,
|
||||
OperationLevel,
|
||||
} from '@/app/infra/entities/operation-log';
|
||||
|
||||
export interface OperationTraceCollectionSettings {
|
||||
level: OperationLevel;
|
||||
retentionDays: number;
|
||||
maxRows: number;
|
||||
dedupeSeconds: number;
|
||||
}
|
||||
|
||||
export interface OperationTraceCollectionDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
governance: OperationGovernance | null;
|
||||
canConfigure: boolean;
|
||||
saving: boolean;
|
||||
onSave: (settings: OperationTraceCollectionSettings) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection settings for the operation log: capture level and retention.
|
||||
*
|
||||
* Edits stay local until Save is pressed, so a click on a level card never
|
||||
* changes what the instance collects. The Save button stays disabled while the
|
||||
* draft matches the persisted policy.
|
||||
*/
|
||||
export default function OperationTraceCollectionDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
governance,
|
||||
canConfigure,
|
||||
saving,
|
||||
onSave,
|
||||
}: OperationTraceCollectionDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [level, setLevel] = useState<OperationLevel>(0);
|
||||
const [retentionDays, setRetentionDays] = useState<number>(30);
|
||||
const [maxRows, setMaxRows] = useState<number>(20000);
|
||||
const [dedupeSeconds, setDedupeSeconds] = useState<number>(60);
|
||||
|
||||
// Every open starts from the persisted policy, so a dialog dismissed with
|
||||
// unsaved edits never leaks them into a later session.
|
||||
useEffect(() => {
|
||||
if (!open || !governance) {
|
||||
return;
|
||||
}
|
||||
setLevel(governance.configured_level);
|
||||
setRetentionDays(governance.retention_days);
|
||||
setMaxRows(governance.max_rows);
|
||||
setDedupeSeconds(governance.dedupe_window_seconds);
|
||||
}, [open, governance]);
|
||||
|
||||
const levelOptions = governance?.supported_levels ?? [];
|
||||
const dirty =
|
||||
governance !== null &&
|
||||
(level !== governance.configured_level ||
|
||||
retentionDays !== governance.retention_days ||
|
||||
maxRows !== governance.max_rows ||
|
||||
dedupeSeconds !== governance.dedupe_window_seconds);
|
||||
|
||||
function resetDraft() {
|
||||
if (!governance) {
|
||||
return;
|
||||
}
|
||||
setLevel(governance.configured_level);
|
||||
setRetentionDays(governance.retention_days);
|
||||
setMaxRows(governance.max_rows);
|
||||
setDedupeSeconds(governance.dedupe_window_seconds);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('operationTrace.collectionSettings')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t('operationTrace.description')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6">
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<SlidersHorizontal className="size-4" />
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.captureLevel')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-2 sm:grid-cols-3">
|
||||
{levelOptions.map((option) => {
|
||||
const selected = level === option.level;
|
||||
return (
|
||||
<button
|
||||
key={option.level}
|
||||
type="button"
|
||||
disabled={!canConfigure || saving}
|
||||
onClick={() => setLevel(option.level)}
|
||||
className={`rounded-lg border p-3 text-left transition-colors disabled:opacity-60 ${
|
||||
selected
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium">
|
||||
{t(`${option.i18n_key}.label`)}
|
||||
</span>
|
||||
<Badge variant={selected ? 'default' : 'outline'}>
|
||||
L{option.level}
|
||||
</Badge>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Timer className="size-4" />
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.retention')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-end gap-2">
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.retentionDays')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_retention_days ?? 1}
|
||||
max={governance?.limits.max_retention_days ?? 3650}
|
||||
value={retentionDays}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setRetentionDays(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.maxRows')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-32"
|
||||
min={governance?.limits.min_max_rows ?? 100}
|
||||
max={governance?.limits.max_max_rows ?? 500000}
|
||||
value={maxRows}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) => setMaxRows(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.dedupeWindow')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_dedupe_window_seconds ?? 0}
|
||||
max={governance?.limits.max_dedupe_window_seconds ?? 3600}
|
||||
value={dedupeSeconds}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setDedupeSeconds(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="gap-2 sm:justify-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={resetDraft}
|
||||
disabled={!dirty || saving}
|
||||
>
|
||||
{t('operationTrace.reset')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
onSave({ level, retentionDays, maxRows, dedupeSeconds })
|
||||
}
|
||||
disabled={!dirty || saving || !canConfigure}
|
||||
>
|
||||
{saving ? <Loader2 className="size-3.5 animate-spin" /> : null}
|
||||
{t('operationTrace.save')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -15,12 +15,6 @@ import { toast } from 'sonner';
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Item, ItemContent, ItemMedia, ItemTitle } from '@/components/ui/item';
|
||||
import {
|
||||
Select,
|
||||
@@ -41,6 +35,7 @@ import type {
|
||||
} from '@/app/infra/entities/operation-log';
|
||||
import { Download } from 'lucide-react';
|
||||
import { backendClient, useCurrentWorkspace } from '@/app/infra/http';
|
||||
import OperationTraceCollectionDialog from './OperationTraceCollectionDialog';
|
||||
import {
|
||||
PanelBody,
|
||||
PanelToolbar,
|
||||
@@ -343,12 +338,7 @@ export default function OperationTracePanel({
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [query, setQuery] = useState<OperationLogQuery>({});
|
||||
const [retentionDays, setRetentionDays] = useState<number>(30);
|
||||
const [maxRows, setMaxRows] = useState<number>(20000);
|
||||
const [dedupeSeconds, setDedupeSeconds] = useState<number>(60);
|
||||
// Retention is an advanced, rarely-touched policy: it collapses by default so
|
||||
// the capture level and the records list stay the focus of the panel.
|
||||
const [retentionOpen, setRetentionOpen] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
// Only one record shows its full change detail at a time, so the list stays
|
||||
// scannable: the payload diff is long and is opt-in per row.
|
||||
const [expandedId, setExpandedId] = useState<number | null>(null);
|
||||
@@ -395,9 +385,6 @@ export default function OperationTracePanel({
|
||||
setGovernance(governanceResponse);
|
||||
setFilters(filtersResponse);
|
||||
setPage(pageResponse);
|
||||
setRetentionDays(governanceResponse.retention_days);
|
||||
setMaxRows(governanceResponse.max_rows);
|
||||
setDedupeSeconds(governanceResponse.dedupe_window_seconds);
|
||||
} catch {
|
||||
toast.error(t('operationTrace.loadFailed'));
|
||||
} finally {
|
||||
@@ -463,16 +450,28 @@ export default function OperationTracePanel({
|
||||
}));
|
||||
}
|
||||
|
||||
async function changeLevel(level: OperationLevel) {
|
||||
async function saveCollectionSettings(settings: {
|
||||
level: OperationLevel;
|
||||
retentionDays: number;
|
||||
maxRows: number;
|
||||
dedupeSeconds: number;
|
||||
}) {
|
||||
if (!canConfigure) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
const updated = await backendClient.updateOperationGovernance({ level });
|
||||
// One governed write per dialog save: the level and the retention policy
|
||||
// are applied together, and the maintenance loop enforces them after.
|
||||
const updated = await backendClient.updateOperationGovernance({
|
||||
level: settings.level,
|
||||
retention_days: settings.retentionDays,
|
||||
max_rows: settings.maxRows,
|
||||
dedupe_window_seconds: settings.dedupeSeconds,
|
||||
});
|
||||
setGovernance(updated);
|
||||
toast.success(t('operationTrace.levelUpdated'));
|
||||
toast.success(t('operationTrace.settingsUpdated'));
|
||||
await load({ ...query, offset: 0 });
|
||||
} catch {
|
||||
toast.error(t('operationTrace.levelUpdateFailed'));
|
||||
toast.error(t('operationTrace.settingsUpdateFailed'));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -518,33 +517,6 @@ export default function OperationTracePanel({
|
||||
}
|
||||
}
|
||||
|
||||
async function saveRetention() {
|
||||
if (!canConfigure || !governance) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
// Saving the retention policy applies it immediately and keeps the
|
||||
// maintenance loop enforcing it afterwards; there is no manual prune.
|
||||
const updated = await backendClient.updateOperationGovernance({
|
||||
level: governance.configured_level,
|
||||
retention_days: retentionDays,
|
||||
max_rows: maxRows,
|
||||
dedupe_window_seconds: dedupeSeconds,
|
||||
});
|
||||
setGovernance(updated);
|
||||
toast.success(t('operationTrace.retentionUpdated'));
|
||||
await load({ ...query, offset: 0 });
|
||||
} catch {
|
||||
toast.error(t('operationTrace.retentionUpdateFailed'));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
const levelOptions = useMemo(
|
||||
() => governance?.supported_levels ?? [],
|
||||
[governance],
|
||||
);
|
||||
|
||||
if (loading && !governance) {
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
@@ -556,23 +528,33 @@ export default function OperationTracePanel({
|
||||
return (
|
||||
<>
|
||||
<PanelToolbar>
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<p className="truncate text-sm font-medium">
|
||||
{t('operationTrace.title')}
|
||||
</p>
|
||||
</div>
|
||||
{/* Keep the actions in one right-aligned cluster: the panel toolbar
|
||||
spreads direct children apart, so they are grouped instead. */}
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{governance && (
|
||||
<Badge
|
||||
variant={governance.configured_level > 0 ? 'default' : 'outline'}
|
||||
>
|
||||
{t(
|
||||
`operationTrace.levelNames.${governance.configured_level_name}`,
|
||||
)}
|
||||
{t('operationTrace.levelBadge', {
|
||||
level: t(
|
||||
`operationTrace.levelNames.${governance.configured_level_name}`,
|
||||
),
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{/* Keep the actions in one right-aligned cluster: the panel toolbar
|
||||
spreads direct children apart, so they are grouped instead. */}
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setSettingsOpen(true)}
|
||||
>
|
||||
<SlidersHorizontal className="size-3.5" />
|
||||
{t('operationTrace.collectionSettings')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@@ -603,121 +585,14 @@ export default function OperationTracePanel({
|
||||
</PanelToolbar>
|
||||
|
||||
<PanelBody className="space-y-6">
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<SlidersHorizontal className="size-4" />
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.captureLevel')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-2 sm:grid-cols-3">
|
||||
{levelOptions.map((option) => {
|
||||
const selected = governance?.configured_level === option.level;
|
||||
return (
|
||||
<button
|
||||
key={option.level}
|
||||
type="button"
|
||||
disabled={!canConfigure || saving}
|
||||
onClick={() => void changeLevel(option.level)}
|
||||
className={`rounded-lg border p-3 text-left transition-colors disabled:opacity-60 ${
|
||||
selected
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium">
|
||||
{t(`${option.i18n_key}.label`)}
|
||||
</span>
|
||||
<Badge variant={selected ? 'default' : 'outline'}>
|
||||
L{option.level}
|
||||
</Badge>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Collapsible open={retentionOpen} onOpenChange={setRetentionOpen}>
|
||||
<section className="space-y-3">
|
||||
<CollapsibleTrigger className="flex w-full items-center gap-2 text-left">
|
||||
<ChevronDown
|
||||
className={`size-4 shrink-0 text-muted-foreground transition-transform ${
|
||||
retentionOpen ? '' : '-rotate-90'
|
||||
}`}
|
||||
/>
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('operationTrace.retention')}
|
||||
</h3>
|
||||
{!retentionOpen && (
|
||||
<span className="ml-auto truncate text-xs text-muted-foreground">
|
||||
{t('operationTrace.retentionSummary', {
|
||||
days: retentionDays,
|
||||
rows: maxRows,
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="flex flex-wrap items-end gap-2 pt-3">
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.retentionDays')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_retention_days ?? 1}
|
||||
max={governance?.limits.max_retention_days ?? 3650}
|
||||
value={retentionDays}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setRetentionDays(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.maxRows')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-32"
|
||||
min={governance?.limits.min_max_rows ?? 100}
|
||||
max={governance?.limits.max_max_rows ?? 500000}
|
||||
value={maxRows}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) => setMaxRows(Number(event.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-xs">
|
||||
<span className="text-muted-foreground">
|
||||
{t('operationTrace.dedupeWindow')}
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
className="w-28"
|
||||
min={governance?.limits.min_dedupe_window_seconds ?? 0}
|
||||
max={governance?.limits.max_dedupe_window_seconds ?? 3600}
|
||||
value={dedupeSeconds}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) =>
|
||||
setDedupeSeconds(Number(event.target.value))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => void saveRetention()}
|
||||
disabled={!canConfigure || saving}
|
||||
>
|
||||
{t('operationTrace.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</section>
|
||||
</Collapsible>
|
||||
<OperationTraceCollectionDialog
|
||||
open={settingsOpen}
|
||||
onOpenChange={setSettingsOpen}
|
||||
governance={governance}
|
||||
canConfigure={canConfigure}
|
||||
saving={saving}
|
||||
onSave={(settings) => void saveCollectionSettings(settings)}
|
||||
/>
|
||||
|
||||
<section className="space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
|
||||
@@ -21,7 +21,7 @@ const enUS = {
|
||||
create_knowledge_base: 'Create knowledge base',
|
||||
get_pipeline: 'Read Pipeline',
|
||||
get_knowledge_schema: 'Read knowledge engine schema',
|
||||
list_operation_logs: 'Read operation trace',
|
||||
list_operation_logs: 'Read operation logs',
|
||||
},
|
||||
resources: {
|
||||
models: 'Find chat models',
|
||||
@@ -2713,7 +2713,7 @@ const enUS = {
|
||||
title: 'Settings',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: 'Traceability',
|
||||
operationTrace: 'Operation Log',
|
||||
models: 'Models',
|
||||
api: 'API',
|
||||
storage: 'Storage',
|
||||
@@ -2721,21 +2721,21 @@ const enUS = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: 'Operation traceability',
|
||||
title: 'Operation Log',
|
||||
description: 'Trace admin and owner edits and views, and what changed.',
|
||||
reset: 'Reset',
|
||||
settingsUpdated: 'Settings saved',
|
||||
settingsUpdateFailed: 'Failed to save settings',
|
||||
levelBadge: 'Level: {{level}}',
|
||||
collectionSettings: 'Collection settings',
|
||||
captureLevel: 'Capture level',
|
||||
retention: 'Retention',
|
||||
retentionSummary: 'Keep {{days}} days · up to {{rows}} records',
|
||||
retentionDays: 'Retention days',
|
||||
maxRows: 'Max records',
|
||||
dedupeWindow: 'Dedupe window (s)',
|
||||
records: 'Operation records',
|
||||
changesCount: '{{count}} change(s)',
|
||||
hideDetails: 'Hide details',
|
||||
levelUpdated: 'Capture level updated',
|
||||
levelUpdateFailed: 'Failed to update capture level',
|
||||
retentionUpdated: 'Retention updated',
|
||||
retentionUpdateFailed: 'Failed to update retention',
|
||||
export: 'Download logs',
|
||||
exportFailed: 'Failed to download operation logs',
|
||||
empty: 'No operation records yet',
|
||||
@@ -2749,7 +2749,7 @@ const enUS = {
|
||||
filterAllResources: 'All resources',
|
||||
filterActor: 'Actor',
|
||||
filterAllActors: 'All actors',
|
||||
loadFailed: 'Failed to load traceability data',
|
||||
loadFailed: 'Failed to load operation logs',
|
||||
refresh: 'Refresh',
|
||||
save: 'Save',
|
||||
redacted: 'Redacted',
|
||||
@@ -2768,7 +2768,7 @@ const enUS = {
|
||||
elidedValue: '{{count}} chars',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'Tracing off',
|
||||
label: 'Logging off',
|
||||
},
|
||||
mutation: {
|
||||
label: 'Mutations only',
|
||||
|
||||
@@ -21,7 +21,7 @@ const esES = {
|
||||
create_knowledge_base: 'Crear base de conocimiento',
|
||||
get_pipeline: 'Consultar Pipeline',
|
||||
get_knowledge_schema: 'Consultar el esquema del motor de conocimiento',
|
||||
list_operation_logs: 'Consultar la trazabilidad de operaciones',
|
||||
list_operation_logs: 'Consultar el registro de operaciones',
|
||||
},
|
||||
resources: {
|
||||
models: 'Buscar modelos de chat',
|
||||
@@ -2768,7 +2768,7 @@ const esES = {
|
||||
title: 'Configuración',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: 'Trazabilidad',
|
||||
operationTrace: 'Registro',
|
||||
models: 'Modelos',
|
||||
api: 'API',
|
||||
storage: 'Almacenamiento',
|
||||
@@ -2776,21 +2776,21 @@ const esES = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: 'Trazabilidad de operaciones',
|
||||
title: 'Registro de operaciones',
|
||||
description: 'Rastrea cambios y consultas de los administradores',
|
||||
reset: 'Restablecer',
|
||||
settingsUpdated: 'Configuración guardada',
|
||||
settingsUpdateFailed: 'No se pudo guardar la configuración',
|
||||
levelBadge: 'Nivel: {{level}}',
|
||||
collectionSettings: 'Configuración de recopilación',
|
||||
captureLevel: 'Nivel de captura',
|
||||
retention: 'Retención',
|
||||
retentionSummary: 'Conservar {{days}} días · hasta {{rows}} registros',
|
||||
retentionDays: 'Días de retención',
|
||||
maxRows: 'Registros máximos',
|
||||
dedupeWindow: 'Ventana de deduplicación (s)',
|
||||
records: 'Registros de operación',
|
||||
changesCount: '{{count}} cambio(s)',
|
||||
hideDetails: 'Ocultar detalles',
|
||||
levelUpdated: 'Nivel de captura actualizado',
|
||||
levelUpdateFailed: 'No se pudo actualizar el nivel de captura',
|
||||
retentionUpdated: 'Retención actualizada',
|
||||
retentionUpdateFailed: 'No se pudo actualizar la retención',
|
||||
export: 'Descargar registros',
|
||||
exportFailed: 'No se pudieron descargar los registros',
|
||||
empty: 'Aún no hay registros de operación',
|
||||
@@ -2804,7 +2804,7 @@ const esES = {
|
||||
filterAllResources: 'Todos los recursos',
|
||||
filterActor: 'Actor',
|
||||
filterAllActors: 'Todos los actores',
|
||||
loadFailed: 'No se pudieron cargar los datos de trazabilidad',
|
||||
loadFailed: 'No se pudieron cargar los registros de operaciones',
|
||||
refresh: 'Actualizar',
|
||||
save: 'Guardar',
|
||||
redacted: 'Oculto',
|
||||
@@ -2823,7 +2823,7 @@ const esES = {
|
||||
elidedValue: '{{count}} caracteres',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'Sin trazabilidad',
|
||||
label: 'Sin registro',
|
||||
},
|
||||
mutation: {
|
||||
label: 'Solo cambios',
|
||||
|
||||
@@ -21,7 +21,7 @@ const jaJP = {
|
||||
create_knowledge_base: 'ナレッジベース作成',
|
||||
get_pipeline: 'Pipeline 参照',
|
||||
get_knowledge_schema: 'エンジン設定の参照',
|
||||
list_operation_logs: '操作トレーサビリティの参照',
|
||||
list_operation_logs: '操作ログの参照',
|
||||
},
|
||||
resources: {
|
||||
models: 'チャットモデル検索',
|
||||
@@ -2731,7 +2731,7 @@ const jaJP = {
|
||||
title: '設定',
|
||||
nav: {
|
||||
workspace: 'ワークスペース',
|
||||
operationTrace: '操作トレーサビリティ',
|
||||
operationTrace: '操作ログ',
|
||||
models: 'モデル',
|
||||
api: 'API',
|
||||
storage: 'ストレージ',
|
||||
@@ -2739,21 +2739,21 @@ const jaJP = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: '操作トレーサビリティ',
|
||||
title: '操作ログ',
|
||||
description: '管理者とオーナーの操作を追跡します。',
|
||||
reset: 'リセット',
|
||||
settingsUpdated: '設定を保存しました',
|
||||
settingsUpdateFailed: '設定の保存に失敗しました',
|
||||
levelBadge: 'レベル:{{level}}',
|
||||
collectionSettings: '収集設定',
|
||||
captureLevel: 'キャプチャレベル',
|
||||
retention: '保持ポリシー',
|
||||
retentionSummary: '{{days}} 日間保持 · 最大 {{rows}} 件',
|
||||
retentionDays: '保持日数',
|
||||
maxRows: '最大レコード数',
|
||||
dedupeWindow: '重複排除ウィンドウ(秒)',
|
||||
records: '操作レコード',
|
||||
changesCount: '変更 {{count}} 件',
|
||||
hideDetails: '詳細を隠す',
|
||||
levelUpdated: 'キャプチャレベルを更新しました',
|
||||
levelUpdateFailed: 'キャプチャレベルの更新に失敗しました',
|
||||
retentionUpdated: '保持ポリシーを更新しました',
|
||||
retentionUpdateFailed: '保持ポリシーの更新に失敗しました',
|
||||
export: 'ログをダウンロード',
|
||||
exportFailed: '操作ログのダウンロードに失敗しました',
|
||||
empty: '操作レコードはまだありません',
|
||||
@@ -2767,7 +2767,7 @@ const jaJP = {
|
||||
filterAllResources: 'すべてのリソース',
|
||||
filterActor: '実行者',
|
||||
filterAllActors: 'すべての実行者',
|
||||
loadFailed: 'トレーサビリティデータの読み込みに失敗しました',
|
||||
loadFailed: '操作ログデータの読み込みに失敗しました',
|
||||
refresh: '更新',
|
||||
save: '保存',
|
||||
redacted: 'マスク済み',
|
||||
@@ -2786,7 +2786,7 @@ const jaJP = {
|
||||
elidedValue: '{{count}} 文字',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'トレース無効',
|
||||
label: 'ログ無効',
|
||||
},
|
||||
mutation: {
|
||||
label: '変更のみ',
|
||||
|
||||
@@ -21,7 +21,7 @@ const ruRU = {
|
||||
create_knowledge_base: 'Создать базу знаний',
|
||||
get_pipeline: 'Посмотреть Pipeline',
|
||||
get_knowledge_schema: 'Посмотреть схему движка знаний',
|
||||
list_operation_logs: 'Просмотр трассировки операций',
|
||||
list_operation_logs: 'Просмотр журнала операций',
|
||||
},
|
||||
resources: {
|
||||
models: 'Найти чат-модели',
|
||||
@@ -2738,7 +2738,7 @@ const ruRU = {
|
||||
title: 'Настройки',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: 'Трассировка',
|
||||
operationTrace: 'Журнал',
|
||||
models: 'Модели',
|
||||
api: 'API',
|
||||
storage: 'Хранилище',
|
||||
@@ -2746,21 +2746,21 @@ const ruRU = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: 'Трассировка операций',
|
||||
title: 'Журнал операций',
|
||||
description: 'Отслеживание изменений и просмотров администраторов',
|
||||
reset: 'Сбросить',
|
||||
settingsUpdated: 'Настройки сохранены',
|
||||
settingsUpdateFailed: 'Не удалось сохранить настройки',
|
||||
levelBadge: 'Уровень: {{level}}',
|
||||
collectionSettings: 'Настройки сбора',
|
||||
captureLevel: 'Уровень записи',
|
||||
retention: 'Хранение',
|
||||
retentionSummary: 'Хранить {{days}} дн. · до {{rows}} записей',
|
||||
retentionDays: 'Дней хранения',
|
||||
maxRows: 'Максимум записей',
|
||||
dedupeWindow: 'Окно дедупликации (с)',
|
||||
records: 'Записи операций',
|
||||
changesCount: 'Изменений: {{count}}',
|
||||
hideDetails: 'Скрыть детали',
|
||||
levelUpdated: 'Уровень записи обновлён',
|
||||
levelUpdateFailed: 'Не удалось обновить уровень записи',
|
||||
retentionUpdated: 'Хранение обновлено',
|
||||
retentionUpdateFailed: 'Не удалось обновить хранение',
|
||||
export: 'Скачать журнал',
|
||||
exportFailed: 'Не удалось скачать журнал операций',
|
||||
empty: 'Записей операций пока нет',
|
||||
@@ -2774,7 +2774,7 @@ const ruRU = {
|
||||
filterAllResources: 'Все ресурсы',
|
||||
filterActor: 'Исполнитель',
|
||||
filterAllActors: 'Все исполнители',
|
||||
loadFailed: 'Не удалось загрузить данные трассировки',
|
||||
loadFailed: 'Не удалось загрузить журнал операций',
|
||||
refresh: 'Обновить',
|
||||
save: 'Сохранить',
|
||||
redacted: 'Скрыто',
|
||||
@@ -2793,7 +2793,7 @@ const ruRU = {
|
||||
elidedValue: '{{count}} символов',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'Трассировка выключена',
|
||||
label: 'Журнал выключен',
|
||||
},
|
||||
mutation: {
|
||||
label: 'Только изменения',
|
||||
|
||||
@@ -21,7 +21,7 @@ const thTH = {
|
||||
create_knowledge_base: 'สร้างฐานความรู้',
|
||||
get_pipeline: 'ดู Pipeline',
|
||||
get_knowledge_schema: 'ดูสคีมาของ knowledge engine',
|
||||
list_operation_logs: 'ดูการติดตามการดำเนินการ',
|
||||
list_operation_logs: 'ดูบันทึกการดำเนินการ',
|
||||
},
|
||||
resources: {
|
||||
models: 'ค้นหาโมเดลแชต',
|
||||
@@ -2665,7 +2665,7 @@ const thTH = {
|
||||
title: 'การตั้งค่า',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: 'การติดตาม',
|
||||
operationTrace: 'บันทึกการดำเนินการ',
|
||||
models: 'โมเดล',
|
||||
api: 'API',
|
||||
storage: 'พื้นที่จัดเก็บ',
|
||||
@@ -2673,21 +2673,21 @@ const thTH = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: 'การติดตามการดำเนินการ',
|
||||
title: 'บันทึกการดำเนินการ',
|
||||
description: 'ติดตามการแก้ไขและการดูของผู้ดูแลและเจ้าของ',
|
||||
reset: 'รีเซ็ต',
|
||||
settingsUpdated: 'บันทึกการตั้งค่าแล้ว',
|
||||
settingsUpdateFailed: 'บันทึกการตั้งค่าไม่สำเร็จ',
|
||||
levelBadge: 'ระดับ: {{level}}',
|
||||
collectionSettings: 'การตั้งค่าการเก็บข้อมูล',
|
||||
captureLevel: 'ระดับการบันทึก',
|
||||
retention: 'นโยบายการเก็บรักษา',
|
||||
retentionSummary: 'เก็บ {{days}} วัน · สูงสุด {{rows}} รายการ',
|
||||
retentionDays: 'จำนวนวันเก็บรักษา',
|
||||
maxRows: 'จำนวนบันทึกสูงสุด',
|
||||
dedupeWindow: 'ช่วงเวลาลบรายการซ้ำ (วินาที)',
|
||||
records: 'บันทึกการดำเนินการ',
|
||||
changesCount: '{{count}} การเปลี่ยนแปลง',
|
||||
hideDetails: 'ซ่อนรายละเอียด',
|
||||
levelUpdated: 'อัปเดตระดับการบันทึกแล้ว',
|
||||
levelUpdateFailed: 'อัปเดตระดับการบันทึกไม่สำเร็จ',
|
||||
retentionUpdated: 'อัปเดตนโยบายการเก็บรักษาแล้ว',
|
||||
retentionUpdateFailed: 'อัปเดตนโยบายการเก็บรักษาไม่สำเร็จ',
|
||||
export: 'ดาวน์โหลดบันทึก',
|
||||
exportFailed: 'ดาวน์โหลดบันทึกการดำเนินการไม่สำเร็จ',
|
||||
empty: 'ยังไม่มีบันทึกการดำเนินการ',
|
||||
@@ -2701,7 +2701,7 @@ const thTH = {
|
||||
filterAllResources: 'ทุกทรัพยากร',
|
||||
filterActor: 'ผู้ดำเนินการ',
|
||||
filterAllActors: 'ผู้ดำเนินการทั้งหมด',
|
||||
loadFailed: 'โหลดข้อมูลการติดตามไม่สำเร็จ',
|
||||
loadFailed: 'โหลดบันทึกการดำเนินการไม่สำเร็จ',
|
||||
refresh: 'รีเฟรช',
|
||||
save: 'บันทึก',
|
||||
redacted: 'ปิดบังแล้ว',
|
||||
@@ -2720,7 +2720,7 @@ const thTH = {
|
||||
elidedValue: '{{count}} ตัวอักษร',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'ปิดการติดตาม',
|
||||
label: 'ปิดบันทึก',
|
||||
},
|
||||
mutation: {
|
||||
label: 'เฉพาะการแก้ไข',
|
||||
|
||||
@@ -22,7 +22,7 @@ const viVN = {
|
||||
create_knowledge_base: 'Tạo cơ sở tri thức',
|
||||
get_pipeline: 'Xem Pipeline',
|
||||
get_knowledge_schema: 'Xem lược đồ knowledge engine',
|
||||
list_operation_logs: 'Truy vấn truy vết thao tác',
|
||||
list_operation_logs: 'Truy vấn nhật ký thao tác',
|
||||
},
|
||||
resources: {
|
||||
models: 'Tìm mô hình trò chuyện',
|
||||
@@ -2700,7 +2700,7 @@ const viVN = {
|
||||
title: 'Cài đặt',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: 'Truy vết',
|
||||
operationTrace: 'Nhật ký',
|
||||
models: 'Mô hình',
|
||||
api: 'API',
|
||||
storage: 'Lưu trữ',
|
||||
@@ -2708,21 +2708,21 @@ const viVN = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: 'Truy vết thao tác',
|
||||
title: 'Nhật ký thao tác',
|
||||
description: 'Theo dõi thao tác sửa và xem của quản trị viên',
|
||||
reset: 'Đặt lại',
|
||||
settingsUpdated: 'Đã lưu cài đặt',
|
||||
settingsUpdateFailed: 'Lưu cài đặt thất bại',
|
||||
levelBadge: 'Cấp độ: {{level}}',
|
||||
collectionSettings: 'Cài đặt thu thập',
|
||||
captureLevel: 'Mức ghi nhận',
|
||||
retention: 'Chính sách lưu trữ',
|
||||
retentionSummary: 'Lưu {{days}} ngày · tối đa {{rows}} bản ghi',
|
||||
retentionDays: 'Số ngày lưu',
|
||||
maxRows: 'Số bản ghi tối đa',
|
||||
dedupeWindow: 'Khoảng khử trùng lặp (giây)',
|
||||
records: 'Bản ghi thao tác',
|
||||
changesCount: '{{count}} thay đổi',
|
||||
hideDetails: 'Ẩn chi tiết',
|
||||
levelUpdated: 'Đã cập nhật mức ghi nhận',
|
||||
levelUpdateFailed: 'Cập nhật mức ghi nhận thất bại',
|
||||
retentionUpdated: 'Đã cập nhật chính sách lưu trữ',
|
||||
retentionUpdateFailed: 'Cập nhật chính sách lưu trữ thất bại',
|
||||
export: 'Tải nhật ký',
|
||||
exportFailed: 'Tải nhật ký thao tác thất bại',
|
||||
empty: 'Chưa có bản ghi thao tác',
|
||||
@@ -2736,7 +2736,7 @@ const viVN = {
|
||||
filterAllResources: 'Tất cả tài nguyên',
|
||||
filterActor: 'Người thực hiện',
|
||||
filterAllActors: 'Tất cả người thực hiện',
|
||||
loadFailed: 'Tải dữ liệu truy vết thất bại',
|
||||
loadFailed: 'Tải nhật ký thao tác thất bại',
|
||||
refresh: 'Làm mới',
|
||||
save: 'Lưu',
|
||||
redacted: 'Đã ẩn',
|
||||
@@ -2755,7 +2755,7 @@ const viVN = {
|
||||
elidedValue: '{{count}} ký tự',
|
||||
levels: {
|
||||
off: {
|
||||
label: 'Tắt truy vết',
|
||||
label: 'Tắt nhật ký',
|
||||
},
|
||||
mutation: {
|
||||
label: 'Chỉ thao tác sửa',
|
||||
|
||||
@@ -19,7 +19,7 @@ const zhHans = {
|
||||
create_knowledge_base: '创建知识库',
|
||||
get_pipeline: '查看 Pipeline',
|
||||
get_knowledge_schema: '查看知识引擎配置',
|
||||
list_operation_logs: '查询操作溯源',
|
||||
list_operation_logs: '查询操作日志',
|
||||
},
|
||||
resources: {
|
||||
models: '查询聊天模型',
|
||||
@@ -2563,7 +2563,7 @@ const zhHans = {
|
||||
title: '设置',
|
||||
nav: {
|
||||
workspace: '工作区',
|
||||
operationTrace: '操作溯源',
|
||||
operationTrace: '操作日志',
|
||||
models: '模型',
|
||||
api: 'API',
|
||||
storage: '存储',
|
||||
@@ -2571,21 +2571,21 @@ const zhHans = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: '操作溯源',
|
||||
title: '操作日志',
|
||||
description: '追踪管理员与拥有者的修改和查看操作,记录“什么改成了什么”。',
|
||||
reset: '重置',
|
||||
settingsUpdated: '设置已保存',
|
||||
settingsUpdateFailed: '保存设置失败',
|
||||
levelBadge: '等级:{{level}}',
|
||||
collectionSettings: '收集设置',
|
||||
captureLevel: '操作等级',
|
||||
retention: '保留策略',
|
||||
retentionSummary: '保留 {{days}} 天 · 最多 {{rows}} 条',
|
||||
retentionDays: '保留天数',
|
||||
maxRows: '最大记录数',
|
||||
dedupeWindow: '去重窗口(秒)',
|
||||
records: '操作记录',
|
||||
changesCount: '{{count}} 项变更',
|
||||
hideDetails: '收起详情',
|
||||
levelUpdated: '操作等级已更新',
|
||||
levelUpdateFailed: '操作等级更新失败',
|
||||
retentionUpdated: '保留策略已更新',
|
||||
retentionUpdateFailed: '保留策略更新失败',
|
||||
export: '下载日志',
|
||||
exportFailed: '下载操作日志失败',
|
||||
empty: '暂无操作记录',
|
||||
@@ -2599,7 +2599,7 @@ const zhHans = {
|
||||
filterAllResources: '全部资源',
|
||||
filterActor: '操作者',
|
||||
filterAllActors: '全部操作者',
|
||||
loadFailed: '加载操作溯源数据失败',
|
||||
loadFailed: '加载操作日志数据失败',
|
||||
refresh: '刷新',
|
||||
save: '保存',
|
||||
redacted: '已脱敏',
|
||||
@@ -2618,7 +2618,7 @@ const zhHans = {
|
||||
elidedValue: '{{count}} 字符',
|
||||
levels: {
|
||||
off: {
|
||||
label: '关闭溯源',
|
||||
label: '关闭日志',
|
||||
},
|
||||
mutation: {
|
||||
label: '仅修改操作',
|
||||
@@ -2666,8 +2666,8 @@ const zhHans = {
|
||||
},
|
||||
actions: {
|
||||
audit_log_view: '查看操作日志',
|
||||
settings_update: '修改溯源设置',
|
||||
settings_view: '查看溯源设置',
|
||||
settings_update: '修改日志设置',
|
||||
settings_view: '查看日志设置',
|
||||
member_invite: '邀请成员',
|
||||
member_role_update: '修改成员角色',
|
||||
member_remove: '移除成员',
|
||||
|
||||
@@ -19,7 +19,7 @@ const zhHant = {
|
||||
create_knowledge_base: '建立知識庫',
|
||||
get_pipeline: '查看 Pipeline',
|
||||
get_knowledge_schema: '查看知識引擎設定',
|
||||
list_operation_logs: '查詢操作溯源',
|
||||
list_operation_logs: '查詢操作日誌',
|
||||
},
|
||||
resources: {
|
||||
models: '查詢聊天模型',
|
||||
@@ -2564,7 +2564,7 @@ const zhHant = {
|
||||
title: '設定',
|
||||
nav: {
|
||||
workspace: 'Workspace',
|
||||
operationTrace: '操作溯源',
|
||||
operationTrace: '操作日誌',
|
||||
models: '模型',
|
||||
api: 'API',
|
||||
storage: '儲存',
|
||||
@@ -2572,21 +2572,21 @@ const zhHant = {
|
||||
},
|
||||
},
|
||||
operationTrace: {
|
||||
title: '操作溯源',
|
||||
title: '操作日誌',
|
||||
description: '追蹤管理員與擁有者的修改與查看操作,記錄「什麼改成了什麼」。',
|
||||
reset: '重設',
|
||||
settingsUpdated: '設定已儲存',
|
||||
settingsUpdateFailed: '儲存設定失敗',
|
||||
levelBadge: '等級:{{level}}',
|
||||
collectionSettings: '收集設定',
|
||||
captureLevel: '操作等級',
|
||||
retention: '保留策略',
|
||||
retentionSummary: '保留 {{days}} 天 · 最多 {{rows}} 條',
|
||||
retentionDays: '保留天數',
|
||||
maxRows: '最大記錄數',
|
||||
dedupeWindow: '去重視窗(秒)',
|
||||
records: '操作記錄',
|
||||
changesCount: '{{count}} 項變更',
|
||||
hideDetails: '收合詳情',
|
||||
levelUpdated: '操作等級已更新',
|
||||
levelUpdateFailed: '操作等級更新失敗',
|
||||
retentionUpdated: '保留策略已更新',
|
||||
retentionUpdateFailed: '保留策略更新失敗',
|
||||
export: '下載日誌',
|
||||
exportFailed: '下載操作日誌失敗',
|
||||
empty: '暫無操作記錄',
|
||||
@@ -2600,7 +2600,7 @@ const zhHant = {
|
||||
filterAllResources: '全部資源',
|
||||
filterActor: '操作者',
|
||||
filterAllActors: '全部操作者',
|
||||
loadFailed: '載入操作溯源資料失敗',
|
||||
loadFailed: '載入操作日誌資料失敗',
|
||||
refresh: '重新整理',
|
||||
save: '儲存',
|
||||
redacted: '已遮罩',
|
||||
@@ -2619,7 +2619,7 @@ const zhHant = {
|
||||
elidedValue: '{{count}} 字元',
|
||||
levels: {
|
||||
off: {
|
||||
label: '關閉溯源',
|
||||
label: '關閉日誌',
|
||||
},
|
||||
mutation: {
|
||||
label: '僅修改操作',
|
||||
@@ -2667,8 +2667,8 @@ const zhHant = {
|
||||
},
|
||||
actions: {
|
||||
audit_log_view: '查看操作日誌',
|
||||
settings_update: '修改溯源設定',
|
||||
settings_view: '查看溯源設定',
|
||||
settings_update: '修改日誌設定',
|
||||
settings_view: '查看日誌設定',
|
||||
member_invite: '邀請成員',
|
||||
member_role_update: '修改成員角色',
|
||||
member_remove: '移除成員',
|
||||
|
||||
Reference in New Issue
Block a user