Removable reference curves, audit fixes across pages, shaded Academy art
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:
2026-08-11 16:59:28 -04:00
co-authored by Claude Fable 5
parent 8b80c3fa6f
commit 385474ecef
14 changed files with 368 additions and 34 deletions
+24 -3
View File
@@ -173,7 +173,8 @@ export function createApp({
const parser =
req.method === "POST" && req.path === "/api/admin/backup/import"
? jsonBodyBackup
: req.method === "POST" && req.path === "/api/roasts"
: req.method === "POST" &&
(req.path === "/api/roasts" || req.path === "/api/alog")
? jsonBodyLarge
: jsonBody;
return parser(req, res, next);
@@ -2483,10 +2484,30 @@ export function createApp({
coerced.fault_cups,
coerced.cup_count,
);
// Optional plan reattachment (mirrors PUT /api/roasts): a session created
// without a plan would otherwise be stuck "Untitled" forever. Ownership of
// the target plan is enforced; absent field leaves the link unchanged.
let planClause = "";
const params = [coerced, total, req.params.id, req.user.id];
if (req.body.roastPlanId !== undefined) {
const planId = req.body.roastPlanId;
if (planId !== null) {
if (!UUID_RE.test(String(planId)))
return res.status(400).json({ ok: false, code: "bad_plan" });
const owned = await db.query(
"SELECT 1 FROM roast_plans WHERE id=$1 AND user_id=$2",
[planId, req.user.id],
);
if (!owned.rowCount)
return res.status(404).json({ ok: false, code: "plan_not_found" });
}
params.push(planId);
planClause = ", roast_plan_id=$5";
}
const row = (
await db.query(
"UPDATE cupping_sessions SET data=$1, total_score=$2, updated_at=now() WHERE id=$3 AND user_id=$4 RETURNING *",
[coerced, total, req.params.id, req.user.id],
`UPDATE cupping_sessions SET data=$1, total_score=$2, updated_at=now()${planClause} WHERE id=$3 AND user_id=$4 RETURNING *`,
params,
)
).rows[0];
if (!row) return res.status(404).json({ ok: false, code: "not_found" });