feat(ui): Phase 8 UI/UX optimization and system documentation update
## Sidebar Enhancement - Change tabs to icon + small label layout for better space utilization - Add Teams tab with team collaboration entry point ## Settings Page Improvements - Connect theme toggle to gatewayStore.saveQuickConfig for persistence - Remove OpenFang backend download section, simplify UI - Add time range filter to UsageStats (7d/30d/all) - Add stat cards with icons (sessions, messages, input/output tokens) - Add token usage overview bar chart - Add 8 ZCLAW system skill definitions with categories ## Bug Fixes - Fix ChannelList duplicate content with deduplication logic - Integrate CreateTriggerModal in TriggersPanel - Add independent SecurityStatusPanel with 12 default enabled layers - Change workflow view to use SchedulerPanel as unified entry ## New Components - CreateTriggerModal: Event trigger creation modal - HandApprovalModal: Hand approval workflow dialog - HandParamsForm: Enhanced Hand parameter form - SecurityLayersPanel: 16-layer security status display ## Architecture - Add TOML config parsing support (toml-utils.ts, config-parser.ts) - Add request timeout and retry mechanism (request-helper.ts) - Add secure token storage (secure-storage.ts, secure_storage.rs) ## Tests - Add unit tests for config-parser, toml-utils, request-helper - Add team-client and teamStore tests ## Documentation - Update SYSTEM_ANALYSIS.md with Phase 8 completion - UI completion: 100% (30/30 components) - API coverage: 93% (63/68 endpoints) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@ const CHANNEL_ICONS: Record<string, string> = {
|
||||
wechat: '微',
|
||||
};
|
||||
|
||||
// 可用频道类型(用于显示未配置的频道)
|
||||
const AVAILABLE_CHANNEL_TYPES = [
|
||||
{ type: 'feishu', name: '飞书 (Feishu)' },
|
||||
{ type: 'wechat', name: '微信' },
|
||||
{ type: 'qqbot', name: 'QQ 机器人' },
|
||||
];
|
||||
|
||||
interface ChannelListProps {
|
||||
onOpenSettings?: () => void;
|
||||
}
|
||||
@@ -27,6 +34,17 @@ export function ChannelList({ onOpenSettings }: ChannelListProps) {
|
||||
loadPluginStatus().then(() => loadChannels());
|
||||
};
|
||||
|
||||
// 去重:基于 channel id
|
||||
const uniqueChannels = channels.filter((ch, index, self) =>
|
||||
index === self.findIndex(c => c.id === ch.id)
|
||||
);
|
||||
|
||||
// 获取已配置的频道类型
|
||||
const configuredTypes = new Set(uniqueChannels.map(c => c.type));
|
||||
|
||||
// 未配置的频道类型
|
||||
const unconfiguredTypes = AVAILABLE_CHANNEL_TYPES.filter(ct => !configuredTypes.has(ct.type));
|
||||
|
||||
if (!connected) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full text-gray-400 text-xs px-4 text-center">
|
||||
@@ -53,7 +71,7 @@ export function ChannelList({ onOpenSettings }: ChannelListProps) {
|
||||
|
||||
<div className="flex-1 overflow-y-auto custom-scrollbar">
|
||||
{/* Configured channels */}
|
||||
{channels.map((ch) => (
|
||||
{uniqueChannels.map((ch) => (
|
||||
<div
|
||||
key={ch.id}
|
||||
className="flex items-center gap-3 px-3 py-3 hover:bg-gray-100 border-b border-gray-50"
|
||||
@@ -77,29 +95,18 @@ export function ChannelList({ onOpenSettings }: ChannelListProps) {
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Always show available channels that aren't configured */}
|
||||
{!channels.find(c => c.type === 'feishu') && (
|
||||
<div className="flex items-center gap-3 px-3 py-3 hover:bg-gray-100 border-b border-gray-50 opacity-60">
|
||||
{/* Unconfigured channels - 只显示一次 */}
|
||||
{unconfiguredTypes.map((ct) => (
|
||||
<div key={ct.type} className="flex items-center gap-3 px-3 py-3 hover:bg-gray-100 border-b border-gray-50 opacity-60">
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center text-white text-xs font-bold flex-shrink-0 bg-gray-300">
|
||||
飞
|
||||
{CHANNEL_ICONS[ct.type] || <MessageCircle className="w-4 h-4" />}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-medium text-gray-600">飞书 (Feishu)</div>
|
||||
<div className="text-xs font-medium text-gray-600">{ct.name}</div>
|
||||
<div className="text-[11px] text-gray-400">未配置</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!channels.find(c => c.type === 'qqbot') && (
|
||||
<div className="flex items-center gap-3 px-3 py-3 hover:bg-gray-100 border-b border-gray-50 opacity-60">
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center text-white text-xs font-bold flex-shrink-0 bg-gray-300">
|
||||
QQ
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-medium text-gray-600">QQ 机器人</div>
|
||||
<div className="text-[11px] text-gray-400">未安装插件</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
|
||||
{/* Help text */}
|
||||
<div className="px-3 py-4 text-center">
|
||||
|
||||
Reference in New Issue
Block a user