From fac675533ccc066c55d1803aa35725d81ed6c92b Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Mon, 5 Jan 2026 10:12:15 -0600 Subject: [PATCH] CRUD de agentes aduanales funcional, edicion y creacion --- .../v1/modules/a76/customs_brokers/routes.py | 54 +- .../lib/api/dashboard/a76/customs-brokers.ts | 213 ++++---- .../customs_brokers/data-table-actions.svelte | 8 +- .../customs_brokers/data-table.svelte | 27 +- .../dashboard/customs_brokers/+page.svelte | 516 +++++++++--------- .../customs_brokers/edit/[[id]]/+page.svelte | 275 ++++++++++ 6 files changed, 688 insertions(+), 405 deletions(-) create mode 100644 frontend/src/routes/dashboard/customs_brokers/edit/[[id]]/+page.svelte diff --git a/backend/api/v1/modules/a76/customs_brokers/routes.py b/backend/api/v1/modules/a76/customs_brokers/routes.py index b4a756e5..e968ac78 100644 --- a/backend/api/v1/modules/a76/customs_brokers/routes.py +++ b/backend/api/v1/modules/a76/customs_brokers/routes.py @@ -1,35 +1,64 @@ from typing import Dict, Any -from api.v1.common.tenant_crud_routes import TenantCRUDRoutes -from core.database import get_core_db -from core.security import get_current_user, validate_access_to_resource from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from core.database import get_core_db +from core.security import get_current_user, validate_access_to_resource + +from api.v1.common.tenant_crud_routes import TenantCRUDRoutes from . import dto, services -# Create main router router = APIRouter() -# Create CRUD routes for CustomsBroker using TenantCRUDRoutes customs_broker_crud = TenantCRUDRoutes( service=services.CustomsBrokerService, create_schema=dto.CustomsBrokerCreateDTO, update_schema=dto.CustomsBrokerUpdateDTO, response_schema=dto.CustomsBrokerResponseDTO, - prefix="/customs-brokers", # No prefix since it's already in the parent router + prefix="/customs-brokers", tags=[], resource_name="Customs Broker", id_name="broker_key", id_type=str, - enable_list=True, # Enable list endpoint with pagination + enable_list=True, ) -# Include the CRUD routes router.include_router(customs_broker_crud.router) -# Additional routes for child resources (CustomsBrokerVU and CustomsBrokerPersonnel) -# These remain as manual routes since they have different patterns +@router.patch( + "/customs-brokers/{broker_key}", + response_model=dto.CustomsBrokerResponseDTO, +) +def update_customs_broker( + broker_key: str, + broker_data: dto.CustomsBrokerUpdateDTO, + company_id: int = Query(..., description="Company ID"), + db: Session = Depends(get_core_db), + current_user: Dict[str, Any] = Depends(get_current_user), +): + """ + Actualización parcial (PATCH). + """ + tenant_id = validate_access_to_resource(db, company_id, current_user) + + broker = services.CustomsBrokerService.get_by_id(db, broker_key, tenant_id, company_id) + if not broker: + raise HTTPException(status_code=404, detail="Customs Broker not found") + + updated_broker = services.CustomsBrokerService.update( + db=db, + broker_key=broker_key, + tenant_id=tenant_id, + broker_data=broker_data, + company_id=company_id + ) + + if not updated_broker: + raise HTTPException(status_code=400, detail="Error updating Customs Broker") + + return updated_broker + @router.put( "/customs-broker-vu/{broker_key}", @@ -44,7 +73,6 @@ def update_customs_broker_vu( ): tenant_id = validate_access_to_resource(db, company_id, current_user) - # Verify the broker exists and belongs to the tenant/company broker = services.CustomsBrokerService.get_by_id(db, broker_key, tenant_id, company_id) if not broker: raise HTTPException(status_code=404, detail="Customs Broker not found") @@ -69,7 +97,6 @@ def update_customs_broker_personnel( ): tenant_id = validate_access_to_resource(db, company_id, current_user) - # Verify the broker exists and belongs to the tenant/company broker = services.CustomsBrokerService.get_by_id(db, broker_key, tenant_id, company_id) if not broker: raise HTTPException(status_code=404, detail="Customs Broker not found") @@ -81,5 +108,4 @@ def update_customs_broker_personnel( raise HTTPException( status_code=404, detail="Customs Broker Personnel not found" ) - return updated_personnel - + return updated_personnel \ No newline at end of file diff --git a/frontend/src/lib/api/dashboard/a76/customs-brokers.ts b/frontend/src/lib/api/dashboard/a76/customs-brokers.ts index 9f18da9e..da400dbc 100644 --- a/frontend/src/lib/api/dashboard/a76/customs-brokers.ts +++ b/frontend/src/lib/api/dashboard/a76/customs-brokers.ts @@ -1,141 +1,118 @@ - import { api } from '$lib/api'; +import type { ApiResponse } from '$lib/api'; export interface CustomsBroker { - type?: string | null; - broker_key: string; - name?: string | null; - address?: string | null; - postal_code?: string | null; - city?: string | null; - state?: string | null; - phone?: string | null; - fax?: string | null; - email?: string | null; - country?: string | null; - tax_id?: string | null; - personal_id?: string | null; - position?: string | null; - license: string; - company?: string | null; - contact?: string | null; - tenant_id: string; - company_id: string; + type?: string | null; + broker_key: string; + name?: string | null; + address?: string | null; + postal_code?: string | null; + city?: string | null; + state?: string | null; + phone?: string | null; + fax?: string | null; + email?: string | null; + country?: string | null; + tax_id?: string | null; + personal_id?: string | null; + position?: string | null; + license: string; + company?: string | null; + contact?: string | null; + tenant_id: string; + company_id: string; } export interface CustomsBrokerVU { - certificate_path?: string | null; - key_path?: string | null; - access_key?: string | null; - fiel_format?: string | null; - signature_read_path?: string | null; - archive_path?: string | null; - fiel_access_key?: string | null; - web_service_user?: string | null; - web_service_access_key?: string | null; - vu_email?: string | null; - vu_figure_type?: string | null; - xml_files_path?: string | null; - query_tax_id?: string | null; - doda_certificate_path?: string | null; - doda_key_path?: string | null; - doda_web_service_user?: string | null; - doda_web_service_access_key?: string | null; - doda_fiel_access_key?: string | null; - doda_xml_files_path?: string | null; + certificate_path?: string | null; + key_path?: string | null; + access_key?: string | null; + fiel_format?: string | null; + signature_read_path?: string | null; + archive_path?: string | null; + fiel_access_key?: string | null; + web_service_user?: string | null; + web_service_access_key?: string | null; + vu_email?: string | null; + vu_figure_type?: string | null; + xml_files_path?: string | null; + query_tax_id?: string | null; + doda_certificate_path?: string | null; + doda_key_path?: string | null; + doda_web_service_user?: string | null; + doda_web_service_access_key?: string | null; + doda_fiel_access_key?: string | null; + doda_xml_files_path?: string | null; } export interface CustomsBrokerPersonnel { - broker_key: string; - line: number; - name?: string | null; - tax_id?: string | null; - personal_id?: string | null; - position?: string | null; - license?: string | null; - first_name?: string | null; - last_name?: string | null; - middle_name?: string | null; - email?: string | null; + broker_key: string; + line: number; + name?: string | null; + tax_id?: string | null; + personal_id?: string | null; + position?: string | null; + license?: string | null; + first_name?: string | null; + last_name?: string | null; + middle_name?: string | null; + email?: string | null; } export interface CreateCustomsBrokerData { - type?: string | null; - broker_key: string; - name?: string | null; - address?: string | null; - postal_code?: string | null; - city?: string | null; - state?: string | null; - phone?: string | null; - fax?: string | null; - email?: string | null; - country?: string | null; - tax_id?: string | null; - personal_id?: string | null; - position?: string | null; - license?: string | null; - company?: string | null; - contact?: string | null; - tenant_id: string; - company_id: string; + type?: string | null; + broker_key: string; + name?: string | null; + address?: string | null; + postal_code?: string | null; + city?: string | null; + state?: string | null; + phone?: string | null; + fax?: string | null; + email?: string | null; + country?: string | null; + tax_id?: string | null; + personal_id?: string | null; + position?: string | null; + license?: string | null; + company?: string | null; + contact?: string | null; + tenant_id?: string; + company_id: string; } /** * API para Agentes Aduanales */ export const customsBrokersApi = { - /** - * Lista todos los agentes aduanales - */ - list: (companyId: string) => { - return api.get(`/v1/a76/customs-brokers/?company_id=${companyId}`); - }, + list: (companyId: string) => { + return api.get(`/v1/a76/customs-brokers/?company_id=${companyId}`); + }, - /** - * Obtiene un agente aduanal por su clave - */ - get: (brokerKey: string) => { - return api.get(`/v1/a76/customs-brokers/${brokerKey}`); - }, + get: (brokerKey: string, companyId: string) => { + return api.get(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`); + }, + + create: (data: CreateCustomsBrokerData, companyId: string) => { + return api.post(`/v1/a76/customs-brokers/?company_id=${companyId}`, data); + }, - /** - * Crea un nuevo agente aduanal - */ - create: (data: CreateCustomsBrokerData) => { - const companyId = data.company_id; - return api.post(`/v1/a76/customs-brokers/?company_id=${companyId}`, data); - }, + update: (brokerKey: string, data: CreateCustomsBrokerData, companyId: string) => { + return api.patch(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`, data); + }, - /** - * Elimina un agente aduanal - */ - delete: (brokerKey: string, companyId: string) => { - return api.delete(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`); - }, + delete: (brokerKey: string, companyId: string) => { + return api.delete(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`); + }, - /** - * Actualiza la información de un agente aduanal - */ - update: (brokerKey: string, data: CreateCustomsBrokerData) => { - const companyId = data.company_id; - return api.put(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`, data); - }, + updateVU: (brokerKey: string, data: CustomsBrokerVU, companyId: string) => { + return api.put(`/v1/a76/customs-broker-vu/${brokerKey}?company_id=${companyId}`, data); + }, - /** - * Actualiza la información de VU de un agente aduanal - */ - updateVU: (brokerKey: string, data: CustomsBrokerVU) => { - return api.put(`/v1/a76/customs-broker-vu/${brokerKey}`, data); - }, - - /** - * Actualiza el personal de un agente aduanal - */ - updatePersonnel: (brokerKey: string, line: number, data: CustomsBrokerPersonnel) => { - return api.put( - `/v1/a76/customs-broker-personnel/${brokerKey}/${line}`, - data - ); - } -}; + updatePersonnel: (brokerKey: string, line: number, data: CustomsBrokerPersonnel, companyId: string) => { + return api.put( + `/v1/a76/customs-broker-personnel/${brokerKey}/${line}?company_id=${companyId}`, + data + ); + } +}; \ No newline at end of file diff --git a/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte b/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte index 3696cbd7..d32046fc 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte @@ -5,7 +5,7 @@ import type { CustomsBroker } from "./columns.js"; import DetailsDialog from "./details-dialog.svelte"; import DeleteDialog from "./delete-dialog.svelte"; - import EditDialog from "./edit-dialog.svelte"; + import { goto } from "$app/navigation"; let { broker, @@ -17,7 +17,6 @@ let showDetailsDialog = $state(false); let showDeleteDialog = $state(false); - let showEditDialog = $state(false); function handleCopyKey() { navigator.clipboard.writeText(broker.broker_key); @@ -52,7 +51,7 @@ Abrir menú - + Acciones @@ -70,7 +69,7 @@ Ver detalles - + goto(`/dashboard/customs_brokers/edit/${broker.broker_key}`)}> Editar @@ -81,5 +80,4 @@ - diff --git a/frontend/src/lib/components/dashboard/customs_brokers/data-table.svelte b/frontend/src/lib/components/dashboard/customs_brokers/data-table.svelte index a922ec82..40e3b137 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/data-table.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/data-table.svelte @@ -1,5 +1,4 @@
@@ -45,9 +45,10 @@ {/each} + {#each table.getRowModel().rows as row (row.id)} - + {#each row.getVisibleCells() as cell (cell.id)} - import { onMount } from 'svelte'; - import { customsBrokersApi, type CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers'; - import DataTable from '$lib/components/dashboard/customs_brokers/data-table.svelte'; - import { createColumns } from '$lib/components/dashboard/customs_brokers/columns.js'; - import CreateDialog from '$lib/components/dashboard/customs_brokers/create-dialog.svelte'; - import * as Card from '$lib/components/ui/card'; - import { Button } from '$lib/components/ui/button'; - import { Input } from '$lib/components/ui/input'; - import { Label } from '$lib/components/ui/label'; - import type { PageData } from './$types'; - import { browser } from '$app/environment'; - import { companyStore } from '$lib/stores/company.svelte'; - import { Plus, Search, Trash2 } from 'lucide-svelte'; + import { onMount } from 'svelte'; + import { customsBrokersApi, type CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers'; + import DataTable from '$lib/components/dashboard/customs_brokers/data-table.svelte'; + import { createColumns } from '$lib/components/dashboard/customs_brokers/columns.js'; + import CreateDialog from '$lib/components/dashboard/customs_brokers/create-dialog.svelte'; + import * as Card from '$lib/components/ui/card'; + import { Button } from '$lib/components/ui/button'; + import { Input } from '$lib/components/ui/input'; + import { Label } from '$lib/components/ui/label'; + import type { PageData } from './$types'; + import { browser } from '$app/environment'; + import { companyStore } from '$lib/stores/company.svelte'; + import { Plus, Search, Trash2, Loader2 } from 'lucide-svelte'; - // Los datos iniciales vienen del servidor - let { data }: { data: PageData } = $props(); - - // Estado para el diálogo de crear - let showCreateDialog = $state(false); + // Los datos iniciales vienen del servidor + let { data }: { data: PageData } = $props(); + + // Estado para el diálogo de crear + let showCreateDialog = $state(false); - // Estado para la lista de agentes aduanales (inicializar con datos del servidor) - let brokersList = $state(data.brokers || []); - let listLoading = $state(false); - let listError = $state(data.error || null); + // Estado para la lista de agentes aduanales + let brokersList = $state(data.brokers || []); + let listLoading = $state(false); + let listError = $state(data.error || null); - // Estado para búsqueda - let searchKey = $state(''); - let searchedBroker = $state(null); - let searchLoading = $state(false); - let searchError = $state(null); + // Estado para búsqueda + let searchKey = $state(''); + let searchedBroker = $state(null); + let searchLoading = $state(false); + let searchError = $state(null); - // Sincronizar token de cookies a localStorage al montar el componente - onMount(() => { - if (browser) { - // Función para obtener el valor de una cookie - const getCookie = (name: string): string | null => { - const value = `; ${document.cookie}`; - const parts = value.split(`; ${name}=`); - if (parts.length === 2) return parts.pop()?.split(';').shift() || null; - return null; - }; + // Control para evitar bucles de recarga + let lastLoadedCompanyId = $state(""); - // Verificar si hay token en las cookies - const cookieToken = getCookie('access_token'); - const localToken = localStorage.getItem('access_token'); + // --- REACTIVIDAD AUTOMÁTICA --- + + // 1. Sincronizar si cambia la data del servidor (navegación) + $effect(() => { + if (data.brokers) { + brokersList = data.brokers; + if (data.currentCompanyId) { + lastLoadedCompanyId = data.currentCompanyId.toString(); + } + } + }); - if (cookieToken && cookieToken !== localToken) { - localStorage.setItem('access_token', cookieToken); - } + // 2. Sincronizar si cambia el store de la empresa + $effect(() => { + const currentId = companyStore.activeCompany?.id?.toString(); + + // Solo recargamos si hay ID, si es diferente al último y estamos en el navegador + if (browser && currentId && currentId !== lastLoadedCompanyId) { + lastLoadedCompanyId = currentId; + // Recarga normal (false), muestra loading porque cambiamos de empresa + reloadData(false); + } + }); - // También sincronizar refresh_token si existe - const cookieRefreshToken = getCookie('refresh_token'); - const localRefreshToken = localStorage.getItem('refresh_token'); + onMount(() => { + if (browser) { + const getCookie = (name: string): string | null => { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) return parts.pop()?.split(';').shift() || null; + return null; + }; - if (cookieRefreshToken && cookieRefreshToken !== localRefreshToken) { - localStorage.setItem('refresh_token', cookieRefreshToken); - } + const cookieToken = getCookie('access_token'); + const localToken = localStorage.getItem('access_token'); + if (cookieToken && cookieToken !== localToken) localStorage.setItem('access_token', cookieToken); - // Escuchar cambios de compañía - const handleCompanyChange = (event: CustomEvent) => { - // Recargar la página para obtener datos de la nueva compañía - reloadData(); - }; + const cookieRefreshToken = getCookie('refresh_token'); + const localRefreshToken = localStorage.getItem('refresh_token'); + if (cookieRefreshToken && cookieRefreshToken !== localRefreshToken) localStorage.setItem('refresh_token', cookieRefreshToken); + } + }); - window.addEventListener('companyChanged', handleCompanyChange as EventListener); + async function handleSearch() { + if (!searchKey.trim()) { + searchError = 'Por favor ingresa una clave de agente aduanal'; + return; + } - // Cleanup - return () => { - window.removeEventListener('companyChanged', handleCompanyChange as EventListener); - }; - } - }); + searchLoading = true; + searchError = null; + searchedBroker = null; - async function handleSearch() { - if (!searchKey.trim()) { - searchError = 'Por favor ingresa una clave de agente aduanal'; - return; - } + try { + const companyId = companyStore.activeCompany?.id.toString() || ''; + const response = await customsBrokersApi.get(searchKey.trim(), companyId); - searchLoading = true; - searchError = null; - searchedBroker = null; + if (response.error) { + if (response.status === 404) { + searchError = 'No se encontró un agente aduanal con esta clave'; + } else if (response.status === 401 || response.status === 403) { + searchError = 'Sesión expirada. Recargando página...'; + setTimeout(() => window.location.reload(), 2000); + } else { + searchError = response.error; + } + return; + } - try { - const response = await customsBrokersApi.get(searchKey.trim()); + if (response.data) { + searchedBroker = response.data; + } + } catch (e) { + searchError = 'Error al buscar agente aduanal'; + console.error('Error searching broker:', e); + } finally { + searchLoading = false; + } + } - if (response.error) { - if (response.status === 404) { - searchError = 'No se encontró un agente aduanal con esta clave'; - } else if (response.status === 401 || response.status === 403) { - searchError = 'Sesión expirada. Recargando página...'; - setTimeout(() => { - window.location.reload(); - }, 2000); - } else { - searchError = response.error; - } - return; - } + /** + * Recarga los datos. + * @param silent Si es true, NO bloquea la tabla con el spinner (ideal para delete/create). + */ + async function reloadData(silent = false) { + searchKey = ''; + searchedBroker = null; + searchError = null; + + if (!companyStore.activeCompany) return; + + // Solo mostramos el spinner visual si NO es silencioso + if (!silent) { + listLoading = true; + } + + listError = null; - if (response.data) { - searchedBroker = response.data; - } - } catch (e) { - searchError = 'Error al buscar agente aduanal'; - console.error('Error searching broker:', e); - } finally { - searchLoading = false; - } - } + try { + const response = await customsBrokersApi.list(companyStore.activeCompany.id.toString()); - async function reloadData() { - // Limpiar búsqueda - searchKey = ''; - searchedBroker = null; - searchError = null; - - // Recargar lista de brokers desde la API - if (!companyStore.activeCompany) return; - - listLoading = true; - listError = null; + if (response.error) { + console.error('📊 [CustomsBrokers] Error en reloadData:', response.error); + if (response.status === 401 || response.status === 403) { + listError = 'Sesión expirada. Recargando página...'; + setTimeout(() => window.location.reload(), 2000); + } else { + if (!silent) listError = response.error; + } + return; + } - try { - const response = await customsBrokersApi.list(companyStore.activeCompany.id.toString()); + if (response.data) { + brokersList = response.data; + } + } catch (e) { + listError = 'Error recargando datos'; + console.error('📊 [CustomsBrokers] Error reloading:', e); + } finally { + listLoading = false; + } + } - if (response.error) { - console.error('📊 [CustomsBrokers] Error en reloadData:', response.error); - if (response.status === 401 || response.status === 403) { - listError = 'Sesión expirada. Recargando página...'; - setTimeout(() => { - window.location.reload(); - }, 2000); - } else { - listError = response.error; - } - return; - } + function handleCreateClick() { + showCreateDialog = true; + } - if (response.data) { - // Reemplazar la lista con los nuevos datos - brokersList = response.data; - } - } catch (e) { - listError = 'Error recargando datos'; - console.error('📊 [CustomsBrokers] Error reloading:', e); - } finally { - listLoading = false; - } - } + function handleSuccess() { + // Esperamos 300ms a la DB y recargamos "en silencio" (sin bloquear la tabla) + setTimeout(() => { + reloadData(true); + }, 300); + } - function handleCreateClick() { - showCreateDialog = true; - } - - function handleSuccess() { - // Recargar datos después de crear/eliminar - reloadData(); - } - - // Crear columnas con el callback onSuccess - const columns = createColumns(handleSuccess); - - // Array para mostrar en la tabla (búsqueda o lista completa) - const brokers = $derived(searchedBroker ? [searchedBroker] : brokersList); + const columns = createColumns(handleSuccess); + const brokers = $derived(searchedBroker ? [searchedBroker] : brokersList);
- -
-
-

Agentes Aduanales

-

- Gestiona el catálogo de agentes aduanales -

-
- -
+
+
+

Agentes Aduanales

+

+ Gestiona el catálogo de agentes aduanales +

+
+ +
- - - - Buscar Agente Aduanal - - Ingresa la clave del agente aduanal para buscarlo - {#if companyStore.activeCompany} - - Compañía: {companyStore.activeCompany.name} - {/if} - - - -
{ e.preventDefault(); handleSearch(); }} class="space-y-4"> -
-
- - -
-
- - {#if searchedBroker} - - {/if} -
-
+ + + Buscar Agente Aduanal + + Ingresa la clave del agente aduanal para buscarlo + {#if companyStore.activeCompany} + - Compañía: {companyStore.activeCompany.name} + {/if} + + + + { e.preventDefault(); handleSearch(); }} class="space-y-4"> +
+
+ + +
+
+ + {#if searchedBroker} + + {/if} +
+
- {#if searchError} -
- {searchError} -
- {/if} - -
-
+ {#if searchError} +
+ {searchError} +
+ {/if} + +
+
- - - -
-
- - {#if searchedBroker} - Resultado de la Búsqueda - {:else} - Agentes Aduanales - {/if} - - - {#if searchedBroker} - Se encontró 1 agente aduanal - {:else if listLoading} - Cargando agentes aduanales... - {:else} - Total: {brokersList.length} agente{brokersList.length !== 1 ? 's' : ''} aduanal{brokersList.length !== 1 ? 'es' : ''} - {/if} - -
-
-
- - {#if listError} -
- {listError} -
- {:else if listLoading} -
-
-
- Cargando agentes aduanales... -
-
- {:else} - - {/if} -
-
+ + +
+
+ + {#if searchedBroker} + Resultado de la Búsqueda + {:else} + Agentes Aduanales + {/if} + + + {#if searchedBroker} + Se encontró 1 agente aduanal + {:else} + Total: {brokersList.length} agente{brokersList.length !== 1 ? 's' : ''} aduanal{brokersList.length !== 1 ? 'es' : ''} + {/if} + +
+
+
+ + + {#if listError} +
+ {listError} +
+ {:else} + + {#if listLoading} +
+
+ + Actualizando... +
+
+ {/if} + +
+ + {#key brokers} + + {/key} +
+ {/if} +
+
- - + \ No newline at end of file diff --git a/frontend/src/routes/dashboard/customs_brokers/edit/[[id]]/+page.svelte b/frontend/src/routes/dashboard/customs_brokers/edit/[[id]]/+page.svelte new file mode 100644 index 00000000..3c6f823f --- /dev/null +++ b/frontend/src/routes/dashboard/customs_brokers/edit/[[id]]/+page.svelte @@ -0,0 +1,275 @@ + + +
+
+ +
+

{title}

+

+ {isEdit ? `Modificando registro: ${routeId}` : 'Completa la información para el nuevo Agente.'} +

+
+
+ + {#if error} +
+ ⚠️ {error} +
+ {/if} + +
+
+ {#each [ + { id: 'general', label: 'Identificación', icon: User }, + { id: 'contacto', label: 'Contacto', icon: Phone }, + { id: 'direccion', label: 'Dirección', icon: MapPin } + ] as tab} + + {/each} +
+ +
+
{ e.preventDefault(); handleSave(); }}> + + {#if activeTab === 'general'} +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + {:else if activeTab === 'contacto'} +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {:else if activeTab === 'direccion'} +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ {/if} + +
+
+ + +
+
+
+
+
+
+ + \ No newline at end of file