Adds POST /api/v1/orders/{id}/label (admin-only) that buys a shipping
label through Shippo and writes carrier + tracking + label fields back
onto the order, transitioning paid -> shipped.
realized per-shipment table is kinda bad, switched to single embedded tracking blob. since im only
shipping from my house no split is needed. adds tracking_status, tracking_status_details,
tracking_status_updated_at columns. reverse-backfills any historical shipments rows back onto orders
(idempotent, table-guarded). UpdateTracking rewritten to write to otder columns direcly
Replaces the manual string-concat + separator layout with a
lipgloss/table row, mirroring the reference TUI structure. Drops the
parallel bold-vs-gray styling for a single-weight accent/base scheme,
extracts a small tab() closure to remove the duplicated active/inactive
branches, and uses m.renderer.NewStyle() for the border so per-session
color profiles render correctly under SSH.
Cart label still tracks inCartFlow() (covers shipping/payment/review/
confirm), which the reference doesn't need since it has no multi-step
checkout.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit events now emit at Info/Warn/Error so the log-level counter has variety
instead of being pinned to info. Panic recovery uses structured slog.Error
with method/path/stack attrs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds Prometheus instrumentation across the checkout funnel, auth flow,
Stripe calls, rate limiters, and HTTP middleware (inflight gauge, request
and response sizes). Introduces a slog handler that counts log lines by
level so the dashboard can show error/warn rates; existing log.Printf
calls are routed through slog and counted as info.
Backs the new "terminalshop — Service Overview" Grafana dashboard.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace ErrorMsg string with typed VisibleError struct. Add catch-all
error handler at top of Update() that auto-recovers cart on shop/cart
errors. ESC now dismisses error banners. Refactor shipping address and
payment card checkout lists to use CreateBox + formatListItem for
consistent bordered cards with radio indicators.
Written with Claude Code (claude.ai/code)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
replacing duplicated border/selection logic in
account_orders.go and cart.go. Also fixes a latent SSH rendering bug
in cart.go where lipgloss.NewStyle() was used instead of theme.Base().
lipgloss.NewStyle() uses the default renderer which can't output colors
over SSH. Switching to m.theme.Base() uses the session renderer, fixing
BorderForeground. Also reverted to NormalBorder since color now works.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BorderForeground color wasn't rendering differently over SSH. Use
DoubleBorder (╔═╗║╚═╝) for selected vs NormalBorder (┌─┐│└─┘) for
unselected — visually distinct regardless of color support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use explicit #4682B4 with bold to ensure the selected border is
visually distinct on all terminal themes including gruvbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scroll triggers when the next item would be off-screen, not when the
current item hits the edge. Selected border now uses highlight color
(steel blue) instead of accent (white) for visibility on all themes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Simplify order list cards to 2 content lines (matching reference) so
itemHeight is always 4. Remove extra margin/buffer from viewport height
calc since account is a viewport page with no margins.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The scroll-to-item calculations used wrong item heights (7/5 instead of
9/7) and ignored the 5-line title header offset, causing the viewport to
under-scroll. Selected order cards now light up in accent color for
better visibility across terminal themes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auth handler used FirstOrCreate which couldn't see soft-deleted keys
(GORM filters by deleted_at IS NULL) but also couldn't insert (unique
index still covers deleted rows), returning 500 on every login after
a key deletion. Use Unscoped lookup and restore soft-deleted keys.
Splash screen also got permanently stuck when auth failed because
splashViewInitCmd was never fired, leaving splash.dataReady false.
when encountering an error, the error would never be removed, i just reset the error msg in the switchPage function, this will have to be redone later to make error messages dissapear after a while even on the same page
replace SSHPublicKeyStr field on Model with a closure-based authCmd that
fires, the public key lives only in the goroutine stack not on the
long-lived Model struct
Prevents key material exposure in heap dumps if the process crashed
added dedicated *Switch() methods (ShopSwitch, CartSwitc, AccountSwitch, ...) for page transitions and remove the catch-all resetPageState(). Each switch owns its pages zero-init, viewport setup, and async data fetching