change UI set Endpoint

This commit is contained in:
2026-06-03 09:12:51 +07:00
parent e7dce2f0e9
commit c01d9c7e40
2 changed files with 220 additions and 22 deletions

View File

@@ -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 (
<div className="app-shell">
<aside className="sidebar">
@@ -536,29 +563,19 @@ function App() {
/>
<div className="nav-label">Endpoint</div>
<label className="settings-field">
<span>Package server</span>
<input
value={draftSettings.packageBaseUrl}
onChange={(event) => setDraftSettings((current) => ({
...current,
packageBaseUrl: event.target.value
}))}
/>
</label>
<label className="settings-field">
<span>Local Agent</span>
<input
value={draftSettings.agentBaseUrl}
onChange={(event) => setDraftSettings((current) => ({
...current,
agentBaseUrl: event.target.value
}))}
/>
</label>
<button className="btn btn-secondary full" type="button" onClick={applySettings}>
<div className="endpoint-summary" aria-label="Current endpoints">
<div className="endpoint-summary-row">
<span>Package server</span>
<strong title={packageBaseUrl}>{packageBaseUrl}</strong>
</div>
<div className="endpoint-summary-row">
<span>Local Agent</span>
<strong title={agentBaseUrl}>{agentBaseUrl}</strong>
</div>
</div>
<button className="btn btn-secondary full" type="button" onClick={openEndpointDialog}>
<Settings size={15} aria-hidden="true" />
Apply
Endpoint settings
</button>
</div>
</aside>
@@ -814,6 +831,78 @@ function App() {
</main>
{toast && <Toast toast={toast} />}
{endpointDialogOpen && (
<EndpointDialog
draftSettings={draftSettings}
onApply={applySettings}
onCancel={closeEndpointDialog}
onChange={setDraftSettings}
/>
)}
</div>
);
}
function EndpointDialog({ draftSettings, onApply, onCancel, onChange }) {
return (
<div className="dialog-backdrop">
<section
aria-labelledby="endpoint-dialog-title"
aria-modal="true"
className="dialog-panel"
role="dialog"
>
<form
onSubmit={(event) => {
event.preventDefault();
onApply();
}}
>
<div className="dialog-header">
<div>
<h2 id="endpoint-dialog-title">Endpoint settings</h2>
<p>Changes only apply after you press Apply.</p>
</div>
<button className="icon-button subtle" type="button" title="Close" onClick={onCancel}>
<X size={16} aria-hidden="true" />
</button>
</div>
<div className="dialog-body">
<label className="settings-field">
<span>Package server</span>
<input
autoFocus
value={draftSettings.packageBaseUrl}
onChange={(event) => onChange((current) => ({
...current,
packageBaseUrl: event.target.value
}))}
/>
</label>
<label className="settings-field">
<span>Local Agent</span>
<input
value={draftSettings.agentBaseUrl}
onChange={(event) => onChange((current) => ({
...current,
agentBaseUrl: event.target.value
}))}
/>
</label>
</div>
<div className="dialog-actions">
<button className="btn btn-secondary" type="button" onClick={onCancel}>
Cancel
</button>
<button className="btn btn-primary" type="submit">
<Settings size={15} aria-hidden="true" />
Apply endpoints
</button>
</div>
</form>
</section>
</div>
);
}