137 lines
6.7 KiB
Vue

<script setup>
import { onMounted, ref } from 'vue';
import { api } from '@Services/Api';
import { getDate } from '@Controllers/DateController';
import { apiTo, viewTo } from './Module'
import GoogleIcon from '@Shared/GoogleIcon.vue';
import PrimaryButton from '@Holos/Button/Primary.vue';
/** Propiedades */
const vacationRequests = ref([]);
/** Metodos */
const getStatusClasses = (status) => {
const statusMap = {
'Pendiente': 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',
'Aprobado': 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
'Rechazado': 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300',
'Agendado': 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',
'Cancelado': 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300',
'Concluido': 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300'
};
return statusMap[status] || 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300';
};
const getStatusIcon = (status) => {
const iconMap = {
'Pendiente': { name: 'schedule', classes: 'text-amber-600 dark:text-amber-400' },
'Aprobado': { name: 'check_circle', classes: 'text-blue-600 dark:text-blue-400' },
'Rechazado': { name: 'cancel', classes: 'text-orange-600 dark:text-orange-400' },
'Agendado': { name: 'event', classes: 'text-green-600 dark:text-green-400' },
'Cancelado': { name: 'block', classes: 'text-red-600 dark:text-red-400' },
'Concluido': { name: 'task_alt', classes: 'text-cyan-600 dark:text-cyan-400' }
};
return iconMap[status] || { name: 'help', classes: 'text-gray-600 dark:text-gray-400' };
};
const getStatusIconContainer = (status) => {
const containerMap = {
'Pendiente': 'bg-amber-100 dark:bg-amber-900/30',
'Aprobado': 'bg-blue-100 dark:bg-blue-900/30',
'Rechazado': 'bg-orange-100 dark:bg-orange-900/30',
'Agendado': 'bg-green-100 dark:bg-green-900/30',
'Cancelado': 'bg-red-100 dark:bg-red-900/30',
'Concluido': 'bg-cyan-100 dark:bg-cyan-900/30'
};
return containerMap[status] || 'bg-gray-100 dark:bg-gray-900/30';
};
/** Ciclos */
onMounted(() => {
api.get(apiTo('index'), {
onSuccess: (r) => {
vacationRequests.value = r.vacation_requests.data;
}
});
});
</script>
<template>
<div>
<!-- Header -->
<div class="flex items-center justify-between mb-8">
<div>
<h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100">
Mis Solicitudes
</h1>
<p class="text-slate-600 dark:text-slate-400">Historial completo de todas tus solicitudes de vacaciones.</p>
</div>
<RouterLink :to="viewTo({ name: 'create' })">
<PrimaryButton
type="button"
class="flex items-center gap-2"
>
<GoogleIcon name="calendar_month" />
Nueva Solicitud
</PrimaryButton>
</RouterLink>
</div>
<!-- Lista de solicitudes -->
<div class="space-y-4">
<div v-for="vacation in vacationRequests" class="bg-white rounded-xl border border-slate-200 p-6 shadow-sm dark:bg-slate-800 dark:border-slate-700">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-3">
<div :class="`flex items-center justify-center w-10 h-10 rounded-lg ${getStatusIconContainer(vacation.status)}`">
<GoogleIcon :name="getStatusIcon(vacation.status).name" :class="getStatusIcon(vacation.status).classes" />
</div>
<div>
<h3 v-if="vacation.vacation_periods.length > 1" class="font-semibold text-slate-900 dark:text-slate-100">{{ vacation.vacation_periods.length }} periodos</h3>
<h3 v-else class="font-semibold text-slate-900 dark:text-slate-100">{{ getDate(vacation.vacation_periods[0].start_date) }} - {{ getDate(vacation.vacation_periods[0].end_date) }}</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">{{ vacation.total_days }} días</p>
</div>
</div>
<span :class="`px-3 py-1 rounded-full text-sm font-medium ${getStatusClasses(vacation.status)}`">
{{ vacation.status }}
</span>
</div>
<div v-for="(period, index) in vacation.vacation_periods" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<p class="text-sm font-medium text-slate-700 dark:text-slate-300">Período {{ index + 1 }}:</p>
<p class="text-sm text-slate-600 dark:text-slate-400">{{ getDate(period.start_date) }} - {{ getDate(period.end_date) }} ({{ period.number_of_days }} días)</p>
</div>
</div>
<div class="flex justify-end gap-3">
<button class="flex items-center gap-2 px-4 py-2 text-sm font-medium text-slate-600 hover:text-slate-800 hover:bg-slate-100 rounded-lg with-transition dark:text-slate-400 dark:hover:text-slate-200 dark:hover:bg-slate-700">
<GoogleIcon name="visibility" />
Ver detalles
</button>
<button class="flex items-center gap-2 px-4 py-2 text-sm font-medium text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg with-transition dark:text-red-400 dark:hover:bg-red-900/20">
<GoogleIcon name="cancel" />
Cancelar
</button>
</div>
</div>
<!-- Estado vacío cuando no hay solicitudes -->
<div v-if="vacationRequests.length == 0" class="text-center py-12">
<GoogleIcon name="event_busy" class="text-slate-300 text-6xl mb-4 dark:text-slate-600" />
<h3 class="text-lg font-medium text-slate-900 dark:text-slate-100 mb-2">No tienes más solicitudes</h3>
<p class="text-slate-600 dark:text-slate-400 mb-6">Cuando realices nuevas solicitudes de vacaciones aparecerán aquí.</p>
<RouterLink :to="viewTo({ name: 'create' })">
<PrimaryButton>
<GoogleIcon name="add" class="mr-2" />
Nueva Solicitud
</PrimaryButton>
</RouterLink>
</div>
</div>
</div>
</template>