From c26e04bcff1df4a22d17146066aa30e14ebabebe Mon Sep 17 00:00:00 2001 From: Aduanasoft Date: Tue, 14 Jul 2026 17:44:21 -0600 Subject: [PATCH] =?UTF-8?q?refactor(crm):=20cat=C3=A1logos=20como=20p?= =?UTF-8?q?=C3=A1ginas=20completas=20con=20pesta=C3=B1as=20(sin=20modales)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clientes/Prospectos y Proveedores: alta y edición en páginas dedicadas (/nuevo y /[id]) respetando el shell del dashboard, en vez de modales - Info segmentada en pestañas: Generales · Comercial · Fiscal · Observaciones · Direcciones · Contactos · Documentos - Componentes reutilizables AccountFields/SupplierFields; RelatedManager ahora renderiza por sección - svelte-check: 0 errores de tipo en archivos del CRM Co-Authored-By: Claude Opus 4.8 (1M context) --- .../lib/components/crm/AccountFields.svelte | 53 ++++++ .../lib/components/crm/RelatedManager.svelte | 179 ++++++++++-------- .../lib/components/crm/SupplierFields.svelte | 71 +++++++ .../routes/dashboard/crm/cuentas/+page.svelte | 115 +---------- .../dashboard/crm/cuentas/[id]/+page.svelte | 96 +++++++--- .../dashboard/crm/cuentas/nuevo/+page.svelte | 76 ++++++++ .../dashboard/crm/proveedores/+page.svelte | 145 +------------- .../crm/proveedores/[id]/+page.svelte | 103 ++++++++-- .../crm/proveedores/nuevo/+page.svelte | 88 +++++++++ 9 files changed, 551 insertions(+), 375 deletions(-) create mode 100644 frontend/src/lib/components/crm/AccountFields.svelte create mode 100644 frontend/src/lib/components/crm/SupplierFields.svelte create mode 100644 frontend/src/routes/dashboard/crm/cuentas/nuevo/+page.svelte create mode 100644 frontend/src/routes/dashboard/crm/proveedores/nuevo/+page.svelte diff --git a/frontend/src/lib/components/crm/AccountFields.svelte b/frontend/src/lib/components/crm/AccountFields.svelte new file mode 100644 index 0000000..81d3757 --- /dev/null +++ b/frontend/src/lib/components/crm/AccountFields.svelte @@ -0,0 +1,53 @@ + + +{#if tab === 'generales'} +
+ + + + + + + + + +
+{:else if tab === 'comercial'} +
+ + + + + + +
+{:else if tab === 'fiscal'} +
+ + + + + + + + + +
+{:else if tab === 'observaciones'} +
+ + +
+{/if} diff --git a/frontend/src/lib/components/crm/RelatedManager.svelte b/frontend/src/lib/components/crm/RelatedManager.svelte index a548a2d..2a10584 100644 --- a/frontend/src/lib/components/crm/RelatedManager.svelte +++ b/frontend/src/lib/components/crm/RelatedManager.svelte @@ -11,8 +11,18 @@ import { ADDRESS_TYPES, DOC_TYPES, CONTACT_AREAS, labelOf } from '$lib/components/crm/format'; import { toast } from 'svelte-sonner'; - // Dueño de los registros relacionados: cliente (account) o proveedor (supplier) - let { ownerType, ownerId }: { ownerType: 'account' | 'supplier'; ownerId: number } = $props(); + // Dueño de los registros relacionados y qué sección mostrar + let { + ownerType, + ownerId, + section = 'all' + }: { + ownerType: 'account' | 'supplier'; + ownerId: number; + section?: 'addresses' | 'contacts' | 'documents' | 'all'; + } = $props(); + + const show = (s: 'addresses' | 'contacts' | 'documents') => section === 'all' || section === s; let addresses = $state([]); let contacts = $state([]); @@ -120,93 +130,96 @@
- - - - Direcciones - - - - {#if addresses.length === 0} -

Sin direcciones.

- {:else} - - TipoDomicilioCPCiudad - - {#each addresses as a (a.id)} - - {labelOf(ADDRESS_TYPES, a.address_type)}{#if a.is_primary}(principal){/if} - {[a.street, a.ext_number, a.neighborhood].filter(Boolean).join(' ') || '—'} - {a.postal_code ?? '—'} - {[a.city, a.state].filter(Boolean).join(', ') || '—'} - - - {/each} - - - {/if} -
-
+ {#if show('addresses')} + + + Direcciones + + + + {#if addresses.length === 0} +

Sin direcciones.

+ {:else} + + TipoDomicilioCPCiudad + + {#each addresses as a (a.id)} + + {labelOf(ADDRESS_TYPES, a.address_type)}{#if a.is_primary}(principal){/if} + {[a.street, a.ext_number, a.neighborhood].filter(Boolean).join(' ') || '—'} + {a.postal_code ?? '—'} + {[a.city, a.state].filter(Boolean).join(', ') || '—'} + + + {/each} + + + {/if} +
+
+ {/if} - - - - Contactos - - - - {#if contacts.length === 0} -

Sin contactos.

- {:else} - - NombrePuesto / ÁreaEmailTeléfono - - {#each contacts as c (c.id)} - - {c.first_name} {c.last_name ?? ''}{#if c.is_primary}(principal){/if} - {[c.job_title, labelOf(CONTACT_AREAS, c.area) !== '—' ? labelOf(CONTACT_AREAS, c.area) : null].filter(Boolean).join(' · ') || '—'} - {c.email ?? '—'} - {c.phone ?? c.mobile ?? '—'} - - - {/each} - - - {/if} -
-
+ {#if show('contacts')} + + + Contactos + + + + {#if contacts.length === 0} +

Sin contactos.

+ {:else} + + NombrePuesto / ÁreaEmailTeléfono + + {#each contacts as c (c.id)} + + {c.first_name} {c.last_name ?? ''}{#if c.is_primary}(principal){/if} + {[c.job_title, labelOf(CONTACT_AREAS, c.area) !== '—' ? labelOf(CONTACT_AREAS, c.area) : null].filter(Boolean).join(' · ') || '—'} + {c.email ?? '—'} + {c.phone ?? c.mobile ?? '—'} + + + {/each} + + + {/if} +
+
+ {/if} - - - - Documentos - - - - {#if documents.length === 0} -

Sin documentos.

- {:else} - - TipoNombreArchivo - - {#each documents as d (d.id)} - - {labelOf(DOC_TYPES, d.doc_type)} - {d.name} - {#if d.file_url}Ver{:else}—{/if} - - - {/each} - - - {/if} -
-
+ {#if show('documents')} + + + Documentos + + + + {#if documents.length === 0} +

Sin documentos.

+ {:else} + + TipoNombreArchivo + + {#each documents as d (d.id)} + + {labelOf(DOC_TYPES, d.doc_type)} + {d.name} + {#if d.file_url}Ver{:else}—{/if} + + + {/each} + + + {/if} +
+
+ {/if}
{#if activeModal}