feature/permisos-desabilitar-botones
This commit is contained in:
@@ -67,6 +67,8 @@ def get_creation_data(
|
||||
required_permissions=["invoice.exp.create"],
|
||||
)
|
||||
return InvoiceCatalogService.get_creation_data(db, tenant_id, company_id)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.exception("get_creation_data failed: %s", e)
|
||||
raise HTTPException(status_code=500, detail=f"Error al cargar datos de creación: {str(e)}")
|
||||
@@ -187,6 +189,8 @@ def create_invoice(
|
||||
validate_access_to_resource(db, company_id, current_user, required_permissions=[f"{perm_base}.create"])
|
||||
|
||||
return services.InvoiceService.create(db, data, tenant_id, company_id)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.exception("create_invoice failed: %s", e)
|
||||
raise HTTPException(status_code=500, detail=f"Error al guardar factura: {str(e)}")
|
||||
|
||||
@@ -30,13 +30,15 @@ async def get_creation_data(
|
||||
Consolidates multiple catalog calls into a single endpoint.
|
||||
"""
|
||||
from core.security import validate_access_to_resource
|
||||
# Alta de pedimento: misma capacidad que editar en negocio / UI (pedimento-permissions).
|
||||
validate_access_to_resource(db, company_id, current_user, ["pedimentos_mgmt.edit"])
|
||||
# Alta de pedimento: requiere permiso create.
|
||||
validate_access_to_resource(db, company_id, current_user, ["pedimentos_mgmt.create"])
|
||||
|
||||
tenant_id = current_user["tenant_id"]
|
||||
|
||||
try:
|
||||
return PedimentoCatalogService.get_creation_data(db, tenant_id, company_id)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error fetching creation data: {e}")
|
||||
import traceback
|
||||
@@ -93,7 +95,7 @@ crud_router = TenantCRUDRoutes(
|
||||
max_page_size=1000,
|
||||
list_permissions=["pedimentos_mgmt.view"],
|
||||
get_permissions=["pedimentos_mgmt.view"],
|
||||
create_permissions=["pedimentos_mgmt.edit"],
|
||||
create_permissions=["pedimentos_mgmt.create"],
|
||||
update_permissions=["pedimentos_mgmt.edit"],
|
||||
delete_permissions=["pedimentos_mgmt.delete"],
|
||||
).router
|
||||
|
||||
@@ -22,31 +22,33 @@
|
||||
let deleteDialogOpen = $state(false);
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger disabled={!canEdit && !canDelete}>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="h-8 w-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisVertical class="h-4 w-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
{#if canEdit}
|
||||
<DropdownMenu.Item onclick={() => editDialogOpen = true}>
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
{#if canDelete}
|
||||
<DropdownMenu.Item onclick={() => deleteDialogOpen = true} class="text-destructive">
|
||||
<Trash2 class="mr-2 h-4 w-4" />
|
||||
Eliminar
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
{#if canEdit || canDelete}
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="h-8 w-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisVertical class="h-4 w-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
{#if canEdit}
|
||||
<DropdownMenu.Item onclick={() => editDialogOpen = true}>
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
{#if canDelete}
|
||||
<DropdownMenu.Item onclick={() => deleteDialogOpen = true} class="text-destructive">
|
||||
<Trash2 class="mr-2 h-4 w-4" />
|
||||
Eliminar
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
{/if}
|
||||
|
||||
{#if canEdit}
|
||||
<CreateEditDialog
|
||||
|
||||
24
frontend/src/lib/permissions/customs-broker-permissions.ts
Normal file
24
frontend/src/lib/permissions/customs-broker-permissions.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { User } from '$lib/auth';
|
||||
import { userHasPermission } from '$lib/auth';
|
||||
|
||||
function hasAssignedCode(user: User | null, code: string): boolean {
|
||||
if (!user) return false;
|
||||
return user.permissions.includes(code);
|
||||
}
|
||||
|
||||
export function canViewCustomsBrokers(user: User | null): boolean {
|
||||
return userHasPermission(user, 'customs_brokers.view');
|
||||
}
|
||||
|
||||
export function canCreateCustomsBrokers(user: User | null): boolean {
|
||||
return hasAssignedCode(user, 'customs_brokers.create');
|
||||
}
|
||||
|
||||
export function canEditCustomsBrokers(user: User | null): boolean {
|
||||
return hasAssignedCode(user, 'customs_brokers.edit');
|
||||
}
|
||||
|
||||
export function canDeleteCustomsBrokers(user: User | null): boolean {
|
||||
return hasAssignedCode(user, 'customs_brokers.delete');
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* - **Ver listado:** `userHasPermission` (incluye comodín de rol Keycloak `admin` como en otras pantallas).
|
||||
* - **Crear / editar / borrar (mutaciones):** solo códigos en `user.permissions` (tras `syncCompanyPermissions`).
|
||||
*
|
||||
* **Alta de pedimento (`Nuevo` / `/edit/new`):** misma regla que editar — `pedimentos_mgmt.edit`
|
||||
* (negocio: alta y edición son la misma capacidad).
|
||||
* **Alta de pedimento (`Nuevo` / `/edit/new`):** requiere `pedimentos_mgmt.create`.
|
||||
*/
|
||||
import type { User } from '$lib/auth';
|
||||
import { userHasPermission } from '$lib/auth';
|
||||
@@ -19,7 +18,7 @@ export function canViewPedimentosList(user: User | null): boolean {
|
||||
return userHasPermission(user, 'pedimentos_mgmt.view');
|
||||
}
|
||||
|
||||
/** Permiso de API/rol para POST crear pedimento; el botón "Nuevo" en UI usa `canOpenNuevoPedimentoForm` (edit). */
|
||||
/** Permiso de API/rol para crear pedimento. */
|
||||
export function canCreatePedimento(user: User | null): boolean {
|
||||
return hasAssignedCode(user, 'pedimentos_mgmt.create');
|
||||
}
|
||||
@@ -32,12 +31,12 @@ export function canDeletePedimento(user: User | null): boolean {
|
||||
return hasAssignedCode(user, 'pedimentos_mgmt.delete');
|
||||
}
|
||||
|
||||
/** Botón "Nuevo Pedimento" y navegación a `/edit/new` — requiere poder editar en RBAC de la app. */
|
||||
/** Botón "Nuevo Pedimento" y navegación a `/edit/new` — requiere permiso create. */
|
||||
export function canOpenNuevoPedimentoForm(user: User | null): boolean {
|
||||
return canEditPedimento(user);
|
||||
return canCreatePedimento(user);
|
||||
}
|
||||
|
||||
/** Pantalla de alta (`/edit/new`) o edición — ambas exigen `pedimentos_mgmt.edit` en cliente (alineado a API). */
|
||||
export function canAccessPedimentoEditorPage(user: User | null, _isCreate: boolean): boolean {
|
||||
return canEditPedimento(user);
|
||||
/** Pantalla de alta (`/edit/new`) o edición. */
|
||||
export function canAccessPedimentoEditorPage(user: User | null, isCreate: boolean): boolean {
|
||||
return isCreate ? canCreatePedimento(user) : canEditPedimento(user);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
import { page } from '$app/stores';
|
||||
import { useShortcuts } from '$lib/hooks/use-shortcuts';
|
||||
import { obtenerAtajosListaAgentes } from '$lib/config/shortcuts/dashboard/customs_brokers/list';
|
||||
import { currentUser } from '$lib/auth';
|
||||
import {
|
||||
canViewCustomsBrokers,
|
||||
canCreateCustomsBrokers,
|
||||
canEditCustomsBrokers,
|
||||
canDeleteCustomsBrokers
|
||||
} from '$lib/permissions/customs-broker-permissions';
|
||||
import ErrorState from '$lib/components/dashboard/common/error-state.svelte';
|
||||
|
||||
import {
|
||||
customsSectionsApi,
|
||||
@@ -60,6 +68,10 @@
|
||||
filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
||||
);
|
||||
let totalItems = $derived(filteredItems.length);
|
||||
const canViewBrokerList = $derived(canViewCustomsBrokers($currentUser));
|
||||
const canCreateBroker = $derived(canCreateCustomsBrokers($currentUser));
|
||||
const canEditBroker = $derived(canEditCustomsBrokers($currentUser));
|
||||
const canDeleteBroker = $derived(canDeleteCustomsBrokers($currentUser));
|
||||
|
||||
// --- Customs Sections State ---
|
||||
let sections = $state<CustomsSection[]>([]);
|
||||
@@ -116,13 +128,25 @@
|
||||
selectedItem = item;
|
||||
}
|
||||
function handleRowDoubleClick(item: CustomsBroker) {
|
||||
if (!canEditBroker) {
|
||||
toast.error('No tienes permiso para editar agentes aduanales');
|
||||
return;
|
||||
}
|
||||
selectedItem = item;
|
||||
goto(`/dashboard/customs_brokers/edit/${item.broker_key}`);
|
||||
}
|
||||
function handleEdit() {
|
||||
if (!canEditBroker) {
|
||||
toast.error('No tienes permiso para editar agentes aduanales');
|
||||
return;
|
||||
}
|
||||
if (selectedItem) goto(`/dashboard/customs_brokers/edit/${selectedItem.broker_key}`);
|
||||
}
|
||||
async function handleDelete() {
|
||||
if (!canDeleteBroker) {
|
||||
toast.error('No tienes permiso para borrar agentes aduanales');
|
||||
return;
|
||||
}
|
||||
if (!selectedItem || !companyStore.activeCompany?.id) return;
|
||||
showDeleteDialog = true;
|
||||
}
|
||||
@@ -166,6 +190,10 @@
|
||||
irAduanas: () => (activeTab = 'customs'),
|
||||
crear: () => {
|
||||
if (activeTab === 'brokers') {
|
||||
if (!canCreateBroker) {
|
||||
toast.error('No tienes permiso para crear agentes aduanales');
|
||||
return;
|
||||
}
|
||||
goto('/dashboard/customs_brokers/edit/new');
|
||||
} else {
|
||||
// For Customs Sections, we might need a dialog.
|
||||
@@ -191,6 +219,13 @@
|
||||
const brokerColumns = createBrokerColumns(handleActionSuccess);
|
||||
</script>
|
||||
|
||||
{#if !canViewBrokerList}
|
||||
<div
|
||||
class="flex h-[calc(100svh-4rem)] flex-col p-6 group-has-data-[collapsible=icon]/sidebar-wrapper:h-[calc(100svh-3rem)]"
|
||||
>
|
||||
<ErrorState status={403} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-6 p-6 h-[calc(100svh-4rem)] group-has-data-[collapsible=icon]/sidebar-wrapper:h-[calc(100svh-3rem)] overflow-hidden">
|
||||
<div class="flex-none flex items-center justify-between">
|
||||
<div class="space-y-1">
|
||||
@@ -419,22 +454,33 @@
|
||||
<div class="mx-auto max-w-[1400px] px-4 py-4">
|
||||
<div class="flex justify-end gap-2">
|
||||
{#if activeTab === 'brokers'}
|
||||
<Button size="sm" href="/dashboard/customs_brokers/edit/new">
|
||||
<Plus class="mr-1 h-4 w-4" />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onclick={handleEdit} disabled={!selectedItem}>
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleDelete}
|
||||
disabled={!selectedItem}
|
||||
class="text-destructive hover:text-destructive"
|
||||
>
|
||||
Borrar
|
||||
</Button>
|
||||
{#if canCreateBroker}
|
||||
<Button size="sm" href="/dashboard/customs_brokers/edit/new">
|
||||
<Plus class="mr-1 h-4 w-4" />
|
||||
Nuevo
|
||||
</Button>
|
||||
{/if}
|
||||
{#if canEditBroker}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleEdit}
|
||||
disabled={!selectedItem}
|
||||
>
|
||||
Editar
|
||||
</Button>
|
||||
{/if}
|
||||
{#if canDeleteBroker}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleDelete}
|
||||
disabled={!selectedItem}
|
||||
class="text-destructive hover:text-destructive"
|
||||
>
|
||||
Borrar
|
||||
</Button>
|
||||
{/if}
|
||||
{:else}
|
||||
<Button size="sm" onclick={() => toast.info('Pendiente')}>
|
||||
<Plus class="mr-1 h-4 w-4" />
|
||||
@@ -444,5 +490,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<DeleteDialog bind:open={showDeleteDialog} broker={selectedItem} onSuccess={handleActionSuccess} />
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import { currentUser } from '$lib/auth';
|
||||
import {
|
||||
customsBrokersApi,
|
||||
uploadCustomsBrokerVuFile,
|
||||
type CreateCustomsBrokerData
|
||||
} from '$lib/api/dashboard/a76/customs-brokers';
|
||||
import {
|
||||
canCreateCustomsBrokers,
|
||||
canEditCustomsBrokers
|
||||
} from '$lib/permissions/customs-broker-permissions';
|
||||
|
||||
// UI Components
|
||||
import CountryDialog from '$lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte';
|
||||
@@ -44,11 +49,15 @@
|
||||
import { getFileDisplayName } from '$lib/utils';
|
||||
import { useShortcuts } from '$lib/hooks/use-shortcuts';
|
||||
import { obtenerAtajosEdicionAgente } from '$lib/config/shortcuts/dashboard/customs_brokers/edit';
|
||||
import ErrorState from '$lib/components/dashboard/common/error-state.svelte';
|
||||
|
||||
// --- 1. LÓGICA DE IDENTIFICACIÓN ---
|
||||
let routeId = $derived($page.params.id);
|
||||
let isEdit = $derived(!!routeId && routeId !== 'new');
|
||||
let title = $derived(isEdit ? 'Editar Agente Aduanal' : 'Nuevo Agente Aduanal');
|
||||
const canAccessBrokerEditor = $derived(
|
||||
isEdit ? canEditCustomsBrokers($currentUser) : canCreateCustomsBrokers($currentUser)
|
||||
);
|
||||
|
||||
// --- 2. ESTADO ---
|
||||
let loading = $state(false);
|
||||
@@ -279,6 +288,10 @@
|
||||
|
||||
// --- 4. GUARDADO ---
|
||||
async function handleSave() {
|
||||
if (!canAccessBrokerEditor) {
|
||||
toast.error('No tienes permiso para guardar agentes aduanales');
|
||||
return;
|
||||
}
|
||||
if (!companyStore.activeCompany) {
|
||||
toast.error('Selecciona una compañía');
|
||||
return;
|
||||
@@ -406,6 +419,13 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if !canAccessBrokerEditor}
|
||||
<div
|
||||
class="flex h-[calc(100svh-4rem)] flex-col p-6 group-has-data-[collapsible=icon]/sidebar-wrapper:h-[calc(100svh-3rem)]"
|
||||
>
|
||||
<ErrorState status={403} />
|
||||
</div>
|
||||
{:else}
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<div class="space-y-3">
|
||||
<!-- Header -->
|
||||
@@ -1144,3 +1164,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Root>
|
||||
{/if}
|
||||
|
||||
@@ -1017,6 +1017,11 @@
|
||||
}
|
||||
|
||||
function handleCreateClick() {
|
||||
if (!canCreateInvoice) {
|
||||
toast.error('No tienes permiso para crear este tipo de factura.');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
const queryString = params.toString();
|
||||
@@ -1533,10 +1538,12 @@
|
||||
<Settings class="mr-2" size={16} />
|
||||
{m.invoice_list_actions_parameters()}
|
||||
</Button>
|
||||
<Button class="h-9" onclick={handleCreateClick} disabled={!canCreateInvoice}>
|
||||
<Plus class="mr-2" size={16} />
|
||||
{m.invoice_list_actions_new_invoice()}
|
||||
</Button>
|
||||
{#if canCreateInvoice}
|
||||
<Button class="h-9" onclick={handleCreateClick}>
|
||||
<Plus class="mr-2" size={16} />
|
||||
{m.invoice_list_actions_new_invoice()}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -74,10 +74,13 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
catalogsData = await creationResponse.json();
|
||||
} else if (creationResponse.status === 403) {
|
||||
throw error(403, 'No tienes permiso para crear este tipo de factura.');
|
||||
} else if (creationResponse.status === 401) {
|
||||
throw error(401, 'Tu sesión expiró. Vuelve a iniciar sesión.');
|
||||
} else if (creationResponse.status === 422) {
|
||||
throw error(422, 'Parámetros inválidos para crear factura.');
|
||||
} else {
|
||||
console.error('Error fetching creation data:', creationResponse.status);
|
||||
// We continuing with empty catalogs might be better than crashing?
|
||||
// But UI will likely be broken. Let's rely on empty arrays initialization below.
|
||||
throw error(creationResponse.status, 'No fue posible preparar el formulario de creación.');
|
||||
}
|
||||
|
||||
if (settingsResult && settingsResult.settings) {
|
||||
@@ -160,7 +163,9 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('Error in load function:', err);
|
||||
if (err && err.status === 404) throw err; // Propagate 404
|
||||
if (err && typeof err.status === 'number' && [401, 403, 404, 422].includes(err.status)) {
|
||||
throw err;
|
||||
}
|
||||
throw error(500, 'Error interno al cargar la página');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
|
||||
const canViewList = $derived(canViewPedimentosList($currentUser));
|
||||
const canNuevoPedimentoAction = $derived(canOpenNuevoPedimentoForm($currentUser));
|
||||
const canEditPedimentoAction = $derived(canEditPedimento($currentUser));
|
||||
const canDeletePedimentoAction = $derived(canDeletePedimento($currentUser));
|
||||
|
||||
// Estado para filtros
|
||||
let filters = $state({
|
||||
@@ -74,21 +76,13 @@ let filters = $state({
|
||||
}
|
||||
|
||||
// Escuchar cambios de compañía
|
||||
const handleCompanyChange = (event: CustomEvent) => {
|
||||
const handleCompanyChange = () => {
|
||||
// Recargar los datos sin recargar la página completa
|
||||
reloadData();
|
||||
};
|
||||
|
||||
window.addEventListener('companyChanged', handleCompanyChange as EventListener);
|
||||
|
||||
void (async () => {
|
||||
const cid = companyStore.activeCompany?.id;
|
||||
if (cid) {
|
||||
const { syncCompanyPermissions } = await import('$lib/auth');
|
||||
await syncCompanyPermissions(cid);
|
||||
}
|
||||
})();
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
window.removeEventListener('companyChanged', handleCompanyChange as EventListener);
|
||||
@@ -468,7 +462,7 @@ let filters = $state({
|
||||
|
||||
function handleCreateClick() {
|
||||
if (!canOpenNuevoPedimentoForm(get(currentUser))) {
|
||||
toast.error('No tiene permiso para editar pedimentos');
|
||||
toast.error('No tiene permiso para crear pedimentos');
|
||||
return;
|
||||
}
|
||||
window.location.href = '/dashboard/pedimentos/edit/new';
|
||||
@@ -543,10 +537,12 @@ let filters = $state({
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Button class="h-9" onclick={handleCreateClick} disabled={!canNuevoPedimentoAction}>
|
||||
<Plus class="mr-2" size={16} />
|
||||
Nuevo Pedimento
|
||||
</Button>
|
||||
{#if canNuevoPedimentoAction}
|
||||
<Button class="h-9" onclick={handleCreateClick}>
|
||||
<Plus class="mr-2" size={16} />
|
||||
Nuevo Pedimento
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -624,24 +620,28 @@ let filters = $state({
|
||||
<div class="mx-auto max-w-[1400px] px-4 py-4">
|
||||
<!-- Botones de acción -->
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleEditSelected}
|
||||
disabled={!canEditSelected}
|
||||
>
|
||||
<Edit size={16} class="mr-1" />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleDelete}
|
||||
disabled={!canDeleteSelected}
|
||||
>
|
||||
<Trash2 size={16} class="mr-1" />
|
||||
Borrar
|
||||
</Button>
|
||||
{#if canEditPedimentoAction}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleEditSelected}
|
||||
disabled={!canEditSelected}
|
||||
>
|
||||
<Edit size={16} class="mr-1" />
|
||||
Editar
|
||||
</Button>
|
||||
{/if}
|
||||
{#if canDeletePedimentoAction}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleDelete}
|
||||
disabled={!canDeleteSelected}
|
||||
>
|
||||
<Trash2 size={16} class="mr-1" />
|
||||
Borrar
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -30,23 +30,15 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
|
||||
if (!response.ok) {
|
||||
console.error('Error fetching creation data:', response.status);
|
||||
if (response.status === 403) {
|
||||
throw error(403, 'No tiene permiso para editar pedimentos');
|
||||
throw error(403, 'No tiene permiso para crear pedimentos');
|
||||
}
|
||||
// Graceful degradation: return empty data (no autorización / errores distintos de 403)
|
||||
return {
|
||||
pedimento: null,
|
||||
pedimentoId: null,
|
||||
isCreate: true,
|
||||
pedimentoCodes: [],
|
||||
customsSections: [],
|
||||
customsBrokers: [],
|
||||
clients: [],
|
||||
codePedimentoRegimens: [],
|
||||
transportTypes: [],
|
||||
transportModes: [],
|
||||
pedimentoTransportCatalog: [],
|
||||
error: 'Error al cargar catálogos. Verifique la conexión con el backend.'
|
||||
};
|
||||
if (response.status === 401) {
|
||||
throw error(401, 'Tu sesión expiró. Vuelve a iniciar sesión.');
|
||||
}
|
||||
if (response.status === 422) {
|
||||
throw error(422, 'Parámetros inválidos para crear pedimento.');
|
||||
}
|
||||
throw error(response.status, 'No fue posible preparar el formulario de creación.');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
@@ -66,21 +58,10 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('❌ Error loading new pedimento data:', e);
|
||||
// Graceful degradation: return empty data so the page loads, but show error
|
||||
return {
|
||||
pedimento: null,
|
||||
pedimentoId: null,
|
||||
isCreate: true,
|
||||
pedimentoCodes: [],
|
||||
customsSections: [],
|
||||
customsBrokers: [],
|
||||
clients: [],
|
||||
codePedimentoRegimens: [],
|
||||
transportTypes: [],
|
||||
transportModes: [],
|
||||
pedimentoTransportCatalog: [],
|
||||
error: 'Error al cargar catálogos. Verifique la conexión con el backend.'
|
||||
};
|
||||
if (e && typeof e === 'object' && 'status' in e) {
|
||||
throw e;
|
||||
}
|
||||
throw error(500, 'Error al cargar catálogos de creación');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user