MOD: actualizar rutas y mejorar la interfaz de usuario en secciones de dirección y concepto

This commit is contained in:
Juan Felipe Zapata Moreno 2025-11-28 12:50:27 -06:00
parent 8d513d215c
commit d79552fafc
16 changed files with 402 additions and 402 deletions

View File

@ -25,33 +25,33 @@ const handleSubmit = () => {
</script> </script>
<template> <template>
<div class="bg-white rounded-xl p-6 m-3 max-w-auto shadow-lg mb-10"> <div class="bg-white rounded-lg p-8 mx-4 mt-4 mb-6 shadow-md">
<h3 class="text-xl font-semibold mb-4 text-gray-800"> <h2 class="text-2xl font-bold mb-6 text-gray-900">
{{ $t('address.create.title') }} Crear nueva dirección
</h3> </h2>
<form @submit.prevent="handleSubmit"> <form @submit.prevent="handleSubmit">
<div class="mb-5"> <div class="mb-6">
<label <label
for="addressName" for="addressName"
class="block text-sm text-gray-600 font-medium mb-2" class="block text-sm text-gray-700 font-medium mb-2"
> >
{{ $t('address.name') }}: Nombre de la Dirección:
</label> </label>
<input <input
type="text" type="text"
id="addressName" id="addressName"
v-model="addressName" v-model="addressName"
:placeholder="$t('address.placeholder')" placeholder="Ingrese el nombre de la dirección"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
/> />
</div> </div>
<div class="mb-3 flex justify-center"> <div class="flex justify-center">
<button <button
type="submit" type="submit"
class="w-1/4 bg-[#7a0b3a] hover:bg-[#68082e] text-white font-medium py-3.5 rounded-lg transition-colors" class="px-16 bg-primary hover:bg-primary/90 text-white font-semibold py-3 rounded-md transition-all duration-200 shadow-sm hover:shadow-md"
> >
{{ $t('save') }} Guardar
</button> </button>
</div> </div>
</form> </form>

View File

@ -7,7 +7,7 @@ import Textarea from "@Holos/Form/Textarea.vue";
import Selectable from "@Holos/Form/Selectable.vue"; import Selectable from "@Holos/Form/Selectable.vue";
/** Eventos */ /** Eventos */
const emit = defineEmits(['concept-created']); const emit = defineEmits(["concept-created"]);
const form = useForm({ const form = useForm({
direction_id: null, direction_id: null,
@ -43,58 +43,71 @@ const resetForm = () => {
form.charge_type = null; form.charge_type = null;
}; };
const isFormOpen = ref(false);
const addresses = ref([]); const addresses = ref([]);
const units = ref([]); const units = ref([]);
const chargeTypeId = computed(() => { const chargeTypeId = computed(() => {
return typeof form.charge_type === 'object' && form.charge_type?.id return typeof form.charge_type === "object" && form.charge_type?.id
? form.charge_type.id ? form.charge_type.id
: form.charge_type; : form.charge_type;
}); });
const chargeTypesOptions = ref([ const chargeTypesOptions = ref([
{ id: 'uma_range', name: 'Rango en UMA' }, { id: "uma_range", name: "Rango en UMA" },
{ id: 'peso_range', name: 'Rango en Peso' }, { id: "peso_range", name: "Rango en Peso" },
{ id: 'uma_fixed', name: 'UMA unitario' }, { id: "uma_fixed", name: "UMA unitario" },
{ id: 'peso_fixed', name: 'Peso unitario' }, { id: "peso_fixed", name: "Peso unitario" },
]); ]);
const showUmaFields = computed(() => { const showUmaFields = computed(() => {
return chargeTypeId.value === 'uma_range' || chargeTypeId.value === 'uma_fixed'; return (
chargeTypeId.value === "uma_range" || chargeTypeId.value === "uma_fixed"
);
}); });
const showPesoFields = computed(() => { const showPesoFields = computed(() => {
return chargeTypeId.value === 'peso_range' || chargeTypeId.value === 'peso_fixed'; return (
chargeTypeId.value === "peso_range" || chargeTypeId.value === "peso_fixed"
);
}); });
const isRangeType = computed(() => { const isRangeType = computed(() => {
return chargeTypeId.value === 'uma_range' || chargeTypeId.value === 'peso_range'; return (
chargeTypeId.value === "uma_range" || chargeTypeId.value === "peso_range"
);
}); });
const isFixedType = computed(() => { const isFixedType = computed(() => {
return chargeTypeId.value === 'uma_fixed' || chargeTypeId.value === 'peso_fixed'; return (
chargeTypeId.value === "uma_fixed" || chargeTypeId.value === "peso_fixed"
);
}); });
/** Cargar cosas */ /** Cargar cosas */
onMounted(async () => { onMounted(async () => {
api.get(apiURL('directions'), { api.get(apiURL("directions"), {
onSuccess: (data) => { onSuccess: (data) => {
addresses.value = data.models?.data || data.data || []; addresses.value = data.models?.data || data.data || [];
} },
}); });
api.get(apiURL('units'), { api.get(apiURL("units"), {
onSuccess: (data) => { onSuccess: (data) => {
units.value = data.models?.data || data.data || []; units.value = data.models?.data || data.data || [];
} },
}); });
}); });
/** Métodos */ /** Métodos */
const handleSubmit = () => { const handleSubmit = () => {
const baseData = { const baseData = {
direction_id: typeof form.direction_id === 'object' ? form.direction_id.id : Number(form.direction_id), direction_id:
unit_id: typeof form.unit_id === 'object' ? form.unit_id.id : Number(form.unit_id), typeof form.direction_id === "object"
? form.direction_id.id
: Number(form.direction_id),
unit_id:
typeof form.unit_id === "object" ? form.unit_id.id : Number(form.unit_id),
short_name: form.short_name, short_name: form.short_name,
name: form.name, name: form.name,
legal_instrument: form.legal_instrument, legal_instrument: form.legal_instrument,
@ -105,13 +118,17 @@ const handleSubmit = () => {
// Agregar campos condicionales solo si tienen valor // Agregar campos condicionales solo si tienen valor
if (showUmaFields.value && isRangeType.value) { if (showUmaFields.value && isRangeType.value) {
if (form.min_amount_uma) baseData.min_amount_uma = Number(form.min_amount_uma); if (form.min_amount_uma)
if (form.max_amount_uma) baseData.max_amount_uma = Number(form.max_amount_uma); baseData.min_amount_uma = Number(form.min_amount_uma);
if (form.max_amount_uma)
baseData.max_amount_uma = Number(form.max_amount_uma);
} }
if (showPesoFields.value && isRangeType.value) { if (showPesoFields.value && isRangeType.value) {
if (form.min_amount_peso) baseData.min_amount_peso = Number(form.min_amount_peso); if (form.min_amount_peso)
if (form.max_amount_peso) baseData.max_amount_peso = Number(form.max_amount_peso); baseData.min_amount_peso = Number(form.min_amount_peso);
if (form.max_amount_peso)
baseData.max_amount_peso = Number(form.max_amount_peso);
} }
if (showUmaFields.value && isFixedType.value && form.unit_cost_uma) { if (showUmaFields.value && isFixedType.value && form.unit_cost_uma) {
@ -122,16 +139,38 @@ const handleSubmit = () => {
baseData.unit_cost_peso = Number(form.unit_cost_peso); baseData.unit_cost_peso = Number(form.unit_cost_peso);
} }
emit('concept-created', baseData); emit("concept-created", baseData);
resetForm(); resetForm();
isFormOpen.value = false;
}; };
</script> </script>
<template> <template>
<div class="bg-white rounded-xl p-6 m-3 max-w-auto shadow-lg mb-10"> <div class="mx-4 mt-4 mb-6">
<h3 class="text-xl font-semibold mb-4 text-gray-800"> <button
{{ $t('concept.create.title') }} v-if="!isFormOpen"
</h3> @click="isFormOpen = true"
class="w-full bg-primary hover:bg-primary/90 text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200 shadow-md hover:shadow-lg flex items-center justify-center space-x-2"
>
<span class="text-2xl">+</span>
<span>Nuevo Concepto</span>
</button>
<div
v-if="isFormOpen"
class="bg-white rounded-lg p-8 mx-4 mt-4 mb-6 shadow-md"
>
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-900">
{{ $t("concept.create.title") }}
</h2>
<button
@click="isFormOpen = false"
class="text-gray-400 hover:text-gray-600 transition-colors"
title="Cerrar"
>
<span class="text-2xl">&times;</span>
</button>
</div>
<form @submit.prevent="handleSubmit"> <form @submit.prevent="handleSubmit">
<div class="mb-5 grid grid-cols-2 gap-4"> <div class="mb-5 grid grid-cols-2 gap-4">
@ -196,7 +235,10 @@ const handleSubmit = () => {
required required
/> />
</div> </div>
<div v-if="showUmaFields && isRangeType" class="mb-5 grid grid-cols-2 gap-4 py-2"> <div
v-if="showUmaFields && isRangeType"
class="mb-5 grid grid-cols-2 gap-4 py-2"
>
<Input <Input
v-model="form.min_amount_uma" v-model="form.min_amount_uma"
:id="$t('concept.minimumAmountUma')" :id="$t('concept.minimumAmountUma')"
@ -212,7 +254,10 @@ const handleSubmit = () => {
:onError="form.errors.max_amount_uma" :onError="form.errors.max_amount_uma"
/> />
</div> </div>
<div v-if="showPesoFields && isRangeType" class="mb-5 grid grid-cols-2 gap-4 py-2"> <div
v-if="showPesoFields && isRangeType"
class="mb-5 grid grid-cols-2 gap-4 py-2"
>
<Input <Input
v-model="form.min_amount_peso" v-model="form.min_amount_peso"
:id="$t('concept.minimumAmountPeso')" :id="$t('concept.minimumAmountPeso')"
@ -239,7 +284,7 @@ const handleSubmit = () => {
<div v-if="isFixedType"> <div v-if="isFixedType">
<hr class="my-4 border-gray-300" /> <hr class="my-4 border-gray-300" />
<h4 class="text-lg font-semibold mb-4 text-gray-700"> <h4 class="text-lg font-semibold mb-4 text-gray-700">
{{ $t('concept.tabulator') }} {{ $t("concept.tabulator") }}
</h4> </h4>
<div class="mb-5 grid grid-cols-3 gap-4 py-2"> <div class="mb-5 grid grid-cols-3 gap-4 py-2">
<Input <Input
@ -260,15 +305,16 @@ const handleSubmit = () => {
/> />
</div> </div>
</div> </div>
<div class="mb-3 p-7 flex justify-center"> <div class="flex justify-center mt-4">
<button <button
type="submit" type="submit"
:disabled="form.processing" :disabled="form.processing"
class="w-1/4 bg-[#7a0b3a] hover:bg-[#68082e] text-white font-medium py-3.5 rounded-lg transition-colors disabled:opacity-50" class="w-full max-w-xs bg-primary hover:bg-primary/90 text-white font-semibold py-3 rounded-md transition-all duration-200 shadow-sm hover:shadow-md disabled:opacity-50"
> >
{{ form.processing ? 'Guardando...' : $t('save') }} {{ form.processing ? "Guardando..." : "Guardar" }}
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div>
</template> </template>

View File

@ -49,7 +49,7 @@ onMounted(() => {
/> />
</div> </div>
<main class="flex h-full justify-center md:p-2"> <main class="flex h-full justify-center md:p-2">
<div class="mt-14 md:mt-0 w-full shadow-lg dark:shadow-xs md:dark:shadow-white h-[calc(100vh-4.5rem)] px-2 pb-4 md:rounded-sm overflow-y-auto overflow-x-auto transition-colors duration-300"> <div class="mt-14 md:mt-0 w-full h-[calc(100vh-4.5rem)] pb-4 md:rounded-sm overflow-y-auto overflow-x-auto bg-gray-50 transition-colors duration-300">
<slot /> <slot />
</div> </div>
</main> </main>

View File

@ -18,7 +18,7 @@ const props = defineProps({
/** Métodos */ /** Métodos */
const home = () => { const home = () => {
if(hasToken()) { if(hasToken()) {
router.push({ name: 'dashboard.index' }); router.push({ name: 'address.index' });
} else { } else {
location.replace('/'); location.replace('/');
} }

View File

@ -1,32 +1,19 @@
<script setup> <script setup>
import { RouterLink } from 'vue-router';
import IconButton from '@Holos/Button/Icon.vue'
defineProps({ defineProps({
title: String title: String
}); });
</script> </script>
<template> <template>
<div v-if="title" class="flex w-full justify-center"> <div v-if="title" class="flex w-full justify-center py-4">
<h2 <h1
class="font-bold text-xl uppercase" class="font-bold text-2xl tracking-wide"
v-text="title" v-text="title"
/> />
</div> </div>
<div class="flex w-full justify-end py-[0.31rem] mb-2 border-y-2 border-page-t dark:border-page-dt"> <div v-if="$slots.default" class="flex w-full justify-end py-2 mb-3">
<div id="buttons" class="flex items-center space-x-1 text-sm"> <div id="buttons" class="flex items-center space-x-1 text-sm">
<slot /> <slot />
<RouterLink :to="$view({ name: 'index' })">
<IconButton
:title="$t('home')"
class="text-white"
icon="home"
filled
/>
</RouterLink>
</div> </div>
</div> </div>
</template> </template>

View File

@ -69,14 +69,6 @@ const clear = () => {
</div> </div>
<div class="flex items-center space-x-1 text-sm" id="buttons"> <div class="flex items-center space-x-1 text-sm" id="buttons">
<slot /> <slot />
<RouterLink :to="$view({name:'index'})">
<IconButton
:title="$t('home')"
class="text-white"
icon="home"
filled
/>
</RouterLink>
</div> </div>
</div> </div>
</template> </template>

View File

@ -66,22 +66,6 @@ const loader = useLoader()
/> />
<span class="text-xs">{{ notifier.counter }}</span> <span class="text-xs">{{ notifier.counter }}</span>
</li> </li>
<li v-if="darkMode.isDark">
<GoogleIcon
class="text-xl mt-1"
name="light_mode"
:title="$t('notifications.title')"
@click="darkMode.applyLight()"
/>
</li>
<li v-else>
<GoogleIcon
class="text-xl mt-1"
name="dark_mode"
:title="$t('notifications.title')"
@click="darkMode.applyDark()"
/>
</li>
<li> <li>
<div class="relative"> <div class="relative">
<Dropdown align="right" width="48"> <Dropdown align="right" width="48">

View File

@ -18,10 +18,10 @@ const props = defineProps({
const classes = computed(() => { const classes = computed(() => {
let status = props.to === vroute.name let status = props.to === vroute.name
? 'bg-secondary/30 dark:bg-secondary-d/30 border-secondary dark:border-secondary-d' ? 'bg-white/20 dark:bg-white/20 border-white dark:border-white font-semibold'
: 'border-transparent'; : 'border-transparent';
return `flex items-center h-11 focus:outline-hidden hover:bg-secondary/30 dark:hover:bg-secondary-d/30 border-l-4 hover:border-secondary dark:hover:border-secondary-d pr-6 ${status} transition` return `flex items-center h-11 focus:outline-hidden hover:bg-white/10 dark:hover:bg-white/10 border-l-4 hover:border-white/50 dark:hover:border-white/50 pr-6 ${status} transition-all duration-200`
}); });
const closeSidebar = () => { const closeSidebar = () => {

View File

@ -16,15 +16,15 @@ const props = defineProps({
<template> <template>
<section class="pb-2"> <section class="pb-2">
<div class="w-full overflow-hidden rounded-sm shadow-lg dark:shadow-xs dark:shadow-white"> <div class="w-full overflow-hidden">
<div class="w-full overflow-x-auto"> <div class="w-full overflow-x-auto">
<table v-if="!processing" class="w-full"> <table v-if="!processing" class="w-full bg-white">
<thead class="bg-primary text-primary-t dark:bg-primary-d dark:text-primary-dt"> <thead class="bg-primary text-primary-t dark:bg-primary-d dark:text-primary-dt">
<tr> <tr>
<slot name="head" /> <slot name="head" />
</tr> </tr>
</thead> </thead>
<tbody> <tbody class="bg-white divide-y divide-gray-200">
<template v-if="items?.total > 0"> <template v-if="items?.total > 0">
<slot <slot
name="body" name="body"

View File

@ -56,15 +56,15 @@
} }
.table-row { .table-row {
@apply hover:bg-secondary/10 dark:hover:bg-secondary-d/10 transition-colors duration-100 @apply hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-150
} }
.table-cell { .table-cell {
@apply px-2 py-0.5 text-sm border border-primary/30 dark:border-primary-dt/30; @apply px-6 py-4 text-sm;
} }
.table-actions { .table-actions {
@apply flex justify-center items-center space-x-1; @apply flex justify-center items-center space-x-2;
} }
nav a.router-link-active { nav a.router-link-active {

View File

@ -29,11 +29,6 @@ onMounted(() => {
<template #leftSidebar> <template #leftSidebar>
<Section name="Principal"> <Section name="Principal">
<Link
icon="monitoring"
name="dashboard"
to="dashboard.index"
/>
<Link <Link
icon="add_home" icon="add_home"
name="Gestionar Direcciones" name="Gestionar Direcciones"

View File

@ -56,29 +56,27 @@ onMounted(() => {
@click="searcher.search()" @click="searcher.search()"
/> />
</SearcherHead> </SearcherHead>
<div class="pt-2 space-y-2 w-full"> <div class="pt-2 space-y-2 w-full px-4">
<p class="text-sm"> <p class="text-sm text-gray-600">
{{ transl('description') }} {{ transl('description') }}
</p> </p>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<Table <Table
:items="models" :items="models"
@send-pagination="(page) => searcher.pagination(page)" @send-pagination="(page) => searcher.pagination(page)"
:processing="searcher.processing" :processing="searcher.processing"
> >
<template #head> <template #head>
<th v-text="$t('name')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="$t('name')" />
<th <th class="px-6 py-3 text-center text-sm font-semibold w-32" v-text="$t('actions')" />
v-text="$t('actions')"
class="w-32 text-center"
/>
</template> </template>
<template #body="{items}"> <template #body="{items}">
<tr v-for="model in items" class="table-row"> <tr v-for="model in items" class="border-b border-gray-200 hover:bg-gray-50 transition-colors">
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.description }} {{ model.description }}
</td> </td>
<td class="table-cell"> <td class="px-6 py-4">
<div class="table-actions"> <div class="flex justify-center items-center space-x-2">
<IconButton <IconButton
v-if="can('edit') && ![1,2].includes(model.id)" v-if="can('edit') && ![1,2].includes(model.id)"
icon="license" icon="license"
@ -110,6 +108,7 @@ onMounted(() => {
</template> </template>
</Table> </Table>
</div> </div>
</div>
<Permissions <Permissions
v-if="can('index')" v-if="can('index')"

View File

@ -58,29 +58,27 @@ onMounted(() => {
@click="searcher.search()" @click="searcher.search()"
/> />
</SearcherHead> </SearcherHead>
<div class="pt-2 w-full"> <div class="pt-2 w-full px-4">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<Table <Table
:items="models" :items="models"
:processing="searcher.processing" :processing="searcher.processing"
@send-pagination="(page) => searcher.pagination(page)" @send-pagination="(page) => searcher.pagination(page)"
> >
<template #head> <template #head>
<th v-text="$t('user')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="$t('user')" />
<th v-text="$t('contact')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="$t('contact')" />
<th <th class="px-6 py-3 text-center text-sm font-semibold w-32" v-text="$t('actions')" />
v-text="$t('actions')"
class="w-32 text-center"
/>
</template> </template>
<template #body="{items}"> <template #body="{items}">
<tr <tr
v-for="model in items" v-for="model in items"
class="table-row" class="border-b border-gray-200 hover:bg-gray-50 transition-colors"
> >
<td class="table-cell"> <td class="px-6 py-4 text-sm text-gray-700">
{{ `${model.name} ${model.paternal}` }} {{ `${model.name} ${model.paternal}` }}
</td> </td>
<td class="table-cell"> <td class="px-6 py-4 text-sm text-gray-700">
<p> <p>
<a <a
class="hover:underline" class="hover:underline"
@ -101,8 +99,8 @@ onMounted(() => {
</span> </span>
</p> </p>
</td> </td>
<td class="table-cell"> <td class="px-6 py-4">
<div class="table-actions"> <div class="flex justify-center items-center space-x-2">
<IconButton <IconButton
icon="visibility" icon="visibility"
:title="$t('crud.show')" :title="$t('crud.show')"
@ -152,18 +150,13 @@ onMounted(() => {
</tr> </tr>
</template> </template>
<template #empty> <template #empty>
<td class="table-cell"> <td colspan="3" class="px-6 py-8 text-center text-gray-500 text-sm">
<div class="flex items-center text-sm">
<p class="font-semibold">
{{ $t('registers.empty') }} {{ $t('registers.empty') }}
</p>
</div>
</td> </td>
<td class="table-cell">-</td>
<td class="table-cell">-</td>
</template> </template>
</Table> </Table>
</div> </div>
</div>
<ShowView <ShowView
v-if="can('index')" v-if="can('index')"

View File

@ -73,34 +73,35 @@ const handleAddressUpdated = async () => {
</script> </script>
<template> <template>
<PageHeader :title="transl('title')" /> <PageHeader title="DIRECCIONES" />
<AddressForm @address-created="handleAddressCreated" /> <AddressForm @address-created="handleAddressCreated" />
<div class="m-3"> <div class="mx-4 mb-4">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<Table <Table
:items="models" :items="models"
:processing="searcher.processing" :processing="searcher.processing"
@send-pagination="(page) => searcher.pagination(page)" @send-pagination="(page) => searcher.pagination(page)"
> >
<template #head> <template #head>
<th v-text="$t('name')" /> <th class="px-6 py-3 text-left text-sm font-semibold">Nombre</th>
<th v-text="$t('actions')" class="w-32 text-center" /> <th class="px-6 py-3 text-center text-sm font-semibold w-32">Acciones</th>
</template> </template>
<template #body="{ items }"> <template #body="{ items }">
<tr v-for="model in items" :key="model.id" class="table-row"> <tr v-for="model in items" :key="model.id" class="border-b border-gray-200 hover:bg-gray-50 transition-colors">
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.name }} {{ model.name }}
</td> </td>
<td class="table-cell"> <td class="px-6 py-4">
<div class="table-actions"> <div class="flex justify-center items-center space-x-2">
<IconButton <IconButton
icon="edit" icon="edit"
:title="$t('crud.edit')" title="Editar"
@click="Modal.switchEditModal(model)" @click="Modal.switchEditModal(model)"
outline outline
/> />
<IconButton <IconButton
icon="delete" icon="delete"
:title="$t('crud.destroy')" title="Eliminar"
@click="Modal.switchDestroyModal(model)" @click="Modal.switchDestroyModal(model)"
outline outline
/> />
@ -109,11 +110,12 @@ const handleAddressUpdated = async () => {
</tr> </tr>
</template> </template>
<template #empty> <template #empty>
<td colspan="2" class="table-cell text-center py-4 text-gray-500"> <td colspan="2" class="px-6 py-8 text-center text-gray-500 text-sm">
{{ transl('list.empty') }} {{ transl('list.empty') }}
</td> </td>
</template> </template>
</Table> </Table>
</div>
<DestroyView <DestroyView
title="name" title="name"
subtitle="" subtitle=""

View File

@ -76,52 +76,53 @@ const handleConceptUpdated = () => {
<template> <template>
<PageHeader :title="transl('title')" /> <PageHeader :title="transl('title')" />
<ConceptForm @concept-created="handleConceptCreated" /> <ConceptForm @concept-created="handleConceptCreated" />
<div class="m-3"> <div class="mx-4 mb-4">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<Table <Table
:items="models" :items="models"
:processing="searcher.processing" :processing="searcher.processing"
@send-pagination="(page) => searcher.pagination(page)" @send-pagination="(page) => searcher.pagination(page)"
> >
<template #head> <template #head>
<th v-text="transl('direction')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('direction')" />
<th v-text="transl('shortName')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('shortName')" />
<th v-text="transl('name')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('name')" />
<th v-text="transl('legal')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('legal')" />
<th v-text="transl('article')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('article')" />
<th v-text="transl('description')" /> <th class="px-6 py-3 text-left text-sm font-semibold" v-text="transl('description')" />
<th v-text="$t('actions')" class="w-32 text-center" /> <th class="px-6 py-3 text-center text-sm font-semibold w-32" v-text="$t('actions')" />
</template> </template>
<template #body="{ items }"> <template #body="{ items }">
<tr v-for="model in items" :key="model.id" class="table-row"> <tr v-for="model in items" :key="model.id" class="border-b border-gray-200 hover:bg-gray-50 transition-colors">
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.direction?.name || "-" }} {{ model.direction?.name || "-" }}
</td> </td>
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.short_name }} {{ model.short_name }}
</td> </td>
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.name }} {{ model.name }}
</td> </td>
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.legal_instrument }} {{ model.legal_instrument }}
</td> </td>
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.article }} {{ model.article }}
</td> </td>
<td class="table-cell border"> <td class="px-6 py-4 text-sm text-gray-700">
{{ model.content }} {{ model.content }}
</td> </td>
<td class="table-cell"> <td class="px-6 py-4">
<div class="table-actions"> <div class="flex justify-center items-center space-x-2">
<IconButton <IconButton
icon="edit" icon="edit"
:title="$t('crud.edit')" title="Editar"
@click="Modal.switchEditModal(model)" @click="Modal.switchEditModal(model)"
outline outline
/> />
<IconButton <IconButton
icon="delete" icon="delete"
:title="$t('crud.destroy')" title="Eliminar"
@click="Modal.switchDestroyModal(model)" @click="Modal.switchDestroyModal(model)"
outline outline
/> />
@ -130,11 +131,12 @@ const handleConceptUpdated = () => {
</tr> </tr>
</template> </template>
<template #empty> <template #empty>
<td colspan="7" class="table-cell text-center py-4 text-gray-500"> <td colspan="7" class="px-6 py-8 text-center text-gray-500 text-sm">
{{ transl("list.empty") }} {{ transl("list.empty") }}
</td> </td>
</template> </template>
</Table> </Table>
</div>
<DestroyView <DestroyView
title="name" title="name"
subtitle="content" subtitle="content"

View File

@ -21,7 +21,7 @@ const router = createRouter({
{ {
path: '', path: '',
name: 'index', name: 'index',
redirect: '/dashboard' redirect: '/address'
}, },
{ {
path: 'dashboard', path: 'dashboard',