github-actions[bot] commited on
Commit
58e5ff2
·
1 Parent(s): a3cd373

deploy: 8e9f55e — 更新 ModelCard.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/components/ModelCard.jsx +28 -11
frontend/src/components/ModelCard.jsx CHANGED
@@ -42,7 +42,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
42
  <div className={`card flex flex-col animate-slide-in transition-all duration-200 ${
43
  model.enabled ? "" : "opacity-50"
44
  }`}>
45
- {/* ── Card header ── */}
46
  <div className="p-4 flex items-start justify-between gap-3">
47
  <div className="min-w-0 flex-1">
48
  <div className="flex items-center gap-2 flex-wrap">
@@ -82,7 +81,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
82
 
83
  <hr className="divider" />
84
 
85
- {/* ── Endpoint info ── */}
86
  <div className="p-4 space-y-2">
87
  <InfoRow
88
  icon={Globe}
@@ -112,12 +110,11 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
112
  <InfoRow
113
  icon={Key}
114
  label="API Key"
115
- value={model.apiKey ? "Configured " : "Not required / None"}
116
  className={model.apiKey ? "text-accent-green" : "text-text-muted"}
117
  />
118
  </div>
119
 
120
- {/* ── Expandable section ── */}
121
  <div className="border-t border-white/5">
122
  <button
123
  onClick={() => setExpanded(e => !e)}
@@ -130,7 +127,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
130
 
131
  {expanded && (
132
  <div className="px-4 pb-4 space-y-3 animate-slide-in">
133
- {/* Curl example */}
134
  <div>
135
  <div className="flex items-center justify-between mb-1.5">
136
  <span className="text-xs text-text-muted font-mono">curl</span>
@@ -146,7 +142,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
146
  </div>
147
  </div>
148
 
149
- {/* Test button */}
150
  <div>
151
  <button
152
  onClick={runTest}
@@ -154,7 +149,7 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
154
  className="btn-secondary w-full justify-center text-xs"
155
  >
156
  {testing ? (
157
- <><RefreshCwIcon className="animate-spin" size={12} /> Testing</>
158
  ) : "Run connectivity test"}
159
  </button>
160
 
@@ -165,8 +160,8 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
165
  : "bg-accent-red/5 border-accent-red/20 text-accent-red"
166
  }`}>
167
  {testResult.success
168
- ? `✓ OK ${testResult.latencyMs}ms`
169
- : `✗ ${JSON.stringify(testResult.error)?.slice(0, 120)}`
170
  }
171
  </div>
172
  )}
@@ -178,6 +173,29 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
178
  );
179
  }
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, className }) {
182
  return (
183
  <div className="flex items-center gap-2 group">
@@ -203,7 +221,6 @@ function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, class
203
  );
204
  }
205
 
206
- // Inline icon to avoid import issues
207
  function RefreshCwIcon({ size, className }) {
208
  return (
209
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
@@ -214,4 +231,4 @@ function RefreshCwIcon({ size, className }) {
214
  <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
215
  </svg>
216
  );
217
- }
 
42
  <div className={`card flex flex-col animate-slide-in transition-all duration-200 ${
43
  model.enabled ? "" : "opacity-50"
44
  }`}>
 
45
  <div className="p-4 flex items-start justify-between gap-3">
46
  <div className="min-w-0 flex-1">
47
  <div className="flex items-center gap-2 flex-wrap">
 
81
 
82
  <hr className="divider" />
83
 
 
84
  <div className="p-4 space-y-2">
85
  <InfoRow
86
  icon={Globe}
 
110
  <InfoRow
111
  icon={Key}
112
  label="API Key"
113
+ value={model.apiKey ? "Configured v" : "Not required / None"}
114
  className={model.apiKey ? "text-accent-green" : "text-text-muted"}
115
  />
116
  </div>
117
 
 
118
  <div className="border-t border-white/5">
119
  <button
120
  onClick={() => setExpanded(e => !e)}
 
127
 
128
  {expanded && (
129
  <div className="px-4 pb-4 space-y-3 animate-slide-in">
 
130
  <div>
131
  <div className="flex items-center justify-between mb-1.5">
132
  <span className="text-xs text-text-muted font-mono">curl</span>
 
142
  </div>
143
  </div>
144
 
 
145
  <div>
146
  <button
147
  onClick={runTest}
 
149
  className="btn-secondary w-full justify-center text-xs"
150
  >
151
  {testing ? (
152
+ <><RefreshCwIcon className="animate-spin" size={12} /> Testing...</>
153
  ) : "Run connectivity test"}
154
  </button>
155
 
 
160
  : "bg-accent-red/5 border-accent-red/20 text-accent-red"
161
  }`}>
162
  {testResult.success
163
+ ? "OK -- " + testResult.latencyMs + "ms"
164
+ : <ErrorDisplay error={testResult.error} />
165
  }
166
  </div>
167
  )}
 
173
  );
174
  }
175
 
176
+ function ErrorDisplay({ error }) {
177
+ const [showFull, setShowFull] = useState(false);
178
+ const text = typeof error === "string" ? error : JSON.stringify(error, null, 2);
179
+ const LIMIT = 200;
180
+ const isLong = text.length > LIMIT;
181
+
182
+ return (
183
+ <div>
184
+ <span className="whitespace-pre-wrap break-all">
185
+ {showFull ? text : text.slice(0, LIMIT)}
186
+ </span>
187
+ {isLong && (
188
+ <button
189
+ onClick={() => setShowFull(v => !v)}
190
+ className="ml-1 underline text-accent-red/70 hover:text-accent-red"
191
+ >
192
+ {showFull ? "show less" : "...show full error"}
193
+ </button>
194
+ )}
195
+ </div>
196
+ );
197
+ }
198
+
199
  function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, className }) {
200
  return (
201
  <div className="flex items-center gap-2 group">
 
221
  );
222
  }
223
 
 
224
  function RefreshCwIcon({ size, className }) {
225
  return (
226
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
 
231
  <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
232
  </svg>
233
  );
234
+ }