v0.1.32 - Mix Calculator search, ingredient categories, throughput tidy-up

Mix Calculator: searchable Mix Name picker (mirrors Throughput search)

Ingredients Editor: add manual Category column; used to order Mix Calculator output

Mix Calculator: surface formula-only mixes (no product yet) via -mix_id sentinel

Throughput: remove unused For order / For stock destination controls from composer

Editor change history: show timestamps in local time (stored UTC) instead of raw UTC
This commit is contained in:
2026-06-21 11:57:14 +12:00
parent 696f1e7b09
commit 87878e70fc
6 changed files with 313 additions and 149 deletions
+3 -8
View File
@@ -3,6 +3,7 @@
import { toast } from '$lib/toast';
import AppSecondaryRailLayout from '$lib/components/navigation/AppSecondaryRailLayout.svelte';
import ChangeHistoryModal from '$lib/components/editor/ChangeHistoryModal.svelte';
import CategoryCombobox from '$lib/components/editor/CategoryCombobox.svelte';
import SortHeader from '$lib/table/SortHeader.svelte';
import { TableController } from '$lib/table/table.svelte';
import { formatNumber } from '$lib/format';
@@ -297,7 +298,7 @@
</label>
<label>
<span>Category</span>
<input bind:value={newIngredient.category} list="ingredient-categories" placeholder="e.g. Grains" />
<CategoryCombobox bind:value={newIngredient.category} options={knownCategories} placeholder="e.g. Grains" inputId="new-ingredient-category" />
</label>
<label>
<span>Rounding</span>
@@ -367,7 +368,7 @@
<div class="cell">
<span class="cell-label">Category</span>
<input bind:value={row.draft_category} list="ingredient-categories" placeholder="—" aria-label="Category" />
<CategoryCombobox bind:value={row.draft_category} options={knownCategories} placeholder="—" ariaLabel={`Category for ${row.name}`} />
</div>
<div class="cell">
@@ -443,12 +444,6 @@
</div>
</section>
<datalist id="ingredient-categories">
{#each knownCategories as category (category)}
<option value={category}></option>
{/each}
</datalist>
{#if historyIngredient}
<ChangeHistoryModal
entityType="ingredient"
@@ -17,7 +17,6 @@
buildConfetti,
compareDate,
compareText,
isStockEntry,
startOfWeekMonday,
toISODate,
type SortDirection,
@@ -70,24 +69,6 @@
statsEntries = statsEntries.filter((e) => e.id !== id);
}
// The destination shown in the log: an order (with job number), stock, or a
// split across both.
function destinationOf(entry: ThroughputEntry): { label: string; detail: string | null } {
const unit = entry.quantity_type === 'bags' ? 'bags' : 'kg';
if (entry.for_order && entry.for_stock) {
const stock = entry.stock_quantity != null ? `${formatNumber(entry.stock_quantity, 1)} ${unit} to stock` : 'split';
const job = entry.job_number ? `Order ${entry.job_number}` : 'Order';
return { label: 'Split', detail: `${job} · ${stock}` };
}
if (entry.for_order) {
return { label: 'Order', detail: entry.job_number ? `Job ${entry.job_number}` : null };
}
if (isStockEntry(entry)) {
return { label: 'Stock', detail: null };
}
return { label: '—', detail: null };
}
// ── Inline "spreadsheet" add row ──────────────────────────────
const today = toISODate(ausToday());
let nDate = $state(today);
@@ -495,10 +476,6 @@
result = (a.calculated_kg ?? 0) - (b.calculated_kg ?? 0);
} else if (sortKey === 'staff') {
result = compareText(a.staff_name, b.staff_name);
} else if (sortKey === 'destination') {
const aDest = destinationOf(a);
const bDest = destinationOf(b);
result = compareText(`${aDest.label} ${aDest.detail ?? ''}`, `${bDest.label} ${bDest.detail ?? ''}`);
} else if (sortKey === 'notes') {
result = compareText(a.notes, b.notes);
}
@@ -582,7 +559,6 @@
{formatNumber}
{packedMain}
{packedDetail}
{destinationOf}
onApplyFilters={applyFilters}
onClearFilters={clearFilters}
onToggleSort={toggleSort}