Rebuild printable roast plans and remove machine-based adjustments
Test and deploy / test-and-deploy (push) Successful in 2m28s
Test and deploy / test-and-deploy (push) Successful in 2m28s
This commit is contained in:
1 parent
e9f2393c96
commit
824a964f9d
30 files changed
+1721
-908
No files matched your search
+11
-82
@@ -11,16 +11,14 @@ const fmtTime = (s) =>
|
||||
? "—"
|
||||
: `${Math.floor(Math.round(s) / 60)}:${String(Math.round(s) % 60).padStart(2, "0")}`;
|
||||
|
||||
// Metric rows of the report: what the planner actually consumes from a machine's profile.
|
||||
// `fmt` renders read-only values; overrides are edited in the raw unit (°C / seconds / ×).
|
||||
// Descriptive history only; machine statistics never modify plans.
|
||||
const METRICS = [
|
||||
{ key: "chargeTempC", label: "Charge temp (curve fallback)", unit: "°C" },
|
||||
{ key: "turningPointS", label: "Turning point time (thermal lag)", unit: "s", fmt: fmtTime },
|
||||
{ key: "turningPointTempC", label: "Turning point temp", unit: "°C" },
|
||||
{ key: "yellowTempC", label: "Yellow temp (curve fallback)", unit: "°C" },
|
||||
{ key: "firstCrackTempC", label: "First crack temp (curve fallback)", unit: "°C" },
|
||||
{ key: "dropTempC", label: "Drop temp (curve fallback)", unit: "°C" },
|
||||
{ key: "paceFactor", label: "Pace factor (multiplies first-crack timing)", unit: "×" },
|
||||
{ key: "chargeTempC", label: "Charge BT", unit: "°F" },
|
||||
{ key: "turningPointS", label: "Turning point time", unit: "", fmt: fmtTime },
|
||||
{ key: "turningPointTempC", label: "Turning point BT", unit: "°F" },
|
||||
{ key: "yellowTempC", label: "Yellow BT", unit: "°F" },
|
||||
{ key: "firstCrackTempC", label: "First crack BT", unit: "°F" },
|
||||
{ key: "dropTempC", label: "Drop BT", unit: "°F" },
|
||||
];
|
||||
|
||||
function renderRoasters() {
|
||||
@@ -135,10 +133,6 @@ function renderRoasters() {
|
||||
);
|
||||
}
|
||||
|
||||
function overrideInputs() {
|
||||
return [...document.querySelectorAll("#report-rows input[data-key]")];
|
||||
}
|
||||
|
||||
function renderReport() {
|
||||
const card = document.getElementById("report-card");
|
||||
if (!selectedId || !profile) {
|
||||
@@ -168,32 +162,9 @@ function renderReport() {
|
||||
? metric.key === "paceFactor"
|
||||
? "learned from plan logs"
|
||||
: "—"
|
||||
: `${metric.fmt ? metric.fmt(learnedValue) : learnedValue} ${metric.fmt ? "" : metric.unit}`.trim();
|
||||
: `${metric.fmt ? metric.fmt(learnedValue) : Math.round(learnedValue * 1.8 + 32)} ${metric.fmt ? "" : metric.unit}`.trim();
|
||||
|
||||
const overrideCell = document.createElement("td");
|
||||
const input = document.createElement("input");
|
||||
input.className = "field-input sm";
|
||||
input.type = "number";
|
||||
input.step = "any";
|
||||
input.style.maxWidth = "110px";
|
||||
input.dataset.key = metric.key;
|
||||
input.placeholder = metric.unit;
|
||||
const overrideValue = profile.overrides?.[metric.key];
|
||||
if (overrideValue != null) input.value = overrideValue;
|
||||
overrideCell.append(input);
|
||||
|
||||
const appliedCell = document.createElement("td");
|
||||
const applied =
|
||||
overrideValue ??
|
||||
(metric.key === "paceFactor" ? null : profile.learnedMedians?.[metric.key]);
|
||||
appliedCell.textContent =
|
||||
applied == null
|
||||
? metric.key === "paceFactor"
|
||||
? "plan-log pace"
|
||||
: "reference default"
|
||||
: `${metric.fmt ? metric.fmt(applied) : applied} ${metric.fmt ? "" : metric.unit}${overrideValue != null ? " (your tweak)" : " (learned)"}`.trim();
|
||||
|
||||
tr.append(label, learnedCell, overrideCell, appliedCell);
|
||||
tr.append(label, learnedCell);
|
||||
return tr;
|
||||
}),
|
||||
);
|
||||
@@ -209,23 +180,7 @@ function renderReport() {
|
||||
const list = document.createElement("ul");
|
||||
list.style.paddingLeft = "18px";
|
||||
list.style.fontSize = "13px";
|
||||
const items = [];
|
||||
if (profile.n > 0)
|
||||
items.push(
|
||||
"Blank milestone temps on the planner's curve use this machine's learned medians instead of the reference machine.",
|
||||
`New plans assume the turning point around ${fmtTime(profile.medians?.turningPointS)} — this machine's measured thermal lag.`,
|
||||
);
|
||||
if (profile.paceFactorOverride != null)
|
||||
items.push(
|
||||
`First-crack timing is multiplied by your pace tweak ×${profile.paceFactorOverride} instead of the plan-log learned pace.`,
|
||||
);
|
||||
items.push(
|
||||
"Roast reviews compare each new roast against this machine's own typical behavior, not a generic one.",
|
||||
);
|
||||
if (!(roaster?.isDefault ?? false))
|
||||
items.push(
|
||||
"Note: the planner uses the DEFAULT machine's profile — make this one default to plan with it.",
|
||||
);
|
||||
const items = ["Historical measurements only. Selecting a default roaster organizes uploads; it does not change roast plans."];
|
||||
for (const text of items) {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = text;
|
||||
@@ -246,31 +201,7 @@ async function selectRoaster(id) {
|
||||
renderReport();
|
||||
}
|
||||
|
||||
async function saveOverrides(clear = false) {
|
||||
if (!selectedId) return;
|
||||
const overrides = {};
|
||||
if (!clear)
|
||||
for (const input of overrideInputs()) {
|
||||
if (input.value === "") continue;
|
||||
const value = Number(input.value);
|
||||
if (!Number.isFinite(value)) {
|
||||
showToast(`"${input.value}" is not a number.`, "fail");
|
||||
return;
|
||||
}
|
||||
overrides[input.dataset.key] = value;
|
||||
}
|
||||
try {
|
||||
await api(`/api/roasters/${selectedId}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ overrides }),
|
||||
});
|
||||
showToast(clear ? "Tweaks cleared — using learned values." : "Tweaks saved.");
|
||||
await loadRoasters();
|
||||
await selectRoaster(selectedId);
|
||||
} catch (error) {
|
||||
showToast(error.message, "fail");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function loadRoasters() {
|
||||
try {
|
||||
@@ -298,8 +229,6 @@ document.getElementById("roaster-form").addEventListener("submit", async (event)
|
||||
showToast(error.message, "fail");
|
||||
}
|
||||
});
|
||||
document.getElementById("save-overrides").addEventListener("click", () => saveOverrides(false));
|
||||
document.getElementById("clear-overrides").addEventListener("click", () => saveOverrides(true));
|
||||
document.getElementById("btn-logout").addEventListener("click", async () => {
|
||||
await protectedFetch("/api/auth/logout", { method: "POST" });
|
||||
location.assign("/");
|
||||
|
||||
Reference in new issue
Block a user