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:
2026-05-25 03:50:46 +02:00
parent 14ae41d0fa
commit 777b711548
9 changed files with 75 additions and 12 deletions

View File

@@ -3,6 +3,8 @@
import { useEffect, useState } from 'react';
import { useForm, useFieldArray } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useFormScrollToError } from '@/hooks/use-form-scroll-to-error';
import { FormErrorSummary } from '@/components/forms/form-error-summary';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Plus, Trash2, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
@@ -104,6 +106,10 @@ export function ClientForm({
tagIds: [],
},
});
// Scroll to / focus the first errored field on submit failure + show a
// top-of-form summary banner when there are ≥2 errors. Critical on tall
// drawers where the failing field is below the fold.
const scrollOnError = useFormScrollToError(handleSubmit, errors);
const { fields, append, remove } = useFieldArray({ control, name: 'contacts' });
const tagIds = watch('tagIds') ?? [];
@@ -313,10 +319,11 @@ export function ClientForm({
if (cleaned.length !== current.length) {
setValue('contacts', cleaned, { shouldValidate: false });
}
return handleSubmit((data) => mutation.mutate(data))(e);
return scrollOnError((data) => mutation.mutate(data))(e);
}}
className="space-y-6 py-6"
>
<FormErrorSummary errors={errors} />
{/* Dedup suggestion - only on the create path. Watches the
live form values for email / phone / name and surfaces
an existing client when one matches. The user can