heating-monitor/internal/web/templates/view.html

251 lines
13 KiB
HTML

{{ template "header.html" . }}
<script>
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("filterDropdownButton");
const menu = document.getElementById("filterDropdownMenu");
button.addEventListener("click", () => {
menu.classList.toggle("hidden");
});
// Cerrar el menú si haces clic fuera de él
document.addEventListener("click", (event) => {
if (!button.contains(event.target) && !menu.contains(event.target)) {
menu.classList.add("hidden");
}
});
});
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="container mx-auto my-8 p-6 bg-white rounded-lg shadow-lg">
<div class="flex justify-between items-center mb-4">
<!-- Dropdown de filtros -->
<div class="relative">
<button id="filterDropdownButton"
class="inline-flex items-center px-4 py-2 bg-blue-500 text-white text-sm font-medium rounded hover:bg-blue-600">
Filtrar por
<svg class="w-4 h-4 ml-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 011.414 0L10 13.414l3.293-3.707a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<div id="filterDropdownMenu"
class="hidden absolute mt-2 w-48 bg-white rounded shadow-lg border">
<a href="/?filter=today" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Hoy</a>
<a href="/?filter=yesterday" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Ayer</a>
<a href="/?filter=this-week" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Esta semana</a>
<a href="/?filter=this-month" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Este mes</a>
<a href="/?filter=this-year" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Este año</a>
<a href="/?startDate=2022-10-01&endDate=2023-05-31" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">2022-2023</a>
<a href="/?startDate=2023-10-01&endDate=2024-05-31" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">2023-2024</a>
<a href="/?startDate=2024-10-01&endDate=2025-05-31" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">2024-2025</a>
</div>
</div>
<!-- Filtros por fechas -->
<div class="flex space-x-4">
<form action="/" method="GET" class="flex space-x-2 items-center">
<div>
<input type="date" id="startDate" name="startDate" class="px-4 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 h-10" />
</div>
<div>
<input type="date" id="endDate" name="endDate" class="px-4 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 h-10" />
</div>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white text-sm font-medium rounded hover:bg-blue-600 h-10">
Filtrar fechas
</button>
</form>
</div>
<!-- Campo para búsquedas (opcional), visible solo en pantallas grandes -->
<div class="hidden lg:block">
<form action="/" method="GET">
<input type="text" name="search"
placeholder="Buscar eventos..."
class="px-4 py-2 border border-gray-300 rounded w-64 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<button type="submit" class="px-4 py-2 bg-blue-500 text-white text-sm font-medium rounded hover:bg-blue-600">
Buscar
</button>
</form>
</div>
</div>
<div class="summary bg-gray-100 border-l-4 border-blue-500 p-4 rounded-lg shadow-lg mb-4">
<p class="text-xl font-semibold text-gray-700">
Total de horas encendidas: <span class="text-blue-600">{{ .totalHours }} horas</span> y
<span class="text-blue-600">{{ .totalMinutes }} minutos</span>
</p>
</div>
<table class="min-w-full divide-y divide-gray-200 border border-gray-300">
<thead class="bg-blue-600 text-white">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">ID</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium
uppercase tracking-wider">Fecha</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium
uppercase tracking-wider">Stamp</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium
uppercase tracking-wider">Acción</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{{range .events}}
<tr class="hover:bg-gray-100">
<td class="px-6 py-4 text-sm text-gray-700">{{.ID}}</td>
<td class="px-6 py-4 text-sm text-gray-700">{{.Timestamp | formatTimestamp }}</td>
<td class="px-6 py-4 text-sm text-gray-700">{{.EventType}}</td>
<td class="px-6 py-4 text-sm text-gray-700">
<a href="/events/edit/{{.ID}}">Editar</a>
<form action="/events/del/{{.ID}}" method="POST" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este evento?');">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="text-red-600 hover:underline">Eliminar</button>
</form>
</td>
</tr>
{{end}}
</tbody>
<tfoot class="bg-blue-600 text-white">
<tr>
<td colspan="4" class="px-6 py-3 text-center">
<div class="flex items-center justify-between">
<!-- Mostrando registros -->
<span class="text-sm text-white opacity-80">Mostrando {{len .events}}
eventos de {{ .count }}</span>
<!-- Paginador -->
<div class="flex space-x-2">
<!-- Primera página -->
<a href="/?page=1&size={{.size}}{{if .search}}&search={{.search}}{{end}}{{if .startDate}}&startDate={{.startDate}}{{end}}{{if .endDate}}&endDate={{.endDate}}{{end}}{{if .filter}}&filter={{.filter}}{{end}}"
class="px-3 py-1 text-sm bg-blue-500 text-white
rounded hover:bg-blue-600 {{if eq .page 1}}cursor-not-allowed opacity-50{{end}}">
Primera
</a>
<!-- Página anterior -->
<a href="/?page={{.prevPage}}&size={{.size}}{{if .search}}&search={{.search}}{{end}}{{if .startDate}}&startDate={{.startDate}}{{end}}{{if .endDate}}&endDate={{.endDate}}{{end}}{{if .filter}}&filter={{.filter}}{{end}}"
class="px-3 py-1 text-sm bg-blue-500 text-white
rounded hover:bg-blue-600 {{if eq .page 1}}cursor-not-allowed opacity-50{{end}}">
Anterior
</a>
<!-- Información de la página actual -->
<span class="px-3 py-1 text-sm text-white opacity-80">
Página {{.page}} de {{.totalPages}}
</span>
<!-- Página siguiente -->
<a href="/?page={{.nextPage}}&size={{.size}}{{if .search}}&search={{.search}}{{end}}{{if .startDate}}&startDate={{.startDate}}{{end}}{{if .endDate}}&endDate={{.endDate}}{{end}}{{if .filter}}&filter={{.filter}}{{end}}"
class="px-3 py-1 text-sm bg-blue-500 text-white
rounded hover:bg-blue-600 {{if eq .page .totalPages}}cursor-not-allowed opacity-50{{end}}">
Siguiente
</a>
<!-- Última página -->
<a href="/?page={{.totalPages}}&size={{.size}}{{if .search}}&search={{.search}}{{end}}{{if .startDate}}&startDate={{.startDate}}{{end}}{{if .endDate}}&endDate={{.endDate}}{{end}}{{if .filter}}&filter={{.filter}}{{end}}"
class="px-3 py-1 text-sm bg-blue-500 text-white
rounded hover:bg-blue-600 {{if eq .page .totalPages}}cursor-not-allowed opacity-50{{end}}">
Última
</a>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
<canvas id="myChart" class="w-full lg:w-auto"></canvas>
<script>
document.addEventListener('DOMContentLoaded', function () {
const ctx = document.getElementById('myChart').getContext('2d');
// Datos sin ordenar
const labels = {{ .labels | json }};
const data = {{ .totals | json }};
// Función para convertir el formato "Month Year" a "YYYY-MM"
function formatMonthYearToDate(monthYear) {
const [month, year] = monthYear.split(" ");
const monthIndex = new Date(`${month} 1`).getMonth();
return `${year}-${String(monthIndex + 1).padStart(2, "0")}`;
}
// Función para asegurarnos de que las fechas estén en un formato "YYYY-MM-DD"
function formatDateForSorting(dateString) {
if (dateString.includes("-")) {
// Si ya está en formato YYYY-MM-DD, no es necesario convertirlo
return dateString;
} else {
// Si es formato "Month Year", lo convertimos a "YYYY-MM"
return formatMonthYearToDate(dateString);
}
}
// Combina los datos y las fechas en un solo array de objetos
const combinedData = labels.map((label, index) => ({
label: label,
data: data[index],
sortableDate: formatDateForSorting(label),
}));
// Ordena los datos por la fecha (label)
combinedData.sort((a, b) => new Date(a.sortableDate) - new Date(b.sortableDate));
// Separa los labels y data ordenados
const sortedLabels = combinedData.map(item => item.label);
const sortedData = combinedData.map(item => item.data);
new Chart(ctx, {
type: 'bar',
data: {
labels: sortedLabels,
datasets: [{
label: 'Consumo (horas)',
data: sortedData,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
const value = context.raw; // Valor numérico del consumo
const hours = Math.floor(value); // Parte entera (horas)
const minutes = Math.round((value - hours) * 60); // Parte decimal convertida a minutos
return `Consumo (horas): ${hours}h ${minutes}m`; // Formato personalizado
}
}
}
},
scales: {
x: {
title: {
display: true,
text: 'Día'
}
},
y: {
title: {
display: true,
text: 'Consumo (horas)'
},
beginAtZero: true
}
}
}
});
});
</script>
{{ template "footer.html" . }}