feat: add Doris Kitchen import review workflow

This commit is contained in:
Fizzlepoof
2026-05-21 23:31:53 +00:00
parent 3f333595a2
commit d5cfdbe8b5
11 changed files with 934 additions and 265 deletions

View File

@@ -4,25 +4,67 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title or 'Doris Kitchen' }}</title>
<link rel="stylesheet" href="/static/app.css?v=5">
<link rel="stylesheet" href="/static/app.css?v=9">
</head>
<body>
<header class="site-header">
<div>
<p class="eyebrow">Doris Kitchen</p>
<h1>Household Recipe Queue</h1>
<p class="lede">Pick recipes, bin bad ones, learn the household taste, and build a cheaper meal week without wasting half a packet of mince. Ha ha.</p>
<body class="doris-hot-fuzz app-kitchen">
<div class="incident-tape" aria-hidden="true"></div>
<header class="site-header casefile-header app-casefile app-casefile-kitchen">
<div class="film-grain" aria-hidden="true"></div>
<div class="cinematic-glow" aria-hidden="true"></div>
<div class="brand-row">
<div class="nav-brand">
<span class="nav-kicker">N.W.A. Case File</span>
<strong>Doris Constabulary</strong>
</div>
<nav class="nav-links" aria-label="Doris Kitchen navigation">
<a class="nav-link" href="http://10.5.1.16:8787/">Dashboard</a>
<a class="nav-link" href="/">Queue</a>
<a class="nav-link" href="/search">Search</a>
<a class="nav-link" href="/planner">Planner</a>
<a class="nav-link" href="/imports">Imports</a>
<a class="nav-link" href="/failures">Failures</a>
</nav>
</div>
<div class="casefile-title-row">
<div>
<p class="eyebrow">Sandford pantry desk</p>
<div class="casefile-stamp recipe-evidence-tag">Pantry Evidence</div>
<h1>Doris Kitchen</h1>
<p class="lede">Household recipe triage, import cleanup, and meal-planning evidence review with more cinematic energy and less boring admin chrome.</p>
<div class="brand-badges" aria-label="Theme badges">
<span class="pill badge-hotfuzz">For the Greater Good</span>
<span class="pill">Evidence locker: recipes</span>
<span class="pill">Case type: Pantry Evidence</span>
</div>
</div>
<div class="hot-fuzz-art" aria-hidden="true">
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
<defs>
<linearGradient id="kitchen-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f2c14e"></stop>
<stop offset="50%" stop-color="#d53600"></stop>
<stop offset="100%" stop-color="#910f3f"></stop>
</linearGradient>
</defs>
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
<path d="M112 180 C110 150 116 126 132 110 C140 100 148 94 154 90 C160 94 168 100 176 110 C192 126 198 150 196 180 Z" class="constable-silhouette lead"></path>
<path d="M174 184 C172 154 178 132 194 118 C202 110 210 104 218 100 C226 104 234 110 242 118 C258 132 264 154 262 184 Z" class="constable-silhouette partner"></path>
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
<path d="M76 64 C80 54 91 48 101 53 C93 52 88 57 89 63 C90 69 101 68 103 76 C94 78 83 76 78 69 L71 76 L66 71 L74 64 Z" class="swan-mark"></path>
<path d="M126 82 h68 v14 h-68z" class="recipe-card"></path>
<path d="M126 104 h82 v10 h-82z" class="recipe-card"></path>
<path d="M126 122 h54 v10 h-54z" class="recipe-card"></path>
<text x="160" y="204" class="poster-callout">PANTRY EVIDENCE</text>
</svg>
</div>
</div>
<nav>
<a href="/">Queue</a>
<a href="/search">Search</a>
<a href="/planner">Planner</a>
<a href="/failures">Failures</a>
</nav>
</header>
<main class="page-shell">
{% block content %}{% endblock %}
</main>
<script src="/static/app.js?v=5"></script>
<script src="/static/app.js?v=6"></script>
</body>
</html>

View File

@@ -1,8 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<section class="hero-grid">
<article class="card spotlight">
<p class="section-label">Household taste</p>
<section class="hero-grid evidence-board dossier-grid">
<article class="card spotlight evidence-card">
<div class="case-legend">
<span class="section-label">Kitchen queue board</span>
<span class="pill">Case focus: approvals</span>
</div>
<h2>Leanne picks. Doris learns.</h2>
<p class="muted">Queue up web recipes, teach the household profile with yes/no decisions, and keep KitchenOwl for the final recipes worth keeping.</p>
<div class="action-row">
@@ -10,7 +13,11 @@
<a class="button-link" href="/search">Search a recipe idea</a>
</div>
</article>
<article class="card stats-card">
<article class="card stats-card evidence-card">
<div class="case-legend">
<span class="section-label">Case counts</span>
<span class="pill">Pantry Evidence</span>
</div>
<div class="stats-grid">
<div><span>Suggested</span><strong id="count-suggested">0</strong></div>
<div><span>Approved</span><strong id="count-approved">0</strong></div>
@@ -18,19 +25,22 @@
<div><span>Rejected</span><strong id="count-rejected">0</strong></div>
<div><span>Plans</span><strong id="count-plans">0</strong></div>
<div><span>Failures</span><strong id="count-failed-searches">0</strong></div>
<div><span>Import flags</span><strong id="count-import-flags">0</strong></div>
</div>
<p class="muted" id="backend-note">Loading state…</p>
<p class="muted dossier-note" id="backend-note">Loading state…</p>
</article>
</section>
<section class="split-grid">
<article class="card">
<section class="split-grid dossier-grid">
<article class="card evidence-card">
<div class="section-heading">
<div>
<p class="section-label">Soft blacklist</p>
<h2>Terms to push down</h2>
</div>
<span class="pill">Lead filter</span>
</div>
<p class="muted case-legend">Mark ingredients, cuisines, or phrases Doris should treat like weak leads.</p>
<form id="blacklist-form" class="inline-form">
<input type="text" id="blacklist-name" placeholder="Add ingredient or term">
<button type="submit">Add / enable</button>
@@ -38,12 +48,13 @@
<div id="blacklist-items" class="chip-list muted">Loading…</div>
</article>
<article class="card">
<article class="card evidence-card">
<div class="section-heading">
<div>
<p class="section-label">Preference notes</p>
<h2>Household guidance</h2>
</div>
<span class="pill">Witness notes</span>
</div>
<form id="settings-form" class="stack-form">
<label>Notes
@@ -61,17 +72,52 @@
</article>
</section>
<section class="card">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Queue</p>
<h2>Recent recipe suggestions</h2>
</div>
<span class="pill">Kitchen queue board</span>
</div>
<div id="suggestion-list" class="suggestion-grid muted">Loading…</div>
<p class="muted case-legend">Review the current stack like pinned case cards: promote the good leads, bury the junk.</p>
<div id="suggestion-list" class="suggestion-grid muted dossier-grid">Loading…</div>
</section>
<section class="card">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Import repair queue</p>
<h2>Flag recipe import problems</h2>
</div>
<a class="button-link" href="/imports">Open import review</a>
</div>
<p class="muted case-legend">Bad imports get tagged like busted evidence bags so Doris can come back and repair them later.</p>
<form id="import-flag-form" class="stack-form intake-docket">
<label>Recipe URL or KitchenOwl link
<input type="url" id="import-flag-url" placeholder="https://...">
</label>
<label>Recipe title (optional)
<input type="text" id="import-flag-title" placeholder="Halal Guys Chicken over Rice But Better">
</label>
<label>Issue type
<select id="import-flag-type">
<option value="formatting">Formatting</option>
<option value="ingredients">Ingredients</option>
<option value="timings">Timings</option>
<option value="duplicate">Duplicate</option>
<option value="other">Other</option>
</select>
</label>
<label>What went wrong
<textarea id="import-flag-notes" rows="4" placeholder="Steps merged together, ingredients split badly, wrong yield, duplicate recipe, or whatever else looks off."></textarea>
</label>
<button type="submit">Flag for Doris review</button>
</form>
<div id="import-flag-preview" class="failure-list muted dossier-grid">Loading flagged imports…</div>
</section>
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Troubleshooting</p>
@@ -79,6 +125,7 @@
</div>
<a class="button-link" href="/failures">Open failure review</a>
</div>
<div id="failed-search-preview" class="failure-list muted">Loading…</div>
<p class="muted case-legend">Search misses stay pinned on the board until Doris can turn them into usable dinner intelligence.</p>
<div id="failed-search-preview" class="failure-list muted dossier-grid">Loading…</div>
</section>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% block content %}
<section class="card">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Failure review</p>
@@ -8,7 +8,7 @@
</div>
<button type="button" id="refresh-failures">Refresh progress</button>
</div>
<p class="muted">Open searches stay here until Doris can turn them into usable recipe results.</p>
<div id="failures-list" class="failure-list muted">Loading failed searches…</div>
<p class="muted case-legend">Open searches stay here until Doris can turn them into usable recipe results.</p>
<div id="failures-list" class="failure-list muted dossier-grid">Loading failed searches…</div>
</section>
{% endblock %}

View File

@@ -0,0 +1,47 @@
{% extends 'base.html' %}
{% block content %}
<section class="hero-grid dossier-grid">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Import review</p>
<h2>Flagged KitchenOwl recipe issues</h2>
</div>
<span class="pill">Repair queue</span>
</div>
<p class="muted case-legend">Use this queue to track recipes that need Doris to clean up a bad import, fix ingredients, or inspect some other KitchenOwl problem.</p>
<form id="import-flag-form" class="stack-form intake-docket">
<label>Recipe URL or KitchenOwl link
<input type="url" id="import-flag-url" placeholder="https://...">
</label>
<label>Recipe title (optional)
<input type="text" id="import-flag-title" placeholder="Recipe title">
</label>
<label>Issue type
<select id="import-flag-type">
<option value="formatting">Formatting</option>
<option value="ingredients">Ingredients</option>
<option value="timings">Timings</option>
<option value="duplicate">Duplicate</option>
<option value="other">Other</option>
</select>
</label>
<label>What went wrong
<textarea id="import-flag-notes" rows="4" placeholder="Describe what needs fixing."></textarea>
</label>
<button type="submit">Add to import repair queue</button>
</form>
</section>
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Queue</p>
<h2>Open and resolved import flags</h2>
</div>
<span class="pill">Evidence tags</span>
</div>
<div id="import-flags-list" class="failure-list muted dossier-grid">Loading import flags…</div>
</section>
</section>
{% endblock %}

View File

@@ -1,14 +1,15 @@
{% extends 'base.html' %}
{% block content %}
<section class="hero-grid">
<article class="card">
<section class="hero-grid dossier-grid">
<article class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Meal planner</p>
<h2>Build the week around a few anchor meals</h2>
</div>
<span class="pill">Planning board</span>
</div>
<form id="planner-form" class="stack-form">
<form id="planner-form" class="stack-form intake-docket">
<label>Week start
<input type="date" id="week-start">
</label>
@@ -21,21 +22,21 @@
<button type="submit">Build the week</button>
</form>
</article>
<article class="card">
<p class="section-label">What this does</p>
<article class="card evidence-board">
<div class="case-legend"><span class="section-label">What this does</span><span class="pill">Waste-aware</span></div>
<h2>Waste-aware planning</h2>
<p class="muted">The planner looks at approved and queued meals, then tries to pair dishes that reuse overlapping ingredients. That means less waste, less random spending, and fewer half-dead veg rattling in the fridge drawer.</p>
</article>
</section>
<section class="card">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Plan output</p>
<h2>Suggested meal week</h2>
</div>
<span class="pill">Lineup board</span>
</div>
<div id="plan-output" class="plan-grid muted">Generate a plan to see the meal lineup.</div>
<div id="plan-output" class="plan-grid muted dossier-grid">Generate a plan to see the meal lineup.</div>
</section>
{% endblock %}

View File

@@ -1,52 +1,59 @@
{% extends 'base.html' %}
{% block content %}
<section class="card search-card">
<div class="section-heading">
<div>
<p class="section-label">Recipe search</p>
<h2>Tell Doris what sort of dinner you want</h2>
<section class="hero-grid dossier-grid">
<section class="card search-card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Recipe search</p>
<h2>Tell Doris what sort of dinner you want</h2>
</div>
<span class="pill">Lead intake</span>
</div>
</div>
<form id="search-form" class="stack-form">
<label>Search request
<textarea id="search-query" rows="3" placeholder="Asian inspired using ground beef with potatoes"></textarea>
</label>
<button type="submit">Find recipe suggestions</button>
</form>
<div class="search-examples">
<p class="muted">Example full searches:</p>
<div class="chip-list">
<button type="button" class="ghost example-query" data-query="Asian inspired using ground beef with potatoes">Asian inspired using ground beef with potatoes</button>
<button type="button" class="ghost example-query" data-query="Irish inspired with cabbage and potatoes">Irish inspired with cabbage and potatoes</button>
<button type="button" class="ghost example-query" data-query="Middle Eastern inspired using ground beef">Middle Eastern inspired using ground beef</button>
<button type="button" class="ghost example-query" data-query="Italian inspired with chicken and spinach">Italian inspired with chicken and spinach</button>
<p class="muted case-legend">Describe the target meal like a witness statement and let Doris pull leads.</p>
<form id="search-form" class="stack-form intake-docket">
<label>Search request
<textarea id="search-query" rows="3" placeholder="Asian inspired using ground beef with potatoes"></textarea>
</label>
<button type="submit">Find recipe suggestions</button>
</form>
<div class="search-examples">
<p class="muted">Example full searches:</p>
<div class="chip-list">
<button type="button" class="ghost example-query" data-query="Asian inspired using ground beef with potatoes">Asian inspired using ground beef with potatoes</button>
<button type="button" class="ghost example-query" data-query="Irish inspired with cabbage and potatoes">Irish inspired with cabbage and potatoes</button>
<button type="button" class="ghost example-query" data-query="Middle Eastern inspired using ground beef">Middle Eastern inspired using ground beef</button>
<button type="button" class="ghost example-query" data-query="Italian inspired with chicken and spinach">Italian inspired with chicken and spinach</button>
</div>
</div>
</div>
</section>
<section class="card search-card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Direct link</p>
<h2>Paste a recipe URL for Doris to ingest</h2>
</div>
<span class="pill">Search warrant</span>
</div>
<p class="muted case-legend">Drop the exact source when you already know the target recipe is worth hauling in.</p>
<form id="url-form" class="stack-form intake-docket">
<label>Recipe URL
<input id="recipe-url" type="url" placeholder="https://example.com/recipe">
</label>
<button type="submit">Ingest recipe link</button>
</form>
</section>
</section>
<section class="card search-card">
<div class="section-heading">
<div>
<p class="section-label">Direct link</p>
<h2>Paste a recipe URL for Doris to ingest</h2>
</div>
</div>
<form id="url-form" class="stack-form">
<label>Recipe URL
<input id="recipe-url" type="url" placeholder="https://example.com/recipe">
</label>
<button type="submit">Ingest recipe link</button>
</form>
</section>
<section class="card">
<section class="card evidence-board">
<div class="section-heading">
<div>
<p class="section-label">Results</p>
<h2>Candidate recipes</h2>
</div>
<span class="pill">Evidence table</span>
</div>
<p id="search-status" class="muted"></p>
<div id="search-results" class="suggestion-grid muted">Search for something to load recipe cards.</div>
<p id="search-status" class="muted case-legend"></p>
<div id="search-results" class="suggestion-grid muted dossier-grid">Search for something to load recipe cards.</div>
</section>
{% endblock %}