Se integro la edicion y creacion de companias y se esta trabajando en DODA
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import { deleteCompany, type Company } from "$lib/api/dashboard/a76/general_catalogs/company";
|
||||
import { EllipsisVertical, Pencil, LoaderCircle, Trash2 } from 'lucide-svelte';
|
||||
import CreateEditDialog from "./create-edit-dialog.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
let {
|
||||
item,
|
||||
@@ -59,7 +60,7 @@
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Item onclick={() => dialogOpen = true}>
|
||||
<DropdownMenu.Item onclick={() => goto(`/dashboard/general_catalogs/company_information/edit/${item.id}`)}>
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
@@ -74,9 +75,3 @@
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
|
||||
<CreateEditDialog
|
||||
bind:open={dialogOpen}
|
||||
item={item}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { deleteDoda } from '$lib/api/dashboard/a76/general_catalogs/doda';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import CreateEditDialog from './create-edit-dialog.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let {
|
||||
item,
|
||||
@@ -60,7 +61,7 @@
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Item onclick={handleEdit}>
|
||||
<DropdownMenu.Item onclick={() => goto(`/dashboard/general_catalogs/doda/edit/${item.id}`)}>
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<!-- <Button onclick={() => dialogOpen = true}> -->
|
||||
<Button href="/dashboard/general_catalogs/company_information/new">
|
||||
<Button href="/dashboard/general_catalogs/company_information/edit">
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
Nueva Empresa
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import {
|
||||
createCompany,
|
||||
updateCompany,
|
||||
getCompany, // Asumiendo que esta función existe en tu API
|
||||
type Company
|
||||
} from '$lib/api/dashboard/a76/general_catalogs/company';
|
||||
import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte';
|
||||
|
||||
// 1. Lógica de Navegación y Modo
|
||||
const id = $derived($page.params.id);
|
||||
const isEdit = $derived(!!id);
|
||||
const title = $derived(isEdit ? "Editar Empresa" : "Nueva Empresa");
|
||||
|
||||
let loading = $state(false);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
// 2. Estado Inicial (Reset)
|
||||
const initialData = {
|
||||
name: '',
|
||||
rfc: '',
|
||||
curp: '',
|
||||
main_activity: '',
|
||||
program: '',
|
||||
program_number: '',
|
||||
prosec: 0,
|
||||
prosec_authorization: '',
|
||||
responsible_name: '',
|
||||
responsible_last_name: '',
|
||||
responsible_mother_last_name: '',
|
||||
responsible_rfc: '',
|
||||
position: '',
|
||||
manufacturer_id: '',
|
||||
has_express_line: false,
|
||||
is_service_company: false,
|
||||
order_format_type: '',
|
||||
ctpat_svi: '',
|
||||
trusted_exporter_number: ''
|
||||
};
|
||||
|
||||
let formData = $state({ ...initialData });
|
||||
|
||||
// 3. Efecto para "Heredar" datos o Limpiar
|
||||
$effect(() => {
|
||||
if (isEdit) {
|
||||
fetchData(id);
|
||||
} else {
|
||||
formData = { ...initialData };
|
||||
error = null;
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchData(companyId: string) {
|
||||
loading = true;
|
||||
try {
|
||||
// Nota: Aquí usamos tu API para traer la info de una sola empresa
|
||||
const response = await getCompany(Number(companyId));
|
||||
if (response.data) {
|
||||
const item = response.data;
|
||||
formData = {
|
||||
name: item.name || '',
|
||||
rfc: item.rfc || '',
|
||||
curp: item.curp || '',
|
||||
main_activity: item.main_activity || '',
|
||||
program: item.program || '',
|
||||
program_number: item.program_number || '',
|
||||
prosec: item.prosec || 0,
|
||||
prosec_authorization: item.prosec_authorization || '',
|
||||
responsible_name: item.responsible_name || '',
|
||||
responsible_last_name: item.responsible_last_name || '',
|
||||
responsible_mother_last_name: item.responsible_mother_last_name || '',
|
||||
responsible_rfc: item.responsible_rfc || '',
|
||||
position: item.position || '',
|
||||
manufacturer_id: item.manufacturer_id || '',
|
||||
has_express_line: item.has_express_line || false,
|
||||
is_service_company: item.is_service_company || false,
|
||||
order_format_type: item.order_format_type || '',
|
||||
ctpat_svi: item.ctpat_svi || '',
|
||||
trusted_exporter_number: item.trusted_exporter_number || ''
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
error = "Error al cargar los datos de la empresa";
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
const clean = (value: any) => (typeof value === 'string' ? value.trim() || null : value);
|
||||
|
||||
async function handleSubmit() {
|
||||
error = null;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
if (!formData.name.trim()) throw new Error('El nombre es requerido');
|
||||
if (!formData.rfc.trim()) throw new Error('El RFC es requerido');
|
||||
|
||||
const payload = {
|
||||
...formData,
|
||||
name: formData.name.trim(),
|
||||
rfc: formData.rfc.trim(),
|
||||
responsible: `${formData.responsible_name} ${formData.responsible_last_name}`.trim() || null,
|
||||
curp: clean(formData.curp),
|
||||
main_activity: clean(formData.main_activity),
|
||||
program: clean(formData.program),
|
||||
program_number: clean(formData.program_number),
|
||||
prosec_authorization: clean(formData.prosec_authorization),
|
||||
responsible_name: clean(formData.responsible_name),
|
||||
responsible_last_name: clean(formData.responsible_last_name),
|
||||
responsible_mother_last_name: clean(formData.responsible_mother_last_name),
|
||||
responsible_rfc: clean(formData.responsible_rfc),
|
||||
position: clean(formData.position),
|
||||
manufacturer_id: clean(formData.manufacturer_id),
|
||||
order_format_type: clean(formData.order_format_type),
|
||||
ctpat_svi: clean(formData.ctpat_svi),
|
||||
trusted_exporter_number: clean(formData.trusted_exporter_number)
|
||||
};
|
||||
|
||||
const response = isEdit
|
||||
? await updateCompany(Number(id), payload)
|
||||
: await createCompany(payload);
|
||||
|
||||
if (response.error) throw new Error(response.error);
|
||||
|
||||
goto('/dashboard/general_catalogs/company_information');
|
||||
} catch (e: any) {
|
||||
error = e.message || 'Error al guardar';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full mx-auto max-w-6xl py-6 px-4 space-y-6 pb-48">
|
||||
<div class="flex items-center gap-4">
|
||||
<Button variant="outline" size="icon" href="/dashboard/general_catalogs/company_information">
|
||||
<ArrowLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">{title}</h1>
|
||||
<p class="text-muted-foreground">Configuración legal y operativa de la entidad.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="p-4 rounded-md bg-destructive/10 text-destructive border border-destructive/20 text-sm font-medium">
|
||||
⚠️ {error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} class="space-y-6">
|
||||
<Card.Root>
|
||||
<Card.Content class="p-6">
|
||||
<Tabs.Root value="general" class="w-full">
|
||||
<div class="min-h-[400px]">
|
||||
<Tabs.Content value="general" class="space-y-4 pt-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="name">Razón Social <span class="text-destructive">*</span></Label>
|
||||
<Input id="name" bind:value={formData.name} placeholder="Nombre oficial" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="rfc">RFC <span class="text-destructive">*</span></Label>
|
||||
<Input id="rfc" bind:value={formData.rfc} maxlength={13} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="curp">CURP</Label>
|
||||
<Input id="curp" bind:value={formData.curp} maxlength={18} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="main_activity">Actividad Principal</Label>
|
||||
<Input id="main_activity" bind:value={formData.main_activity} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="programas" class="space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="program">Programa (IMMEX)</Label>
|
||||
<Input id="program" bind:value={formData.program} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="program_number">No. Programa</Label>
|
||||
<Input id="program_number" bind:value={formData.program_number} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="prosec">Sector PROSEC (ID)</Label>
|
||||
<Input id="prosec" type="number" bind:value={formData.prosec} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="prosec_auth">Autorización PROSEC</Label>
|
||||
<Input id="prosec_auth" bind:value={formData.prosec_authorization} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="responsable" class="space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_name">Nombre</Label>
|
||||
<Input id="resp_name" bind:value={formData.responsible_name} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_last">Apellido Paterno</Label>
|
||||
<Input id="resp_last" bind:value={formData.responsible_last_name} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_mother">Apellido Materno</Label>
|
||||
<Input id="resp_mother" bind:value={formData.responsible_mother_last_name} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_rfc">RFC Responsable</Label>
|
||||
<Input id="resp_rfc" bind:value={formData.responsible_rfc} maxlength={13} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="position">Puesto / Cargo</Label>
|
||||
<Input id="position" bind:value={formData.position} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="config" class="space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<div class="flex items-center gap-3 p-4 border rounded-lg">
|
||||
<Switch id="express" bind:checked={formData.has_express_line} />
|
||||
<Label for="express">Carril Exprés (OEA/NEEC)</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 p-4 border rounded-lg">
|
||||
<Switch id="service" bind:checked={formData.is_service_company} />
|
||||
<Label for="service">Es Empresa de Servicios</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 pt-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="man_id">Manufacturer ID (MID)</Label>
|
||||
<Input id="man_id" bind:value={formData.manufacturer_id} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="order_format">Formato de Pedido</Label>
|
||||
<Input id="order_format" bind:value={formData.order_format_type} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="ctpat">C-TPAT / SVI</Label>
|
||||
<Input id="ctpat" bind:value={formData.ctpat_svi} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="exporter">No. Exportador Confiable</Label>
|
||||
<Input id="exporter" bind:value={formData.trusted_exporter_number} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</div>
|
||||
|
||||
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[95%] max-w-2xl z-40 shadow-2xl bg-background border p-1 rounded-xl">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="programas">Programas</Tabs.Trigger>
|
||||
<Tabs.Trigger value="responsable">Responsable</Tabs.Trigger>
|
||||
<Tabs.Trigger value="config">Config</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50">
|
||||
<div class="max-w-6xl mx-auto flex justify-end gap-4">
|
||||
<Button type="button" variant="ghost" onclick={() => goto('/dashboard/general_catalogs/company_information')} disabled={loading}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading} class="min-w-[140px]">
|
||||
{#if loading}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{:else}
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
{/if}
|
||||
{isEdit ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,247 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { createCompany } from '$lib/api/dashboard/a76/general_catalogs/company';
|
||||
import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte';
|
||||
|
||||
let loading = $state(false);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
let formData = $state({
|
||||
name: '',
|
||||
rfc: '',
|
||||
curp: '',
|
||||
main_activity: '',
|
||||
program: '',
|
||||
program_number: '',
|
||||
prosec: 0,
|
||||
prosec_authorization: '',
|
||||
responsible_name: '',
|
||||
responsible_last_name: '',
|
||||
responsible_mother_last_name: '',
|
||||
responsible_rfc: '',
|
||||
position: '',
|
||||
manufacturer_id: '',
|
||||
has_express_line: false,
|
||||
is_service_company: false,
|
||||
order_format_type: '',
|
||||
ctpat_svi: '',
|
||||
trusted_exporter_number: ''
|
||||
});
|
||||
|
||||
const clean = (value: string) => value.trim() || null;
|
||||
|
||||
async function handleSubmit() {
|
||||
error = null;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
if (!formData.name.trim()) throw new Error('El nombre es requerido');
|
||||
if (!formData.rfc.trim()) throw new Error('El RFC es requerido');
|
||||
|
||||
const payload = {
|
||||
name: formData.name.trim(),
|
||||
rfc: formData.rfc.trim(),
|
||||
curp: clean(formData.curp),
|
||||
main_activity: clean(formData.main_activity),
|
||||
program: clean(formData.program),
|
||||
program_number: clean(formData.program_number),
|
||||
prosec: formData.prosec || null,
|
||||
prosec_authorization: clean(formData.prosec_authorization),
|
||||
responsible: `${formData.responsible_name} ${formData.responsible_last_name}`.trim() || null,
|
||||
responsible_name: clean(formData.responsible_name),
|
||||
responsible_last_name: clean(formData.responsible_last_name),
|
||||
responsible_mother_last_name: clean(formData.responsible_mother_last_name),
|
||||
responsible_rfc: clean(formData.responsible_rfc),
|
||||
position: clean(formData.position),
|
||||
manufacturer_id: clean(formData.manufacturer_id),
|
||||
has_express_line: formData.has_express_line,
|
||||
is_service_company: formData.is_service_company,
|
||||
order_format_type: clean(formData.order_format_type),
|
||||
ctpat_svi: clean(formData.ctpat_svi),
|
||||
trusted_exporter_number: clean(formData.trusted_exporter_number)
|
||||
};
|
||||
|
||||
const response = await createCompany(payload);
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
|
||||
await goto('/dashboard/general_catalogs/company_information');
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Error al guardar';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
goto('/dashboard/general_catalogs/company_information');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full mx-auto max-w-6xl py-6 px-4 space-y-6 pb-48">
|
||||
<div class="flex items-center gap-4">
|
||||
<Button variant="outline" size="icon" href="/dashboard/general_catalogs/company_information">
|
||||
<ArrowLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">Nueva Empresa</h1>
|
||||
<p class="text-muted-foreground">Crea la información de la empresa.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="p-4 rounded-md bg-destructive/10 text-destructive border border-destructive/20 text-sm font-medium">
|
||||
⚠️ {error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form class="space-y-6" on:submit|preventDefault={handleSubmit}>
|
||||
<Card.Root>
|
||||
<Card.Content class="p-6">
|
||||
<Tabs.Root value="general" class="w-full">
|
||||
<Tabs.Content value="general" class="space-y-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="name">Razón Social <span class="text-destructive">*</span></Label>
|
||||
<Input id="name" bind:value={formData.name} placeholder="Nombre de la empresa" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="rfc">RFC <span class="text-destructive">*</span></Label>
|
||||
<Input id="rfc" bind:value={formData.rfc} maxlength={13} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="curp">CURP</Label>
|
||||
<Input id="curp" bind:value={formData.curp} maxlength={18} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="main_activity">Actividad Principal</Label>
|
||||
<Input id="main_activity" bind:value={formData.main_activity} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="programas" class="space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="program">Programa (IMMEX)</Label>
|
||||
<Input id="program" bind:value={formData.program} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="program_number">No. Programa</Label>
|
||||
<Input id="program_number" bind:value={formData.program_number} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="prosec">Sector PROSEC (ID)</Label>
|
||||
<Input id="prosec" type="number" bind:value={formData.prosec} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="prosec_auth">Autorización PROSEC</Label>
|
||||
<Input id="prosec_auth" bind:value={formData.prosec_authorization} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="responsable" class="space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_name">Nombre</Label>
|
||||
<Input id="resp_name" bind:value={formData.responsible_name} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_last">Apellido Paterno</Label>
|
||||
<Input id="resp_last" bind:value={formData.responsible_last_name} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_mother">Apellido Materno</Label>
|
||||
<Input id="resp_mother" bind:value={formData.responsible_mother_last_name} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="resp_rfc">RFC Responsable</Label>
|
||||
<Input id="resp_rfc" bind:value={formData.responsible_rfc} maxlength={13} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="position">Puesto / Cargo</Label>
|
||||
<Input id="position" bind:value={formData.position} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="config" class="space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="express" bind:checked={formData.has_express_line} />
|
||||
<Label for="express">Carril Exprés (OEA/NEEC)</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="service" bind:checked={formData.is_service_company} />
|
||||
<Label for="service">Es Empresa de Servicios</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t" />
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="man_id">Manufacturer ID (MID)</Label>
|
||||
<Input id="man_id" bind:value={formData.manufacturer_id} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="order_format">Formato de Pedido</Label>
|
||||
<Input id="order_format" bind:value={formData.order_format_type} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="ctpat">C-TPAT / SVI</Label>
|
||||
<Input id="ctpat" bind:value={formData.ctpat_svi} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="exporter">No. Exportador Confiable</Label>
|
||||
<Input id="exporter" bind:value={formData.trusted_exporter_number} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[90%] max-w-2xl z-40 shadow-xl bg-background border rounded-xl">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="programas">Programas</Tabs.Trigger>
|
||||
<Tabs.Trigger value="responsable">Responsable</Tabs.Trigger>
|
||||
<Tabs.Trigger value="config">Config</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50 flex justify-end gap-4 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]">
|
||||
<div class="w-full mx-auto flex justify-end gap-4 px-4">
|
||||
<Button type="button" variant="outline" onclick={handleCancel}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
Guardar Empresa
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<!-- <Button onclick={() => dialogOpen = true}> -->
|
||||
<Button href="/dashboard/general_catalogs/doda/new">
|
||||
<Button href="/dashboard/general_catalogs/doda/edit">
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
Nuevo DODA
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import {
|
||||
createDoda,
|
||||
updateDoda,
|
||||
getDoda,
|
||||
type DodaCreate
|
||||
} from '$lib/api/dashboard/a76/general_catalogs/doda';
|
||||
import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte';
|
||||
|
||||
// 1. Identificación reactiva de la URL
|
||||
let id = $derived($page.params.id);
|
||||
let isEdit = $derived(!!id);
|
||||
let title = $derived(isEdit ? "Editar DODA" : "Nuevo DODA");
|
||||
|
||||
let loading = $state(false);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
// 2. Función para obtener un objeto limpio
|
||||
function getEmptyForm(): DodaCreate {
|
||||
return {
|
||||
integration_number: '', doda_date: undefined, doda_time: undefined,
|
||||
dispatch_customs: '', customs_sections: '', patent: '',
|
||||
pedimentos: '', caat: '', transport_identification: '',
|
||||
fast_id: '', operation_type: '', selected: false,
|
||||
user_selected: '', last_user: '', responsible: '',
|
||||
carrier: '', shipments: '', pedimento_type: '',
|
||||
original_chain: '', serial_number: '', electronic_signature: '',
|
||||
transaction_number: '', status: '', linq_sat_qr: '',
|
||||
sat_certificate: '', sat_digital_seal: '', xml_doda_sent_path: '',
|
||||
xml_doda_response_path: '', sat_original_chain: '',
|
||||
customs_clearance: undefined, unique_badge_number: ''
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Estado reactivo del formulario
|
||||
let formData = $state<DodaCreate>(getEmptyForm());
|
||||
|
||||
// 4. Efecto de carga: Detecta cambios en el ID y trae datos
|
||||
$effect(() => {
|
||||
if (id) {
|
||||
// Si hay un ID, lo cargamos
|
||||
loadDoda(Number(id));
|
||||
} else {
|
||||
// Si no hay ID, limpiamos para "Nuevo"
|
||||
formData = getEmptyForm();
|
||||
error = null;
|
||||
}
|
||||
});
|
||||
|
||||
async function loadDoda(dodaId: number) {
|
||||
loading = true;
|
||||
try {
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
// IMPORTANTE: Esperamos la respuesta de tu API
|
||||
const data = await getDoda(dodaId, companyId);
|
||||
|
||||
// Reasignamos el objeto completo para forzar a Svelte 5 a actualizar la UI
|
||||
if (data) {
|
||||
formData = { ...getEmptyForm(), ...data };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
error = "No se pudo cargar la información del DODA";
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
error = null;
|
||||
loading = true;
|
||||
try {
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) throw new Error('Selecciona una compañía');
|
||||
if (!formData.integration_number?.trim()) throw new Error('El número de integración es requerido');
|
||||
|
||||
if (isEdit) {
|
||||
await updateDoda(Number(id), formData, companyId);
|
||||
} else {
|
||||
await createDoda(formData, companyId);
|
||||
}
|
||||
goto('/dashboard/general_catalogs/doda');
|
||||
} catch (e: any) {
|
||||
error = e.message || 'Error al guardar';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full mx-auto max-w-6xl py-6 px-4 space-y-6 pb-48">
|
||||
<div class="flex items-center gap-4">
|
||||
<Button variant="outline" size="icon" href="/dashboard/general_catalogs/doda">
|
||||
<ArrowLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">{title}</h1>
|
||||
<p class="text-muted-foreground">Captura la información técnica del DODA.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="p-4 rounded-md bg-destructive/10 text-destructive border border-destructive/20 text-sm font-medium">
|
||||
⚠️ {error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} class="space-y-6">
|
||||
<Card.Root>
|
||||
<Card.Content class="p-6">
|
||||
<Tabs.Root value="general" class="w-full">
|
||||
<div class="min-h-[500px]">
|
||||
<Tabs.Content value="general" class="space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="integration_number">No. Integración <span class="text-destructive">*</span></Label>
|
||||
<Input id="integration_number" bind:value={formData.integration_number} maxlength={30} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="status">Estatus</Label>
|
||||
<Input id="status" bind:value={formData.status} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="doda_date">Fecha (YYYYMMDD)</Label>
|
||||
<Input type="number" id="doda_date" bind:value={formData.doda_date} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="doda_time">Hora (HHMMSS)</Label>
|
||||
<Input type="number" id="doda_time" bind:value={formData.doda_time} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="operation_type">Tipo Operación</Label>
|
||||
<Input id="operation_type" bind:value={formData.operation_type} maxlength={1} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="pedimentos">Pedimentos</Label>
|
||||
<Input id="pedimentos" bind:value={formData.pedimentos} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="transport" class="space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="patent">Patente</Label>
|
||||
<Input id="patent" bind:value={formData.patent} maxlength={4} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="dispatch_customs">Aduana Despacho</Label>
|
||||
<Input id="dispatch_customs" bind:value={formData.dispatch_customs} maxlength={3} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="customs_sections">Sección Aduanera</Label>
|
||||
<Input id="customs_sections" bind:value={formData.customs_sections} maxlength={3} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="caat">CAAT</Label>
|
||||
<Input id="caat" bind:value={formData.caat} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="carrier">Carrier</Label>
|
||||
<Input id="carrier" bind:value={formData.carrier} />
|
||||
</div>
|
||||
<div class="grid gap-2 md:col-span-2">
|
||||
<Label for="transport_id">Ident. Transporte</Label>
|
||||
<Input id="transport_id" bind:value={formData.transport_identification} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="sat" class="space-y-4 pt-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="serial">Número de Serie</Label>
|
||||
<Input id="serial" bind:value={formData.serial_number} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="chain">Cadena Original</Label>
|
||||
<Textarea id="chain" bind:value={formData.original_chain} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="signature">Firma Electrónica</Label>
|
||||
<Textarea id="signature" bind:value={formData.electronic_signature} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="other" class="space-y-4 pt-4">
|
||||
<div class="flex items-center gap-3 p-4 border rounded-lg">
|
||||
<Switch id="selected" bind:checked={formData.selected} />
|
||||
<Label for="selected">Seleccionado para operación</Label>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="responsible">RFC Responsable</Label>
|
||||
<Input id="responsible" bind:value={formData.responsible} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="badge">Número de Gafete</Label>
|
||||
<Input id="badge" bind:value={formData.unique_badge_number} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</div>
|
||||
|
||||
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[95%] max-w-2xl z-40 shadow-xl bg-background border p-1 rounded-xl">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="transport">Aduana</Tabs.Trigger>
|
||||
<Tabs.Trigger value="sat">SAT</Tabs.Trigger>
|
||||
<Tabs.Trigger value="other">Otros</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50 flex justify-end gap-4 shadow-inner">
|
||||
<div class="max-w-6xl mx-auto flex justify-end gap-4 px-4 w-full">
|
||||
<Button type="button" variant="ghost" href="/dashboard/general_catalogs/doda" disabled={loading}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading} class="min-w-[140px]">
|
||||
{#if loading}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
{isEdit ? 'Actualizar' : 'Guardar'}
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,277 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import { createDoda } from '$lib/api/dashboard/a76/general_catalogs/doda';
|
||||
import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte';
|
||||
|
||||
let loading = $state(false);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
let formData = $state({
|
||||
integration_number: '',
|
||||
doda_date: undefined as number | undefined,
|
||||
doda_time: undefined as number | undefined,
|
||||
dispatch_customs: '',
|
||||
customs_sections: '',
|
||||
patent: '',
|
||||
pedimentos: '',
|
||||
caat: '',
|
||||
transport_identification: '',
|
||||
fast_id: '',
|
||||
operation_type: '',
|
||||
selected: false,
|
||||
user_selected: '',
|
||||
last_user: '',
|
||||
responsible: '',
|
||||
carrier: '',
|
||||
shipments: '',
|
||||
pedimento_type: '',
|
||||
original_chain: '',
|
||||
serial_number: '',
|
||||
electronic_signature: '',
|
||||
transaction_number: '',
|
||||
status: '',
|
||||
linq_sat_qr: '',
|
||||
sat_certificate: '',
|
||||
sat_digital_seal: '',
|
||||
xml_doda_sent_path: '',
|
||||
xml_doda_response_path: '',
|
||||
sat_original_chain: '',
|
||||
customs_clearance: undefined as number | undefined,
|
||||
unique_badge_number: ''
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
error = null;
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
if (!formData.integration_number.trim()) throw new Error('El número de integración es requerido');
|
||||
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) throw new Error('Selecciona una compañía para crear el DODA');
|
||||
|
||||
const payload = { ...formData, integration_number: formData.integration_number.trim() };
|
||||
await createDoda(payload, companyId);
|
||||
|
||||
await goto('/dashboard/general_catalogs/doda');
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Error al guardar';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
goto('/dashboard/general_catalogs/doda');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full mx-auto max-w-6xl py-6 px-4 space-y-6 pb-48">
|
||||
<div class="flex items-center gap-4">
|
||||
<Button variant="outline" size="icon" href="/dashboard/general_catalogs/doda">
|
||||
<ArrowLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">Nuevo DODA</h1>
|
||||
<p class="text-muted-foreground">Captura la información del documento.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="p-4 rounded-md bg-destructive/10 text-destructive border border-destructive/20 text-sm font-medium">
|
||||
⚠️ {error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form class="space-y-6" on:submit|preventDefault={handleSubmit}>
|
||||
<Card.Root>
|
||||
<Card.Content class="p-6">
|
||||
<Tabs.Root value="general" class="w-full">
|
||||
<Tabs.Content value="general" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="integration_number">No. Integración <span class="text-destructive">*</span></Label>
|
||||
<Input id="integration_number" bind:value={formData.integration_number} maxlength={30} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="status">Estatus</Label>
|
||||
<Input id="status" bind:value={formData.status} maxlength={30} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="doda_date">Fecha (YYYYMMDD)</Label>
|
||||
<Input type="number" id="doda_date" bind:value={formData.doda_date} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="doda_time">Hora (HHMMSS)</Label>
|
||||
<Input type="number" id="doda_time" bind:value={formData.doda_time} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="operation_type">Tipo Operación</Label>
|
||||
<Input id="operation_type" bind:value={formData.operation_type} maxlength={1} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="pedimentos">Pedimentos</Label>
|
||||
<Input id="pedimentos" bind:value={formData.pedimentos} maxlength={80} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="pedimento_type">Tipo Pedimento</Label>
|
||||
<Input id="pedimento_type" bind:value={formData.pedimento_type} maxlength={30} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="transport" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="patent">Patente</Label>
|
||||
<Input id="patent" bind:value={formData.patent} maxlength={4} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="dispatch_customs">Aduana Despacho</Label>
|
||||
<Input id="dispatch_customs" bind:value={formData.dispatch_customs} maxlength={3} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="customs_sections">Sección Aduanera</Label>
|
||||
<Input id="customs_sections" bind:value={formData.customs_sections} maxlength={3} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="caat">CAAT</Label>
|
||||
<Input id="caat" bind:value={formData.caat} maxlength={10} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="carrier">Transportista (Carrier)</Label>
|
||||
<Input id="carrier" bind:value={formData.carrier} maxlength={8} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="transport_identification">Ident. Transporte</Label>
|
||||
<Input id="transport_identification" bind:value={formData.transport_identification} maxlength={20} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="fast_id">FAST ID</Label>
|
||||
<Input id="fast_id" bind:value={formData.fast_id} maxlength={20} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="shipments">Embarques (Shipments)</Label>
|
||||
<Input id="shipments" bind:value={formData.shipments} maxlength={80} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="customs_clearance">Despacho Aduanero (ID)</Label>
|
||||
<Input type="number" id="customs_clearance" bind:value={formData.customs_clearance} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="sat" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="serial_number">Número de Serie</Label>
|
||||
<Input id="serial_number" bind:value={formData.serial_number} maxlength={21} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="transaction_number">No. Transacción</Label>
|
||||
<Input id="transaction_number" bind:value={formData.transaction_number} maxlength={30} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="unique_badge_number">Número Único de Gafete</Label>
|
||||
<Input id="unique_badge_number" bind:value={formData.unique_badge_number} maxlength={250} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="original_chain">Cadena Original</Label>
|
||||
<Textarea id="original_chain" bind:value={formData.original_chain} class="h-20" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="electronic_signature">Firma Electrónica</Label>
|
||||
<Textarea id="electronic_signature" bind:value={formData.electronic_signature} class="h-20" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="sat_digital_seal">Sello Digital SAT</Label>
|
||||
<Textarea id="sat_digital_seal" bind:value={formData.sat_digital_seal} class="h-20" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="sat_original_chain">Cadena Original SAT</Label>
|
||||
<Textarea id="sat_original_chain" bind:value={formData.sat_original_chain} class="h-20" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="xml_doda_sent_path">Ruta XML Enviado</Label>
|
||||
<Input id="xml_doda_sent_path" bind:value={formData.xml_doda_sent_path} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="xml_doda_response_path">Ruta XML Respuesta</Label>
|
||||
<Input id="xml_doda_response_path" bind:value={formData.xml_doda_response_path} />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="other" class="space-y-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch id="selected" bind:checked={formData.selected} />
|
||||
<Label for="selected">Seleccionado</Label>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="user_selected">Usuario Selección</Label>
|
||||
<Input id="user_selected" bind:value={formData.user_selected} maxlength={30} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="last_user">Último Usuario</Label>
|
||||
<Input id="last_user" bind:value={formData.last_user} maxlength={30} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="responsible">Responsable</Label>
|
||||
<Input id="responsible" bind:value={formData.responsible} maxlength={14} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="linq_sat_qr">LINQ SAT QR</Label>
|
||||
<Input id="linq_sat_qr" bind:value={formData.linq_sat_qr} maxlength={1000} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="sat_certificate">Certificado SAT</Label>
|
||||
<Input id="sat_certificate" bind:value={formData.sat_certificate} maxlength={2001} />
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[90%] max-w-3xl z-40 shadow-xl bg-background border rounded-xl">
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="transport">Aduana/Transp.</Tabs.Trigger>
|
||||
<Tabs.Trigger value="sat">SAT / Digital</Tabs.Trigger>
|
||||
<Tabs.Trigger value="other">Otros</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50 flex justify-end gap-4 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]">
|
||||
<div class="w-full mx-auto flex justify-end gap-4 px-4">
|
||||
<Button type="button" variant="outline" onclick={handleCancel}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
Guardar DODA
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
Reference in New Issue
Block a user