From c01d9c7e40b8dfaaf5be978c2d57f64246029e07 Mon Sep 17 00:00:00 2001 From: DungTT Date: Wed, 3 Jun 2026 09:12:51 +0700 Subject: [PATCH] change UI set Endpoint --- web-client/src/main.jsx | 133 +++++++++++++++++++++++++++++++------- web-client/src/styles.css | 109 +++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+), 22 deletions(-) diff --git a/web-client/src/main.jsx b/web-client/src/main.jsx index 37f161d..fa20e0a 100644 --- a/web-client/src/main.jsx +++ b/web-client/src/main.jsx @@ -22,6 +22,7 @@ import { ShieldCheck, Trash2, WifiOff, + X, XCircle } from 'lucide-react'; import { @@ -159,6 +160,7 @@ function App() { const [toast, setToast] = useState(null); const [busyAction, setBusyAction] = useState(''); const [activeTask, setActiveTask] = useState(null); + const [endpointDialogOpen, setEndpointDialogOpen] = useState(false); const packageBaseUrl = settings.packageBaseUrl; const agentBaseUrl = settings.agentBaseUrl; @@ -425,13 +427,25 @@ function App() { } }, [agentBaseUrl, agentHealth, notify, packageBaseUrl, startPreflightFailedTask, startTask]); + const openEndpointDialog = useCallback(() => { + setDraftSettings(settings); + setEndpointDialogOpen(true); + }, [settings]); + + const closeEndpointDialog = useCallback(() => { + setDraftSettings(settings); + setEndpointDialogOpen(false); + }, [settings]); + const applySettings = useCallback(() => { const nextSettings = { packageBaseUrl: normalizeUrl(draftSettings.packageBaseUrl || DEFAULT_PACKAGE_BASE_URL), agentBaseUrl: normalizeUrl(draftSettings.agentBaseUrl || DEFAULT_AGENT_BASE_URL) }; setSettings(nextSettings); + setDraftSettings(nextSettings); saveSettings(nextSettings); + setEndpointDialogOpen(false); notify('info', 'Đã cập nhật endpoint test'); }, [draftSettings, notify]); @@ -507,6 +521,19 @@ function App() { return () => window.clearTimeout(timer); }, [toast]); + useEffect(() => { + if (!endpointDialogOpen) return undefined; + + function onKeyDown(event) { + if (event.key === 'Escape') { + closeEndpointDialog(); + } + } + + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, [closeEndpointDialog, endpointDialogOpen]); + return (
@@ -814,6 +831,78 @@ function App() { {toast && } + {endpointDialogOpen && ( + + )} + + ); +} + +function EndpointDialog({ draftSettings, onApply, onCancel, onChange }) { + return ( +
+
+
{ + event.preventDefault(); + onApply(); + }} + > +
+
+

Endpoint settings

+

Changes only apply after you press Apply.

+
+ +
+ +
+ + +
+ +
+ + +
+
+
); } diff --git a/web-client/src/styles.css b/web-client/src/styles.css index b6b36b5..dbfa414 100644 --- a/web-client/src/styles.css +++ b/web-client/src/styles.css @@ -144,6 +144,7 @@ code { } .brand-copy span, +.endpoint-summary-row span, .settings-field span, .nav-label { color: var(--on-surface-variant); @@ -237,6 +238,35 @@ code { gap: 5px; } +.endpoint-summary { + background: rgba(255, 255, 255, 0.68); + border: 1px solid rgba(169, 180, 185, 0.35); + border-radius: var(--radius); + display: flex; + flex-direction: column; + gap: 9px; + padding: 10px; +} + +.endpoint-summary-row { + min-width: 0; +} + +.endpoint-summary-row span, +.endpoint-summary-row strong { + display: block; +} + +.endpoint-summary-row strong { + color: #172033; + font-size: 12px; + font-weight: 800; + margin-top: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .settings-field input, .filter-field input, .filter-field select { @@ -998,6 +1028,71 @@ tbody tr.selected-row td.action-col { overflow-wrap: anywhere; } +.dialog-backdrop { + align-items: center; + background: rgba(15, 23, 42, 0.36); + display: flex; + inset: 0; + justify-content: center; + padding: 24px; + position: fixed; + z-index: 110; +} + +.dialog-panel { + background: var(--surface-lowest); + border: 1px solid rgba(169, 180, 185, 0.5); + border-radius: var(--radius); + box-shadow: var(--shadow-lg); + max-height: calc(100vh - 48px); + overflow: auto; + width: min(480px, 100%); +} + +.dialog-panel form { + display: flex; + flex-direction: column; +} + +.dialog-header { + align-items: flex-start; + border-bottom: 1px solid #eef2f7; + display: flex; + gap: 14px; + justify-content: space-between; + padding: 16px; +} + +.dialog-header h2 { + color: #111827; + font-size: 18px; + font-weight: 800; + line-height: 1.2; +} + +.dialog-header p { + color: var(--on-surface-variant); + font-size: 12px; + line-height: 1.45; + margin-top: 4px; +} + +.dialog-body { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; +} + +.dialog-actions { + align-items: center; + border-top: 1px solid #eef2f7; + display: flex; + gap: 8px; + justify-content: flex-end; + padding: 12px 16px 16px; +} + .toast { align-items: center; background: #111827; @@ -1143,4 +1238,18 @@ tbody tr.selected-row td.action-col { gap: 6px; } + .dialog-backdrop { + align-items: flex-start; + padding: 16px; + } + + .dialog-actions { + align-items: stretch; + flex-direction: column-reverse; + } + + .dialog-actions .btn { + width: 100%; + } + }