236 lines
8.0 KiB
Vue
236 lines
8.0 KiB
Vue
<script setup>
|
|
import { ref, watch, computed } from 'vue';
|
|
import VueDatePicker from '@vuepic/vue-datepicker';
|
|
import '@vuepic/vue-datepicker/dist/main.css';
|
|
|
|
import PrimaryButton from '@Holos/Button/Primary.vue';
|
|
import Input from '@Holos/Form/Input.vue';
|
|
import Textarea from '@Holos/Form/Textarea.vue';
|
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
|
|
|
/** Eventos */
|
|
const emit = defineEmits([
|
|
'submit'
|
|
])
|
|
|
|
/** Propiedades */
|
|
const props = defineProps({
|
|
form: Object
|
|
})
|
|
|
|
/** Métodos */
|
|
function submit() {
|
|
emit('submit')
|
|
}
|
|
|
|
function addPeriod() {
|
|
props.form.periods.push({
|
|
start_date: '',
|
|
end_date: '',
|
|
number_of_days: 0,
|
|
});
|
|
}
|
|
|
|
function removePeriod(index) {
|
|
if (props.form.periods.length > 1) {
|
|
props.form.periods.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
function calculateDays(startDate, endDate) {
|
|
if (!startDate || !endDate) return 0;
|
|
|
|
const start = new Date(startDate);
|
|
const end = new Date(endDate);
|
|
|
|
// Validar que la fecha de inicio no sea posterior a la de fin
|
|
if (start > end) return 0;
|
|
|
|
// Calcular la diferencia en días (incluyendo ambos días)
|
|
const timeDiff = end.getTime() - start.getTime();
|
|
const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24)) + 1;
|
|
|
|
return daysDiff;
|
|
}
|
|
|
|
function updatePeriodDays(index) {
|
|
const period = props.form.periods[index];
|
|
if (period.start_date && period.end_date) {
|
|
period.number_of_days = calculateDays(period.start_date, period.end_date);
|
|
} else {
|
|
period.number_of_days = 0;
|
|
}
|
|
}
|
|
|
|
function getMinDate() {
|
|
const today = new Date();
|
|
let daysToAdd = 15;
|
|
let currentDate = new Date(today);
|
|
|
|
while (daysToAdd > 0) {
|
|
currentDate.setDate(currentDate.getDate() + 1);
|
|
// Si no es domingo (0), contar el día
|
|
if (currentDate.getDay() !== 0) {
|
|
daysToAdd--;
|
|
}
|
|
}
|
|
|
|
return currentDate.toISOString().split('T')[0];
|
|
}
|
|
|
|
// Función para desactivar domingos
|
|
function isDisabled(date) {
|
|
return date.getDay() === 0; // 0 = domingo
|
|
}
|
|
|
|
// Función para formatear fecha a Y-m-d
|
|
function formatDate(date) {
|
|
if (!date) return '';
|
|
|
|
// Si la fecha ya es un string en formato correcto, devolverla
|
|
if (typeof date === 'string' && date.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
return date;
|
|
}
|
|
|
|
let d;
|
|
if (date instanceof Date) {
|
|
d = date;
|
|
} else {
|
|
d = new Date(date);
|
|
}
|
|
|
|
// Verificar que la fecha es válida
|
|
if (isNaN(d.getTime())) {
|
|
console.warn('Fecha inválida recibida:', date);
|
|
return '';
|
|
}
|
|
|
|
const year = d.getFullYear();
|
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
// Watcher para detectar cambios en las fechas
|
|
watch(() => props.form.periods, (newPeriods) => {
|
|
newPeriods.forEach((period, index) => {
|
|
if (period.start_date && period.end_date) {
|
|
updatePeriodDays(index);
|
|
}
|
|
});
|
|
}, { deep: true });
|
|
|
|
// Calcular total de días seleccionados
|
|
const totalSelectedDays = computed(() => {
|
|
return props.form.periods.reduce((total, period) => {
|
|
return total + (period.number_of_days || 0);
|
|
}, 0);
|
|
});
|
|
|
|
// Exponer el total para el componente padre
|
|
defineExpose({
|
|
totalSelectedDays
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<form @submit.prevent="submit" class="space-y-6">
|
|
<!-- Períodos de vacaciones -->
|
|
<div class="space-y-4">
|
|
<div v-for="(period, index) in form.periods" :key="index" class="space-y-4">
|
|
<div class="flex items-center justify-between">
|
|
<h4 class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
|
Período {{ index + 1 }}
|
|
</h4>
|
|
<button
|
|
v-if="form.periods.length > 1"
|
|
type="button"
|
|
@click="removePeriod(index)"
|
|
class="flex items-center gap-1 px-2 py-1 text-xs font-medium text-red-600 hover:text-red-700 hover:bg-red-50 rounded-md with-transition dark:text-red-400 dark:hover:bg-red-500/10"
|
|
>
|
|
<GoogleIcon name="delete" class="w-4 h-4" />
|
|
Eliminar
|
|
</button>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<div class="relative">
|
|
<VueDatePicker
|
|
v-model="period.start_date"
|
|
class="w-full"
|
|
placeholder="Fecha de inicio"
|
|
format="dd/MM/yyyy"
|
|
locale="es"
|
|
:min-date="getMinDate()"
|
|
:disabled-dates="isDisabled"
|
|
:enable-time-picker="false"
|
|
:auto-apply="true"
|
|
:close-on-auto-apply="true"
|
|
@update:model-value="(date) => {
|
|
console.log('Fecha seleccionada (inicio):', date);
|
|
period.start_date = formatDate(date);
|
|
console.log('Fecha formateada (inicio):', period.start_date);
|
|
updatePeriodDays(index);
|
|
}"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="relative">
|
|
<VueDatePicker
|
|
v-model="period.end_date"
|
|
class="w-full"
|
|
placeholder="Fecha de fin"
|
|
format="dd/MM/yyyy"
|
|
locale="es"
|
|
:min-date="getMinDate()"
|
|
:disabled-dates="isDisabled"
|
|
:enable-time-picker="false"
|
|
:auto-apply="true"
|
|
:close-on-auto-apply="true"
|
|
@update:model-value="(date) => {
|
|
console.log('Fecha seleccionada (fin):', date);
|
|
period.end_date = formatDate(date);
|
|
console.log('Fecha formateada (fin):', period.end_date);
|
|
updatePeriodDays(index);
|
|
}"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Botón añadir período -->
|
|
<button
|
|
type="button"
|
|
@click="addPeriod"
|
|
class="flex items-center gap-2 px-4 py-2 text-sm font-medium text-blue-600 hover:text-blue-700 hover:bg-blue-50 rounded-lg with-transition dark:text-blue-400 dark:hover:bg-blue-500/10"
|
|
>
|
|
<GoogleIcon name="add" />
|
|
Añadir período
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Comentarios -->
|
|
<div>
|
|
<Textarea
|
|
v-model="form.comments"
|
|
id="comments"
|
|
rows="4"
|
|
class="w-full px-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none dark:bg-slate-700 dark:border-slate-600 dark:text-slate-100"
|
|
placeholder="Agrega cualquier información adicional sobre tu solicitud..."
|
|
:onError="form.errors.comments"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Botón de envío -->
|
|
<div class="col-span-1 md:col-span-2 flex justify-center">
|
|
<PrimaryButton
|
|
v-text="$t('request')"
|
|
:class="{ 'opacity-25': form.processing }"
|
|
:disabled="form.processing"
|
|
/>
|
|
</div>
|
|
</form>
|
|
</template> |