WIP
This commit is contained in:
parent
d12fa85d73
commit
7ff8bc642e
|
@ -0,0 +1,123 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hour: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['onHourEntryDeleted']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
|
||||||
|
const directionIconTooltip = computed(() => {
|
||||||
|
const tooltipDictionary = {
|
||||||
|
in: t('Entrada'),
|
||||||
|
out: t('Salida'),
|
||||||
|
};
|
||||||
|
return tooltipDictionary[$props.direction] || null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const directionIconName = computed(() => {
|
||||||
|
const tooltipDictionary = {
|
||||||
|
in: 'arrow_forward',
|
||||||
|
out: 'arrow_back',
|
||||||
|
};
|
||||||
|
return tooltipDictionary[$props.direction] || null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteHourEntry = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
`WorkerTimeControls/${$props.id}/deleteTimeEntry`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!data) return;
|
||||||
|
emit('onHourEntryDeleted');
|
||||||
|
notify('Entry removed', 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting hour entry');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<QIcon class="direction-icon" :name="directionIconName" size="sm">
|
||||||
|
<QTooltip>{{ directionIconTooltip }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QBadge rounded class="chip">
|
||||||
|
<QIcon name="edit" size="sm" class="fill-icon">
|
||||||
|
<QTooltip>{{ t('Edit') }}</QTooltip></QIcon
|
||||||
|
>
|
||||||
|
<span class="q-px-sm text-subtitle2 text-weight-regular">{{ hour }}</span>
|
||||||
|
<QIcon
|
||||||
|
name="cancel"
|
||||||
|
class="remove-icon"
|
||||||
|
size="sm"
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('This time entry will be deleted'),
|
||||||
|
t('Are you sure you want to delete this entry?'),
|
||||||
|
deleteHourEntry
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</QBadge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.chip {
|
||||||
|
height: 28px;
|
||||||
|
color: var(--vn-dark);
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $primary;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.direction-icon {
|
||||||
|
color: var(--vn-label);
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Entrada: Entrada
|
||||||
|
Salida: Salida
|
||||||
|
Edit: Editar
|
||||||
|
This time entry will be deleted: Se eliminará la hora fichada
|
||||||
|
Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla?
|
||||||
|
Entry removed: Fichada borrada
|
||||||
|
</i18n>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
export function useVnConfirm() {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const openConfirmationModal = (title, message, promise, successFn) => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
promise: promise,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
if (successFn) successFn();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openConfirmationModal };
|
||||||
|
}
|
|
@ -97,6 +97,10 @@ select:-webkit-autofill {
|
||||||
background-color: var(--vn-light-gray);
|
background-color: var(--vn-light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fill-icon {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Estilo para el asterisco en campos requeridos */
|
/* Estilo para el asterisco en campos requeridos */
|
||||||
.q-field.required .q-field__label:after {
|
.q-field.required .q-field__label:after {
|
||||||
content: ' *';
|
content: ' *';
|
||||||
|
|
|
@ -1,121 +1,404 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { onMounted, ref, computed, onBeforeMount } from 'vue';
|
import { onMounted, ref, computed, onBeforeMount, nextTick } from 'vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import WorkerTimeHourChip from 'components/WorkerTimeHourChip.vue';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRole } from 'src/composables/useRole';
|
import { useRole } from 'src/composables/useRole';
|
||||||
import { useWeekdayStore } from 'src/stores/useWeekdayStore';
|
import { useWeekdayStore } from 'src/stores/useWeekdayStore';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const { t } = useI18n();
|
|
||||||
// const { notify } = useNotify();
|
|
||||||
const { hasAny } = useRole();
|
|
||||||
const state = useState();
|
|
||||||
const user = state.getUser();
|
|
||||||
const stateStore = useStateStore();
|
|
||||||
const weekdayStore = useWeekdayStore();
|
|
||||||
const weekDays = ref([]);
|
|
||||||
// const entryDirections = [
|
// const entryDirections = [
|
||||||
// { code: 'in', description: t('Entrada') },
|
// { code: 'in', description: t('Entrada') },
|
||||||
// { code: 'middle', description: t('Intermedio') },
|
// { code: 'middle', description: t('Intermedio') },
|
||||||
// { code: 'out', description: t('Salida') },
|
// { code: 'out', description: t('Salida') },
|
||||||
// ];
|
// ];
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const { hasAny } = useRole();
|
||||||
|
const _state = useState();
|
||||||
|
const user = _state.getUser();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const weekdayStore = useWeekdayStore();
|
||||||
|
const weekDays = ref([]);
|
||||||
|
|
||||||
const isHr = computed(() => hasAny(['hr']));
|
const isHr = computed(() => hasAny(['hr']));
|
||||||
|
|
||||||
const isHimSelf = computed(() => user.value.id === route.params.id);
|
const isHimSelf = computed(() => user.value.id === route.params.id);
|
||||||
|
|
||||||
const columns = computed(() => {
|
const columns = computed(() => {
|
||||||
return weekdayStore.localeWeekdays?.map((day) => {
|
return weekdayStore.localeWeekdays?.map((day, index) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
label: day.locale,
|
label: day.locale,
|
||||||
|
formattedDate: getHeaderFormattedDate(weekDays.value[index]?.dated),
|
||||||
name: day.name,
|
name: day.name,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
colIndex: index,
|
||||||
|
dayData: weekDays.value[index],
|
||||||
};
|
};
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchWorkerTimeControlRef = ref(null);
|
const workerHoursRef = ref(null);
|
||||||
const calendarRef = ref(null);
|
const calendarRef = ref(null);
|
||||||
|
const selectedDate = ref(null);
|
||||||
// Dates formateadas para bindear al componente QDate
|
// Dates formateadas para bindear al componente QDate
|
||||||
const selectedCalendarDates = ref([]);
|
const selectedCalendarDates = ref([]);
|
||||||
const startOfWeek = ref(null);
|
const startOfWeek = ref(null);
|
||||||
const endOfWeek = ref(null);
|
const endOfWeek = ref(null);
|
||||||
|
const selectedWeekNumber = ref(null);
|
||||||
|
const state = ref(null);
|
||||||
|
const reason = ref(null);
|
||||||
|
const canResend = ref(null);
|
||||||
|
const weekTotalHours = ref(null);
|
||||||
|
const workerTimeControlMails = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => setDate(Date.vnNew()));
|
||||||
|
|
||||||
|
const getHeaderFormattedDate = (date) => {
|
||||||
|
//TODO:: Ver si se puede hacer una funcion reutilizable o complementar a utils de dates
|
||||||
|
const newDate = new Date(date);
|
||||||
|
const day = String(newDate.getDate()).padStart(2, '0');
|
||||||
|
const monthName = newDate.toLocaleString('es-ES', { month: 'long' });
|
||||||
|
return `${day} ${monthName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getChipFormattedHour = (date) => {
|
||||||
|
//TODO:: Ver si se puede hacer una funcion reutilizable o complementar a utils de dates
|
||||||
|
const newDate = new Date(date);
|
||||||
|
const hour = newDate.getHours();
|
||||||
|
const min = newDate.getMinutes();
|
||||||
|
return `${hour < 10 ? '0' + hour : hour}:${min < 10 ? '0' + min : min}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatHours = (timestamp = 0) => {
|
||||||
|
//TODO:: Ver si se puede hacer una funcion reutilizable o complementar a utils de dates
|
||||||
|
let hour = Math.floor(timestamp / 3600);
|
||||||
|
let min = Math.floor(timestamp / 60 - 60 * hour);
|
||||||
|
|
||||||
|
if (hour < 10) hour = `0${hour}`;
|
||||||
|
if (min < 10) min = `0${min}`;
|
||||||
|
|
||||||
|
return `${hour}:${min}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formattedWeekTotalHours = computed(() => formatHours(weekTotalHours.value));
|
||||||
|
|
||||||
const defaultDate = computed(() => {
|
const defaultDate = computed(() => {
|
||||||
const date = Date.vnNew();
|
const date = Date.vnNew();
|
||||||
return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}`;
|
return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleDateSelection = async (dates, _, selectedDateDetails) => {
|
const onInputChange = async (dates, _, selectedDateDetails) => {
|
||||||
if (!dates.length || !selectedDateDetails) return;
|
if (!dates.length || !selectedDateDetails) return;
|
||||||
|
|
||||||
const { year, month, day } = selectedDateDetails;
|
const { year, month, day } = selectedDateDetails;
|
||||||
const selectedDate = new Date(year, month - 1, day);
|
const date = new Date(year, month - 1, day);
|
||||||
|
setDate(date);
|
||||||
startOfWeek.value = getStartOfWeek(selectedDate);
|
|
||||||
endOfWeek.value = getEndOfWeek(startOfWeek.value);
|
|
||||||
|
|
||||||
getWeekDates(startOfWeek.value, endOfWeek.value);
|
|
||||||
|
|
||||||
await fetchWorkerTimeControl();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setDate = async (date) => {
|
||||||
|
if (!date) return;
|
||||||
|
selectedDate.value = date;
|
||||||
|
selectedDate.value.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const newStartOfWeek = getStartOfWeek(selectedDate.value); // Obtener el día de inicio de la semana a partir de la fecha seleccionada
|
||||||
|
const newEndOfWeek = getEndOfWeek(newStartOfWeek); // Obtener el fin de la semana a partir de la fecha de inicio de la semana
|
||||||
|
|
||||||
|
startOfWeek.value = newStartOfWeek;
|
||||||
|
endOfWeek.value = newEndOfWeek;
|
||||||
|
selectedWeekNumber.value = getWeekNumber(newStartOfWeek); // Asignar el número de la semana
|
||||||
|
console.log('selectedWeekNumber:: ', selectedWeekNumber.value);
|
||||||
|
|
||||||
|
getWeekDates(newStartOfWeek, newEndOfWeek);
|
||||||
|
|
||||||
|
await nextTick(); // Esperar actualización del DOM y luego fetchear data necesaria
|
||||||
|
await fetchHours();
|
||||||
|
await fetchWeekData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Función para obtener el inicio de la semana a partir de una fecha
|
||||||
const getStartOfWeek = (selectedDate) => {
|
const getStartOfWeek = (selectedDate) => {
|
||||||
const dayOfWeek = selectedDate.getDay();
|
const dayOfWeek = selectedDate.getDay(); // Obtener el día de la semana de la fecha seleccionada
|
||||||
const startOfWeek = new Date(selectedDate);
|
const startOfWeek = new Date(selectedDate);
|
||||||
startOfWeek.setDate(selectedDate.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1));
|
startOfWeek.setDate(selectedDate.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1)); // Calcular el inicio de la semana
|
||||||
return startOfWeek;
|
return startOfWeek;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Función para obtener el fin de la semana a partir del inicio de la semana
|
||||||
const getEndOfWeek = (startOfWeek) => {
|
const getEndOfWeek = (startOfWeek) => {
|
||||||
const endOfWeek = new Date(startOfWeek);
|
const endOfWeek = new Date(startOfWeek);
|
||||||
endOfWeek.setDate(startOfWeek.getDate() + 6);
|
endOfWeek.setHours(23, 59, 59, 59);
|
||||||
|
endOfWeek.setDate(startOfWeek.getDate() + 6); // Calcular el fin de la semana sumando 6 días al inicio de la semana
|
||||||
return endOfWeek;
|
return endOfWeek;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Función para obtener las fechas de la semana seleccionada
|
||||||
const getWeekDates = (startOfWeek, endOfWeek) => {
|
const getWeekDates = (startOfWeek, endOfWeek) => {
|
||||||
selectedCalendarDates.value = [];
|
selectedCalendarDates.value = []; // Limpiar las fechas seleccionadas previamente usadas por QDate
|
||||||
|
weekDays.value = []; // Limpiar la información de las fechas seleccionadas previamente
|
||||||
let currentDate = new Date(startOfWeek);
|
let currentDate = new Date(startOfWeek);
|
||||||
|
|
||||||
while (currentDate <= endOfWeek) {
|
while (currentDate <= endOfWeek) {
|
||||||
selectedCalendarDates.value.push(formatDate(currentDate));
|
// Iterar sobre los días de la semana
|
||||||
weekDays.value.push({
|
selectedCalendarDates.value.push(formatDate(currentDate)); // Agregar fecha formateada para el array de fechas bindeado al componente QDate
|
||||||
dated: new Date(currentDate.getTime()),
|
weekDays.value.push({ dated: new Date(currentDate.getTime()) }); // Agregar el día de la semana al array información de días de la semana
|
||||||
});
|
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1)); // Avanzar al siguiente día
|
||||||
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Función para Convertir la fecha al formato que acepta el componente QDate: '2001-01-01'
|
||||||
const formatDate = (date) => {
|
const formatDate = (date) => {
|
||||||
return date.toISOString().slice(0, 10);
|
return date.toISOString().slice(0, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
const workerTimeControlsFilter = computed(() => ({
|
const workerHoursFilter = computed(() => ({
|
||||||
where: {
|
where: {
|
||||||
and: [{ timed: { gte: startOfWeek.value } }, { timed: { lte: endOfWeek.value } }],
|
and: [{ timed: { gte: startOfWeek.value } }, { timed: { lte: endOfWeek.value } }],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const fetchWorkerTimeControl = async () => {
|
const getWeekNumber = (date) => {
|
||||||
|
if (!date) return;
|
||||||
|
const startOfYear = new Date(date.getFullYear(), 0, 1);
|
||||||
|
const dayOfWeek = startOfYear.getDay();
|
||||||
|
const offset = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
||||||
|
const firstWeekMilliseconds = startOfYear.getTime() - offset * 24 * 60 * 60 * 1000;
|
||||||
|
const dateMilliseconds = date.getTime();
|
||||||
|
const weekNumber =
|
||||||
|
Math.floor(
|
||||||
|
(dateMilliseconds - firstWeekMilliseconds) / (7 * 24 * 60 * 60 * 1000)
|
||||||
|
) + 1;
|
||||||
|
return weekNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWorkedHours = async (from, to) => {
|
||||||
|
weekTotalHours.value = null;
|
||||||
|
let _weekTotalHours = 0;
|
||||||
|
let params = {
|
||||||
|
from: from,
|
||||||
|
id: route.params.id,
|
||||||
|
to: to,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await axios.get(`Workers/${route.params.id}/getWorkedHours`, {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
const workDays = data;
|
||||||
|
const map = new Map();
|
||||||
|
|
||||||
|
for (const workDay of workDays) {
|
||||||
|
workDay.dated = new Date(workDay.dated);
|
||||||
|
map.set(workDay.dated, workDay);
|
||||||
|
_weekTotalHours += workDay.workedHours;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const weekDay of weekDays.value) {
|
||||||
|
const workDay = workDays.find((day) => {
|
||||||
|
let from = new Date(day.dated);
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
let to = new Date(day.dated);
|
||||||
|
to.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
return weekDay.dated >= from && weekDay.dated <= to;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (workDay) {
|
||||||
|
weekDay.expectedHours = workDay.expectedHours;
|
||||||
|
weekDay.workedHours = workDay.workedHours;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
weekTotalHours.value = _weekTotalHours;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAbsences = async () => {
|
||||||
|
const params = {
|
||||||
|
workerFk: route.params.id,
|
||||||
|
businessFk: null,
|
||||||
|
year: startOfWeek.value.getFullYear(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await axios.get('Calendars/absences', { params });
|
||||||
|
console.log('absenses:: ', data);
|
||||||
|
if (data) addEvents(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasEvents = (day) => {
|
||||||
|
return day >= startOfWeek.value && day < endOfWeek.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addEvents = (data) => {
|
||||||
|
console.log('ADD EVENTS:: ');
|
||||||
|
const events = {};
|
||||||
|
|
||||||
|
const addEvent = (day, event) => {
|
||||||
|
events[new Date(day).getTime()] = event;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.holidays) {
|
||||||
|
data.holidays.forEach((holiday) => {
|
||||||
|
const holidayDetail = holiday.detail && holiday.detail.description;
|
||||||
|
const holidayType = holiday.type && holiday.type.name;
|
||||||
|
const holidayName = holidayDetail || holidayType;
|
||||||
|
|
||||||
|
addEvent(holiday.dated, {
|
||||||
|
name: holidayName,
|
||||||
|
color: '#ff0',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.absences) {
|
||||||
|
data.absences.forEach((absence) => {
|
||||||
|
const type = absence.absenceType;
|
||||||
|
addEvent(absence.dated, {
|
||||||
|
name: type.name,
|
||||||
|
color: type.rgb,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
weekDays.value.forEach((day) => {
|
||||||
|
const timestamp = day.dated.getTime();
|
||||||
|
if (events[timestamp]) day.event = events[timestamp];
|
||||||
|
});
|
||||||
|
console.log('weekdays after events:: ', weekDays.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchHours = async () => {
|
||||||
try {
|
try {
|
||||||
await fetchWorkerTimeControlRef.value.fetch();
|
await workerHoursRef.value.fetch();
|
||||||
|
await getWorkedHours(startOfWeek.value, endOfWeek.value);
|
||||||
|
await getAbsences();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching worker time control');
|
console.error('Error fetching worker hours');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setWorkerTimeControls = (data) => {
|
const fetchWorkerTimeControlMails = async (filter) => {
|
||||||
console.log('worker time controls:: ', data);
|
try {
|
||||||
|
const { data } = await axios.get('WorkerTimeControlMails', {
|
||||||
|
params: { filter: JSON.stringify(filter) },
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching worker time control mails');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('columns:: ', columns.value);
|
const fetchWeekData = async () => {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
workerFk: route.params.id,
|
||||||
|
year: selectedDate.value ? selectedDate.value?.getFullYear() : null,
|
||||||
|
week: getWeekNumber(selectedDate.value),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await fetchWorkerTimeControlMails(filter);
|
||||||
|
|
||||||
|
if (!data.length) {
|
||||||
|
state.value = null;
|
||||||
|
} else {
|
||||||
|
const [mail] = data;
|
||||||
|
state.value = mail.state;
|
||||||
|
reason.value.value = mail.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
await canBeResend();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching week data');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const canBeResend = async () => {
|
||||||
|
canResend.value = false;
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
year: selectedDate.value.getFullYear(),
|
||||||
|
week: getWeekNumber(selectedDate.value),
|
||||||
|
},
|
||||||
|
limit: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await fetchWorkerTimeControlMails(filter);
|
||||||
|
|
||||||
|
if (data.length) canResend.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setHours = (data) => {
|
||||||
|
for (const weekDay of weekDays.value) {
|
||||||
|
if (data) {
|
||||||
|
let day = weekDay.dated.getDay();
|
||||||
|
weekDay.hours = data
|
||||||
|
.filter((hour) => new Date(hour.timed).getDay() == day)
|
||||||
|
.sort((a, b) => new Date(a.timed) - new Date(b.timed));
|
||||||
|
} else weekDay.hours = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getFinishTime = () => {
|
||||||
|
if (!weekDays.value || weekDays.value.length === 0) return;
|
||||||
|
|
||||||
|
let today = Date.vnNew();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
let todayInWeek = weekDays.value.find(
|
||||||
|
(day) => day.dated.getTime() === today.getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) {
|
||||||
|
const remainingTime = todayInWeek.workedHours
|
||||||
|
? (todayInWeek.expectedHours - todayInWeek.workedHours) * 1000
|
||||||
|
: null;
|
||||||
|
const lastKnownEntry = todayInWeek.hours[todayInWeek.hours.length - 1];
|
||||||
|
const lastKnownTime = new Date(lastKnownEntry.timed).getTime();
|
||||||
|
const finishTimeStamp =
|
||||||
|
lastKnownTime && remainingTime ? lastKnownTime + remainingTime : null;
|
||||||
|
|
||||||
|
if (finishTimeStamp) {
|
||||||
|
let finishDate = new Date(finishTimeStamp);
|
||||||
|
let hour = finishDate.getHours();
|
||||||
|
let minute = finishDate.getMinutes();
|
||||||
|
|
||||||
|
if (hour < 10) hour = `0${hour}`;
|
||||||
|
if (minute < 10) minute = `0${minute}`;
|
||||||
|
return `${hour}:${minute} h.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onHourEntryDeleted = async () => {
|
||||||
|
await fetchHours();
|
||||||
|
await getMailStates(selectedDate.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMailStates = async (date) => {
|
||||||
|
const params = {
|
||||||
|
month: date.getMonth() + 1,
|
||||||
|
year: date.getFullYear(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`WorkerTimeControls/${route.params.id}/getMailStates`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
workerTimeControlMails.value = data;
|
||||||
|
console.log('workerTimeControlMails: ', workerTimeControlMails.value);
|
||||||
|
// await repaint();
|
||||||
|
};
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
weekdayStore.initStore();
|
weekdayStore.initStore();
|
||||||
|
@ -129,40 +412,149 @@ onMounted(async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
ref="fetchWorkerTimeControlRef"
|
ref="workerHoursRef"
|
||||||
url="WorkerTimeControls/filter"
|
url="WorkerTimeControls/filter"
|
||||||
:filter="workerTimeControlsFilter"
|
:filter="workerHoursFilter"
|
||||||
:params="{
|
:params="{
|
||||||
workerFk: route.params.id,
|
workerFk: route.params.id,
|
||||||
}"
|
}"
|
||||||
@on-fetch="(data) => setWorkerTimeControls(data)"
|
@on-fetch="(data) => setHours(data)"
|
||||||
/>
|
/>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="300" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="260" class="q-pa-md">
|
||||||
|
<div class="q-pa-md q-mb-md" style="border: 2px solid black">
|
||||||
|
<QCardSection horizontal>
|
||||||
|
<span class="text-weight-bold text-subtitle1 text-center full-width">
|
||||||
|
{{ t('Hours') }}
|
||||||
|
</span>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardSection class="column items-center" horizontal>
|
||||||
|
<div>
|
||||||
|
<span class="details-label">{{ t('Total semana') }} </span>
|
||||||
|
<span>: {{ formattedWeekTotalHours }} h.</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="details-label">{{ t('Termina a las') }}:</span>
|
||||||
|
<span>{{ dashIfEmpty(getFinishTime()) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span></span>
|
||||||
|
</QCardSection>
|
||||||
|
</div>
|
||||||
<QDate
|
<QDate
|
||||||
ref="calendarRef"
|
ref="calendarRef"
|
||||||
:model-value="selectedCalendarDates"
|
:model-value="selectedCalendarDates"
|
||||||
@update:model-value="handleDateSelection"
|
@update:model-value="onInputChange"
|
||||||
mask="YYYY-MM-DD"
|
mask="YYYY-MM-DD"
|
||||||
color="primary"
|
color="primary"
|
||||||
minimal
|
minimal
|
||||||
multiple
|
multiple
|
||||||
bordered
|
bordered
|
||||||
|
dense
|
||||||
:default-year-month="defaultDate"
|
:default-year-month="defaultDate"
|
||||||
/>
|
/>
|
||||||
<pre>date range: {{ selectedDates }}</pre>
|
<pre>date range: {{ selectedCalendarDates }}</pre>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
<QCard class="full-width">
|
<QCard class="full-width">
|
||||||
<QTable :columns="columns"> </QTable>
|
<QTable :columns="columns" :rows="['']" hide-bottom>
|
||||||
|
<template #header="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<QTh
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
|
auto-width
|
||||||
|
>
|
||||||
|
<div class="column-title-container">
|
||||||
|
<span class="text-primary">{{ t(col.label) }}</span>
|
||||||
|
<span>{{ t(col.formattedDate) }}</span>
|
||||||
|
</div>
|
||||||
|
</QTh>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
<template #body="props">
|
||||||
|
<QTr>
|
||||||
|
<QTd
|
||||||
|
v-for="(day, index) in props.cols"
|
||||||
|
:key="index"
|
||||||
|
style="padding: 20px 0 20px 0 !important"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(hour, index) in day.dayData?.hours"
|
||||||
|
:key="index"
|
||||||
|
:props="props"
|
||||||
|
class="hour-chip"
|
||||||
|
>
|
||||||
|
<WorkerTimeHourChip
|
||||||
|
:hour="getChipFormattedHour(hour.timed)"
|
||||||
|
:direction="hour.direction"
|
||||||
|
:id="hour.id"
|
||||||
|
@on-hour-entry-deleted="onHourEntryDeleted()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
<QTr>
|
||||||
|
<QTd v-for="(day, index) in props.cols" :key="index">
|
||||||
|
<div class="column items-center justify-center">
|
||||||
|
<span class="q-mb-md text-sm text-body1">
|
||||||
|
{{ formatHours(day.dayData?.workedHours) }} h.
|
||||||
|
</span>
|
||||||
|
<QIcon
|
||||||
|
name="add_circle"
|
||||||
|
color="primary"
|
||||||
|
class="fill-icon cursor-pointer"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Add time') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
<pre>{{ columns }}</pre>
|
||||||
|
<!-- <pre>{{ weekDays }}</pre> -->
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style lang="scss">
|
||||||
|
.column-title-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-label {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-date,
|
||||||
|
.q-date__content,
|
||||||
|
.q-date__main {
|
||||||
|
max-width: 228px !important;
|
||||||
|
min-width: 228px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hour-chip {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Entrada: Entrada
|
Entrada: Entrada
|
||||||
Intermedio: Intermedio
|
Intermedio: Intermedio
|
||||||
Salida: Salida
|
Salida: Salida
|
||||||
|
Hours: Horas
|
||||||
|
Total semana: Total semana
|
||||||
|
Termina a las: Termina a las
|
||||||
|
Add time: Añadir hora
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
Loading…
Reference in New Issue