feat(client-registrations): reordena tela — Dados PJ, Sócios, Endereço, Contatos

Ordem pedida pelo usuário. O <form> único virou dois (Sócios renderiza
seu próprio <form> interno entre eles, e form aninhado é inválido em
HTML) — ambos chamam o mesmo handleSubmit, que sempre lê o estado React
completo, então salvar continua gravando tudo de uma vez independente
de qual dos dois disparou. Testado ao vivo.

pnpm turbo run typecheck build: 28/28.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 13:30:35 -03:00
parent 93cd42b22e
commit 205d83a5f5
2 changed files with 147 additions and 121 deletions

View File

@@ -21,10 +21,14 @@ import { PartnersSection } from "./PartnersSection.js";
* (breadcrumb + title/status header, grouped info blocks) and `customers.tsx`
* (status badge) — rebuilt with `@eden/ui` Card sections, one per group of
* fields, same pattern as QuoteDetailPage/CompanyFormPage. Unlike those two
* (pure display or pure form), this screen mixes both: everything below the
* header is one editable form (single PATCH on submit, matching how the
* API accepts a fully partial `UpdateClientRegistrationDto`), plus a
* separate Sócios sub-section with its own independent list/add/edit calls.
* (pure display or pure form), this screen mixes both: one editable form
* (single PATCH on submit, matching how the API accepts a fully partial
* `UpdateClientRegistrationDto`) split across two `<form>` elements —
* Status/Dados PF-PJ, then (after the non-form Sócios sub-section) Endereço/
* Contatos/Portabilidade — both calling the same `handleSubmit`, which
* always reads the complete React `form` state regardless of which one
* triggered it, so the split is purely visual (see the JSX comment above
* the first `<form>`). Sócios has its own independent list/add/edit calls.
*/
const STATUS_LABEL: Record<RegistrationStatus, string> = {
rascunho: "Rascunho",
@@ -350,6 +354,15 @@ export function ClientRegistrationDetailPage() {
</Alert>
) : null}
{/*
Ordem pedida pelo usuário: Dados PJ/PF -> Sócios -> Endereço ->
Contatos. `Status` fica primeiro (não mencionado, posição natural) e
`Portabilidade`/botão de salvar ficam no final (idem). `PartnersSection`
renderiza seu próprio <form> interno (form aninhado é inválido em
HTML), então o form único original virou dois — ambos chamam o mesmo
`handleSubmit`, que sempre lê o estado React completo (`form`), não o
DOM, então dividir em dois <form> não muda o que é salvo.
*/}
<form className="space-y-4" onSubmit={handleSubmit}>
<Card title="Status">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
@@ -381,113 +394,6 @@ export function ClientRegistrationDetailPage() {
</div>
</Card>
<Card title="Endereço">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Input label="CEP" value={form.addressZip} onChange={(e) => updateField("addressZip", e.target.value)} />
<div className="sm:col-span-2">
<Input
label="Logradouro"
value={form.addressStreet}
onChange={(e) => updateField("addressStreet", e.target.value)}
/>
</div>
<Input
label="Número"
value={form.addressNumber}
onChange={(e) => updateField("addressNumber", e.target.value)}
/>
<Input
label="Complemento"
value={form.addressComplement}
onChange={(e) => updateField("addressComplement", e.target.value)}
/>
<Input
label="Bairro"
value={form.addressNeighborhood}
onChange={(e) => updateField("addressNeighborhood", e.target.value)}
/>
<Input
label="Cidade"
value={form.addressCity}
onChange={(e) => updateField("addressCity", e.target.value)}
/>
<Input
label="UF"
maxLength={2}
value={form.addressState}
onChange={(e) => updateField("addressState", e.target.value.toUpperCase())}
/>
<Input
label="País"
value={form.addressCountry}
onChange={(e) => updateField("addressCountry", e.target.value)}
placeholder="Brasil"
/>
</div>
</Card>
<Card title="Contatos">
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Principal</p>
<Input
label="Nome"
value={form.contactPrincipalName}
onChange={(e) => updateField("contactPrincipalName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactPrincipalEmail}
onChange={(e) => updateField("contactPrincipalEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactPrincipalPhone}
onChange={(e) => updateField("contactPrincipalPhone", e.target.value)}
/>
</div>
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Financeiro</p>
<Input
label="Nome"
value={form.contactFinancialName}
onChange={(e) => updateField("contactFinancialName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactFinancialEmail}
onChange={(e) => updateField("contactFinancialEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactFinancialPhone}
onChange={(e) => updateField("contactFinancialPhone", e.target.value)}
/>
</div>
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Técnico</p>
<Input
label="Nome"
value={form.contactTechnicalName}
onChange={(e) => updateField("contactTechnicalName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactTechnicalEmail}
onChange={(e) => updateField("contactTechnicalEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactTechnicalPhone}
onChange={(e) => updateField("contactTechnicalPhone", e.target.value)}
/>
</div>
</div>
</Card>
{registration.person_type === "pf" ? (
<Card title="Dados PF">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
@@ -666,6 +572,125 @@ export function ClientRegistrationDetailPage() {
</div>
</Card>
)}
</form>
{registration.person_type === "pj" ? (
<div className="mt-4">
<PartnersSection
registrationId={registration.id}
accessToken={accessToken}
refreshKey={partnersRefreshKey}
/>
</div>
) : null}
<form className="mt-4 space-y-4" onSubmit={handleSubmit}>
<Card title="Endereço">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Input label="CEP" value={form.addressZip} onChange={(e) => updateField("addressZip", e.target.value)} />
<div className="sm:col-span-2">
<Input
label="Logradouro"
value={form.addressStreet}
onChange={(e) => updateField("addressStreet", e.target.value)}
/>
</div>
<Input
label="Número"
value={form.addressNumber}
onChange={(e) => updateField("addressNumber", e.target.value)}
/>
<Input
label="Complemento"
value={form.addressComplement}
onChange={(e) => updateField("addressComplement", e.target.value)}
/>
<Input
label="Bairro"
value={form.addressNeighborhood}
onChange={(e) => updateField("addressNeighborhood", e.target.value)}
/>
<Input
label="Cidade"
value={form.addressCity}
onChange={(e) => updateField("addressCity", e.target.value)}
/>
<Input
label="UF"
maxLength={2}
value={form.addressState}
onChange={(e) => updateField("addressState", e.target.value.toUpperCase())}
/>
<Input
label="País"
value={form.addressCountry}
onChange={(e) => updateField("addressCountry", e.target.value)}
placeholder="Brasil"
/>
</div>
</Card>
<Card title="Contatos">
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Principal</p>
<Input
label="Nome"
value={form.contactPrincipalName}
onChange={(e) => updateField("contactPrincipalName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactPrincipalEmail}
onChange={(e) => updateField("contactPrincipalEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactPrincipalPhone}
onChange={(e) => updateField("contactPrincipalPhone", e.target.value)}
/>
</div>
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Financeiro</p>
<Input
label="Nome"
value={form.contactFinancialName}
onChange={(e) => updateField("contactFinancialName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactFinancialEmail}
onChange={(e) => updateField("contactFinancialEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactFinancialPhone}
onChange={(e) => updateField("contactFinancialPhone", e.target.value)}
/>
</div>
<div className="space-y-3">
<p className="text-xs font-semibold uppercase text-default">Técnico</p>
<Input
label="Nome"
value={form.contactTechnicalName}
onChange={(e) => updateField("contactTechnicalName", e.target.value)}
/>
<Input
label="E-mail"
type="email"
value={form.contactTechnicalEmail}
onChange={(e) => updateField("contactTechnicalEmail", e.target.value)}
/>
<Input
label="Telefone"
value={form.contactTechnicalPhone}
onChange={(e) => updateField("contactTechnicalPhone", e.target.value)}
/>
</div>
</div>
</Card>
{registration.is_portability ? (
<Card title="Portabilidade" bodyClassName="p-5">
@@ -686,16 +711,6 @@ export function ClientRegistrationDetailPage() {
</Button>
</div>
</form>
{registration.person_type === "pj" ? (
<div className="mt-4">
<PartnersSection
registrationId={registration.id}
accessToken={accessToken}
refreshKey={partnersRefreshKey}
/>
</div>
) : null}
</div>
);
}