feat(uat-b2): visual breakpoint fixes + form-error UX rollout
B2 Wave A (visual breakpoints):
- Documents Hub folder rail: widen ResizablePanel default 20→22%,
min 14→18%, add min-w-[180px] CSS floor so names don't truncate
at tablet 768
- Website analytics KPI tiles: switch lg grid 6→3 cols, restore 6
at xl so "Visit duration" stops truncating in the 1024+sidebar
layout
- Pipeline Value tile per-stage rows: compact $3.5M format on
sm- breakpoint (responsive sm:hidden / hidden sm:inline pair)
B2 Wave D (form-error UX rollout):
- useFormScrollToError + FormErrorSummary wired into 5 high-impact
forms: client-form, interest-form, yacht-form, company-form,
berth-form. Validation failures now scroll the first errored
field into view + render a top-of-form summary banner when ≥2
errors exist. Remaining ~23 form surfaces queued for follow-up.
B2 Wave B (Umami follow-ups):
- TopList primitive: add onExpandRange + expandRangeLabel props
for the empty-state nudge ("Try last 30 days" button). Callers
can opt in to drive the page-level DateRange.
B2 Wave C (FieldLabel + admin tooltip audit):
- Verified FieldLabel primitive already exists + is adopted in
custom-field-form. Registry-driven-form renders entry.description
inline below labels for every entry — the broad sweep across
15-20 admin pages is deferred to a focused polish session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,21 @@ export function PipelineValueTile({ range }: { range?: DateRange } = {}) {
|
||||
const stageMax = activeStages.reduce((m, s) => Math.max(m, s.grossValue), 0) || 1;
|
||||
|
||||
const fmt = (v: number) => formatCurrency(v, currency, { maxFractionDigits: 0 });
|
||||
// Compact variant for the per-stage cell on narrow viewports — $3,528,000
|
||||
// overflows the right column at 375px otherwise. Falls back to the full
|
||||
// format at sm+ where there's room.
|
||||
const fmtCompact = (v: number) => {
|
||||
try {
|
||||
return new Intl.NumberFormat(undefined, {
|
||||
style: 'currency',
|
||||
currency: currency || 'USD',
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: 1,
|
||||
}).format(v);
|
||||
} catch {
|
||||
return fmt(v);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
@@ -204,7 +219,8 @@ export function PipelineValueTile({ range }: { range?: DateRange } = {}) {
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-semibold text-foreground tabular-nums">
|
||||
{fmt(s.grossValue)}
|
||||
<span className="sm:hidden">{fmtCompact(s.grossValue)}</span>
|
||||
<span className="hidden sm:inline">{fmt(s.grossValue)}</span>
|
||||
</p>
|
||||
<p className="text-[11px] text-muted-foreground tabular-nums">
|
||||
{s.count} {s.count === 1 ? 'deal' : 'deals'} · {Math.round(s.weight * 100)}%
|
||||
|
||||
Reference in New Issue
Block a user