feat: implement server-side loading for invoices page with authentication and filters feat: create invoices page with filtering, infinite scroll, and data table feat: add server-side loading for invoice edit page with data fetching feat: implement invoice edit page with tabbed interface and form handling
112 lines
3.3 KiB
Svelte
112 lines
3.3 KiB
Svelte
<script lang="ts">
|
|
import { Button } from "$lib/components/ui/button";
|
|
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
|
import { invoicesApi, type Invoice } from "$lib/api/dashboard/a76/invoices";
|
|
import { companyStore } from "$lib/stores/company.svelte";
|
|
import { LoaderCircle } from 'lucide-svelte';
|
|
|
|
interface Props {
|
|
invoice: Invoice;
|
|
onClose: () => void;
|
|
onSuccess?: () => void;
|
|
}
|
|
|
|
let { invoice, onClose, onSuccess }: Props = $props();
|
|
let open = $state(true);
|
|
|
|
let loading = $state(false);
|
|
let error = $state<string | null>(null);
|
|
|
|
async function handleDelete() {
|
|
if (!invoice || !companyStore.activeCompany) return;
|
|
|
|
loading = true;
|
|
error = null;
|
|
|
|
try {
|
|
const response = await invoicesApi.delete(invoice.id, companyStore.activeCompany.id);
|
|
|
|
if (response.error) {
|
|
error = response.error;
|
|
return;
|
|
}
|
|
|
|
// Éxito
|
|
onClose();
|
|
if (onSuccess) {
|
|
onSuccess();
|
|
}
|
|
} catch (e) {
|
|
error = e instanceof Error ? e.message : "Error al eliminar";
|
|
console.error("Error deleting:", e);
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
function handleOpenChange(newOpen: boolean) {
|
|
if (!newOpen) {
|
|
error = null;
|
|
onClose();
|
|
} else {
|
|
open = newOpen;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<AlertDialog.Root bind:open onOpenChange={handleOpenChange}>
|
|
<AlertDialog.Content>
|
|
<AlertDialog.Header>
|
|
<AlertDialog.Title>¿Estás seguro?</AlertDialog.Title>
|
|
<AlertDialog.Description class="space-y-2">
|
|
<p>Esta acción no se puede deshacer. Se eliminará permanentemente esta factura:</p>
|
|
{#if invoice}
|
|
<div class="mt-2 rounded-lg bg-muted p-3 space-y-2">
|
|
<div class="flex items-center justify-between text-sm">
|
|
<span class="font-medium">ID:</span>
|
|
<span class="font-semibold">{invoice.id}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm">
|
|
<span class="font-medium">Número de Factura:</span>
|
|
<code class="font-mono font-semibold">{invoice.invoice_number || 'N/A'}</code>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm">
|
|
<span class="font-medium">Tipo:</span>
|
|
<span class="text-xs">
|
|
{invoice.operation_type === 'imp' ? 'Importación' :
|
|
invoice.operation_type === 'exp' ? 'Exportación' : 'N/A'}
|
|
</span>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm">
|
|
<span class="font-medium">Proyecto:</span>
|
|
<span class="text-xs">{invoice.project_number || 'N/A'}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm">
|
|
<span class="font-medium">Pedimento:</span>
|
|
<span class="text-xs">{invoice.compliance_mx?.pedimento || 'N/A'}</span>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
{#if error}
|
|
<div class="mt-2 rounded-lg border border-destructive bg-destructive/10 p-3 text-sm text-destructive">
|
|
{error}
|
|
</div>
|
|
{/if}
|
|
</AlertDialog.Description>
|
|
</AlertDialog.Header>
|
|
<AlertDialog.Footer>
|
|
<AlertDialog.Cancel disabled={loading}>Cancelar</AlertDialog.Cancel>
|
|
<AlertDialog.Action
|
|
onclick={handleDelete}
|
|
disabled={loading}
|
|
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
>
|
|
{#if loading}
|
|
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
|
{/if}
|
|
Eliminar
|
|
</AlertDialog.Action>
|
|
</AlertDialog.Footer>
|
|
</AlertDialog.Content>
|
|
</AlertDialog.Root>
|