feat(pending-banner): add Discard button to cancel pending restart without applying
- DELETE /api/config/pending endpoint calls _clear_pending_restart() - cellAPI.cancelPending() calls the new endpoint - PendingRestartBanner shows a "Discard" button alongside "Apply Now"; clicking it drops the pending state without restarting any containers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+34
-11
@@ -35,9 +35,10 @@ import Vault from './pages/Vault';
|
||||
import ContainerDashboard from './components/ContainerDashboard';
|
||||
import CellNetwork from './pages/CellNetwork';
|
||||
|
||||
function PendingRestartBanner({ pending, onApply }) {
|
||||
function PendingRestartBanner({ pending, onApply, onCancel }) {
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
const [applying, setApplying] = useState(false);
|
||||
const [cancelling, setCancelling] = useState(false);
|
||||
|
||||
const handleApply = async () => {
|
||||
setApplying(true);
|
||||
@@ -49,6 +50,15 @@ function PendingRestartBanner({ pending, onApply }) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = async () => {
|
||||
setCancelling(true);
|
||||
try {
|
||||
await onCancel();
|
||||
} finally {
|
||||
setCancelling(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 bg-warning-50 border border-warning-300 rounded-lg p-4">
|
||||
@@ -66,14 +76,23 @@ function PendingRestartBanner({ pending, onApply }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setConfirming(true)}
|
||||
disabled={applying}
|
||||
className="ml-4 flex-shrink-0 flex items-center gap-1.5 px-3 py-1.5 bg-warning-600 hover:bg-warning-700 disabled:opacity-50 text-white text-sm font-medium rounded-md transition-colors"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${applying ? 'animate-spin' : ''}`} />
|
||||
{applying ? 'Restarting…' : 'Apply Now'}
|
||||
</button>
|
||||
<div className="ml-4 flex-shrink-0 flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
disabled={applying || cancelling}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-white hover:bg-gray-50 disabled:opacity-50 text-warning-700 text-sm font-medium rounded-md border border-warning-300 transition-colors"
|
||||
>
|
||||
{cancelling ? 'Discarding…' : 'Discard'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setConfirming(true)}
|
||||
disabled={applying || cancelling}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-warning-600 hover:bg-warning-700 disabled:opacity-50 text-white text-sm font-medium rounded-md transition-colors"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${applying ? 'animate-spin' : ''}`} />
|
||||
{applying ? 'Restarting…' : 'Apply Now'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +166,11 @@ function App() {
|
||||
|
||||
const handleApply = useCallback(async () => {
|
||||
await cellAPI.applyPending();
|
||||
// Optimistically clear the banner; containers are restarting
|
||||
setPending({ needs_restart: false, changes: [] });
|
||||
}, []);
|
||||
|
||||
const handleCancel = useCallback(async () => {
|
||||
await cellAPI.cancelPending();
|
||||
setPending({ needs_restart: false, changes: [] });
|
||||
}, []);
|
||||
|
||||
@@ -209,7 +232,7 @@ function App() {
|
||||
)}
|
||||
|
||||
{isOnline && pending.needs_restart && (
|
||||
<PendingRestartBanner pending={pending} onApply={handleApply} />
|
||||
<PendingRestartBanner pending={pending} onApply={handleApply} onCancel={handleCancel} />
|
||||
)}
|
||||
|
||||
<Routes>
|
||||
|
||||
@@ -44,6 +44,7 @@ export const cellAPI = {
|
||||
exportConfig: (format = 'json') => api.get('/api/config/export', { params: { format } }),
|
||||
importConfig: (config, format = 'json') => api.post('/api/config/import', { config, format }),
|
||||
getPending: () => api.get('/api/config/pending'),
|
||||
cancelPending: () => api.delete('/api/config/pending'),
|
||||
applyPending: () => api.post('/api/config/apply'),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user