Se agregaron las gráficas de Beneficiarios y DIF
This commit is contained in:
parent
6917e031ef
commit
d95cc85b53
65
app/Http/Controllers/Dashboard/AtencionController.php
Normal file
65
app/Http/Controllers/Dashboard/AtencionController.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php namespace App\Http\Controllers\Dashboard;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AtencionController extends BaseController
|
||||
{
|
||||
public function Atencion(Request $request)
|
||||
{
|
||||
$query = $request->only(['start', 'end', 'type', 'action']);
|
||||
$input = array_merge(['action' => 'index', 'type' => 'api'], $query);
|
||||
|
||||
$startDate = $input['start'] ?? date('Y-01-01');
|
||||
$endDate = $input['end'] ?? date('Y-12-31');
|
||||
|
||||
$baseParams = [
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'type' => $input['type'] ?? 'api',
|
||||
'action' => $input['action'] ?? 'index',
|
||||
];
|
||||
|
||||
$baseUrl = 'https://apoyos.comalcalco.gob.mx/beneficiaries/stats-by-date-range';
|
||||
$countsUrl = 'https://apoyos.comalcalco.gob.mx/beneficiaries/counts-by-date';
|
||||
|
||||
try{
|
||||
$response = Http::get($baseUrl, $baseParams);
|
||||
|
||||
if(!$response->successful()){
|
||||
return response()->json(['error' => 'Error del servicio'], $response->status());
|
||||
}
|
||||
|
||||
$mainData = $response->json();
|
||||
$countsAc = [];
|
||||
try{
|
||||
$countsResp = Http::get($countsUrl, [
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
]);
|
||||
|
||||
if($countsResp->successful()){
|
||||
$countsAc = $countsResp->json();
|
||||
} else {
|
||||
Log::error('Error al obtener los conteos de atención', ['status' => $countsResp->status()]);
|
||||
}
|
||||
}catch (\Exception $e) {
|
||||
Log::error('Error al obtener los conteos de atención', ['exception' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$combinedData = array_merge(
|
||||
is_array($mainData) ? $mainData : [],
|
||||
[
|
||||
'counts' => $countsAc,
|
||||
]
|
||||
);
|
||||
return response()->json($combinedData, 200)
|
||||
->header('Content-Type', 'application/json');
|
||||
}catch (\Exception $e) {
|
||||
Log::error('Error en la consulta de atención', ['exception' => $e->getMessage()]);
|
||||
return response()->json(['error' => 'Error en la consulta de atención'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,278 +1,406 @@
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, computed } from "vue";
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import axios from "axios";
|
||||
import DateRange from "@/Components/Dashboard/Form/DateRange.vue";
|
||||
import Bars from "@/Components/Dashboard/Charts/Bars.vue";
|
||||
import { data } from "autoprefixer";
|
||||
import Pie from "@/Components/Dashboard/Charts/Pie.vue";
|
||||
import Lines from "@/Components/Dashboard/Charts/Lines.vue";
|
||||
|
||||
const dateRange = ref({
|
||||
start: new Date().toISOString().slice(0, 10),
|
||||
end: new Date().toISOString().slice(0, 10),
|
||||
});
|
||||
const fmtToday = () => new Date().toISOString().slice(0, 10);
|
||||
const today = fmtToday();
|
||||
|
||||
const dateRange = ref({ start: today, end: today });
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const raw = ref([]);
|
||||
|
||||
// Toggle para usar datos falsos
|
||||
const USE_MOCK = ref(false);
|
||||
|
||||
// Tipos de apoyo (inventados)
|
||||
const supportTypes = [
|
||||
"Despensas",
|
||||
"Despensas Funerarias",
|
||||
"Laboratorio",
|
||||
"Rayos X",
|
||||
"USG",
|
||||
];
|
||||
|
||||
// Distribución por defecto para mock (suman 1)
|
||||
const mockDistribution = [0.35, 0.08, 0.2, 0.22, 0.15];
|
||||
|
||||
const buildMockCounts = (total) => {
|
||||
const counts = supportTypes.map((_, i) =>
|
||||
Math.round(total * (mockDistribution[i] || 0))
|
||||
);
|
||||
// Ajustar diferencia por redondeo
|
||||
const diff = total - counts.reduce((s, v) => s + v, 0);
|
||||
if (diff !== 0) counts[0] += diff;
|
||||
return counts;
|
||||
};
|
||||
|
||||
// helpers para sumar por rango
|
||||
const isoDate = (d) => new Date(d + "T00:00:00");
|
||||
const totalForRange = (startIso, endIso) => {
|
||||
const s = isoDate(startIso);
|
||||
const e = isoDate(endIso);
|
||||
return raw.value.reduce((acc, r) => {
|
||||
const rd = isoDate(r.date);
|
||||
if (rd >= s && rd <= e) acc += Number(r.available || 0);
|
||||
return acc;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const dayStart = (iso) => iso; // ya vienen en formato YYYY-MM-DD
|
||||
const weekStartFrom = (endIso) => {
|
||||
const d = new Date(endIso + "T00:00:00");
|
||||
d.setDate(d.getDate() - 6); // 7 días inclusive
|
||||
return d.toISOString().slice(0, 10);
|
||||
};
|
||||
const monthRangeFrom = (endIso) => {
|
||||
const d = new Date(endIso + "T00:00:00");
|
||||
d.setDate(1);
|
||||
return d.toISOString().slice(0, 10);
|
||||
};
|
||||
|
||||
// Construye chartData ahora por tipos de apoyo
|
||||
const chartData = computed(() => {
|
||||
const colors = ["#ef4444", "#3b82f6", "#f59e0b"];
|
||||
|
||||
if(USE_MOCK.value) {
|
||||
const todayCounts = buildMockCounts(20);
|
||||
const weekCounts = buildMockCounts(100);
|
||||
const monthCounts = buildMockCounts(400);
|
||||
return {
|
||||
labels: supportTypes,
|
||||
datasets: [
|
||||
{ label: "Hoy", data: todayCounts, backgroundColor: colors[0] },
|
||||
{ label: "Semana", data: weekCounts, backgroundColor: colors[1] },
|
||||
{ label: "Mes", data: monthCounts, backgroundColor: colors[2] },
|
||||
],
|
||||
}
|
||||
}
|
||||
const end = dateRange.value.end;
|
||||
const todayTotal = totalForRange(end, end);
|
||||
const weekTotal = totalForRange(end, weekStartFrom(end));
|
||||
const monthTotal = totalForRange(end, monthRangeFrom(end));
|
||||
|
||||
const todayCounts = buildMockCounts(todayTotal || 0);
|
||||
const weekCounts = buildMockCounts(weekTotal || 0);
|
||||
const monthCounts = buildMockCounts(monthTotal || 0);
|
||||
|
||||
return {
|
||||
labels: supportTypes,
|
||||
datasets: [
|
||||
{ label: "Hoy", data: todayCounts, backgroundColor: colors[0] },
|
||||
{ label: "Semana", data: weekCounts, backgroundColor: colors[1] },
|
||||
{ label: "Mes", data: monthCounts, backgroundColor: colors[2] },
|
||||
],
|
||||
}
|
||||
});
|
||||
let debounceId = null;
|
||||
let cancelTokenSource = null;
|
||||
|
||||
const totals = ref({ day: null, week: null, month: null });
|
||||
|
||||
const tipoChart = ref({ labels: [], datasets: [] });
|
||||
const generoChart = ref({ labels: [], datasets: [] });
|
||||
const edadChart = ref({ labels: [], datasets: [] });
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: { stacked: false },
|
||||
x: { beginAtZero: true },
|
||||
y: { beginAtZero: true },
|
||||
},
|
||||
};
|
||||
|
||||
const buildEmptyRange = (start, end) => {
|
||||
const s = new Date(start);
|
||||
const e = new Date(end);
|
||||
const list = [];
|
||||
for (let d = new Date(s); d <= e; d.setDate(d.getDate() + 1)) {
|
||||
list.push({
|
||||
date: new Date(d).toISOString().slice(0, 10),
|
||||
available: 0,
|
||||
unavailable: 0,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
const normalizeResponse = (res) => {
|
||||
const main = res?.data ?? {};
|
||||
const counts = res?.counts ?? res?.counts_ac ?? [];
|
||||
|
||||
const porTipo =
|
||||
main?.por_tipo_apoyo ??
|
||||
main?.data?.por_tipo_apoyo ??
|
||||
counts?.data?.por_tipo_apoyo ??
|
||||
counts?.por_tipo_apoyo ??
|
||||
{};
|
||||
|
||||
const porGenero =
|
||||
main?.por_genero ??
|
||||
main?.data?.por_genero ??
|
||||
counts?.data?.por_genero ??
|
||||
{};
|
||||
const porEdad =
|
||||
main?.por_rango_edad ??
|
||||
main?.data?.por_rango_edad ??
|
||||
counts?.data?.por_rango_edad ??
|
||||
{};
|
||||
const totalsShape = counts?.data_ac ?? counts?.data ?? counts ?? {};
|
||||
return {
|
||||
porGenero,
|
||||
porEdad,
|
||||
porTipo,
|
||||
totalsShape,
|
||||
};
|
||||
};
|
||||
|
||||
const mapApiToSeries = (items, start, end) => {
|
||||
const base = buildEmptyRange(start, end);
|
||||
const byDate = {};
|
||||
(items || []).forEach((it) => {
|
||||
const key = (it.date || it.fecha || it.day || "").slice(0, 10);
|
||||
if (key)
|
||||
byDate[key] = {
|
||||
date: key,
|
||||
available: Number(
|
||||
it.available ?? it.disponibles ?? it.available_count ?? 0
|
||||
),
|
||||
};
|
||||
});
|
||||
return base.map((b) => byDate[b.date] || b);
|
||||
const buildTipoChart = (porTipo) => {
|
||||
const labels = Object.keys(porTipo || {});
|
||||
const values = labels.map((k) => Number(porTipo[k] ?? 0));
|
||||
tipoChart.value = {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "Apoyos entregados",
|
||||
data: values,
|
||||
backgroundColor: ["#3b82f6"],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const loadData = async () => {
|
||||
const buildGeneroChart = (porGenero) => {
|
||||
const map = {
|
||||
hombres: porGenero?.hombres ?? porGenero?.Hombres ?? porGenero?.Hombre ?? 0,
|
||||
mujeres: porGenero?.mujeres ?? porGenero?.Mujeres ?? porGenero?.Mujer ?? 0,
|
||||
};
|
||||
const labels = ["Hombres", "Mujeres"];
|
||||
const values = [Number(map.hombres || 0), Number(map.mujeres || 0)];
|
||||
generoChart.value = {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "Beneficiarios por género",
|
||||
data: values,
|
||||
backgroundColor: ["#3b82f6", "#fb7185"],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const buildEdadChart = (porEdad) => {
|
||||
// orden predecible para la X de la gráfica
|
||||
const ORDER = [
|
||||
"0-17",
|
||||
"18-29",
|
||||
"30-39",
|
||||
"40-49",
|
||||
"50-59",
|
||||
"60-69",
|
||||
"70+",
|
||||
"sin_fecha",
|
||||
];
|
||||
const labels = ORDER.filter((k) =>
|
||||
Object.prototype.hasOwnProperty.call(porEdad || {}, k)
|
||||
);
|
||||
// si no hay claves exactamente como ORDER, fallback a keys actuales
|
||||
const finalLabels = labels.length ? labels : Object.keys(porEdad || {});
|
||||
const values = finalLabels.map((k) => Number(porEdad[k] ?? 0));
|
||||
const color = "#10B981"; // verde
|
||||
edadChart.value = {
|
||||
labels: finalLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: "Beneficiarios por rango de edad",
|
||||
data: values,
|
||||
borderColor: color,
|
||||
backgroundColor: "rgba(16,185,129,0.12)",
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
if (cancelTokenSource) {
|
||||
try {
|
||||
cancelTokenSource.cancel("cancel");
|
||||
} catch (e) {}
|
||||
}
|
||||
cancelTokenSource = axios.CancelToken.source();
|
||||
|
||||
try {
|
||||
const res = await axios.get("/api/atencion-ciudadana", {
|
||||
params: {
|
||||
start: dateRange.value.start,
|
||||
end: dateRange.value.end,
|
||||
},
|
||||
const res = await axios.get("/api/reporte-atencion", {
|
||||
params: { start: dateRange.value.start, end: dateRange.value.end },
|
||||
cancelToken: cancelTokenSource.token,
|
||||
headers: { "X-Requested-With": "XMLHttpRequest" },
|
||||
});
|
||||
const payload = res.data;
|
||||
const items = payload?.data ?? payload?.items ?? payload ?? [];
|
||||
raw.value = mapApiToSeries(
|
||||
Array.isArray(items) ? items : [],
|
||||
dateRange.value.start,
|
||||
dateRange.value.end
|
||||
);
|
||||
|
||||
const payload = res.data || {};
|
||||
const { porTipo, porGenero, porEdad, totalsShape } =
|
||||
normalizeResponse(payload);
|
||||
|
||||
totals.value.day = totalsShape?.today ?? totalsShape?.dia ?? null;
|
||||
totals.value.week = totalsShape?.week ?? totalsShape?.semana ?? null;
|
||||
totals.value.month = totalsShape?.month ?? totalsShape?.mes ?? null;
|
||||
|
||||
buildTipoChart(porTipo);
|
||||
buildGeneroChart(porGenero);
|
||||
buildEdadChart(porEdad);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
error.value =
|
||||
e.response?.data?.message || e.message || "Error al cargar datos";
|
||||
raw.value = mapApiToSeries([], dateRange.value.start, dateRange.value.end);
|
||||
if (!(axios.isCancel && axios.isCancel(e))) {
|
||||
console.error(e);
|
||||
error.value =
|
||||
e.response?.data?.error || e.message || "Error al cargar datos";
|
||||
tipoChart.value = { labels: [], datasets: [] };
|
||||
generoChart.value = { labels: [], datasets: [] };
|
||||
edadChart.value = { labels: [], datasets: [] };
|
||||
totals.value = { day: null, week: null, month: null };
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(dateRange, loadData, { deep: true });
|
||||
onMounted(loadData);
|
||||
watch(
|
||||
dateRange,
|
||||
() => {
|
||||
clearTimeout(debounceId);
|
||||
debounceId = setTimeout(load, 300);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(load);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<header>
|
||||
<div class="bg-gray-100 py-2">
|
||||
<div class="text-left container mx-auto sm:px-6 lg:px-8">
|
||||
<span class="text-sm text-gray-700 font-bold">COMALCALCO.GOB.MX</span>
|
||||
<div class="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50">
|
||||
<header class="relative">
|
||||
<div class="bg-gradient-to-r from-gray-100 to-gray-50 py-3 shadow-sm">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-bold text-gray-800 tracking-wide">COMALCALCO.GOB.MX</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #621132" class="shadow-lg">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center h-28">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-24 w-auto object-contain"
|
||||
/>
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #621132 0%, #7d1a42 50%, #621132 100%)"
|
||||
class="shadow-xl relative overflow-hidden"
|
||||
>
|
||||
<!-- Patrón decorativo de fondo -->
|
||||
<div class="absolute inset-0 opacity-10">
|
||||
<div class="absolute top-0 left-0 w-full h-full"
|
||||
style="background-image: repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(255,255,255,.1) 35px, rgba(255,255,255,.1) 70px);"></div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 relative">
|
||||
<div class="flex items-center justify-between h-32">
|
||||
<div class="flex items-center space-x-4">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-20 w-auto object-contain filter drop-shadow-lg transition-transform hover:scale-105"
|
||||
/>
|
||||
<div class="hidden md:block">
|
||||
<h1 class="text-2xl font-bold text-white">Atención Ciudadana y DIF</h1>
|
||||
<p class="text-blue-100 text-sm">Sistema de seguimiento de apoyos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="container mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<h1 class="text-xl font-semibold mb-4">Atención Ciudadana</h1>
|
||||
<!-- Loading overlay mejorado -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="fixed inset-0 bg-white/95 backdrop-blur-sm flex items-center justify-center z-50 transition-all duration-300"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="relative">
|
||||
<div class="animate-spin rounded-full h-16 w-16 border-4 border-blue-200 border-t-blue-600 mx-auto"></div>
|
||||
</div>
|
||||
<p class="mt-6 text-gray-700 font-medium">Cargando datos...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<section class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Controles mejorados -->
|
||||
<div class="mb-8 flex flex-col sm:flex-row items-start sm:items-center gap-4 bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<div class="flex-1">
|
||||
<h2 class="text-lg font-semibold text-gray-900 mb-2">Filtros de fecha</h2>
|
||||
<DateRange v-model="dateRange" :presets="true" />
|
||||
<!-- Toggle para usar datos falsos -->
|
||||
<label class="inline-flex items-center text-sm text-gray-600 ml-4">
|
||||
<input type="checkbox" v-model="USE_MOCK" class="mr-2 rounded border-gray-300" />
|
||||
Usar datos falsos
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 flex items-center gap-4">
|
||||
<div v-if="loading" class="ml-auto text-sm text-gray-500">
|
||||
Cargando...
|
||||
<div class="flex items-center space-x-4">
|
||||
<div v-if="loading" class="flex items-center text-blue-600">
|
||||
<div class="animate-spin h-4 w-4 border-2 border-blue-600 border-t-transparent rounded-full mr-2"></div>
|
||||
<span class="text-sm font-medium">Cargando...</span>
|
||||
</div>
|
||||
<div v-if="error" class="ml-auto text-sm text-red-600">
|
||||
{{ error }}
|
||||
<div v-if="error" class="flex items-center text-red-600">
|
||||
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium">{{ error }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-64">
|
||||
<Bars :chartData="chartData" :chartOptions="chartOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div
|
||||
class="bg-gradient-to-r from-green-600 to-emerald-700 rounded-lg shadow-lg p-6 text-white"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-lg font-medium opacity-90">Apoyos Mes</h2>
|
||||
<span >Entregados en el mes</span>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-full w-16 h-16 flex items-center justify-center"
|
||||
>
|
||||
<span class="text-2xl font-bold text-green-600">20</span>
|
||||
<!-- Cards mejoradas -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-6 mb-8">
|
||||
<div class="bg-gradient-to-br from-blue-500 to-blue-600 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="p-6 text-white">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<h3 class="text-sm font-semibold opacity-90">Apoyos del Mes</h3>
|
||||
</div>
|
||||
<p class="text-3xl font-bold">{{ totals.month ?? "--" }}</p>
|
||||
<p class="text-xs text-blue-100 mt-1">Total mensual</p>
|
||||
</div>
|
||||
<div class="bg-white/20 rounded-full p-3">
|
||||
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-gradient-to-r from-blue-600 to-indigo-700 rounded-lg shadow-lg p-6 text-white"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-lg font-medium opacity-90">Apoyos Semana</h2>
|
||||
<span class="text-sm">Entregados en la semana</span>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-full w-16 h-16 flex items-center justify-center"
|
||||
>
|
||||
<span class="text-2xl font-bold text-blue-600">20</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-br from-emerald-500 to-emerald-600 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="p-6 text-white">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<h3 class="text-sm font-semibold opacity-90">Apoyos Semanales</h3>
|
||||
</div>
|
||||
<p class="text-3xl font-bold">{{ totals.week ?? "--" }}</p>
|
||||
<p class="text-xs text-emerald-100 mt-1">Últimos 7 días</p>
|
||||
</div>
|
||||
<div class="bg-white/20 rounded-full p-3">
|
||||
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-gradient-to-r from-yellow-600 to-amber-700 rounded-lg shadow-lg p-6 text-white"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-lg font-medium opacity-90">Apoyos Hoy</h2>
|
||||
<span class="text-sm">Entregados hoy</span>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-full w-16 h-16 flex items-center justify-center"
|
||||
>
|
||||
<span class="text-2xl font-bold text-amber-600">20</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-br from-amber-500 to-amber-600 rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="p-6 text-white">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 2a8 8 0 100 16 8 8 0 000-16zM8 12a1 1 0 102 0V9a1 1 0 10-2 0v3zm2-8a1 1 0 100 2 1 1 0 000-2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<h3 class="text-sm font-semibold opacity-90">Apoyos de Hoy</h3>
|
||||
</div>
|
||||
<p class="text-3xl font-bold">{{ totals.day ?? "--" }}</p>
|
||||
<p class="text-xs text-amber-100 mt-1">En el día actual</p>
|
||||
</div>
|
||||
<div class="bg-white/20 rounded-full p-3">
|
||||
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 2a8 8 0 100 16 8 8 0 000-16zM8 12a1 1 0 102 0V9a1 1 0 10-2 0v3zm2-8a1 1 0 100 2 1 1 0 000-2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenedor de gráficas mejorado -->
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- Gráfica principal -->
|
||||
<div class="bg-white rounded-2xl shadow-xl border border-gray-100 p-8 mb-8 transition-all duration-300 hover:shadow-2xl">
|
||||
<div class="text-center mb-8">
|
||||
<div class="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-r from-blue-500 to-indigo-600 rounded-2xl mb-4 shadow-lg">
|
||||
<svg class="w-8 h-8 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-2">Distribución por Tipo de Apoyo</h2>
|
||||
<p class="text-gray-600">Análisis detallado de los tipos de apoyos otorgados</p>
|
||||
</div>
|
||||
<div class="h-96">
|
||||
<Bars :chartData="tipoChart" :chartOptions="chartOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gráficas secundarias -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div class="bg-white rounded-2xl shadow-xl border border-gray-100 p-6 transition-all duration-300 hover:shadow-2xl">
|
||||
<div class="text-center mb-6">
|
||||
<div class="inline-flex items-center justify-center w-12 h-12 bg-gradient-to-r from-pink-500 to-rose-600 rounded-xl mb-4 shadow-lg">
|
||||
<svg class="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-gray-900 mb-1">Distribución por Género</h3>
|
||||
<p class="text-sm text-gray-600">Beneficiarios por género</p>
|
||||
</div>
|
||||
<div>
|
||||
<Pie
|
||||
:chartData="generoChart"
|
||||
:chartOptions="{ responsive: true, maintainAspectRatio: false }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-xl border border-gray-100 p-6 transition-all duration-300 hover:shadow-2xl">
|
||||
<div class="text-center mb-6">
|
||||
<div class="inline-flex items-center justify-center w-12 h-12 bg-gradient-to-r from-emerald-500 to-teal-600 rounded-xl mb-4 shadow-lg">
|
||||
<svg class="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3zm11.707 4.707a1 1 0 00-1.414-1.414L10 9.586 8.707 8.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-gray-900 mb-1">Distribución por Edad</h3>
|
||||
<p class="text-sm text-gray-600">Beneficiarios por rango etario</p>
|
||||
</div>
|
||||
<div class="h-64">
|
||||
<Lines
|
||||
:chartData="edadChart"
|
||||
:chartOptions="{
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer mejorado -->
|
||||
<footer class="bg-gradient-to-r from-gray-800 to-gray-900 text-white py-8 mt-16">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -83,7 +83,7 @@ const logout = () => {
|
||||
|
||||
<!-- Botón 2 -->
|
||||
<a
|
||||
href="https://apoyos.comalcalco.gob.mx/beneficiaries/dashboard?department=1&period=week&type=almacen"
|
||||
:href="route('dashboard.api-atencion')"
|
||||
target="_blank"
|
||||
class="group relative overflow-hidden rounded-2xl border border-white/10 bg-white/10 p-4 sm:p-5 transition hover:bg-white/15 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400 active:scale-[0.99]"
|
||||
aria-label="Ir al sitio 2"
|
||||
@ -93,9 +93,9 @@ const logout = () => {
|
||||
<GoogleIcon name="open_in_new" class="text-white" />
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<h2 class="font-semibold text-white">Apoyo a Beneficiarios</h2>
|
||||
<h2 class="font-semibold text-white">Apoyo a Beneficiarios y DIF</h2>
|
||||
<p class="text-sm text-slate-300">
|
||||
Dashboard de apoyo a Beneficiarios
|
||||
Gráficas de apoyo a Beneficiarios y DIF
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -141,7 +141,7 @@ const logout = () => {
|
||||
|
||||
<!-- Botón 4 -->
|
||||
<a
|
||||
href="https://apoyos.comalcalco.gob.mx/beneficiaries/dashboard?department=3&period=week&type=almacen"
|
||||
href="https://apoyos.comalcalco.gob.mx/beneficiaries/dashboard?department=&period=week&type=almacen"
|
||||
target="_blank"
|
||||
class="group relative overflow-hidden rounded-2xl border border-white/10 bg-white/10 p-4 sm:p-5 transition hover:bg-white/15 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-400 active:scale-[0.99]"
|
||||
aria-label="Ir al sitio 4"
|
||||
@ -151,8 +151,8 @@ const logout = () => {
|
||||
<GoogleIcon name="open_in_new" class="text-white" />
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<h2 class="font-semibold text-white">DIF</h2>
|
||||
<p class="text-sm text-slate-300">Dashboard de DIF</p>
|
||||
<h2 class="font-semibold text-white">Información de Apoyos a Beneficiarios y DIF</h2>
|
||||
<p class="text-sm text-slate-300"></p>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
|
||||
@ -291,21 +291,61 @@ onMounted(() => {
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Header -->
|
||||
<header>
|
||||
<div class="bg-gray-100 py-2">
|
||||
<div class="text-left container mx-auto sm:px-6 lg:px-8">
|
||||
<span class="text-sm text-gray-700 font-bold">COMALCALCO.GOB.MX</span>
|
||||
<header class="relative">
|
||||
<div class="bg-gradient-to-r from-gray-100 to-gray-50 py-3 shadow-sm">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-bold text-gray-800 tracking-wide"
|
||||
>COMALCALCO.GOB.MX</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #621132" class="shadow-lg">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center h-28">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-24 w-auto object-contain"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#621132 0%,
|
||||
#7d1a42 50%,
|
||||
#621132 100%
|
||||
);
|
||||
"
|
||||
class="shadow-xl relative overflow-hidden"
|
||||
>
|
||||
<!-- Patrón decorativo de fondo -->
|
||||
<div class="absolute inset-0 opacity-10">
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full"
|
||||
style="
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 35px,
|
||||
rgba(255, 255, 255, 0.1) 35px,
|
||||
rgba(255, 255, 255, 0.1) 70px
|
||||
);
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 relative">
|
||||
<div class="flex items-center justify-between h-32">
|
||||
<div class="flex items-center space-x-4">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-20 w-auto object-contain filter drop-shadow-lg transition-transform hover:scale-105"
|
||||
/>
|
||||
<div class="hidden md:block">
|
||||
<h1 class="text-2xl font-bold text-white">
|
||||
Información de Obras
|
||||
</h1>
|
||||
<p class="text-blue-100 text-sm">
|
||||
Sistema de seguimiento de obras
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -314,13 +354,15 @@ onMounted(() => {
|
||||
<!-- Loading Overlay -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="fixed inset-0 bg-white bg-opacity-90 flex items-center justify-center z-50"
|
||||
class="fixed inset-0 bg-white/95 backdrop-blur-sm flex items-center justify-center z-50 transition-all duration-300"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"
|
||||
></div>
|
||||
<p class="mt-4 text-gray-600">Cargando datos...</p>
|
||||
<div class="relative">
|
||||
<div
|
||||
class="animate-spin rounded-full h-16 w-16 border-4 border-blue-200 border-t-blue-600 mx-auto"
|
||||
></div>
|
||||
</div>
|
||||
<p class="mt-6 text-gray-700 font-medium">Cargando datos...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1198,65 +1240,116 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="mt-8 bg-white rounded-lg shadow-lg overflow-hidden">
|
||||
<div class="px-6 py-4 bg-gradient-to-r from-gray-50 to-gray-100 border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Supervisores y sus Últimas Acciones - Últimos 30 Días</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">Registro de actividad de supervisores en proyectos</p>
|
||||
<div
|
||||
class="px-6 py-4 bg-gradient-to-r from-gray-50 to-gray-100 border-b border-gray-200"
|
||||
>
|
||||
<h3 class="text-lg font-medium text-gray-900">
|
||||
Supervisores y sus Últimas Acciones - Últimos 30 Días
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
Registro de actividad de supervisores en proyectos
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Supervisor
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Tipo Acción
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Detalle Acción
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Fecha
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Proyecto
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr v-for="supervisor in users" :key="supervisor.id" class="hover:bg-gray-50 transition-colors">
|
||||
<tr
|
||||
v-for="supervisor in users"
|
||||
:key="supervisor.id"
|
||||
class="hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 h-10 w-10">
|
||||
<div class="h-10 w-10 rounded-full bg-gray-300 flex items-center justify-center">
|
||||
<div
|
||||
class="h-10 w-10 rounded-full bg-gray-300 flex items-center justify-center"
|
||||
>
|
||||
<span class="text-sm font-medium text-gray-700">
|
||||
{{ supervisor.nombre.split(' ').map(n => n.charAt(0)).join('').substring(0, 2).toUpperCase() }}
|
||||
{{
|
||||
supervisor.nombre
|
||||
.split(" ")
|
||||
.map((n) => n.charAt(0))
|
||||
.join("")
|
||||
.substring(0, 2)
|
||||
.toUpperCase()
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">{{ supervisor.nombre }}</div>
|
||||
<div class="text-sm text-gray-500">{{ supervisor.correo }}</div>
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{{ supervisor.nombre }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ supervisor.correo }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div v-if="supervisor.ultima_accion" class="text-xs text-gray-500 mt-1 max-w-xs truncate" :title="supervisor.ultima_accion">
|
||||
<div
|
||||
v-if="supervisor.ultima_accion"
|
||||
class="text-xs text-gray-500 mt-1 max-w-xs truncate"
|
||||
:title="supervisor.ultima_accion"
|
||||
>
|
||||
{{ supervisor.ultima_accion }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm text-gray-900">{{ supervisor.detalle_accion }}</div>
|
||||
<div v-if="supervisor.detalle_accion" class="text-xs text-gray-500 mt-1 max-w-xs truncate" :title="supervisor.detalle_accion">
|
||||
<div class="text-sm text-gray-900">
|
||||
{{ supervisor.detalle_accion }}
|
||||
</div>
|
||||
<div
|
||||
v-if="supervisor.detalle_accion"
|
||||
class="text-xs text-gray-500 mt-1 max-w-xs truncate"
|
||||
:title="supervisor.detalle_accion"
|
||||
>
|
||||
{{ supervisor.detalle_accion }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{{ supervisor.fecha_accion ? formatDateTime(supervisor.fecha_accion) : '--' }}
|
||||
{{
|
||||
supervisor.fecha_accion
|
||||
? formatDateTime(supervisor.fecha_accion)
|
||||
: "--"
|
||||
}}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div v-if="supervisor.proyecto_info" class="text-sm text-gray-900 max-w-xs truncate" :title="supervisor.proyecto_info">
|
||||
<div
|
||||
v-if="supervisor.proyecto_info"
|
||||
class="text-sm text-gray-900 max-w-xs truncate"
|
||||
:title="supervisor.proyecto_info"
|
||||
>
|
||||
{{ supervisor.proyecto_info }}
|
||||
</div>
|
||||
<div v-else class="text-sm text-gray-500">--</div>
|
||||
@ -1266,7 +1359,9 @@ onMounted(() => {
|
||||
<!-- Empty state -->
|
||||
<tr v-if="!users.length">
|
||||
<td colspan="5" class="px-6 py-8 text-center">
|
||||
<div class="text-gray-500">No hay supervisores registrados</div>
|
||||
<div class="text-gray-500">
|
||||
No hay supervisores registrados
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@ -176,21 +176,61 @@ const togglePie = () => (showPie.value = !showPie.value);
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<header>
|
||||
<div class="bg-gray-100 py-2">
|
||||
<div class="text-left container mx-auto sm:px-6 lg:px-8">
|
||||
<span class="text-sm text-gray-700 font-bold">COMALCALCO.GOB.MX</span>
|
||||
<header class="relative">
|
||||
<div class="bg-gradient-to-r from-gray-100 to-gray-50 py-3 shadow-sm">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-bold text-gray-800 tracking-wide"
|
||||
>COMALCALCO.GOB.MX</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #621132" class="shadow-lg">
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center h-28">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-24 w-auto object-contain"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#621132 0%,
|
||||
#7d1a42 50%,
|
||||
#621132 100%
|
||||
);
|
||||
"
|
||||
class="shadow-xl relative overflow-hidden"
|
||||
>
|
||||
<!-- Patrón decorativo de fondo -->
|
||||
<div class="absolute inset-0 opacity-10">
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full"
|
||||
style="
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 35px,
|
||||
rgba(255, 255, 255, 0.1) 35px,
|
||||
rgba(255, 255, 255, 0.1) 70px
|
||||
);
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 relative">
|
||||
<div class="flex items-center justify-between h-32">
|
||||
<div class="flex items-center space-x-4">
|
||||
<img
|
||||
src="https://apoyos.comalcalco.gob.mx/images/logo_blanco.png"
|
||||
alt="Logo Comalcalco"
|
||||
class="h-20 w-auto object-contain filter drop-shadow-lg transition-transform hover:scale-105"
|
||||
/>
|
||||
<div class="hidden md:block">
|
||||
<h1 class="text-2xl font-bold text-white">
|
||||
Reporte Especial de Trámites
|
||||
</h1>
|
||||
<p class="text-blue-100 text-sm">
|
||||
Sistema de seguimiento de trámites
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -199,13 +239,15 @@ const togglePie = () => (showPie.value = !showPie.value);
|
||||
<!-- Loading Overlay -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="fixed inset-0 bg-white bg-opacity-90 flex items-center justify-center z-50"
|
||||
class="fixed inset-0 bg-white/95 backdrop-blur-sm flex items-center justify-center z-50 transition-all duration-300"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"
|
||||
></div>
|
||||
<p class="mt-4 text-gray-600">Cargando datos...</p>
|
||||
<div class="relative">
|
||||
<div
|
||||
class="animate-spin rounded-full h-16 w-16 border-4 border-blue-200 border-t-blue-600 mx-auto"
|
||||
></div>
|
||||
</div>
|
||||
<p class="mt-6 text-gray-700 font-medium">Cargando datos...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Dashboard\AtencionController;
|
||||
use App\Http\Controllers\Dashboard\ObrasController;
|
||||
use App\Http\Controllers\Dashboard\TramiteController;
|
||||
use Illuminate\Http\Request;
|
||||
@ -23,4 +24,5 @@
|
||||
Route::middleware('api')->group(function () {
|
||||
Route::get('/reporte-obras', [ObrasController::class, 'Obras'])->middleware(['throttle:60,1']);
|
||||
Route::get('/reporte-especial', [TramiteController::class, 'tramiteEspecial'])->middleware(['throttle:60,1']);
|
||||
Route::get('/reporte-atencion', [AtencionController::class, 'Atencion'])->middleware(['throttle:60,1']);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user