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:
@@ -629,6 +629,13 @@ def get_pending_config():
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/config/pending', methods=['DELETE'])
|
||||
def cancel_pending_config():
|
||||
"""Discard pending configuration changes without restarting any containers."""
|
||||
_clear_pending_restart()
|
||||
return jsonify({'message': 'Pending changes discarded'})
|
||||
|
||||
|
||||
@app.route('/api/config/apply', methods=['POST'])
|
||||
def apply_pending_config():
|
||||
"""Apply pending configuration by restarting containers via docker compose up -d."""
|
||||
|
||||
+28
-5
@@ -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,16 +76,25 @@ function PendingRestartBanner({ pending, onApply }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<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}
|
||||
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"
|
||||
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>
|
||||
|
||||
{confirming && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
|
||||
@@ -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