183 lines
9.0 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import Card from 'primevue/card';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import IconField from 'primevue/iconfield';
import InputIcon from 'primevue/inputicon';
import Select from 'primevue/select';
import Paginator from 'primevue/paginator';
import { fixedAssetStructuresService } from '../../services/fixedAssetStructuresService';
import type { FixedAssetStructure, StructureStatus } from '../../types/fixedAssetStructure';
const router = useRouter();
const loading = ref(false);
const first = ref(0);
const rows = ref(6);
const searchTerm = ref('');
const selectedStatus = ref<'all' | StructureStatus>('all');
const structures = ref<FixedAssetStructure[]>([]);
const statusOptions = [
{ label: 'Todos los estatus', value: 'all' },
{ label: 'Borrador', value: 'BORRADOR' },
{ label: 'Activa', value: 'ACTIVA' },
{ label: 'En revision', value: 'EN_REVISION' },
{ label: 'Inactiva', value: 'INACTIVA' }
];
const statusClasses: Record<StructureStatus, string> = {
BORRADOR: 'bg-surface-200 text-surface-700',
ACTIVA: 'bg-emerald-100 text-emerald-700',
EN_REVISION: 'bg-amber-100 text-amber-700',
INACTIVA: 'bg-red-100 text-red-700'
};
const formatCurrency = (value: number) =>
`$${value.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
const filteredStructures = computed(() => {
const query = searchTerm.value.trim().toLowerCase();
return structures.value.filter((structure) => {
const matchesQuery = !query
|| structure.code.toLowerCase().includes(query)
|| structure.name.toLowerCase().includes(query)
|| structure.containerSerial.toLowerCase().includes(query);
const matchesStatus = selectedStatus.value === 'all' || structure.status === selectedStatus.value;
return matchesQuery && matchesStatus;
});
});
const paginatedStructures = computed(() =>
filteredStructures.value.slice(first.value, first.value + rows.value)
);
const loadStructures = async () => {
loading.value = true;
structures.value = await fixedAssetStructuresService.getStructures();
loading.value = false;
};
const onPage = (event: { first: number; rows: number }) => {
first.value = event.first;
rows.value = event.rows;
};
const goToCreate = () => router.push('/fixed-assets/structures/create');
const goToDetails = (id: string) => router.push(`/fixed-assets/structures/${id}`);
const goToEdit = (id: string) => router.push(`/fixed-assets/structures/${id}/edit`);
onMounted(loadStructures);
</script>
<template>
<section class="space-y-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h1 class="text-3xl font-black tracking-tight text-surface-900 dark:text-surface-0">
Estructuras de Activos Fijos
</h1>
<p class="mt-1 text-surface-500 dark:text-surface-400">
Define, organiza y monitorea activos compuestos.
</p>
</div>
<Button label="Crear estructura" icon="pi pi-plus" @click="goToCreate" />
</div>
<Card class="shadow-sm">
<template #content>
<div class="space-y-4">
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<IconField iconPosition="left" class="w-full md:max-w-lg">
<InputIcon class="pi pi-search" />
<InputText
v-model="searchTerm"
class="w-full"
placeholder="Buscar por codigo, nombre o numero de serie..."
/>
</IconField>
<Select
v-model="selectedStatus"
:options="statusOptions"
optionLabel="label"
optionValue="value"
class="w-full md:w-56"
/>
</div>
<div class="overflow-x-auto rounded-xl border border-surface-200 dark:border-surface-700">
<table class="min-w-full border-collapse">
<thead>
<tr class="bg-surface-50 text-left text-xs font-semibold uppercase tracking-wide text-surface-500 dark:bg-surface-800 dark:text-surface-300">
<th class="px-4 py-3">Codigo</th>
<th class="px-4 py-3">Estructura</th>
<th class="px-4 py-3">Categoria</th>
<th class="px-4 py-3">Ubicacion</th>
<th class="px-4 py-3">Contenidos</th>
<th class="px-4 py-3">Valor Total</th>
<th class="px-4 py-3">Estatus</th>
<th class="px-4 py-3 text-right">Acciones</th>
</tr>
</thead>
<tbody>
<tr v-if="loading">
<td colspan="8" class="px-4 py-8 text-center text-sm text-surface-500">
Cargando estructuras...
</td>
</tr>
<tr
v-for="structure in paginatedStructures"
:key="structure.id"
class="border-t border-surface-200 text-sm dark:border-surface-700"
>
<td class="px-4 py-3 font-mono text-xs text-primary">{{ structure.code }}</td>
<td class="px-4 py-3">
<p class="font-semibold text-surface-900 dark:text-surface-0">{{ structure.name }}</p>
<p class="text-xs text-surface-500 dark:text-surface-400">Serie: {{ structure.containerSerial }}</p>
</td>
<td class="px-4 py-3">{{ structure.containerCategory }}</td>
<td class="px-4 py-3">{{ structure.location }}</td>
<td class="px-4 py-3 text-center font-semibold">{{ structure.contents.length }}</td>
<td class="px-4 py-3 font-semibold">{{ formatCurrency(structure.containerValue + structure.contents.reduce((sum, item) => sum + item.value, 0)) }}</td>
<td class="px-4 py-3">
<span class="inline-flex rounded-full px-2 py-1 text-xs font-semibold" :class="statusClasses[structure.status]">
{{ structure.status }}
</span>
</td>
<td class="px-4 py-3">
<div class="flex items-center justify-end gap-1">
<Button icon="pi pi-eye" text rounded size="small" @click="goToDetails(structure.id)" />
<Button icon="pi pi-pencil" text rounded size="small" @click="goToEdit(structure.id)" />
</div>
</td>
</tr>
<tr v-if="!loading && paginatedStructures.length === 0">
<td colspan="8" class="px-4 py-8 text-center text-sm text-surface-500 dark:text-surface-400">
No hay estructuras para mostrar.
</td>
</tr>
</tbody>
</table>
</div>
<div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<p class="text-sm text-surface-500 dark:text-surface-400">
Mostrando {{ paginatedStructures.length }} de {{ filteredStructures.length }} estructuras
</p>
<Paginator
:first="first"
:rows="rows"
:totalRecords="filteredStructures.length"
:rowsPerPageOptions="[6, 12, 18]"
template="PrevPageLink PageLinks NextPageLink"
@page="onPage"
/>
</div>
</div>
</template>
</Card>
</section>
</template>