v0.1.19 - Throughput overview & responsive header

- Throughput: new "Throughput Overview" header with Gauge icon and brand-green
  badge icons on each card (Today, This week, 4-week average, Horse Mix, Grain Mix)
- Throughput: inline rolling-range selector (7d / 4w / 6w / 12w, default 4 weeks)
  driving the customer-mix cards; stats window widened to 12 weeks so switching
  range is a pure client-side re-filter
- Throughput: cards collapse to a single even 5-across row on laptop and up,
  with container-query value text that scales to each card's width
- Throughput: date logic pinned to Australian Eastern time (fixes the day-early
  date); This week subtitle shows the Mon-Sun date range
- Throughput: subtler tinted add-form; removed the inline-entry kicker and the
  "Open full form" link
- Topbar: fix cramped laptop header - action toggles no longer wrap above the
  user button; search drops to its own row earlier

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 10:01:10 +12:00
co-authored by Claude Opus 4.8
parent 4ff372d307
commit 2de82776cb
64 changed files with 6034 additions and 1134 deletions
@@ -61,6 +61,15 @@
}
});
// Reflect a selection set from outside (e.g. when an entry is loaded into the
// composer to be edited) so the search box shows the chosen product, not blank.
$effect(() => {
if (productId && !focused) {
const match = products.find((p) => String(p.id) === productId);
if (match) query = label(match);
}
});
// If the active client no longer contains the selected product, drop it.
$effect(() => {
if (selected && clientName && (selected.client_name ?? '') !== clientName) {
@@ -120,20 +129,18 @@
</script>
<div class="picker" bind:this={root} onfocusin={() => (focused = true)} onfocusout={onFocusOut}>
<div class="client-row">
<label class="client-label" for={`${inputId}-client`}>Client</label>
<select
id={`${inputId}-client`}
class="client-select"
bind:value={clientName}
{disabled}
>
<option value="">All clients</option>
{#each clients as client (client)}
<option value={client}>{client}</option>
{/each}
</select>
</div>
<select
id={`${inputId}-client`}
class="client-select"
bind:value={clientName}
aria-label="Filter by client"
{disabled}
>
<option value="">All clients</option>
{#each clients as client (client)}
<option value={client}>{client}</option>
{/each}
</select>
<div class="combo" role="combobox" aria-expanded={open} aria-haspopup="listbox" aria-controls={`${inputId}-list`}>
<span class="combo-icon" aria-hidden="true"><Search size={16} strokeWidth={2.2} /></span>
@@ -196,30 +203,23 @@
</div>
<style>
/* Client filter and product search sit side by side so the product is never
stacked underneath the client. They wrap to two rows only when the cell is
too narrow to keep both readable. */
.picker {
display: flex;
flex-direction: column;
gap: 0.4rem;
flex-direction: row;
align-items: stretch;
gap: 0.45rem;
min-width: 0;
}
.client-row {
display: flex;
align-items: center;
gap: 0.4rem;
}
.client-label {
font-size: 0.8rem;
font-weight: 600;
color: var(--color-text-muted, #6b7280);
white-space: nowrap;
}
.client-select {
flex: 1 1 auto;
min-width: 0;
min-height: 40px;
flex: 0 1 8.5rem;
min-width: 6rem;
min-height: 48px;
padding: 0.4rem 0.55rem;
border: 1px solid var(--color-border, #d1d5db);
border-radius: 0.5rem;
border-radius: 0.55rem;
font: inherit;
background: var(--color-bg-surface, #fff);
color: var(--color-text-primary, #111827);
@@ -228,8 +228,17 @@
position: relative;
display: flex;
align-items: center;
flex: 1 1 auto;
min-width: 0;
}
@media (max-width: 560px) {
.picker {
flex-wrap: wrap;
}
.client-select {
flex: 1 1 100%;
}
}
.combo-icon {
position: absolute;
left: 0.6rem;
@@ -276,7 +285,7 @@
top: calc(100% + 4px);
left: 0;
right: 0;
z-index: 50;
z-index: 200;
margin: 0;
padding: 0.25rem;
list-style: none;