Add green-bean inventory and cupping features

Ports inventory management and SCA-style cupping scoring from
hope_roaster: lot tracking with audit-logged consumption, cupping
sessions with server-authoritative scoring and a live radar chart,
and links from the planner (draw-from-lot, open-cupping-session).

Also fixes the Blend/Single-origin toggle layout, replaces tooltips
with an in-context "why" teaching layer, and stages the planner UI
into pre-roast vs. post-roast phases.
This commit is contained in:
2026-07-30 14:47:15 -04:00
parent 9f0a3dbc74
commit 59645e59b5
43 changed files with 7582 additions and 566 deletions
+1219 -51
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -13,7 +13,11 @@ export function createDb(connectionString = process.env.DATABASE_URL) {
? { rejectUnauthorized: true }
: undefined,
});
return { query: (...args) => pool.query(...args), close: () => pool.end() };
return {
query: (...args) => pool.query(...args),
connect: () => pool.connect(),
close: () => pool.end(),
};
}
/** Apply each versioned SQL file once; failed migrations are not recorded. */
+9
View File
@@ -0,0 +1,9 @@
/** Sends transactional email via SMTP when configured; the caller decides what to do when this
* returns false (e.g. log the link so an admin can hand it to the user manually). */
export async function sendMail({ smtpUrl, from, to, subject, text }) {
if (!smtpUrl) return false;
const { default: nodemailer } = await import("nodemailer");
const transporter = nodemailer.createTransport(smtpUrl);
await transporter.sendMail({ from, to, subject, text });
return true;
}