215 lines
6.7 KiB
Vue
215 lines
6.7 KiB
Vue
<script setup>
|
|
import { ref, computed, onMounted, watch } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { api, useForm } from '@Services/Api';
|
|
import { apiTo, viewTo } from './Module';
|
|
|
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
|
import Input from '@Holos/Form/Input.vue';
|
|
import PageHeader from '@Holos/PageHeader.vue';
|
|
import IconButton from '@Holos/Button/Icon.vue';
|
|
|
|
/** Definiciones */
|
|
const router = useRouter();
|
|
const vroute = useRoute();
|
|
|
|
/** Propiedades */
|
|
const form = useForm({
|
|
users: [],
|
|
start_date: '',
|
|
end_date: '',
|
|
exam_date: '',
|
|
});
|
|
|
|
const course = ref(null);
|
|
const users = ref([]);
|
|
|
|
// Computed para contar usuarios seleccionados
|
|
const selectedUsersCount = computed(() => {
|
|
return users.value.filter(u => u.selected).length;
|
|
});
|
|
|
|
// Funciones
|
|
const toggleUser = (user) => {
|
|
user.selected = !user.selected;
|
|
};
|
|
|
|
const selectAllUsers = () => {
|
|
const allSelected = users.value.every(u => u.selected);
|
|
users.value.forEach(u => u.selected = !allSelected);
|
|
};
|
|
|
|
// Función para calcular la fecha final del curso
|
|
const calculateEndDate = () => {
|
|
if (form.start_date && course.value?.duration) {
|
|
const startDate = new Date(form.start_date);
|
|
const duration = parseInt(course.value.duration);
|
|
|
|
// Agregar la duración en días a la fecha de inicio
|
|
const endDate = new Date(startDate);
|
|
endDate.setDate(startDate.getDate() + duration);
|
|
|
|
// Formatear la fecha como YYYY-MM-DD para el input
|
|
const formattedDate = endDate.toISOString().split('T')[0];
|
|
form.end_date = formattedDate;
|
|
}
|
|
};
|
|
|
|
// Watcher para calcular automáticamente la fecha final cuando cambie la fecha de inicio
|
|
watch(() => form.start_date, () => {
|
|
calculateEndDate();
|
|
});
|
|
|
|
const submit = () => {
|
|
form.transform(data => ({
|
|
...data,
|
|
users: users.value.filter(u => u.selected).map(u => u.id)
|
|
})).post(apiTo('assign-course', { course: vroute.params.id }), {
|
|
onSuccess: () => {
|
|
Notify.success(Lang('register.assign.onSuccess'))
|
|
router.push(viewTo({ name: 'index' }));
|
|
}
|
|
});
|
|
};
|
|
|
|
/** Ciclos */
|
|
onMounted(() => {
|
|
api.get(apiTo('show', { course: vroute.params.id }), {
|
|
onSuccess: (r) => {
|
|
course.value = r.course
|
|
users.value = r.course.users ?? []
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<PageHeader
|
|
title="Asignación de Cursos a Personal"
|
|
>
|
|
<RouterLink :to="viewTo({ name: 'index' })">
|
|
<IconButton
|
|
class="text-white"
|
|
icon="arrow_back"
|
|
:title="$t('return')"
|
|
filled
|
|
/>
|
|
</RouterLink>
|
|
</PageHeader>
|
|
|
|
<div class="w-full pb-2">
|
|
<p class="text-justify text-sm">Asigna cursos aprobados al personal seleccionado</p>
|
|
</div>
|
|
|
|
<!-- Card principal -->
|
|
<section class="mt-6 bg-white rounded-lg shadow-sm p-6 dark:bg-primary-d dark:border-primary/20 dark:text-primary-dt">
|
|
|
|
<!-- Información del Curso -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-primary-dt mb-2">
|
|
Curso Seleccionado
|
|
</label>
|
|
|
|
<div v-if="course" class="w-full px-4 py-3 bg-gray-50 border border-gray-300 rounded-lg dark:bg-primary-d dark:border-primary/20">
|
|
<div class="font-medium text-gray-900 dark:text-primary-dt">{{ course.name }}</div>
|
|
<div class="text-sm text-gray-500 dark:text-primary-dt/70">
|
|
{{ course.department?.name }} • {{ course.cost }} {{ course.cost_currency }}
|
|
</div>
|
|
<div v-if="course.description" class="text-sm text-gray-600 dark:text-primary-dt/80 mt-1">
|
|
{{ course.description }}
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="w-full px-4 py-3 bg-gray-50 border border-gray-300 rounded-lg dark:bg-primary-d dark:border-primary/20">
|
|
<div class="text-gray-500 dark:text-primary-dt/70">Cargando información del curso...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Asignación de Usuarios -->
|
|
<div class="mb-6">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 dark:text-primary-dt">
|
|
Asignar a usuarios
|
|
</label>
|
|
<button
|
|
type="button"
|
|
@click="selectAllUsers"
|
|
class="text-sm text-[#2563eb] hover:text-[#1e40af] font-medium"
|
|
>
|
|
{{ users.every(u => u.selected) ? 'Deseleccionar todo' : 'Seleccionar todo' }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="border border-gray-300 rounded-lg p-4 max-h-48 overflow-y-auto dark:bg-primary-d dark:border-primary/20">
|
|
<div class="space-y-2">
|
|
<div
|
|
v-for="user in users"
|
|
:key="user.id"
|
|
@click="toggleUser(user)"
|
|
class="flex items-center p-2 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-primary/10 transition-colors"
|
|
>
|
|
<div class="flex items-center h-5">
|
|
<input
|
|
:checked="user.selected"
|
|
type="checkbox"
|
|
class="w-4 h-4 text-[#2563eb] bg-gray-100 border-gray-300 rounded focus:ring-[#2563eb] dark:focus:ring-[#2563eb] dark:ring-offset-gray-800 focus:ring-2 dark:bg-primary-d dark:border-primary/20"
|
|
@click.stop
|
|
@change="toggleUser(user)"
|
|
>
|
|
</div>
|
|
<div class="ml-3 text-sm">
|
|
<label class="font-medium text-gray-900 dark:text-primary-dt cursor-pointer">
|
|
{{ user.name }} {{ user.paternal }} {{ user.maternal }}
|
|
</label>
|
|
<div class="text-xs text-gray-500 dark:text-primary-dt/70">
|
|
{{ user.email }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-2 text-sm text-gray-500 dark:text-primary-dt/70">
|
|
{{ selectedUsersCount }} usuario(s) seleccionado(s)
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fechas -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<Input
|
|
v-model="form.start_date"
|
|
id="start_date"
|
|
title="dates.start"
|
|
type="date"
|
|
required
|
|
/>
|
|
<Input
|
|
v-model="form.end_date"
|
|
id="end_date"
|
|
title="dates.end"
|
|
type="date"
|
|
required
|
|
/>
|
|
<Input
|
|
v-model="form.exam_date"
|
|
id="exam_date"
|
|
title="exam_date"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Botones de Acción -->
|
|
<div class="flex items-center justify-end gap-3 pt-4 border-t border-gray-100 dark:border-primary/20">
|
|
|
|
<button
|
|
@click="submit"
|
|
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[#7c3aed] hover:bg-[#6d28d9] text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-[#7c3aed] focus:ring-offset-2"
|
|
>
|
|
<GoogleIcon class="text-white text-xl" name="notifications" />
|
|
Notificar
|
|
</button>
|
|
</div>
|
|
|
|
</section>
|
|
</template>
|