Compare commits
No commits in common. "vacations" and "main" have entirely different histories.
@ -1,150 +1,147 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { api } from '@Services/Api';
|
|
||||||
import { apiTo } from './Module';
|
|
||||||
|
|
||||||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||||||
import VueApexCharts from 'vue3-apexcharts';
|
import VueApexCharts from 'vue3-apexcharts';
|
||||||
|
|
||||||
// Propiedades
|
// Props
|
||||||
const adminInfo = ref({});
|
const props = defineProps({
|
||||||
const vacationsCharts = ref({});
|
adminInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
charts: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Opciones para gráfica de solicitudes por mes (línea)
|
// Datos procesados para los charts
|
||||||
|
const chartData = computed(() => {
|
||||||
|
// Datos para el gráfico de línea (Solicitudes por Mes)
|
||||||
|
const monthlyData = props.charts.requests_by_month || [];
|
||||||
|
const lineChartCategories = monthlyData.map(item => item.month_name);
|
||||||
|
const lineChartSeries = [{
|
||||||
|
name: 'Solicitudes',
|
||||||
|
data: monthlyData.map(item => item.total_requests)
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Datos para el gráfico de dona (Solicitudes por Departamento)
|
||||||
|
const departmentData = props.charts.requests_by_department || [];
|
||||||
|
const donutChartLabels = departmentData.map(item => item.department_name);
|
||||||
|
const donutChartSeries = departmentData.map(item => item.total_requests);
|
||||||
|
|
||||||
|
return {
|
||||||
|
lineChartCategories,
|
||||||
|
lineChartSeries,
|
||||||
|
donutChartLabels,
|
||||||
|
donutChartSeries
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Datos para el gráfico de línea (Solicitudes por Mes) - Solo para admin
|
||||||
const lineChartOptions = computed(() => ({
|
const lineChartOptions = computed(() => ({
|
||||||
chart: {
|
chart: {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
height: 350,
|
height: 350,
|
||||||
toolbar: {
|
toolbar: {
|
||||||
show: true
|
show: false
|
||||||
},
|
},
|
||||||
zoom: {
|
background: 'transparent'
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
colors: ['#3B82F6'],
|
colors: ['#3b82f6'],
|
||||||
stroke: {
|
stroke: {
|
||||||
curve: 'smooth',
|
curve: 'smooth',
|
||||||
width: 3
|
width: 3
|
||||||
},
|
},
|
||||||
|
grid: {
|
||||||
|
borderColor: 'rgba(148, 163, 184, 0.2)',
|
||||||
|
strokeDashArray: 3,
|
||||||
|
xaxis: {
|
||||||
|
lines: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
lines: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: chartData.value.lineChartCategories,
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
colors: '#64748b',
|
||||||
|
fontSize: '12px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
color: 'rgba(148, 163, 184, 0.2)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
min: 0,
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
colors: '#64748b',
|
||||||
|
fontSize: '12px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
markers: {
|
markers: {
|
||||||
size: 6,
|
size: 6,
|
||||||
|
colors: ['#3b82f6'],
|
||||||
|
strokeColors: '#fff',
|
||||||
|
strokeWidth: 2,
|
||||||
hover: {
|
hover: {
|
||||||
size: 8
|
size: 8
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xaxis: {
|
|
||||||
categories: vacationsCharts.value.requests_by_month?.map(item => item.month_name) || [],
|
|
||||||
title: {
|
|
||||||
text: 'Mes'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
title: {
|
|
||||||
text: 'Número de Solicitudes'
|
|
||||||
},
|
|
||||||
min: 0
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
y: {
|
theme: 'dark'
|
||||||
formatter: function (val) {
|
|
||||||
return val + " solicitudes"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
borderColor: '#f1f1f1'
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Series para gráfica de solicitudes por mes
|
const lineChartSeries = computed(() => chartData.value.lineChartSeries);
|
||||||
const lineChartSeries = computed(() => [{
|
|
||||||
name: 'Solicitudes',
|
|
||||||
data: vacationsCharts.value.requests_by_month?.map(item => item.total_requests) || []
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Opciones para gráfica de solicitudes por departamento (donut)
|
// Datos para el gráfico de dona (Solicitudes por Departamento) - Solo para admin
|
||||||
const donutChartOptions = computed(() => ({
|
const donutChartOptions = computed(() => ({
|
||||||
chart: {
|
chart: {
|
||||||
type: 'donut',
|
type: 'donut',
|
||||||
height: 350
|
height: 350
|
||||||
},
|
},
|
||||||
colors: ['#3B82F6', '#8B5CF6', '#06B6D4', '#10B981', '#F59E0B', '#EF4444', '#EC4899', '#84CC16'],
|
colors: ['#8b5cf6', '#10b981', '#f59e0b', '#ef4444', '#3b82f6', '#f97316'],
|
||||||
labels: vacationsCharts.value.requests_by_department?.map(item => item.department_name) || [],
|
labels: chartData.value.donutChartLabels,
|
||||||
legend: {
|
legend: {
|
||||||
position: 'bottom',
|
position: 'right',
|
||||||
horizontalAlign: 'center'
|
fontSize: '14px',
|
||||||
|
labels: {
|
||||||
|
colors: '#64748b'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
plotOptions: {
|
plotOptions: {
|
||||||
pie: {
|
pie: {
|
||||||
donut: {
|
donut: {
|
||||||
size: '70%',
|
size: '60%'
|
||||||
labels: {
|
}
|
||||||
show: true,
|
}
|
||||||
name: {
|
},
|
||||||
show: true,
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
formatter: function(val, opts) {
|
||||||
|
return opts.w.config.series[opts.seriesIndex]
|
||||||
|
},
|
||||||
|
style: {
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 600
|
colors: ['#fff']
|
||||||
},
|
|
||||||
value: {
|
|
||||||
show: true,
|
|
||||||
fontSize: '16px',
|
|
||||||
fontWeight: 700,
|
|
||||||
formatter: function (val) {
|
|
||||||
return val + " solicitudes"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
total: {
|
|
||||||
show: true,
|
|
||||||
showAlways: false,
|
|
||||||
label: 'Total',
|
|
||||||
fontSize: '16px',
|
|
||||||
fontWeight: 700,
|
|
||||||
color: '#373d3f',
|
|
||||||
formatter: function (w) {
|
|
||||||
return w.globals.seriesTotals.reduce((a, b) => {
|
|
||||||
return a + b
|
|
||||||
}, 0) + " solicitudes"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
y: {
|
theme: 'dark'
|
||||||
formatter: function (val) {
|
|
||||||
return val + " solicitudes"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
responsive: [{
|
|
||||||
breakpoint: 480,
|
|
||||||
options: {
|
|
||||||
chart: {
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
position: 'bottom'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Series para gráfica de solicitudes por departamento
|
const donutChartSeries = computed(() => chartData.value.donutChartSeries);
|
||||||
const donutChartSeries = computed(() =>
|
|
||||||
vacationsCharts.value.requests_by_department?.map(item => item.total_requests) || []
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Ciclos */
|
|
||||||
onMounted(() => {
|
|
||||||
api.get(apiTo('admin'), {
|
|
||||||
onSuccess: (r) => {
|
|
||||||
adminInfo.value = r.admin_info;
|
|
||||||
vacationsCharts.value = r.admin_info.charts;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -219,7 +216,7 @@ onMounted(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Gráficos -->
|
<!-- Gráficos -->
|
||||||
<section v-if="vacationsCharts.requests_by_month && vacationsCharts.requests_by_department" class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
<section v-if="charts.requests_by_month && charts.requests_by_department" class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||||
<!-- Gráfico de Solicitudes por Mes -->
|
<!-- Gráfico de Solicitudes por Mes -->
|
||||||
<article class="rounded-xl bg-white p-6 shadow dark:bg-slate-800">
|
<article class="rounded-xl bg-white p-6 shadow dark:bg-slate-800">
|
||||||
<div class="mb-6 flex items-center gap-3">
|
<div class="mb-6 flex items-center gap-3">
|
||||||
|
|||||||
@ -1,28 +1,30 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { api } from '@Services/Api';
|
|
||||||
import { apiTo } from './Module';
|
|
||||||
|
|
||||||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||||||
|
|
||||||
// Propiedades
|
// Props
|
||||||
const vacations = ref({});
|
const props = defineProps({
|
||||||
const vacationsRequests = ref([]);
|
vacations: {
|
||||||
const coordinatorInfo = ref({});
|
type: Object,
|
||||||
const lastVacationRequests = ref([]);
|
default: () => ({})
|
||||||
const employeeStatusDepartment = ref([]);
|
},
|
||||||
|
vacationsRequests: {
|
||||||
/** Ciclos */
|
type: Array,
|
||||||
onMounted(() => {
|
default: () => []
|
||||||
api.get(apiTo('coordinator'), {
|
},
|
||||||
onSuccess: (r) => {
|
coordinatorInfo: {
|
||||||
vacations.value = r.vacations;
|
type: Object,
|
||||||
vacationsRequests.value = r.vacation_requests;
|
default: () => ({})
|
||||||
coordinatorInfo.value = r.coordinator_info;
|
},
|
||||||
lastVacationRequests.value = r.coordinator_info?.last_vacation_requests;
|
lastVacationRequests: {
|
||||||
employeeStatusDepartment.value = r.coordinator_info?.employee_status_department;
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
employeeStatusDepartment: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,20 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { api } from '@Services/Api';
|
|
||||||
import { apiTo } from './Module';
|
|
||||||
|
|
||||||
import PrimaryButton from '@Holos/Button/Primary.vue';
|
import PrimaryButton from '@Holos/Button/Primary.vue';
|
||||||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||||||
|
|
||||||
// Propiedades
|
// Props
|
||||||
const vacations = ref({});
|
const props = defineProps({
|
||||||
const vacationsRequests = ref([]);
|
vacations: {
|
||||||
|
type: Object,
|
||||||
/** Ciclos */
|
default: () => ({})
|
||||||
onMounted(() => {
|
},
|
||||||
api.get(apiTo('employee'), {
|
vacationsRequests: {
|
||||||
onSuccess: (r) => {
|
type: Array,
|
||||||
vacations.value = r.vacations;
|
default: () => []
|
||||||
vacationsRequests.value = r.vacation_requests;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -90,8 +90,12 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between my-4 sm:my-8 gap-3 sm:gap-0">
|
||||||
|
<div>
|
||||||
<h1 class="text-xl sm:text-2xl font-bold text-slate-900 dark:text-slate-100">Gestión de Solicitudes</h1>
|
<h1 class="text-xl sm:text-2xl font-bold text-slate-900 dark:text-slate-100">Gestión de Solicitudes</h1>
|
||||||
<p class="text-sm sm:text-base text-slate-600 dark:text-slate-400">Revisa y gestiona las solicitudes de vacaciones de tu equipo</p>
|
<p class="text-sm sm:text-base text-slate-600 dark:text-slate-400">Revisa y gestiona las solicitudes de vacaciones de tu equipo</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-4 lg:gap-6">
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-4 lg:gap-6">
|
||||||
<div class="order-2 lg:order-1 lg:col-span-8">
|
<div class="order-2 lg:order-1 lg:col-span-8">
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, computed } from 'vue';
|
import { ref, watch, computed } from 'vue';
|
||||||
|
|
||||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||||
import '@vuepic/vue-datepicker/dist/main.css';
|
import '@vuepic/vue-datepicker/dist/main.css';
|
||||||
|
|
||||||
import PrimaryButton from '@Holos/Button/Primary.vue';
|
import PrimaryButton from '@Holos/Button/Primary.vue';
|
||||||
|
import Input from '@Holos/Form/Input.vue';
|
||||||
import Textarea from '@Holos/Form/Textarea.vue';
|
import Textarea from '@Holos/Form/Textarea.vue';
|
||||||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user