Removable reference curves, audit fixes across pages, shaded Academy art
Test and deploy / test-and-deploy (push) Successful in 1m8s
Test and deploy / test-and-deploy (push) Successful in 1m8s
The planner's Artisan .alog drawer now shows what's attached with a "Remove reference curve" control (re-rendered per drawer open), so adding a reference curve is no longer a one-way door; /api/alog shares the 8 MB body cap so real-sized logs parse instead of failing with "bad_request". Deep-evaluation fixes: editing a brew of an archived bean no longer silently detaches the bean; the roasts pending-review poll no longer wipes in-progress after-roast edits; roasters gain an Edit (rename/model) action; the gear page refuses to autosave over a failed load; duplicating a plan carries its custom name; cupping sessions can attach a plan after creation (ownership-checked PUT + selector); admin user deletion also refreshes plans/audit; cupping cup-count subtitle stays live; roasts error-row colspan corrected. Regression tests cover the new cupping PUT and the /api/alog body cap. Academy scenes drop the flat paper-cutout look: shared defs provide radial-gradient shading on every bean/half-bean/particle, flame gradients with radiant halos, soft ground shadows, and a warm-lit stage background; fill-shift animations now ride a partial-opacity tint overlay so shading survives the color change. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
+14
-2
@@ -86,7 +86,7 @@ async function loadRoasts() {
|
||||
schedulePoll();
|
||||
} catch {
|
||||
document.getElementById("roasts-body").innerHTML =
|
||||
`<tr><td colspan="7" class="empty-state">Could not load roasts.</td></tr>`;
|
||||
`<tr><td colspan="6" class="empty-state">Could not load roasts.</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,7 +406,12 @@ async function openDetail(id, { keepScroll = false } = {}) {
|
||||
`/api/roasts/${encodeURIComponent(id)}/download`;
|
||||
document.getElementById("detail-download-updated").href =
|
||||
`/api/roasts/${encodeURIComponent(id)}/download?variant=updated`;
|
||||
renderAfterForm(detail);
|
||||
// The 4s pending-evaluation poll re-runs openDetail; never let that background refresh
|
||||
// overwrite after-roast fields the user is currently editing.
|
||||
const afterForm = document.getElementById("after-form");
|
||||
if (!keepScroll || !(afterFormDirty || afterForm.contains(document.activeElement)))
|
||||
renderAfterForm(detail);
|
||||
if (!keepScroll) afterFormDirty = false;
|
||||
renderGraph(detail);
|
||||
renderStats(detail);
|
||||
renderEvaluation(detail);
|
||||
@@ -421,6 +426,12 @@ const AFTER_FIELDS = [
|
||||
"restedDays", "brewRatio", "method", "cupNotes", "oneChange", "disproof",
|
||||
];
|
||||
|
||||
// True while the after-roast form holds unsaved user edits (cleared on save/open).
|
||||
let afterFormDirty = false;
|
||||
document.getElementById("after-form").addEventListener("input", () => {
|
||||
afterFormDirty = true;
|
||||
});
|
||||
|
||||
function renderAfterForm(detail) {
|
||||
const form = document.getElementById("after-form");
|
||||
const after = detail.after ?? {};
|
||||
@@ -449,6 +460,7 @@ document.getElementById("after-form").addEventListener("submit", async (event) =
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ after }),
|
||||
});
|
||||
afterFormDirty = false;
|
||||
showToast("After-roast saved — included in the updated .alog.");
|
||||
} catch (error) {
|
||||
showToast(error.message, "fail");
|
||||
|
||||
Reference in New Issue
Block a user