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:
@@ -43,6 +43,7 @@ export async function GET({ params }: { params: { id: string } }) {
|
||||
return json({
|
||||
script,
|
||||
versions,
|
||||
installedVersions: script.kind === 'script' || script.kind === 'plugin' ? db().prepare(`SELECT i.server, v.version, v.id AS versionId, i.updated_at AS recordedAt FROM server_scripts i JOIN script_versions v ON v.id = i.script_version_id WHERE v.script_id = ? AND i.rel_path = ? ORDER BY i.server`).all(id, script.target_rel_path) : [],
|
||||
matchedServers: matched,
|
||||
runningBetaId: runningBeta?.id ?? null
|
||||
});
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { getScript, setScope, ScriptError, type ScopeType } from '$lib/server/fleet/scripts.js';
|
||||
import { getScript, setScope, serversMatchingScope, ScriptError, type ScopeType } from '$lib/server/fleet/scripts.js';
|
||||
import { unsetScopeAndDetach } from '$lib/server/fleet/beta.js';
|
||||
import { db } from '$lib/server/db/index.js';
|
||||
|
||||
export async function GET({ params, url }: { params: { id: string }; url: URL }) {
|
||||
const id = Number(params.id);
|
||||
if (!Number.isSafeInteger(id) || id <= 0) return json({ error: 'Invalid file ID.' }, { status: 400 });
|
||||
const script = getScript(id);
|
||||
if (!script) return json({ error: 'File not found.' }, { status: 404 });
|
||||
const type = url.searchParams.get('type');
|
||||
const value = url.searchParams.get('value');
|
||||
if (type !== 'none' && type !== 'game' && type !== 'map' || type !== 'none' && !value) return json({ error: 'Choose a complete scope.' }, { status: 400 });
|
||||
const before = serversMatchingScope(script.scope_type, script.scope_value);
|
||||
const after = type === 'none' ? [] : serversMatchingScope(type as ScopeType, value);
|
||||
const composed = script.kind === 'config' || script.kind === 'baseline';
|
||||
const tracked = composed ? [] : (db().prepare(`SELECT DISTINCT server FROM server_scripts WHERE rel_path = ? AND script_version_id IN (SELECT id FROM script_versions WHERE script_id = ?) ORDER BY server`).all(script.target_rel_path, script.id) as Array<{ server: string }>).map(row => row.server);
|
||||
return json({ before, after, added: after.filter(name => !before.includes(name)), removed: before.filter(name => !after.includes(name)),
|
||||
affected: composed ? [...new Set([...before, ...after])].sort() : type === 'none' ? tracked : [...new Set([...before, ...after])].sort(),
|
||||
composed, detached: type === 'none' ? tracked : [] }, { headers: { 'Cache-Control': 'no-store' } });
|
||||
}
|
||||
|
||||
function parseId(raw: string): number | null {
|
||||
const n = Number.parseInt(raw, 10);
|
||||
|
||||
Reference in New Issue
Block a user