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
+2
View File
@@ -52,6 +52,8 @@ class MixCalculatorSessionLine(Base):
required_kg: Mapped[float] = mapped_column(Float)
mix_percentage: Mapped[float] = mapped_column(Float)
unit: Mapped[str] = mapped_column(String(64))
# Snapshot of the ingredient's rounding setting at save time.
rounding_decimals: Mapped[int] = mapped_column(Integer, default=2)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
session: Mapped[MixCalculatorSession] = relationship(back_populates="lines")
+28
View File
@@ -376,6 +376,34 @@ class NotificationSetting(Base):
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
class XeroContactLink(Base):
"""Persistent link between a customer (:class:`ClientAccount`) and a Xero
contact.
Maintained from the Integrations console. Once a customer is linked, order
invoices reference the real Xero ``ContactID`` instead of falling back to the
client code — which is what lets Xero attach the invoice to the right
contact rather than creating a duplicate. One link per customer per tenant.
"""
__tablename__ = "xero_contact_links"
__table_args__ = (
UniqueConstraint("tenant_id", "client_account_id", name="uq_xero_contact_link_customer"),
)
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[str] = mapped_column(String(64), default="default", index=True)
client_account_id: Mapped[int] = mapped_column(ForeignKey("client_accounts.id"), index=True)
xero_contact_id: Mapped[str] = mapped_column(String(128))
xero_contact_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
xero_contact_email: Mapped[str | None] = mapped_column(String(255), nullable=True)
# When the contact details were last reconciled with Xero (a future live
# sync can refresh name/email and stamp this).
last_synced_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class XeroSyncLog(Base):
__tablename__ = "xero_sync_log"
+4 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import date, datetime
from sqlalchemy import Date, DateTime, Float, ForeignKey, String, Text
from sqlalchemy import Date, DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.session import Base
@@ -18,6 +18,9 @@ class RawMaterial(Base):
unit_of_measure: Mapped[str] = mapped_column(String(64))
kg_per_unit: Mapped[float] = mapped_column(Float)
status: Mapped[str] = mapped_column(String(32), default="active")
# Decimal places this ingredient's required-kg is rounded to in the mix
# calculator output. Set per-ingredient from the Ingredients Editor.
rounding_decimals: Mapped[int] = mapped_column(Integer, default=2)
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)