US04-05: Operate Rename Plans in the Browser (#69)

This commit was merged in pull request #69.
This commit is contained in:
2026-08-16 11:52:10 +02:00
parent 7103cb84bf
commit 8d96acd067
12 changed files with 809 additions and 9 deletions

View File

@@ -327,11 +327,15 @@ export async function renderStats(root) {
// Approving never renames anything — it records the approved name only.
export async function renderAlbums(root, params = {}) {
setActiveNav("albums");
let evidence, proposals;
let evidence, proposals, recovery;
try {
[evidence, proposals] = await Promise.all([
[evidence, proposals, recovery] = await Promise.all([
api.albumEvidence({ limit: 500 }),
api.albumProposals(),
// An unresolved rename means folder paths on disk may disagree with the
// database, so the server refuses name changes until it is resolved. Ask up
// front rather than offering a button that is guaranteed to fail.
api.renameRecovery(),
]);
} catch (error) {
root.replaceChildren(errorBanner(`Failed to load albums: ${error.message}`));
@@ -370,17 +374,27 @@ export async function renderAlbums(root, params = {}) {
})
);
const renameBlocked = recovery.blocks_mutation;
const detail = selected
? albumDetail(
root,
folders.find((folder) => folder.album === selected),
byAlbum.get(selected),
params
params,
renameBlocked
)
: el("p", { class: "muted" }, "No albums with evidence yet.");
root.replaceChildren(
el("h1", {}, "Albums"),
renameBlocked
? el(
"div",
{ class: "alert", role: "alert", "data-testid": "rename-blocked" },
"An interrupted rename is unresolved. Name changes are paused until it is " +
"resolved on the Renames page; browsing stays available."
)
: null,
el(
"div",
{ class: "toolbar" },
@@ -389,6 +403,8 @@ export async function renderAlbums(root, params = {}) {
{
class: "primary",
"data-testid": "generate-proposals",
disabled: renameBlocked ? "disabled" : false,
title: renameBlocked ? "an unresolved rename must be resolved first" : false,
onclick: async () => {
try {
await api.generateProposals({});
@@ -405,7 +421,7 @@ export async function renderAlbums(root, params = {}) {
);
}
function albumDetail(root, folder, proposal, params) {
function albumDetail(root, folder, proposal, params, renameBlocked = false) {
if (!folder) return el("p", { class: "muted" }, "Album not found.");
const evidenceBlock = el(
@@ -473,8 +489,13 @@ function albumDetail(root, folder, proposal, params) {
{
class: "primary",
"data-testid": "approve",
disabled: proposal.stale || proposal.status === "error" ? "disabled" : false,
title: proposal.stale ? "evidence changed — regenerate first" : false,
disabled:
renameBlocked || proposal.stale || proposal.status === "error" ? "disabled" : false,
title: renameBlocked
? "an unresolved rename must be resolved first"
: proposal.stale
? "evidence changed — regenerate first"
: false,
onclick: () => mutate(root, () => api.approveProposal(proposal.album, { expected_version: proposal.version }), params),
},
proposal.status === "approved" ? "Approved" : "Approve name"