feat(hands): restructure Hands UI with Chinese localization
Major changes: - Add HandList.tsx component for left sidebar - Add HandTaskPanel.tsx for middle content area - Restructure Sidebar tabs: 分身/HANDS/Workflow - Remove Hands tab from RightPanel - Localize all UI text to Chinese - Archive legacy OpenClaw documentation - Add Hands integration lessons document - Update feature checklist with new components UI improvements: - Left sidebar now shows Hands list with status icons - Middle area shows selected Hand's tasks and results - Consistent styling with Tailwind CSS - Chinese status labels and buttons Documentation: - Create docs/archive/openclaw-legacy/ for old docs - Add docs/knowledge-base/hands-integration-lessons.md - Update docs/knowledge-base/feature-checklist.md - Update docs/knowledge-base/README.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
108
desktop/src/components/AuditLogsPanel.tsx
Normal file
108
desktop/src/components/AuditLogsPanel.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* AuditLogsPanel - OpenFang Audit Logs UI
|
||||
*
|
||||
* Displays OpenFang's Merkle hash chain audit logs.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
|
||||
export function AuditLogsPanel() {
|
||||
const { auditLogs, loadAuditLogs, isLoading } = useGatewayStore();
|
||||
const [limit, setLimit] = useState(50);
|
||||
|
||||
useEffect(() => {
|
||||
loadAuditLogs({ limit });
|
||||
}, [loadAuditLogs, limit]);
|
||||
|
||||
const formatTimestamp = (timestamp: string) => {
|
||||
try {
|
||||
return new Date(timestamp).toLocaleString('zh-CN');
|
||||
} catch {
|
||||
return timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
const resultColor = {
|
||||
success: 'text-green-600 dark:text-green-400',
|
||||
failure: 'text-red-600 dark:text-red-400',
|
||||
};
|
||||
|
||||
if (isLoading && auditLogs.length === 0) {
|
||||
return (
|
||||
<div className="p-4 text-center text-gray-500 dark:text-gray-400">
|
||||
加载中...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
审计日志
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<select
|
||||
value={limit}
|
||||
onChange={(e) => setLimit(Number(e.target.value))}
|
||||
className="text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1"
|
||||
>
|
||||
<option value={25}>25 条</option>
|
||||
<option value={50}>50 条</option>
|
||||
<option value={100}>100 条</option>
|
||||
<option value={200}>200 条</option>
|
||||
</select>
|
||||
<button
|
||||
onClick={() => loadAuditLogs({ limit })}
|
||||
className="text-sm text-blue-600 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{auditLogs.length === 0 ? (
|
||||
<div className="p-4 text-center text-gray-500 dark:text-gray-400">
|
||||
暂无审计日志
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-gray-200 dark:border-gray-700">
|
||||
<th className="text-left py-2 px-3 font-medium text-gray-700 dark:text-gray-300">时间</th>
|
||||
<th className="text-left py-2 px-3 font-medium text-gray-700 dark:text-gray-300">操作</th>
|
||||
<th className="text-left py-2 px-3 font-medium text-gray-700 dark:text-gray-300">执行者</th>
|
||||
<th className="text-left py-2 px-3 font-medium text-gray-700 dark:text-gray-300">结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{auditLogs.map((log, index) => (
|
||||
<tr
|
||||
key={log.id || index}
|
||||
className="border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
||||
>
|
||||
<td className="py-2 px-3 text-gray-600 dark:text-gray-400">
|
||||
{formatTimestamp(log.timestamp)}
|
||||
</td>
|
||||
<td className="py-2 px-3 text-gray-900 dark:text-white">
|
||||
{log.action}
|
||||
</td>
|
||||
<td className="py-2 px-3 text-gray-600 dark:text-gray-400">
|
||||
{log.actor || '-'}
|
||||
</td>
|
||||
<td className={`py-2 px-3 ${log.result ? resultColor[log.result] : 'text-gray-600 dark:text-gray-400'}`}>
|
||||
{log.result === 'success' ? '成功' : log.result === 'failure' ? '失败' : '-'}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuditLogsPanel;
|
||||
Reference in New Issue
Block a user