VanguardAI/app/Views/home/home.php
2024-10-27 12:50:51 -06:00

440 lines
18 KiB
PHP

<?php include __DIR__ . "/../layouts/header.php"; ?>
<style>
.task-form {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
background-color: #f9f9f9;
border-radius: 0.5rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.task-form__input-group {
display: flex;
gap: 1rem;
align-items: center;
}
.task-form__input {
flex: 1;
}
.task-form__badge {
margin-top: 0.5rem;
}
@media (max-width: 767.98px) {
#calendar {
max-width: 100%;
}
.fc-header-toolbar {
font-size: 0.75rem;
}
}
</style>
<div class="container-fluid mt-4">
<div class="row g-3">
<!-- Título del Dashboard -->
<div class="col-12 text-center my-4">
<h1 class="display-4 text-dark"><?php echo $_ENV["APP_NAME"]; ?></h1>
<p class="lead text-muted">Automatiza tus tareas diarias y mejora tu productividad con
<b><?php echo $_ENV["APP_NAME"]; ?></b></p>
</div>
<!-- Tarjetas de Resumen -->
<div class="row g-3 mb-4">
<!-- Balance General de Dinero -->
<div class="col-12 col-sm-6 col-xl-3">
<div class="card border-start border-success border-4 shadow h-100">
<div class="card-body py-3">
<div class="row align-items-center g-0">
<div class="col-8">
<div class="small fw-bold text-success text-uppercase mb-1">
Balance General</div>
<div class="h5 mb-0 fw-bold text-gray-800" id="balanceGeneralAmount">Q 0</div>
</div>
<div class="col-4 text-end">
<i class="fas fa-wallet fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Eventos Próximos -->
<div class="col-12 col-sm-6 col-xl-3">
<div class="card border-start border-warning border-4 shadow h-100">
<div class="card-body py-3">
<div class="row align-items-center g-0">
<div class="col-8">
<div class="small fw-bold text-warning text-uppercase mb-1">
Eventos Próximos</div>
<div class="h5 mb-0 fw-bold text-gray-800" id="upcomingEventsCount">0</div>
</div>
<div class="col-4 text-end">
<i class="fas fa-calendar-alt fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Tareas En Proceso -->
<div class="col-12 col-sm-6 col-xl-3">
<div class="card border-start border-info border-4 shadow h-100">
<div class="card-body py-3">
<div class="row align-items-center g-0">
<div class="col-8">
<div class="small fw-bold text-info text-uppercase mb-1">
Tareas En Proceso</div>
<div class="h5 mb-0 fw-bold text-gray-800" id="inProgressTasksCount">0</div>
</div>
<div class="col-4 text-end">
<i class="fas fa-tasks fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Tareas Completadas -->
<div class="col-12 col-sm-6 col-xl-3">
<div class="card border-start border-primary border-4 shadow h-100">
<div class="card-body py-3">
<div class="row align-items-center g-0">
<div class="col-8">
<div class="small fw-bold text-primary text-uppercase mb-1">
Tareas Completadas</div>
<div class="h5 mb-0 fw-bold text-gray-800" id="completedTasksCount">0</div>
</div>
<div class="col-4 text-end">
<i class="fas fa-check-circle fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Calendario y Gráficos -->
<div class="row mb-5 g-3">
<!-- Calendario -->
<div class="col-12 col-lg-8">
<div class="card shadow h-100">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h6 class="my-0">Calendario de Eventos</h6>
<button class="btn btn-sm btn-light" id="newEventBtn">
<i class="fas fa-plus"></i> Nuevo Evento
</button>
</div>
<div class="card-body">
<div id="calendar"></div>
</div>
</div>
</div>
<!-- Panel Lateral -->
<div class="col-12 col-lg-4">
<!-- Gráfico de Actividades Fitness -->
<div class="card shadow mb-4">
<div class="card-header bg-primary text-white">
<h6 class="my-0">Actividad Física Últimos 7 Días</h6>
</div>
<div class="card-body">
<canvas id="fitnessActivityChart"></canvas>
</div>
</div>
<!-- Lista de Próximos Eventos -->
<div class="card shadow">
<div class="card-header bg-warning text-white">
<h6 class="my-0">Próximos Eventos</h6>
</div>
<div class="card-body">
<div id="upcomingEventsList" class="list-group list-group-flush">
<!-- Los eventos se cargarán dinámicamente aquí -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Scripts -->
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/locales-all.min.js'></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Datos simulados
const data = {
balanceGeneral: 3500,
upcomingEvents: 5,
inProgressTasks: 8,
completedTasks: 15,
fitnessActivities: [10000, 6000, 6500, 5000, 12000, 5500, 9500],
events: [
{
id: '1',
title: 'Reunión de equipo',
start: '2024-10-28T10:00:00',
end: '2024-10-28T11:30:00',
backgroundColor: '#0275d8',
description: 'Reunión semanal de seguimiento'
},
{
id: '2',
title: 'Entrenamiento',
start: '2024-10-29T15:00:00',
end: '2024-10-29T16:00:00',
backgroundColor: '#5cb85c',
description: 'Sesión de ejercicio'
}
// ... agrega más eventos si lo deseas
]
};
// Actualizar tarjetas de resumen
document.getElementById('balanceGeneralAmount').textContent = `Q ${data.balanceGeneral}`;
document.getElementById('upcomingEventsCount').textContent = data.upcomingEvents;
document.getElementById('inProgressTasksCount').textContent = data.inProgressTasks;
document.getElementById('completedTasksCount').textContent = data.completedTasks;
// Inicializar FullCalendar
const calendarEl = document.getElementById('calendar');
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
locale: 'es',
events: data.events,
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
eventClick: function (info) {
showEventDetails(info.event);
},
select: function (info) {
showEventForm(null, info);
}
});
calendar.render();
// Gráfico de Actividades Fitness
const fitnessActivityCtx = document.getElementById('fitnessActivityChart').getContext('2d');
new Chart(fitnessActivityCtx, {
type: 'bar',
data: {
labels: ['Hace 6 días', 'Hace 5 días', 'Hace 4 días', 'Hace 3 días', 'Hace 2 días', 'Ayer', 'Hoy'],
datasets: [{
label: 'Pasos diarios',
data: data.fitnessActivities,
backgroundColor: 'rgba(0, 123, 255, 0.7)',
borderColor: 'rgba(0, 123, 255, 1)',
borderWidth: 1,
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
// Actualizar lista de próximos eventos
updateUpcomingEventsList();
// Manejadores de eventos
document.getElementById('newEventBtn').addEventListener('click', () => showEventForm());
// Funciones auxiliares
function showEventForm(event = null, selectInfo = null) {
let eventData = {
id: '',
title: '',
start: '',
end: '',
color: '#0275d8',
description: ''
};
if (event) {
// Editar evento existente
eventData = {
id: event.id,
title: event.title,
start: event.startStr,
end: event.endStr,
color: event.backgroundColor,
description: event.extendedProps.description
};
} else if (selectInfo) {
// Nuevo evento con fechas seleccionadas
eventData.start = selectInfo.startStr;
eventData.end = selectInfo.endStr;
}
Swal.fire({
title: event ? 'Editar Evento' : 'Nuevo Evento',
html: `
<div class="task-form">
<input type="text" id="swalEvtTitle" class="swal2-input" placeholder="Título" value="${escapeHtml(eventData.title)}">
<input type="datetime-local" id="swalEvtStart" class="swal2-input" value="${formatDateTimeInput(eventData.start)}">
<input type="datetime-local" id="swalEvtEnd" class="swal2-input" value="${formatDateTimeInput(eventData.end)}">
<div class="task-form__input-group">
<label for="swalEvtColor">Color:</label>
<select id="swalEvtColor" class="swal2-input task-form__input">
<option value="#0275d8" ${eventData.color === '#0275d8' ? 'selected' : ''}>Azul</option>
<option value="#5cb85c" ${eventData.color === '#5cb85c' ? 'selected' : ''}>Verde</option>
<option value="#f0ad4e" ${eventData.color === '#f0ad4e' ? 'selected' : ''}>Amarillo</option>
<option value="#d9534f" ${eventData.color === '#d9534f' ? 'selected' : ''}>Rojo</option>
</select>
</div>
<textarea id="swalEvtDesc" class="swal2-textarea" placeholder="Descripción">${escapeHtml(eventData.description)}</textarea>
</div>
`,
focusConfirm: false,
showCancelButton: true,
confirmButtonText: 'Guardar',
cancelButtonText: 'Cancelar',
preConfirm: () => {
const title = document.getElementById('swalEvtTitle').value;
const start = document.getElementById('swalEvtStart').value;
const end = document.getElementById('swalEvtEnd').value;
const color = document.getElementById('swalEvtColor').value;
const description = document.getElementById('swalEvtDesc').value;
if (!title || !start || !end) {
Swal.showValidationMessage('Por favor completa todos los campos obligatorios');
return false;
}
return { title, start, end, color, description };
}
}).then((result) => {
if (result.isConfirmed) {
const formData = result.value;
if (event) {
// Actualizar evento existente
event.setProp('title', formData.title);
event.setStart(formData.start);
event.setEnd(formData.end);
event.setProp('backgroundColor', formData.color);
event.setExtendedProp('description', formData.description);
} else {
// Crear nuevo evento
calendar.addEvent({
id: String(Date.now()), // Generar un ID único
title: formData.title,
start: formData.start,
end: formData.end,
backgroundColor: formData.color,
description: formData.description
});
}
updateUpcomingEventsList();
}
});
}
function showEventDetails(event) {
Swal.fire({
title: event.title,
html: `
<div class="task-form">
<p><strong>Inicio:</strong> ${formatDateTimeDisplay(event.start)}</p>
<p><strong>Fin:</strong> ${formatDateTimeDisplay(event.end)}</p>
<p><strong>Descripción:</strong> ${escapeHtml(event.extendedProps.description || 'Sin descripción')}</p>
</div>
`,
showCancelButton: true,
showDenyButton: true,
confirmButtonText: 'Editar',
denyButtonText: 'Eliminar',
cancelButtonText: 'Cerrar',
confirmButtonColor: '#0d6efd',
denyButtonColor: '#dc3545',
}).then((result) => {
if (result.isConfirmed) {
// Editar evento
showEventForm(event);
} else if (result.isDenied) {
// Eliminar evento
Swal.fire({
title: '¿Estás seguro?',
text: 'Esta acción no se puede deshacer',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, eliminar',
cancelButtonText: 'Cancelar',
confirmButtonColor: '#dc3545',
}).then((res) => {
if (res.isConfirmed) {
event.remove();
updateUpcomingEventsList();
Swal.fire('Eliminado', 'El evento ha sido eliminado', 'success');
}
});
}
});
}
function updateUpcomingEventsList() {
const events = calendar.getEvents();
const upcomingEvents = events
.filter(event => event.start >= new Date())
.sort((a, b) => a.start - b.start)
.slice(0, 5);
const listContainer = document.getElementById('upcomingEventsList');
listContainer.innerHTML = '';
upcomingEvents.forEach(event => {
const item = document.createElement('div');
item.className = 'list-group-item';
item.innerHTML = `
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">${event.title}</h6>
<small>${formatDateTimeDisplay(event.start)}</small>
</div>
<p class="mb-1">${event.extendedProps.description || ''}</p>
`;
listContainer.appendChild(item);
});
// Actualizar el contador de eventos próximos
document.getElementById('upcomingEventsCount').textContent = upcomingEvents.length;
}
function formatDateTimeInput(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
const isoString = date.toISOString();
return isoString.slice(0, 16);
}
function formatDateTimeDisplay(date) {
if (!date) return '';
const options = { dateStyle: 'medium', timeStyle: 'short' };
return new Intl.DateTimeFormat('es-ES', options).format(new Date(date));
}
});
</script>
<?php include __DIR__ . "/../layouts/footer.php"; ?>