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:
@@ -48,6 +48,8 @@ import { YachtPicker } from '@/components/yachts/yacht-picker';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import { useEntityOptions } from '@/hooks/use-entity-options';
|
||||
import { formatBerthRange } from '@/lib/templates/berth-range';
|
||||
import { useFormScrollToError } from '@/hooks/use-form-scroll-to-error';
|
||||
import { FormErrorSummary } from '@/components/forms/form-error-summary';
|
||||
import type { z } from 'zod';
|
||||
import { createInterestSchema, type CreateInterestInput } from '@/lib/validators/interests';
|
||||
import { PIPELINE_STAGES, STAGE_LABELS, LEAD_CATEGORIES, SOURCES } from '@/lib/constants';
|
||||
@@ -147,6 +149,8 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedBerthId]);
|
||||
|
||||
const scrollOnError = useFormScrollToError(handleSubmit, errors);
|
||||
|
||||
function requestClose() {
|
||||
if (isDirty && !isSubmitting && !mutation.isPending) {
|
||||
setDiscardConfirmOpen(true);
|
||||
@@ -356,7 +360,8 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
|
||||
<SheetTitle>{isEdit ? 'Edit Interest' : 'New Interest'}</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<form onSubmit={handleSubmit((data) => mutation.mutate(data))} className="space-y-6 py-6">
|
||||
<form onSubmit={scrollOnError((data) => mutation.mutate(data))} className="space-y-6 py-6">
|
||||
<FormErrorSummary errors={errors} />
|
||||
{/* Client */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
|
||||
|
||||
Reference in New Issue
Block a user