2025-08-14 15:49:46 -06:00

119 lines
3.3 KiB
Vue

<script setup>
import { ref, watch, computed, onBeforeUnmount } from 'vue';
import Input from '@/Components/Dashboard/Form/Input.vue';
const props = defineProps({
modelValue: {
type: Object,
default: () => ({ start: '', end: '' })
},
required: Boolean,
onError: String,
idStart: { type: String, default: 'startDate' },
idEnd: { type: String, default: 'endDate' },
titleStart: { type: String, default: 'Fecha inicio' },
titleEnd: { type: String, default: 'Fecha fin' },
presets: {
type: Boolean,
default: true
},
debounceMs: {
type: Number,
default: 350
}
});
const emit = defineEmits(['update:modelValue', 'invalid']);
const local = ref({ start: props.modelValue.start, end: props.modelValue.end });
let debounceId = null;
// --- utilidades fechas ---
const fmt = (d) => new Date(d).toISOString().slice(0,10);
const today = fmt(new Date());
const firstDayOfMonth = fmt(new Date(new Date().getFullYear(), new Date().getMonth(), 1));
const daysAgo = (n) => {
const d = new Date(); d.setDate(d.getDate()-n);
return fmt(d);
};
// --- validación ---
const isValid = computed(() => {
const { start, end } = local.value;
if (!start || !end) return !props.required; // si no son obligatorias
return new Date(start) <= new Date(end);
});
// sincroniza si el padre cambia externamente
watch(() => props.modelValue, (nv) => {
if (nv?.start !== local.value.start || nv?.end !== local.value.end) {
local.value = { start: nv?.start || '', end: nv?.end || '' };
}
}, { deep: true });
// emite con debounce solo si es válido
const emitChange = () => {
clearTimeout(debounceId);
debounceId = setTimeout(() => {
if (isValid.value) {
emit('update:modelValue', { ...local.value });
} else {
emit('invalid', { ...local.value });
}
}, props.debounceMs);
};
watch(() => local.value.start, emitChange);
watch(() => local.value.end, emitChange);
// presets
const applyPreset = (type) => {
if (type === 'today') {
local.value = { start: today, end: today };
} else if (type === 'last7') {
local.value = { start: daysAgo(6), end: today }; // 7 días incluyendo hoy
} else if (type === 'month') {
local.value = { start: firstDayOfMonth, end: today };
}
emitChange();
};
onBeforeUnmount(() => clearTimeout(debounceId));
</script>
<template>
<div class="flex flex-wrap items-end gap-3">
<div class="flex space-x-4">
<Input
:id="idStart"
type="date"
:title="titleStart"
v-model="local.start"
:required="required"
:onError="onError"
/>
<Input
:id="idEnd"
type="date"
:title="titleEnd"
v-model="local.end"
:required="required"
:onError="onError"
/>
</div>
<div v-if="presets" class="flex gap-2">
<button type="button" class="px-3 py-2 rounded bg-gray-200 hover:bg-gray-300"
@click="applyPreset('today')">Hoy</button>
<button type="button" class="px-3 py-2 rounded bg-gray-200 hover:bg-gray-300"
@click="applyPreset('last7')">Últimos 7 días</button>
<button type="button" class="px-3 py-2 rounded bg-gray-200 hover:bg-gray-300"
@click="applyPreset('month')">Mes actual</button>
</div>
<p v-if="!isValid" class="text-sm text-red-600 w-full">
La fecha fin debe ser mayor o igual a la fecha inicio.
</p>
</div>
</template>