Files
LauckCycles/resources/views/reports/index.blade.php
T
Bryam105 2b3b87b0ba UPDATE:
- Mejorados los graficos en la parte de reportes.
- Agregados 3 nuevos charts para mayor utilidad de la pagina.
2026-06-05 08:44:32 -03:00

275 lines
14 KiB
PHP

<x-layout title="Lauck - Reportes y Estadísticas">
<x-section-header subtitle="Gestión Financiera" title="Reportes y " highlight="Estadísticas" />
<div class="w-full mx-auto pb-10">
<!-- Tarjetas de Resumen -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<!-- Ingresos -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 uppercase tracking-wider font-bold mb-1">Total Ingresos</p>
<h3 class="text-3xl font-black text-green-600 dark:text-neon-lime">${{ number_format($totalIncome, 2, ',', '.') }}</h3>
<p class="text-xs text-gray-500 mt-2">Ventas realizadas</p>
</div>
<div class="p-3 bg-green-200 dark:bg-green-900/50 rounded-lg">
<svg class="w-6 h-6 text-green-700 dark:text-neon-lime" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>
</div>
</div>
</div>
<!-- Egresos -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 uppercase tracking-wider font-bold mb-1">Total Egresos</p>
<h3 class="text-3xl font-black text-red-600 dark:text-red-500">${{ number_format($totalOutcome, 2, ',', '.') }}</h3>
<p class="text-xs text-gray-500 mt-2">Gastos pagados</p>
</div>
<div class="p-3 bg-red-200 dark:bg-red-900/50 rounded-lg">
<svg class="w-6 h-6 text-red-700 dark:text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"></path></svg>
</div>
</div>
</div>
<!-- Balance -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 uppercase tracking-wider font-bold mb-1">Balance Neto</p>
<h3 class="text-3xl font-black {{ $balance >= 0 ? 'text-green-600 dark:text-neon-lime' : 'text-red-600 dark:text-red-500' }}">
${{ number_format($balance, 2, ',', '.') }}
</h3>
<p class="text-xs text-gray-500 mt-2">Ingresos - Egresos</p>
</div>
<div class="p-3 bg-gray-300 dark:bg-neutral-800 rounded-lg">
<svg class="w-6 h-6 text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3"></path></svg>
</div>
</div>
</div>
</div>
<!-- Gráficos -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Gráfico de Líneas/Barras -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Evolución de Ingresos y Egresos (Últimos 6 meses)</h3>
<div class="relative h-72 w-full">
<canvas id="balanceChart"></canvas>
</div>
</div>
<!-- Gráfico de Torta -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Distribución de Gastos por Categoría</h3>
<div class="relative h-72 w-full flex justify-center">
<canvas id="expensesChart"></canvas>
</div>
</div>
</div>
<!-- Nuevas Métricas -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-6">
<!-- Top 5 Productos -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Top 5 Productos Vendidos</h3>
<div class="relative h-64 w-full">
<canvas id="topProductsChart"></canvas>
</div>
</div>
<!-- Top 5 Repuestos -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Top 5 Repuestos Usados</h3>
<div class="relative h-64 w-full">
<canvas id="topSparesChart"></canvas>
</div>
</div>
<!-- Top Clientes -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Mejores Clientes</h3>
<ul class="space-y-4">
@forelse($topClients as $clientSale)
<li class="flex items-center justify-between border-b border-gray-300 dark:border-neutral-700 pb-2">
<div>
<p class="text-sm font-bold text-neutral-900 dark:text-white">{{ $clientSale->client->name ?? 'Cliente Eliminado' }}</p>
<p class="text-xs text-gray-500">{{ $clientSale->client->phone ?? 'Sin teléfono' }}</p>
</div>
<div class="text-right">
<span class="px-2 py-1 bg-green-200 text-green-800 dark:bg-green-900/50 dark:text-neon-lime text-xs font-bold rounded-lg border border-green-400 dark:border-green-600">
${{ number_format($clientSale->total_spent, 2, ',', '.') }}
</span>
</div>
</li>
@empty
<li class="text-sm text-gray-500 italic">No hay ventas registradas aún.</li>
@endforelse
</ul>
</div>
</div>
</div>
<!-- Cargar Chart.js desde CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Colores basados en el tema actual (usaremos Tailwind colors aprox)
const isDark = document.documentElement.classList.contains('dark');
const textColor = isDark ? '#e5e7eb' : '#111827';
const gridColor = isDark ? '#374151' : '#d1d5db';
// --- GRÁFICO DE BALANCE (Barras) ---
const ctxBalance = document.getElementById('balanceChart').getContext('2d');
const months = @json($months);
const incomeData = @json($monthlyIncomeData);
const outcomeData = @json($monthlyOutcomeData);
new Chart(ctxBalance, {
type: 'bar',
data: {
labels: months,
datasets: [
{
label: 'Ingresos',
data: incomeData,
backgroundColor: 'rgba(132, 204, 22, 0.7)', // neon-lime ish
borderColor: 'rgb(101, 163, 13)',
borderWidth: 1,
borderRadius: 4
},
{
label: 'Egresos',
data: outcomeData,
backgroundColor: 'rgba(239, 68, 68, 0.7)', // red-500
borderColor: 'rgb(220, 38, 38)',
borderWidth: 1,
borderRadius: 4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { labels: { color: textColor } }
},
scales: {
x: {
ticks: { color: textColor },
grid: { color: gridColor, drawBorder: false }
},
y: {
ticks: { color: textColor },
grid: { color: gridColor, drawBorder: false }
}
}
}
});
// --- GRÁFICO DE GASTOS POR CATEGORÍA (Torta) ---
const ctxExpenses = document.getElementById('expensesChart').getContext('2d');
const rawCategories = @json($expensesByCategory);
const catLabels = rawCategories.map(item => item.expense_category);
const catData = rawCategories.map(item => item.total);
// Paleta de colores para las categorías
const pieColors = [
'#3b82f6', // blue-500
'#f59e0b', // amber-500
'#10b981', // emerald-500
'#8b5cf6', // violet-500
'#ec4899', // pink-500
];
new Chart(ctxExpenses, {
type: 'doughnut',
data: {
labels: catLabels,
datasets: [{
data: catData,
backgroundColor: pieColors,
borderWidth: 2,
borderColor: isDark ? '#1f2937' : '#ffffff' // panel-bg color aprox
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: { color: textColor, padding: 20 }
}
},
cutout: '60%'
}
});
// --- GRÁFICO DE TOP PRODUCTOS ---
const ctxTopProducts = document.getElementById('topProductsChart').getContext('2d');
const rawTopProducts = @json($topProducts);
new Chart(ctxTopProducts, {
type: 'bar',
data: {
labels: rawTopProducts.map(i => i.product ? i.product.name : 'Eliminado'),
datasets: [{
label: 'Cantidad Vendida',
data: rawTopProducts.map(i => i.total_qty),
backgroundColor: 'rgba(59, 130, 246, 0.7)', // blue-500
borderColor: 'rgb(37, 99, 235)',
borderWidth: 1,
borderRadius: 4
}]
},
options: {
indexAxis: 'y', // barras horizontales
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } },
y: { ticks: { color: textColor }, grid: { display: false } }
}
}
});
// --- GRÁFICO DE TOP REPUESTOS ---
const ctxTopSpares = document.getElementById('topSparesChart').getContext('2d');
const rawTopSpares = @json($topSpares);
new Chart(ctxTopSpares, {
type: 'bar',
data: {
labels: rawTopSpares.map(i => i.product ? i.product.name : 'Eliminado'),
datasets: [{
label: 'Cantidad Usada/Vendida',
data: rawTopSpares.map(i => i.total_qty),
backgroundColor: 'rgba(245, 158, 11, 0.7)', // amber-500
borderColor: 'rgb(217, 119, 6)',
borderWidth: 1,
borderRadius: 4
}]
},
options: {
indexAxis: 'y', // barras horizontales
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } },
y: { ticks: { color: textColor }, grid: { display: false } }
}
}
});
});
</script>
</x-layout>