Add roaster (machine) management, mobile header-tools redesign, and profile pictures
Test and deploy / test-and-deploy (push) Successful in 1m0s

Machines:
- roasters table + actual_roasts.roaster_id (migrations 009): per-user
  machines with a default; uploads attach to the default roaster and are
  reassignable from the roast detail view
- Learning is now per machine: /api/roaster-profile scopes to a roaster
  (default when unspecified) and applies the user's override tweaks
  (milestone temps, TP time, pace factor) on top of learned medians
- /roasters page: machine list + analysis report showing learned vs
  applied values with editable tweaks, typical RoR, and a plain-language
  list of the adjustments applied to plans; planner honors a manual pace
  override; evaluations use the roast's own machine profile

Mobile header redesign:
- The planner's header tools collapse behind a single 'Tools ▾'
  disclosure (dropdown card) at ≤720px, keeping the fixed three-row
  mobile header with no wrapped or scrolling button rows; desktop
  renders inline via display:contents (same DOM, same handlers)

Profile pictures:
- users.avatar (migration 010) + PUT/GET/DELETE /api/account/avatar;
  client-side centre-crop resize to 128px JPEG; avatar shows in the nav
  chip on every page; included in full backups

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Shane Maynard
2026-08-09 08:17:10 -04:00
co-authored by Claude Fable 5
parent e62b9601a2
commit a341d67467
15 changed files with 1291 additions and 62 deletions
+57 -1
View File
@@ -3001,10 +3001,66 @@ select.f {
min-height: 40px;
}
/* ── Planner header tools (page-scoped actions moved out of the left nav) ── */
/* ── Planner header tools (page-scoped actions moved out of the left nav) ──
Desktop: the tools render inline in the header actions row (display: contents).
≤720px: they collapse behind a single "Tools ▾" disclosure that opens a dropdown card, so
the mobile header keeps its fixed three-row height (--header-h) with no wrapped or
horizontally scrolling button rows. */
.header-tools {
flex-wrap: wrap;
justify-content: flex-end;
row-gap: 6px;
max-width: 100%;
position: relative;
}
.header-tools-toggle {
display: none;
}
.header-tools-group {
display: contents;
}
@media (max-width: 720px) {
.header-tools {
flex-wrap: nowrap;
}
.header-tools-toggle {
display: inline-flex;
align-items: center;
flex: 0 0 auto;
}
.header-tools-toggle[aria-expanded="true"] {
background: var(--ember-soft);
border-color: var(--ember);
color: var(--ember-strong);
}
.header-tools-group {
display: none;
}
.header-tools-group.open {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 6px;
position: absolute;
right: 0;
top: calc(100% + 6px);
z-index: 70;
min-width: 220px;
padding: 10px;
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius-md);
box-shadow: var(--shadow-pop);
}
.header-tools-group .ghost-btn {
justify-content: flex-start;
text-align: left;
width: 100%;
}
.header-tools-group .header-divider {
display: block;
width: 100%;
height: 1px;
margin: 2px 0;
}
}