diff --git a/apps/core-web/src/components/PartnersSection.tsx b/apps/core-web/src/components/PartnersSection.tsx
new file mode 100644
index 0000000..0f9ce94
--- /dev/null
+++ b/apps/core-web/src/components/PartnersSection.tsx
@@ -0,0 +1,310 @@
+import { useCallback, useEffect, useState } from "react";
+import type { FormEvent } from "react";
+import { Alert, Badge, Button, Card, Input, Table } from "@eden/ui";
+import type { TableColumn } from "@eden/ui";
+import { ApiError } from "../lib/api/http.js";
+
+/**
+ * Sócios/QSA sub-section, shared shape between `client_registration_partners`
+ * and `reseller_partners` — same columns (name/document/qualification/cpf/
+ * will_sign/signature_email), same "add inline + edit cpf/willSign/email"
+ * behavior, only the backing endpoints differ. Generalized out of
+ * client-registrations' original `PartnersSection` so the reseller screen
+ * doesn't duplicate the whole component — callers pass their own
+ * list/create/update functions (already the exact shape of
+ * `client-registrations.ts`'s and `resellers.ts`'s partner functions, no
+ * adapter needed at the call site).
+ */
+export interface PartnerLike {
+ id: string;
+ name: string;
+ document: string | null;
+ qualification: string | null;
+ cpf: string | null;
+ will_sign: boolean;
+ signature_email: string | null;
+}
+
+export interface PartnerCreateInput {
+ name: string;
+ document?: string;
+ qualification?: string;
+ cpf?: string;
+ willSign?: boolean;
+ signatureEmail?: string;
+}
+
+export interface PartnerUpdateInput {
+ cpf?: string;
+ willSign?: boolean;
+ signatureEmail?: string;
+}
+
+const EMPTY_NEW_PARTNER: PartnerCreateInput = {
+ name: "",
+ document: "",
+ qualification: "",
+ cpf: "",
+ willSign: false,
+ signatureEmail: "",
+};
+
+function toOptional(value: string | undefined): string | undefined {
+ const trimmed = (value ?? "").trim();
+ return trimmed === "" ? undefined : trimmed;
+}
+
+interface EditState {
+ cpf: string;
+ willSign: boolean;
+ signatureEmail: string;
+}
+
+export interface PartnersSectionProps
{
+ /** Owning entity id (client registration or reseller) — `:id` in the partner endpoints. */
+ entityId: string;
+ accessToken: string | null;
+ /** Bump (e.g. a counter) to force a reload — used after a successful CNPJ lookup adds/updates partners. */
+ refreshKey?: unknown;
+ listPartners: (entityId: string, accessToken: string) => Promise
;
+ createPartner: (entityId: string, input: PartnerCreateInput, accessToken: string) => Promise
;
+ updatePartner: (entityId: string, partnerId: string, input: PartnerUpdateInput, accessToken: string) => Promise
;
+ title?: string;
+}
+
+export function PartnersSection
({
+ entityId,
+ accessToken,
+ refreshKey,
+ listPartners,
+ createPartner,
+ updatePartner,
+ title = "Sócios",
+}: PartnersSectionProps
) {
+ const [partners, setPartners] = useState
(null);
+ const [error, setError] = useState(null);
+
+ const [newPartner, setNewPartner] = useState(EMPTY_NEW_PARTNER);
+ const [creating, setCreating] = useState(false);
+ const [createError, setCreateError] = useState(null);
+
+ const [editingId, setEditingId] = useState(null);
+ const [editState, setEditState] = useState({ cpf: "", willSign: false, signatureEmail: "" });
+ const [savingEdit, setSavingEdit] = useState(false);
+ const [editError, setEditError] = useState(null);
+
+ const load = useCallback(() => {
+ if (!accessToken) return;
+ setError(null);
+ listPartners(entityId, accessToken)
+ .then(setPartners)
+ .catch((err) => setError(err instanceof ApiError ? err.message : "Não foi possível carregar os sócios."));
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [entityId, accessToken, refreshKey]);
+
+ useEffect(() => {
+ load();
+ }, [load]);
+
+ async function handleCreate(event: FormEvent) {
+ event.preventDefault();
+ if (!accessToken) return;
+ setCreateError(null);
+ setCreating(true);
+ try {
+ await createPartner(
+ entityId,
+ {
+ name: newPartner.name.trim(),
+ document: toOptional(newPartner.document),
+ qualification: toOptional(newPartner.qualification),
+ cpf: toOptional(newPartner.cpf),
+ willSign: newPartner.willSign,
+ signatureEmail: toOptional(newPartner.signatureEmail),
+ },
+ accessToken,
+ );
+ setNewPartner(EMPTY_NEW_PARTNER);
+ load();
+ } catch (err) {
+ setCreateError(err instanceof ApiError ? err.message : "Não foi possível adicionar o sócio.");
+ } finally {
+ setCreating(false);
+ }
+ }
+
+ function startEdit(partner: P) {
+ setEditingId(partner.id);
+ setEditError(null);
+ setEditState({
+ cpf: partner.cpf ?? "",
+ willSign: partner.will_sign,
+ signatureEmail: partner.signature_email ?? "",
+ });
+ }
+
+ async function handleSaveEdit(partnerId: string) {
+ if (!accessToken) return;
+ setEditError(null);
+ setSavingEdit(true);
+ try {
+ await updatePartner(
+ entityId,
+ partnerId,
+ {
+ cpf: toOptional(editState.cpf),
+ willSign: editState.willSign,
+ signatureEmail: toOptional(editState.signatureEmail),
+ },
+ accessToken,
+ );
+ setEditingId(null);
+ load();
+ } catch (err) {
+ setEditError(err instanceof ApiError ? err.message : "Não foi possível salvar o sócio.");
+ } finally {
+ setSavingEdit(false);
+ }
+ }
+
+ const columns: Array> = [
+ { key: "name", header: "Nome", render: (p) => {p.name} },
+ { key: "document", header: "Documento", render: (p) => p.document ?? "—" },
+ { key: "qualification", header: "Qualificação", render: (p) => p.qualification ?? "—" },
+ {
+ key: "cpf",
+ header: "CPF",
+ render: (p) =>
+ editingId === p.id ? (
+ setEditState((s) => ({ ...s, cpf: e.target.value }))} />
+ ) : (
+ p.cpf ?? "—"
+ ),
+ },
+ {
+ key: "willSign",
+ header: "Vai assinar",
+ render: (p) =>
+ editingId === p.id ? (
+
+ ) : (
+ {p.will_sign ? "Sim" : "Não"}
+ ),
+ },
+ {
+ key: "signatureEmail",
+ header: "E-mail p/ assinatura",
+ render: (p) =>
+ editingId === p.id ? (
+ setEditState((s) => ({ ...s, signatureEmail: e.target.value }))}
+ />
+ ) : (
+ p.signature_email ?? "—"
+ ),
+ },
+ {
+ key: "actions",
+ header: "",
+ className: "text-right",
+ render: (p) =>
+ editingId === p.id ? (
+
+
+
+
+ ) : (
+
+ ),
+ },
+ ];
+
+ return (
+
+ {error ? (
+
+ {error}
+
+ ) : null}
+ {editError ? (
+
+ {editError}
+
+ ) : null}
+
+ {partners === null && !error ? (
+ Carregando sócios…
+ ) : (
+ p.id} emptyMessage="Nenhum sócio cadastrado." />
+ )}
+
+
+
Adicionar sócio
+ {createError ? (
+
+ {createError}
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/apps/core-web/src/lib/api/resellers.ts b/apps/core-web/src/lib/api/resellers.ts
index e2aa409..691fadc 100644
--- a/apps/core-web/src/lib/api/resellers.ts
+++ b/apps/core-web/src/lib/api/resellers.ts
@@ -1,7 +1,7 @@
/**
* Client for `apps/api` `/v1/resellers` (see apps/api/src/resellers/resellers.controller.ts,
- * resellers.service.ts and packages/database/src/repositories/resellers.ts for the source of
- * truth — shapes below are mirrored, not invented).
+ * resellers.service.ts and packages/database/src/repositories/resellers.ts +
+ * reseller-partners.ts for the source of truth — shapes below are mirrored, not invented).
*/
import { apiRequest } from "./http.js";
@@ -15,6 +15,30 @@ export interface Reseller {
document: string | null;
contact_email: string | null;
active: boolean;
+ trade_name: string | null;
+ state_registration: string | null;
+ municipal_registration: string | null;
+ opening_date: string | null;
+ legal_nature: string | null;
+ cnpj_situation: string | null;
+ main_activity: string | null;
+ address_zip: string | null;
+ address_street: string | null;
+ address_number: string | null;
+ address_complement: string | null;
+ address_neighborhood: string | null;
+ address_city: string | null;
+ address_state: string | null;
+ address_country: string;
+ contact_principal_name: string | null;
+ contact_principal_email: string | null;
+ contact_principal_phone: string | null;
+ contact_financial_name: string | null;
+ contact_financial_email: string | null;
+ contact_financial_phone: string | null;
+ contact_technical_name: string | null;
+ contact_technical_email: string | null;
+ contact_technical_phone: string | null;
}
/** Request body — camelCase, matches `CreateResellerDto` exactly. */
@@ -23,6 +47,33 @@ export interface CreateResellerInput {
document?: string;
contactEmail?: string;
active?: boolean;
+
+ tradeName?: string;
+ stateRegistration?: string;
+ municipalRegistration?: string;
+ openingDate?: string;
+ legalNature?: string;
+ cnpjSituation?: string;
+ mainActivity?: string;
+
+ addressZip?: string;
+ addressStreet?: string;
+ addressNumber?: string;
+ addressComplement?: string;
+ addressNeighborhood?: string;
+ addressCity?: string;
+ addressState?: string;
+ addressCountry?: string;
+
+ contactPrincipalName?: string;
+ contactPrincipalEmail?: string;
+ contactPrincipalPhone?: string;
+ contactFinancialName?: string;
+ contactFinancialEmail?: string;
+ contactFinancialPhone?: string;
+ contactTechnicalName?: string;
+ contactTechnicalEmail?: string;
+ contactTechnicalPhone?: string;
}
/** Matches `UpdateResellerDto`: everything optional. */
@@ -76,3 +127,88 @@ export function unassignUserFromReseller(
{ accessToken },
);
}
+
+/** `reseller_partners` row — see packages/database/src/repositories/reseller-partners.ts. */
+export interface ResellerPartner {
+ id: string;
+ reseller_id: string;
+ name: string;
+ document: string | null;
+ qualification: string | null;
+ source: string;
+ cpf: string | null;
+ will_sign: boolean;
+ signature_email: string | null;
+ created_at: string;
+}
+
+/** Body of `POST /v1/resellers/:id/partners` — matches `CreateResellerPartnerDto`. */
+export interface CreateResellerPartnerInput {
+ name: string;
+ document?: string;
+ qualification?: string;
+ cpf?: string;
+ willSign?: boolean;
+ signatureEmail?: string;
+}
+
+/** Body of `PATCH /v1/resellers/:id/partners/:partnerId` — matches `UpdateResellerPartnerDto`. */
+export interface UpdateResellerPartnerInput {
+ cpf?: string;
+ willSign?: boolean;
+ signatureEmail?: string;
+}
+
+export function listResellerPartners(id: string, accessToken: string): Promise {
+ return apiRequest(`/resellers/${id}/partners`, { method: "GET" }, { accessToken });
+}
+
+export function createResellerPartner(
+ id: string,
+ input: CreateResellerPartnerInput,
+ accessToken: string,
+): Promise {
+ return apiRequest(
+ `/resellers/${id}/partners`,
+ { method: "POST", body: JSON.stringify(input) },
+ { accessToken },
+ );
+}
+
+export function updateResellerPartner(
+ id: string,
+ partnerId: string,
+ input: UpdateResellerPartnerInput,
+ accessToken: string,
+): Promise {
+ return apiRequest(
+ `/resellers/${id}/partners/${partnerId}`,
+ { method: "PATCH", body: JSON.stringify(input) },
+ { accessToken },
+ );
+}
+
+/** Result of `POST /v1/resellers/:id/cnpj-lookup` on a match (201). */
+export interface ResellerCnpjLookupResult {
+ reseller: Reseller;
+ partners: ResellerPartner[];
+}
+
+/**
+ * Looks up a CNPJ against the external source and applies the result to the
+ * reseller (company data, address, partners) server-side. 404 means the CNPJ
+ * wasn't found — caller should surface the API's message and leave the
+ * reseller untouched (caller catches `ApiError` and inspects
+ * `.status`/`.message`, same pattern as client-registrations).
+ */
+export function applyResellerCnpjLookup(
+ id: string,
+ cnpj: string,
+ accessToken: string,
+): Promise {
+ return apiRequest(
+ `/resellers/${id}/cnpj-lookup`,
+ { method: "POST", body: JSON.stringify({ cnpj }) },
+ { accessToken },
+ );
+}
diff --git a/apps/core-web/src/routes/resellers/ResellerFormPage.tsx b/apps/core-web/src/routes/resellers/ResellerFormPage.tsx
index be7c921..d8bc752 100644
--- a/apps/core-web/src/routes/resellers/ResellerFormPage.tsx
+++ b/apps/core-web/src/routes/resellers/ResellerFormPage.tsx
@@ -1,63 +1,187 @@
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import type { FormEvent } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import { Alert, Button, Card, Input, PageHeader } from "@eden/ui";
-import { createReseller, getReseller, updateReseller } from "../../lib/api/resellers.js";
-import type { CreateResellerInput } from "../../lib/api/resellers.js";
+import {
+ applyResellerCnpjLookup,
+ createReseller,
+ getReseller,
+ updateReseller,
+} from "../../lib/api/resellers.js";
+import type { CreateResellerInput, Reseller, UpdateResellerInput } from "../../lib/api/resellers.js";
import { ApiError } from "../../lib/api/http.js";
import { useAuth } from "../../lib/auth/AuthContext.js";
+import { ResellerPartnersSection } from "./ResellerPartnersSection.js";
-interface ResellerFormState {
+/**
+ * Reused for both `/resellers/new` and `/resellers/:id/edit`, but the two
+ * modes diverge: creating a reseller has nowhere to persist CNPJ-lookup
+ * results or Sócios yet (both endpoints need an existing `:id`), so `/new`
+ * only shows the minimal fields, same as before. On successful creation the
+ * user is redirected into `/resellers/:id/edit` (not back to the list) so
+ * they can immediately use CNPJ-lookup/Sócios — see `handleSubmit`.
+ *
+ * Section order when editing (per product direction, same order as
+ * `ClientRegistrationDetailPage`): Dados da revenda (incl. CNPJ lookup) ->
+ * Sócios -> Endereço -> Contatos -> Status.
+ */
+interface FormState {
name: string;
document: string;
contactEmail: string;
active: boolean;
+
+ tradeName: string;
+ stateRegistration: string;
+ municipalRegistration: string;
+ openingDate: string;
+ legalNature: string;
+ cnpjSituation: string;
+ mainActivity: string;
+
+ addressZip: string;
+ addressStreet: string;
+ addressNumber: string;
+ addressComplement: string;
+ addressNeighborhood: string;
+ addressCity: string;
+ addressState: string;
+ addressCountry: string;
+
+ contactPrincipalName: string;
+ contactPrincipalEmail: string;
+ contactPrincipalPhone: string;
+ contactFinancialName: string;
+ contactFinancialEmail: string;
+ contactFinancialPhone: string;
+ contactTechnicalName: string;
+ contactTechnicalEmail: string;
+ contactTechnicalPhone: string;
}
-const EMPTY_FORM: ResellerFormState = { name: "", document: "", contactEmail: "", active: true };
+const EMPTY_FORM: FormState = {
+ name: "",
+ document: "",
+ contactEmail: "",
+ active: true,
+
+ tradeName: "",
+ stateRegistration: "",
+ municipalRegistration: "",
+ openingDate: "",
+ legalNature: "",
+ cnpjSituation: "",
+ mainActivity: "",
+
+ addressZip: "",
+ addressStreet: "",
+ addressNumber: "",
+ addressComplement: "",
+ addressNeighborhood: "",
+ addressCity: "",
+ addressState: "",
+ addressCountry: "",
+
+ contactPrincipalName: "",
+ contactPrincipalEmail: "",
+ contactPrincipalPhone: "",
+ contactFinancialName: "",
+ contactFinancialEmail: "",
+ contactFinancialPhone: "",
+ contactTechnicalName: "",
+ contactTechnicalEmail: "",
+ contactTechnicalPhone: "",
+};
+
+function toDateInputValue(value: string | null): string {
+ return value ? value.slice(0, 10) : "";
+}
+
+function toFormState(reseller: Reseller): FormState {
+ return {
+ name: reseller.name,
+ document: reseller.document ?? "",
+ contactEmail: reseller.contact_email ?? "",
+ active: reseller.active,
+
+ tradeName: reseller.trade_name ?? "",
+ stateRegistration: reseller.state_registration ?? "",
+ municipalRegistration: reseller.municipal_registration ?? "",
+ openingDate: toDateInputValue(reseller.opening_date),
+ legalNature: reseller.legal_nature ?? "",
+ cnpjSituation: reseller.cnpj_situation ?? "",
+ mainActivity: reseller.main_activity ?? "",
+
+ addressZip: reseller.address_zip ?? "",
+ addressStreet: reseller.address_street ?? "",
+ addressNumber: reseller.address_number ?? "",
+ addressComplement: reseller.address_complement ?? "",
+ addressNeighborhood: reseller.address_neighborhood ?? "",
+ addressCity: reseller.address_city ?? "",
+ addressState: reseller.address_state ?? "",
+ addressCountry: reseller.address_country ?? "",
+
+ contactPrincipalName: reseller.contact_principal_name ?? "",
+ contactPrincipalEmail: reseller.contact_principal_email ?? "",
+ contactPrincipalPhone: reseller.contact_principal_phone ?? "",
+ contactFinancialName: reseller.contact_financial_name ?? "",
+ contactFinancialEmail: reseller.contact_financial_email ?? "",
+ contactFinancialPhone: reseller.contact_financial_phone ?? "",
+ contactTechnicalName: reseller.contact_technical_name ?? "",
+ contactTechnicalEmail: reseller.contact_technical_email ?? "",
+ contactTechnicalPhone: reseller.contact_technical_phone ?? "",
+ };
+}
function toOptional(value: string): string | undefined {
const trimmed = value.trim();
return trimmed === "" ? undefined : trimmed;
}
-/** Reused for both `/resellers/new` and `/resellers/:id/edit`. */
export function ResellerFormPage() {
const { id } = useParams<{ id: string }>();
const isEditing = Boolean(id);
const { accessToken } = useAuth();
const navigate = useNavigate();
- const [form, setForm] = useState(EMPTY_FORM);
+ const [form, setForm] = useState(EMPTY_FORM);
const [loading, setLoading] = useState(isEditing);
+ const [loadError, setLoadError] = useState(null);
const [error, setError] = useState(null);
+ const [saveSuccess, setSaveSuccess] = useState(false);
const [submitting, setSubmitting] = useState(false);
- useEffect(() => {
+ const [cnpjLookupLoading, setCnpjLookupLoading] = useState(false);
+ const [cnpjLookupError, setCnpjLookupError] = useState(null);
+ const [cnpjLookupNotFound, setCnpjLookupNotFound] = useState(null);
+ const [cnpjLookupSuccess, setCnpjLookupSuccess] = useState(null);
+ const [partnersRefreshKey, setPartnersRefreshKey] = useState(0);
+
+ // Persisted value (from the server), never the live `form.document` being
+ // typed — the "Buscar CNPJ" vs. "Buscar novamente" label reflects whether
+ // this reseller already HAS a saved document, not whether the field
+ // currently holds 14 digits (typing a CNPJ before ever searching/saving
+ // must not flip the label prematurely).
+ const [savedDocument, setSavedDocument] = useState(null);
+
+ const load = useCallback(() => {
if (!id || !accessToken) return;
- let cancelled = false;
+ setLoading(true);
+ setLoadError(null);
getReseller(id, accessToken)
.then((reseller) => {
- if (cancelled) return;
- setForm({
- name: reseller.name,
- document: reseller.document ?? "",
- contactEmail: reseller.contact_email ?? "",
- active: reseller.active,
- });
+ setForm(toFormState(reseller));
+ setSavedDocument(reseller.document);
})
- .catch((err) => {
- if (!cancelled) setError(err instanceof ApiError ? err.message : "Não foi possível carregar a revenda.");
- })
- .finally(() => {
- if (!cancelled) setLoading(false);
- });
- return () => {
- cancelled = true;
- };
+ .catch((err) => setLoadError(err instanceof ApiError ? err.message : "Não foi possível carregar a revenda."))
+ .finally(() => setLoading(false));
}, [id, accessToken]);
- function updateField(field: K, value: ResellerFormState[K]) {
+ useEffect(() => {
+ load();
+ }, [load]);
+
+ function updateField(field: K, value: FormState[K]) {
setForm((prev) => ({ ...prev, [field]: value }));
}
@@ -65,21 +189,52 @@ export function ResellerFormPage() {
event.preventDefault();
if (!accessToken) return;
setError(null);
+ setSaveSuccess(false);
setSubmitting(true);
try {
- const payload: CreateResellerInput = {
+ const payload: CreateResellerInput | UpdateResellerInput = {
name: form.name.trim(),
document: toOptional(form.document),
contactEmail: toOptional(form.contactEmail),
active: form.active,
+
+ tradeName: toOptional(form.tradeName),
+ stateRegistration: toOptional(form.stateRegistration),
+ municipalRegistration: toOptional(form.municipalRegistration),
+ openingDate: toOptional(form.openingDate),
+ legalNature: toOptional(form.legalNature),
+ cnpjSituation: toOptional(form.cnpjSituation),
+ mainActivity: toOptional(form.mainActivity),
+
+ addressZip: toOptional(form.addressZip),
+ addressStreet: toOptional(form.addressStreet),
+ addressNumber: toOptional(form.addressNumber),
+ addressComplement: toOptional(form.addressComplement),
+ addressNeighborhood: toOptional(form.addressNeighborhood),
+ addressCity: toOptional(form.addressCity),
+ addressState: toOptional(form.addressState),
+ addressCountry: toOptional(form.addressCountry),
+
+ contactPrincipalName: toOptional(form.contactPrincipalName),
+ contactPrincipalEmail: toOptional(form.contactPrincipalEmail),
+ contactPrincipalPhone: toOptional(form.contactPrincipalPhone),
+ contactFinancialName: toOptional(form.contactFinancialName),
+ contactFinancialEmail: toOptional(form.contactFinancialEmail),
+ contactFinancialPhone: toOptional(form.contactFinancialPhone),
+ contactTechnicalName: toOptional(form.contactTechnicalName),
+ contactTechnicalEmail: toOptional(form.contactTechnicalEmail),
+ contactTechnicalPhone: toOptional(form.contactTechnicalPhone),
};
if (isEditing && id) {
- await updateReseller(id, payload, accessToken);
+ const updated = await updateReseller(id, payload, accessToken);
+ setForm(toFormState(updated));
+ setSavedDocument(updated.document);
+ setSaveSuccess(true);
} else {
- await createReseller(payload, accessToken);
+ const created = await createReseller(payload as CreateResellerInput, accessToken);
+ navigate(`/resellers/${created.id}/edit`, { replace: true });
}
- navigate("/resellers", { replace: true });
} catch (err) {
setError(err instanceof ApiError ? err.message : "Não foi possível salvar a revenda. Tente novamente.");
} finally {
@@ -87,6 +242,40 @@ export function ResellerFormPage() {
}
}
+ async function runCnpjLookup(cnpj: string) {
+ if (!accessToken || !id) return;
+ setCnpjLookupError(null);
+ setCnpjLookupNotFound(null);
+ setCnpjLookupSuccess(null);
+ setCnpjLookupLoading(true);
+ try {
+ const result = await applyResellerCnpjLookup(id, cnpj, accessToken);
+ setForm(toFormState(result.reseller));
+ setSavedDocument(result.reseller.document);
+ setCnpjLookupSuccess(
+ `Dados encontrados e preenchidos automaticamente. ${result.partners.length} sócio(s) no cadastro.`,
+ );
+ setPartnersRefreshKey((k) => k + 1);
+ } catch (err) {
+ if (err instanceof ApiError && err.status === 404) {
+ setCnpjLookupNotFound(err.message);
+ } else {
+ setCnpjLookupError(err instanceof ApiError ? err.message : "Não foi possível buscar o CNPJ.");
+ }
+ } finally {
+ setCnpjLookupLoading(false);
+ }
+ }
+
+ if (loadError) {
+ return (
+
+ );
+ }
+
return (
) : null}
+ {saveSuccess ? (
+
+ Revenda salva com sucesso.
+
+ ) : null}
{loading ? (
Carregando…
) : (
-