Improve operational UX, recovery flows, and action safety
Implement the approved UX review across Fleet, Betas, Library, deployments, and Settings. Add reviewed promotion and reconciliation plans, atomic queued-removal cancellation, recoverable beta drafts and launch inputs, accessible controls, and mobile improvements. Validated with 473 tests, a clean typecheck, production build, and isolated desktop/mobile browser walkthroughs. Include the review and implementation coverage notes.
This commit is contained in:
@@ -30,12 +30,31 @@ export function getDeployment(id: string) {
|
||||
const collection = db().prepare(`SELECT c.id, c.name FROM server_collection_members m
|
||||
JOIN server_launch_collections c ON c.id = m.collection_id WHERE m.deployment_id = ?`).get(id) ?? null;
|
||||
return { deployment, events, recipe: launch ? summarizeRecipe(launch.recipe) : null, blueprintName: launch?.blueprintName ?? null,
|
||||
canResumeVerification: canResumeVerification(id), canResumeCleanup: canResumeCleanup(id), collection };
|
||||
canResumeVerification: canResumeVerification(id), canResumeCleanup: canResumeCleanup(id), canCancelRemoval: canCancelRemoval(id), collection };
|
||||
}
|
||||
|
||||
// This stage is written only after Komodo has confirmed deployment deletion.
|
||||
export const REMOVING_SERVER = 'Removing server files and registry';
|
||||
|
||||
export function canCancelRemoval(id: string): boolean {
|
||||
return !!db().prepare("SELECT id FROM server_deployments WHERE id = ? AND action = 'destroy' AND state = 'waiting' AND stage != ?").get(id, REMOVING_SERVER);
|
||||
}
|
||||
|
||||
/** Competes atomically with the worker's claim, before any external removal. */
|
||||
export function cancelRemoval(id: string): boolean {
|
||||
return db().transaction(() => {
|
||||
const result = db().prepare(`UPDATE server_deployments SET state = 'cancelled', stage = 'Removal cancelled', finished_at = ?, error = NULL
|
||||
WHERE id = ? AND action = 'destroy' AND state = 'waiting' AND stage != ?`).run(new Date().toISOString(), id, REMOVING_SERVER);
|
||||
if (!result.changes) return false;
|
||||
recordDeploymentEvent(id, 'Queued removal cancelled. The server and its files were kept.');
|
||||
return true;
|
||||
})();
|
||||
}
|
||||
|
||||
export function claimRemoval(id: string): boolean {
|
||||
return db().prepare("UPDATE server_deployments SET state = 'running' WHERE id = ? AND action = 'destroy' AND state = 'waiting'").run(id).changes > 0;
|
||||
}
|
||||
|
||||
export function canResumeCleanup(id: string): boolean {
|
||||
return !!db().prepare(`SELECT d.id FROM server_deployments d
|
||||
WHERE d.id = ? AND d.action = 'destroy' AND d.state IN ('failed', 'interrupted') AND d.stage = ?
|
||||
@@ -81,8 +100,9 @@ export function finishDeployment(id: string, result: { ok: boolean; message?: st
|
||||
}
|
||||
|
||||
export function listDeployments() {
|
||||
return db().prepare(`SELECT d.*, EXISTS(SELECT 1 FROM servers s WHERE s.name = d.server) AS server_exists
|
||||
FROM server_deployments d ORDER BY started_at DESC, rowid DESC`).all();
|
||||
return db().prepare(`SELECT d.*, EXISTS(SELECT 1 FROM servers s WHERE s.name = d.server) AS server_exists,
|
||||
(d.action = 'destroy' AND d.state = 'waiting' AND d.stage != ?) AS canCancelRemoval
|
||||
FROM server_deployments d ORDER BY started_at DESC, rowid DESC`).all(REMOVING_SERVER);
|
||||
}
|
||||
|
||||
export function interruptDeployments(): void {
|
||||
|
||||
Reference in New Issue
Block a user