Test and deploy / test-and-deploy (push) Successful in 56s
One system: post-roast observations (weights, colour, DTR, cup notes, 'one change next batch', disproof) now live on the uploaded roast (actual_roasts.after, migration 011), edited on the roast detail view with weights/DTR prefilled from the .alog itself. The planner's screen section is removed (the print worksheet keeps its hand-fill copy), and the lot 'last refine' suggestion reads the note from both the new home and legacy plans, newest wins. Every roast now downloads two ways: the untouched original .alog, and an updated supplemental copy with the app's data written back as valid Artisan fields (weight, beans, roastingnotes incl. the LLM review, cuppingnotes incl. linked cupping score/flavors) — serialized as a Python literal so Artisan's ast.literal_eval re-imports it. Co-Authored-By: Claude Fable 5 <[email protected]>
228 lines
9.2 KiB
HTML
228 lines
9.2 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
<title>Roast history — Roast Planner</title>
|
|
<link rel="stylesheet" href="/app.css?v=__ASSET_VERSION__" />
|
|
<link rel="icon" href="/icon.svg?v=__ASSET_VERSION__" type="image/svg+xml" />
|
|
<meta name="theme-color" content="#A8481A" />
|
|
</head>
|
|
<body>
|
|
<div class="app-shell">
|
|
<nav class="side-nav" id="side-nav" aria-label="Main navigation">
|
|
<div class="side-nav-brand">
|
|
<span class="brand-mark" aria-hidden="true">◐</span>
|
|
<span class="brand-name">Roast Planner</span>
|
|
</div>
|
|
<div id="nav-links"></div>
|
|
<div class="nav-spacer"></div>
|
|
<button
|
|
class="icon-btn nav-collapse-toggle"
|
|
type="button"
|
|
id="nav-collapse"
|
|
aria-label="Collapse navigation"
|
|
title="Collapse navigation"
|
|
>
|
|
«
|
|
</button>
|
|
<div class="nav-user">
|
|
<div class="nav-user-avatar" id="nav-user-avatar" aria-hidden="true"></div>
|
|
<div class="nav-user-detail">
|
|
<span class="nav-user-email" id="account-email"></span>
|
|
<button class="nav-user-logout" type="button" id="btn-logout">
|
|
Log out
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="app-workspace" id="app-workspace">
|
|
<header class="app-header">
|
|
<div class="header-row-top">
|
|
<button
|
|
class="icon-btn nav-hamburger"
|
|
type="button"
|
|
id="nav-hamburger"
|
|
aria-label="Open navigation"
|
|
aria-expanded="false"
|
|
>
|
|
☰
|
|
</button>
|
|
<div class="brand">
|
|
<div class="brand-text">
|
|
<h1>Roast history</h1>
|
|
<p class="brand-sub">
|
|
Finished roasts uploaded from Artisan, with LLM reviews
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<div class="drawer-overlay hidden" id="drawer-overlay"></div>
|
|
|
|
<main class="page-content">
|
|
<section class="panel-card" id="roasts">
|
|
<div class="panel-head">
|
|
<h2>Actual roasts</h2>
|
|
<label class="filebtn ghost-btn small"
|
|
>Upload .alog<input
|
|
type="file"
|
|
id="roast-upload"
|
|
accept=".alog"
|
|
multiple
|
|
/></label>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p class="field-note" id="upload-status" hidden></p>
|
|
<div class="table-wrap">
|
|
<table class="data-table" id="roasts-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Roast</th>
|
|
<th>Plan</th>
|
|
<th>First crack</th>
|
|
<th>Drop</th>
|
|
<th>DTR</th>
|
|
<th>Loss</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="roasts-body"></tbody>
|
|
</table>
|
|
</div>
|
|
<p class="field-note">
|
|
Upload finished roasts here (they stay unattached to a plan), or
|
|
from the planner's Reference curve drawer to attach them to the
|
|
open plan. Click a row for the full review and curve.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel-card hidden" id="roast-detail-card">
|
|
<div class="panel-head">
|
|
<h2 id="detail-title">Roast detail</h2>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
<a class="ghost-btn small" id="detail-download" href="#" download
|
|
>Original .alog</a
|
|
>
|
|
<a
|
|
class="ghost-btn small"
|
|
id="detail-download-updated"
|
|
href="#"
|
|
download
|
|
title="The original file with this app's data (weights, notes, cupping) written back as Artisan fields — importable into Artisan"
|
|
>Updated .alog</a
|
|
>
|
|
<button class="ghost-btn small" type="button" id="detail-reevaluate">
|
|
Re-evaluate
|
|
</button>
|
|
<button class="ghost-btn small" type="button" id="detail-delete">
|
|
Delete
|
|
</button>
|
|
<button class="ghost-btn small" type="button" id="detail-close">
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p class="field-note" id="detail-meta"></p>
|
|
<div class="table-wrap">
|
|
<svg
|
|
id="roast-graph"
|
|
viewBox="0 0 714 360"
|
|
style="width:100%;max-width:900px;display:block"
|
|
role="img"
|
|
aria-label="Bean temperature curve of this roast"
|
|
></svg>
|
|
</div>
|
|
<div id="detail-stats" class="field-grid" style="margin-top:10px"></div>
|
|
|
|
<div id="detail-after" style="margin-top:16px">
|
|
<h3 style="margin:0 0 4px">After the roast</h3>
|
|
<p class="field-note" style="margin-top:0">
|
|
One change per batch. Weight loss is the honest instrument —
|
|
match the cup to a symptom, write the winner into "One
|
|
change", and the planner's ± Refine picks it up next time
|
|
you roast this lot. These values also go into the updated
|
|
.alog download.
|
|
</p>
|
|
<form id="after-form">
|
|
<div class="field-grid">
|
|
<label class="field"
|
|
><span class="field-label">Green in</span>
|
|
<div class="unit-input">
|
|
<input class="field-input" name="greenIn" /><span class="unit">g</span>
|
|
</div></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">Out</span>
|
|
<div class="unit-input">
|
|
<input class="field-input" name="out" /><span class="unit">g</span>
|
|
</div></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">Weight loss</span>
|
|
<div class="unit-input">
|
|
<input class="field-input" name="weightLossPct" /><span class="unit">%</span>
|
|
</div></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">Actual DTR</span>
|
|
<div class="unit-input">
|
|
<input class="field-input" name="actualDtrPct" /><span class="unit">%</span>
|
|
</div></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">vs target</span
|
|
><input class="field-input" name="vsTarget"
|
|
/></label>
|
|
<label class="field"
|
|
><span class="field-label">Colour WB/GR</span
|
|
><input class="field-input" name="colour"
|
|
/></label>
|
|
<label class="field"
|
|
><span class="field-label">Rested</span>
|
|
<div class="unit-input">
|
|
<input class="field-input" name="restedDays" /><span class="unit">days</span>
|
|
</div></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">Brew ratio</span
|
|
><input class="field-input" name="brewRatio"
|
|
/></label>
|
|
<label class="field"
|
|
><span class="field-label">Method</span
|
|
><input class="field-input" name="method"
|
|
/></label>
|
|
</div>
|
|
<label class="field"
|
|
><span class="field-label">Cup notes</span
|
|
><textarea class="field-input" name="cupNotes" rows="2"></textarea></label
|
|
>
|
|
<label class="field"
|
|
><span class="field-label">One change next batch</span
|
|
><input
|
|
class="field-input"
|
|
name="oneChange"
|
|
placeholder="e.g. first crack +0:30"
|
|
/></label>
|
|
<label class="field"
|
|
><span class="field-label">What would disprove it</span
|
|
><input class="field-input" name="disproof"
|
|
/></label>
|
|
<button class="primary-btn" type="submit" id="after-save">
|
|
Save after-roast
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="detail-evaluation" style="margin-top:14px"></div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
<script type="module" src="/js/roasts.js?v=__ASSET_VERSION__"></script>
|
|
</body>
|
|
</html>
|