返回 DeepSeek-Reasonix
SidebarImConnectionDetail.tsx
根目录 / desktop / frontend / src / app-shell / SidebarImConnectionDetail.tsx
1 import { MessageSquare, Settings as SettingsIcon } from "lucide-react";
2 import { CopyButton } from "../components/CopyButton";
3 import { useT, type Translator } from "../lib/i18n";
4 import { sidebarImScopeLabel, sidebarImSessionTarget, type SidebarImConnection } from "../app-runtime/sidebarImProjection";
5
6 type SidebarImConnectionDetailProps = {
7 connection: SidebarImConnection;
8 onClose: () => void;
9 onOpenSession: () => void;
10 onOpenSettings: () => void;
11 onManageAllowlist: () => void;
12 };
13
14 function sidebarImSessionLabel(connection: SidebarImConnection, translate: Translator): string {
15 const target = sidebarImSessionTarget(connection);
16 if (!target) {
17 return connection.remoteId ? translate("botDetail.readOnlyChannel") : translate("botDetail.noSession");
18 }
19 if (connection.sessionSource === "auto") return translate("botDetail.readOnlyChannel");
20 if (target.kind === "path") return target.value.split(/[\\/]/).pop() || target.value;
21 return target.value;
22 }
23
24 function sidebarImAccessModeLabel(connection: SidebarImConnection, translate: Translator): string {
25 if (connection.allowAll) return translate("botDetail.accessAllowAll");
26 if (connection.allowlistEnabled) return translate("botDetail.accessWhitelist");
27 return translate("botDetail.accessDisabled");
28 }
29
30 function sidebarImAccessStatusLabel(connection: SidebarImConnection, translate: Translator): string {
31 if (connection.allowAll) return translate("botDetail.accessOpen");
32 if (!connection.remoteId) return translate("botDetail.accessUnknown");
33 return connection.allowlistMatched ? translate("botDetail.accessMatched") : translate("botDetail.accessMissing");
34 }
35
36 function sidebarImAccessStatusClass(connection: SidebarImConnection): string {
37 if (connection.allowAll || connection.allowlistMatched) return "ok";
38 if (!connection.remoteId) return "muted";
39 return "warn";
40 }
41
42 export function SidebarImConnectionDetail({ connection, onClose, onOpenSession, onOpenSettings, onManageAllowlist }: SidebarImConnectionDetailProps) {
43 const translate = useT();
44 const target = sidebarImSessionTarget(connection);
45 const accessStatusClass = sidebarImAccessStatusClass(connection);
46 return (
47 <div className="bot-detail">
48 <section className="bot-detail__summary">
49 <div className={`bot-detail__avatar bot-detail__avatar--${connection.platform}`} aria-hidden="true">
50 {connection.platform === "qq" ? "Q" : connection.platform === "weixin" ? "微" : connection.platform === "lark" ? "L" : "飞"}
51 </div>
52 <div className="bot-detail__summary-main">
53 <span>{translate("botDetail.subtitle")}</span>
54 <h2>{connection.title}</h2>
55 <div className="bot-detail__chips">
56 <span>{connection.platformLabel}</span>
57 <span>{connection.statusLabel}</span>
58 <span>{sidebarImScopeLabel(connection, translate)}</span>
59 </div>
60 </div>
61 <div className="bot-detail__summary-actions">
62 <button type="button" className="btn btn--primary btn--small bot-detail__primary" disabled={!target} title={target ? undefined : translate("botDetail.openDisabled")} onClick={onOpenSession}>
63 <MessageSquare size={14} />
64 {translate("botDetail.openSession")}
65 </button>
66 <button type="button" className="btn btn--secondary btn--small" onClick={onOpenSettings}>
67 <SettingsIcon size={14} />
68 {translate("botDetail.manage")}
69 </button>
70 <button type="button" className="btn btn--secondary btn--small" onClick={onClose}>
71 {translate("botDetail.close")}
72 </button>
73 </div>
74 </section>
75
76 <section className="bot-detail__panel bot-detail__panel--access" aria-label={translate("botDetail.access")}>
77 <div className="bot-detail__section-head">
78 <span>{translate("botDetail.access")}</span>
79 <div className="bot-detail__section-actions">
80 {connection.remoteId ? (
81 <CopyButton text={connection.remoteId} label={translate("botDetail.copyRemoteId")} />
82 ) : null}
83 <button type="button" className="btn btn--secondary btn--small" onClick={onManageAllowlist}>
84 {translate("botDetail.manageAllowlist")}
85 </button>
86 </div>
87 </div>
88 <div className="bot-detail__access-grid">
89 <div>
90 <span>{translate("botDetail.accessMode")}</span>
91 <strong>{sidebarImAccessModeLabel(connection, translate)}</strong>
92 </div>
93 <div>
94 <span>{translate("botDetail.accessCurrentUser")}</span>
95 <code title={connection.remoteId || undefined}>{connection.remoteId || "—"}</code>
96 </div>
97 <div>
98 <span>{translate("botDetail.accessStatus")}</span>
99 <strong className={`bot-detail__access-status bot-detail__access-status--${accessStatusClass}`}>
100 {sidebarImAccessStatusLabel(connection, translate)}
101 </strong>
102 </div>
103 </div>
104 <div className="bot-detail__allowlist">
105 <span>{translate("botDetail.channelAllowlistUsers")}</span>
106 <div className="bot-detail__id-list">
107 {connection.allowlistUsers.length > 0 ? (
108 connection.allowlistUsers.map((id) => (
109 <code
110 key={id}
111 className={id === connection.remoteId ? "bot-detail__id-list-item--active" : ""}
112 title={id}
113 >
114 {id}
115 </code>
116 ))
117 ) : (
118 <em>{translate("botDetail.emptyAllowlistUsers")}</em>
119 )}
120 </div>
121 </div>
122 </section>
123
124 <section className="bot-detail__panel bot-detail__panel--facts" aria-label={translate("botDetail.summary")}>
125 <div className="bot-detail__section-head">
126 <span>{translate("botDetail.summary")}</span>
127 </div>
128 <div className="bot-detail__facts">
129 <div>
130 <span>{translate("botDetail.remoteId")}</span>
131 <code>{connection.remoteId || "—"}</code>
132 </div>
133 <div>
134 <span>{translate("botDetail.localTopic")}</span>
135 <strong>{sidebarImSessionLabel(connection, translate)}</strong>
136 </div>
137 <div>
138 <span>{translate("botDetail.scope")}</span>
139 <strong>{sidebarImScopeLabel(connection, translate)}</strong>
140 </div>
141 </div>
142 </section>
143 </div>
144 );
145 }
146
146 lines Plain Text