Refactor code structure for improved readability and maintainability

This commit is contained in:
Juan Felipe Zapata Moreno 2026-04-01 17:53:53 -06:00
parent 817e6c76c4
commit 8632e5c34a
8 changed files with 2110 additions and 25 deletions

1923
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -237,8 +237,8 @@ const handleDelete = (asset: Asset) => {
<td class="px-4 py-4">{{ asset.asset_tag ?? '—' }}</td> <td class="px-4 py-4">{{ asset.asset_tag ?? '—' }}</td>
<td class="px-4 py-4"> <td class="px-4 py-4">
<template v-if="asset.active_assignment"> <template v-if="asset.active_assignment">
<p class="font-medium">{{ asset.active_assignment.employee.name }}</p> <p class="font-medium">{{ asset.active_assignment.employee?.name ?? (asset.active_assignment as any).external_name ?? '—' }}</p>
<p class="text-xs text-surface-500">{{ asset.active_assignment.employee.department?.name }}</p> <p class="text-xs text-surface-500">{{ asset.active_assignment.employee?.department?.name ?? (asset.active_assignment as any).external_company ?? '' }}</p>
</template> </template>
<span v-else class="text-surface-400">Sin asignar</span> <span v-else class="text-surface-400">Sin asignar</span>
</td> </td>

View File

@ -49,7 +49,7 @@ const loadAsset = async () => {
residual_value: asset.residual_value ? parseFloat(asset.residual_value) : null, residual_value: asset.residual_value ? parseFloat(asset.residual_value) : null,
asset_tag: asset.asset_tag ?? '', asset_tag: asset.asset_tag ?? '',
warranty_days: asset.warranty_days, warranty_days: asset.warranty_days,
warranty_end_date: asset.warranty_end_date ?? '' warranty_end_date: asset.warranty_end_date?.slice(0, 10) ?? ''
}; };
currentProductName.value = asset.inventory_warehouse?.product?.name ?? ''; currentProductName.value = asset.inventory_warehouse?.product?.name ?? '';
} catch { } catch {
@ -140,7 +140,8 @@ const saveAsset = async () => {
<template #content> <template #content>
<div class="space-y-1"> <div class="space-y-1">
<p class="text-sm text-surface-500">Producto</p> <p class="text-sm text-surface-500">Producto</p>
<p class="font-semibold text-surface-900 dark:text-surface-0">{{ currentProductName || '—' }}</p> <p class="font-semibold text-surface-900 dark:text-surface-0">{{ currentProductName || '—' }}
</p>
</div> </div>
</template> </template>
</Card> </Card>
@ -153,12 +154,8 @@ const saveAsset = async () => {
<div class="flex flex-wrap items-center justify-end gap-3"> <div class="flex flex-wrap items-center justify-end gap-3">
<Button label="Cancelar" text severity="secondary" @click="cancel" /> <Button label="Cancelar" text severity="secondary" @click="cancel" />
<Button <Button :label="isEditing ? 'Guardar Cambios' : 'Guardar Registro'" icon="pi pi-save" :loading="saving"
:label="isEditing ? 'Guardar Cambios' : 'Guardar Registro'" @click="saveAsset" />
icon="pi pi-save"
:loading="saving"
@click="saveAsset"
/>
</div> </div>
</template> </template>
</section> </section>

View File

@ -8,6 +8,13 @@ interface Props {
employees: AssignmentEmployeeOption[]; employees: AssignmentEmployeeOption[];
searchTerm: string; searchTerm: string;
selectedEmployeeId: number | null; selectedEmployeeId: number | null;
assigneeType: 1 | 2;
externalName: string;
externalPaternal: string;
externalMaternal: string;
externalCompany: string;
externalPhone: string;
externalEmail: string;
} }
const props = defineProps<Props>(); const props = defineProps<Props>();
@ -15,6 +22,13 @@ const props = defineProps<Props>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:searchTerm', value: string): void; (e: 'update:searchTerm', value: string): void;
(e: 'update:selectedEmployeeId', value: number): void; (e: 'update:selectedEmployeeId', value: number): void;
(e: 'update:assigneeType', value: 1 | 2): void;
(e: 'update:externalName', value: string): void;
(e: 'update:externalPaternal', value: string): void;
(e: 'update:externalMaternal', value: string): void;
(e: 'update:externalCompany', value: string): void;
(e: 'update:externalPhone', value: string): void;
(e: 'update:externalEmail', value: string): void;
}>(); }>();
const visibleEmployees = computed(() => { const visibleEmployees = computed(() => {
@ -31,13 +45,38 @@ const visibleEmployees = computed(() => {
<template> <template>
<Card class="shadow-sm"> <Card class="shadow-sm">
<template #title> <template #title>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl"> <div class="flex items-center gap-2 text-xl">
<i class="pi pi-users text-primary"></i> <i class="pi pi-users text-primary"></i>
<span>Seleccionar Colaborador</span> <span>Seleccionar Asignado</span>
</div>
<div class="flex overflow-hidden rounded-lg border border-surface-200 text-sm dark:border-surface-700">
<button
type="button"
class="px-4 py-1.5 font-medium transition-colors"
:class="assigneeType === 1
? 'bg-primary text-white'
: 'text-surface-600 hover:bg-surface-100 dark:text-surface-300 dark:hover:bg-surface-800'"
@click="emit('update:assigneeType', 1)"
>
Colaborador Interno
</button>
<button
type="button"
class="px-4 py-1.5 font-medium transition-colors"
:class="assigneeType === 2
? 'bg-primary text-white'
: 'text-surface-600 hover:bg-surface-100 dark:text-surface-300 dark:hover:bg-surface-800'"
@click="emit('update:assigneeType', 2)"
>
Persona Externa
</button>
</div>
</div> </div>
</template> </template>
<template #content> <template #content>
<div class="space-y-4"> <!-- Empleado interno -->
<div v-if="assigneeType === 1" class="space-y-4">
<InputText <InputText
:model-value="searchTerm" :model-value="searchTerm"
class="w-full" class="w-full"
@ -75,6 +114,66 @@ const visibleEmployees = computed(() => {
</button> </button>
</div> </div>
</div> </div>
<!-- Persona externa -->
<div v-else class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">
Nombre <span class="text-red-500">*</span>
</label>
<InputText
:model-value="externalName"
placeholder="Nombre"
class="w-full"
@update:model-value="emit('update:externalName', String($event ?? ''))"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">Apellido Paterno</label>
<InputText
:model-value="externalPaternal"
placeholder="Apellido paterno"
class="w-full"
@update:model-value="emit('update:externalPaternal', String($event ?? ''))"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">Apellido Materno</label>
<InputText
:model-value="externalMaternal"
placeholder="Apellido materno"
class="w-full"
@update:model-value="emit('update:externalMaternal', String($event ?? ''))"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">Empresa / Organización</label>
<InputText
:model-value="externalCompany"
placeholder="Empresa u organización"
class="w-full"
@update:model-value="emit('update:externalCompany', String($event ?? ''))"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">Teléfono</label>
<InputText
:model-value="externalPhone"
placeholder="Teléfono de contacto"
class="w-full"
@update:model-value="emit('update:externalPhone', String($event ?? ''))"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-sm font-medium text-surface-700 dark:text-surface-300">Correo electrónico</label>
<InputText
:model-value="externalEmail"
placeholder="correo@ejemplo.com"
class="w-full"
@update:model-value="emit('update:externalEmail', String($event ?? ''))"
/>
</div>
</div>
</template> </template>
</Card> </Card>
</template> </template>

View File

@ -28,7 +28,14 @@ const users = ref<UserOption[]>([]);
const form = ref<FixedAssetAssignmentFormData>({ const form = ref<FixedAssetAssignmentFormData>({
assetId: null, assetId: null,
assigneeType: 1,
employeeId: null, employeeId: null,
externalName: '',
externalPaternal: '',
externalMaternal: '',
externalCompany: '',
externalPhone: '',
externalEmail: '',
authorizedById: null, authorizedById: null,
deliveredById: null, deliveredById: null,
assignedAt: new Date().toISOString().slice(0, 10), assignedAt: new Date().toISOString().slice(0, 10),
@ -81,11 +88,16 @@ onMounted(async () => {
const cancel = () => router.push('/fixed-assets/assignments'); const cancel = () => router.push('/fixed-assets/assignments');
const save = async () => { const save = async () => {
if (!form.value.assetId || !form.value.employeeId) { const isExternal = form.value.assigneeType === 2;
const missingAssignee = isExternal ? !form.value.externalName.trim() : !form.value.employeeId;
if (!form.value.assetId || missingAssignee) {
toast.add({ toast.add({
severity: 'warn', severity: 'warn',
summary: 'Campos pendientes', summary: 'Campos pendientes',
detail: 'Seleccione un activo y un colaborador para confirmar la asignacion.', detail: isExternal
? 'Seleccione un activo e ingrese el nombre de la persona externa.'
: 'Seleccione un activo y un colaborador para confirmar la asignacion.',
life: 3000 life: 3000
}); });
return; return;
@ -94,7 +106,17 @@ const save = async () => {
loading.value = true; loading.value = true;
try { try {
await fixedAssetsService.assignAsset(form.value.assetId, { await fixedAssetsService.assignAsset(form.value.assetId, {
employee_id: form.value.employeeId, assignee_type: form.value.assigneeType,
...(isExternal ? {
external_name: form.value.externalName,
external_paternal: form.value.externalPaternal || undefined,
external_maternal: form.value.externalMaternal || undefined,
external_company: form.value.externalCompany || undefined,
external_phone: form.value.externalPhone || undefined,
external_email: form.value.externalEmail || undefined,
} : {
employee_id: form.value.employeeId ?? undefined,
}),
assigned_at: form.value.assignedAt, assigned_at: form.value.assignedAt,
receipt_folio: form.value.receiptFolio || undefined, receipt_folio: form.value.receiptFolio || undefined,
authorized_by: form.value.authorizedById ?? undefined, authorized_by: form.value.authorizedById ?? undefined,
@ -125,10 +147,10 @@ const save = async () => {
<div> <div>
<h1 class="text-3xl font-black tracking-tight text-surface-900 dark:text-surface-0"> <h1 class="text-3xl font-black tracking-tight text-surface-900 dark:text-surface-0">
Asignacion de Activo a Empleado Asignacion de Activo
</h1> </h1>
<p class="mt-1 text-surface-500 dark:text-surface-400"> <p class="mt-1 text-surface-500 dark:text-surface-400">
Registre la entrega de una herramienta o equipo a un colaborador del almacen. Registre la entrega de una herramienta o equipo a un colaborador o persona externa.
</p> </p>
</div> </div>
@ -145,8 +167,22 @@ const save = async () => {
:employees="employees" :employees="employees"
:search-term="employeeSearch" :search-term="employeeSearch"
:selected-employee-id="form.employeeId" :selected-employee-id="form.employeeId"
:assignee-type="form.assigneeType"
:external-name="form.externalName"
:external-paternal="form.externalPaternal"
:external-maternal="form.externalMaternal"
:external-company="form.externalCompany"
:external-phone="form.externalPhone"
:external-email="form.externalEmail"
@update:search-term="employeeSearch = $event" @update:search-term="employeeSearch = $event"
@update:selected-employee-id="form.employeeId = $event" @update:selected-employee-id="form.employeeId = $event"
@update:assignee-type="form.assigneeType = $event; form.employeeId = null; form.externalName = ''"
@update:external-name="form.externalName = $event"
@update:external-paternal="form.externalPaternal = $event"
@update:external-maternal="form.externalMaternal = $event"
@update:external-company="form.externalCompany = $event"
@update:external-phone="form.externalPhone = $event"
@update:external-email="form.externalEmail = $event"
/> />
<AssignmentDetailsCard :form="form" :users="users" /> <AssignmentDetailsCard :form="form" :users="users" />

View File

@ -38,12 +38,21 @@ const formatDate = (dateStr: string | null) => {
return new Date(dateStr).toLocaleDateString('es-MX', { day: '2-digit', month: 'short', year: 'numeric' }); return new Date(dateStr).toLocaleDateString('es-MX', { day: '2-digit', month: 'short', year: 'numeric' });
}; };
const employeeFullName = (assignment: AssetAssignment) => { const assigneeName = (assignment: AssetAssignment) => {
if (assignment.assignee_type?.id === 2) {
return [assignment.external_name, assignment.external_paternal, assignment.external_maternal]
.filter(Boolean).join(' ') || '—';
}
const e = assignment.employee; const e = assignment.employee;
if (!e) return '—'; if (!e) return '—';
return `${e.name} ${e.paternal} ${e.maternal ?? ''}`.trim(); return `${e.name} ${e.paternal} ${e.maternal ?? ''}`.trim();
}; };
const assigneeSubtitle = (assignment: AssetAssignment) => {
if (assignment.assignee_type?.id === 2) return assignment.external_company ?? 'Externo';
return assignment.employee?.department?.name ?? '—';
};
const loadAssignments = async () => { const loadAssignments = async () => {
loading.value = true; loading.value = true;
try { try {
@ -139,7 +148,7 @@ onMounted(loadAssignments);
<thead> <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"> <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">Activo</th> <th class="px-4 py-3">Activo</th>
<th class="px-4 py-3">Empleado</th> <th class="px-4 py-3">Asignado a</th>
<th class="px-4 py-3">Fecha Entrega</th> <th class="px-4 py-3">Fecha Entrega</th>
<th class="px-4 py-3">Fecha Devolucion</th> <th class="px-4 py-3">Fecha Devolucion</th>
<th class="px-4 py-3">Estado</th> <th class="px-4 py-3">Estado</th>
@ -168,10 +177,10 @@ onMounted(loadAssignments);
</td> </td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<p class="font-semibold text-surface-900 dark:text-surface-0"> <p class="font-semibold text-surface-900 dark:text-surface-0">
{{ employeeFullName(assignment) }} {{ assigneeName(assignment) }}
</p> </p>
<p class="text-xs text-surface-500 dark:text-surface-400"> <p class="text-xs text-surface-500 dark:text-surface-400">
{{ assignment.employee?.department?.name ?? '—' }} {{ assigneeSubtitle(assignment) }}
</p> </p>
</td> </td>
<td class="px-4 py-3">{{ formatDate(assignment.assigned_at) }}</td> <td class="px-4 py-3">{{ formatDate(assignment.assigned_at) }}</td>

View File

@ -86,7 +86,14 @@ interface AssetFilters {
export interface AssetAssignment { export interface AssetAssignment {
id: number; id: number;
asset_id: number; asset_id: number;
employee_id: number; employee_id: number | null;
assignee_type: { id: number; name: string };
external_name: string | null;
external_paternal: string | null;
external_maternal: string | null;
external_company: string | null;
external_phone: string | null;
external_email: string | null;
assigned_at: string; assigned_at: string;
returned_at: string | null; returned_at: string | null;
receipt_folio: string | null; receipt_folio: string | null;
@ -133,7 +140,14 @@ export interface UsersResponse {
} }
interface AssignAssetData { interface AssignAssetData {
employee_id: number; assignee_type: number;
employee_id?: number;
external_name?: string;
external_paternal?: string;
external_maternal?: string;
external_company?: string;
external_phone?: string;
external_email?: string;
assigned_at?: string; assigned_at?: string;
receipt_folio?: string; receipt_folio?: string;
authorized_by?: number; authorized_by?: number;

View File

@ -16,7 +16,14 @@ export interface AssignmentEmployeeOption {
export interface FixedAssetAssignmentFormData { export interface FixedAssetAssignmentFormData {
assetId: number | null; assetId: number | null;
assigneeType: 1 | 2;
employeeId: number | null; employeeId: number | null;
externalName: string;
externalPaternal: string;
externalMaternal: string;
externalCompany: string;
externalPhone: string;
externalEmail: string;
authorizedById: number | null; authorizedById: number | null;
deliveredById: number | null; deliveredById: number | null;
assignedAt: string; assignedAt: string;