cambios imagenes en el catalogo, permisos al empleado para que pued agregar clientes y arreglos en la vista del report

This commit is contained in:
gianella
2026-06-12 13:09:25 -03:00
parent 8ed7abc5fe
commit eab0698979
5 changed files with 54 additions and 35 deletions
+2 -2
View File
@@ -38,7 +38,7 @@ class ClientController extends Controller
*/ */
public function create() public function create()
{ {
$this->checkAdmin(); //$this->checkAdmin();
return view('clients.create'); return view('clients.create');
} }
@@ -47,7 +47,7 @@ class ClientController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$this->checkAdmin(); //$this->checkAdmin();
$validated = $request->validate([ $validated = $request->validate([
'name' => ['required', 'string', 'max:255', 'not_regex:/[0-9]/'], 'name' => ['required', 'string', 'max:255', 'not_regex:/[0-9]/'],
'phone' => 'nullable|string|max:50', 'phone' => 'nullable|string|max:50',
+1 -1
View File
@@ -120,7 +120,7 @@
{{-- Imagen si existe --}} {{-- Imagen si existe --}}
@if (!empty($product->image_path)) @if (!empty($product->image_path))
<img src="{{ asset('storage/' . $product->image_path) }}" alt="{{ $product->name }}" <img src="{{ asset('storage/' . $product->image_path) }}" alt="{{ $product->name }}"
class="w-full h-40 object-cover rounded mb-4"> class="w-full h-40 object-contain bg-white p-2 rounded mb-4">
@else @else
<div class="w-full border border-neutral-200 dark:border-neutral-800 h-40 bg-stone-200 dark:bg-stone-900 flex items-center justify-center rounded mb-4"> <div class="w-full border border-neutral-200 dark:border-neutral-800 h-40 bg-stone-200 dark:bg-stone-900 flex items-center justify-center rounded mb-4">
<span class="text-lime-800 dark:text-neon-lime text-md uppercase font-bold">Sin imagen</span> <span class="text-lime-800 dark:text-neon-lime text-md uppercase font-bold">Sin imagen</span>
+7 -9
View File
@@ -21,15 +21,13 @@
</div> </div>
</form> </form>
@if(auth()->user()->role === 'admin') <form action="{{ route('clients.index') }}" method="GET" class="w-full md:w-1/2">
<a href="{{ route('clients.create') }}" class="w-full md:w-auto text-center px-5 py-3 text-sm font-bold text-neon-lime dark:text-neutral-900 bg-neutral-950 dark:bg-neon-lime rounded-lg hover:bg-neutral-900/80 hover:dark:bg-[#b3e600] transition-colors uppercase tracking-wide"> </form>
+ Nuevo Cliente
</a> <a href="{{ route('clients.create') }}" class="w-full md:w-auto text-center px-5 py-3 text-sm font-bold text-neon-lime dark:text-neutral-900 bg-neutral-950 dark:bg-neon-lime rounded-lg hover:bg-neutral-900/80 hover:dark:bg-[#b3e600] transition-colors uppercase tracking-wide">
@else + Nuevo Cliente
<span title="Acceso denegado" class="w-full md:w-auto text-center px-5 py-3 text-sm font-bold text-gray-500 dark:text-neutral-500 bg-gray-300 dark:bg-neutral-800 border border-gray-400 dark:border-neutral-700 rounded-lg cursor-not-allowed pointer-events-none select-none uppercase tracking-wide"> </a>
+ Nuevo Cliente </div>
</span>
@endif
</div> </div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg border border-gray-400 dark:border-neutral-800 w-full"> <div class="relative overflow-x-auto shadow-md sm:rounded-lg border border-gray-400 dark:border-neutral-800 w-full">
+15 -1
View File
@@ -169,7 +169,21 @@
</div> </div>
</details> </details>
</div> </div>
<details class="group bg-white dark:bg-neutral-900 border border-gray-200 dark:border-neutral-800 rounded-lg shadow-sm [&_summary::-webkit-details-marker]:hidden">
<summary class="flex items-center justify-between p-5 cursor-pointer font-semibold text-gray-900 dark:text-white transition-colors hover:bg-gray-50 dark:hover:bg-neutral-800/50 rounded-lg">
<span>¿Qué diferencias de permisos hay entre Administrador y Empleado?</span>
<span class="transition-transform duration-300 group-open:-rotate-180 text-gray-500">
<svg fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" viewBox="0 0 24 24" width="24"><path d="M6 9l6 6 6-6"></path></svg>
</span>
</summary>
<div class="px-5 pb-5 pt-2 text-gray-600 dark:text-gray-400 border-t border-gray-100 dark:border-neutral-800/50 space-y-3">
<p>El sistema cuenta con dos niveles de acceso para garantizar la seguridad de la información:</p>
<ul class="list-disc pl-5 space-y-1">
<li><strong>Administrador (Dueño):</strong> Tiene control total del sistema. Posee acceso exclusivo al módulo de personal (para dar de alta, baja o modificar empleados). Además, es el <strong>único</strong> autorizado para modificar o eliminar registros en el sistema (productos, clientes, proveedores, etc.).</li>
<li><strong>Empleado:</strong> Tiene un acceso diseñado específicamente para la operatoria diaria del local. Puede agregar nuevos registros (crear ventas, cargar clientes nuevos, agendar turnos en el taller), pero <strong>no puede modificar ni eliminar</strong> información ya guardada.</li>
</ul>
</div>
</details>
<div id="contact" class="mt-8 p-6 bg-lime-50 dark:bg-lime-900/10 border border-lime-200 dark:border-lime-900/30 rounded-xl text-center shadow-sm"> <div id="contact" class="mt-8 p-6 bg-lime-50 dark:bg-lime-900/10 border border-lime-200 dark:border-lime-900/30 rounded-xl text-center shadow-sm">
<h3 class="text-lg font-bold text-lime-800 dark:text-neon-lime mb-2">¿Necesitas más ayuda?</h3> <h3 class="text-lg font-bold text-lime-800 dark:text-neon-lime mb-2">¿Necesitas más ayuda?</h3>
<p class="text-sm text-lime-700 dark:text-gray-400 mb-5">Si tienes alguna otra duda o experimentas problemas técnicos, nuestro equipo de soporte está disponible para ayudarte.</p> <p class="text-sm text-lime-700 dark:text-gray-400 mb-5">Si tienes alguna otra duda o experimentas problemas técnicos, nuestro equipo de soporte está disponible para ayudarte.</p>
+28 -21
View File
@@ -3,9 +3,7 @@
<div class="w-full mx-auto pb-10"> <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"> <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="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 class="flex justify-between items-start">
<div> <div>
@@ -19,7 +17,6 @@
</div> </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="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 class="flex justify-between items-start">
<div> <div>
@@ -33,7 +30,6 @@
</div> </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="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 class="flex justify-between items-start">
<div> <div>
@@ -50,7 +46,6 @@
</div> </div>
</div> </div>
<!-- Gráfico de Evolución -->
<div class="mb-6 bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg"> <div class="mb-6 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> <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"> <div class="relative h-72 w-full">
@@ -58,9 +53,7 @@
</div> </div>
</div> </div>
<!-- Gráficos de Torta (Distribución) -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Gráfico de Torta 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="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">Ingresos por Categoría</h3> <h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Ingresos por Categoría</h3>
<div class="relative h-72 w-full flex justify-center"> <div class="relative h-72 w-full flex justify-center">
@@ -68,7 +61,6 @@
</div> </div>
</div> </div>
<!-- Gráfico de Torta Gastos -->
<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="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">Gastos por Categoría</h3> <h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Gastos por Categoría</h3>
<div class="relative h-72 w-full flex justify-center"> <div class="relative h-72 w-full flex justify-center">
@@ -77,26 +69,21 @@
</div> </div>
</div> </div>
<!-- Nuevas Métricas -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-6"> <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"> <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> <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"> <div class="relative h-96 w-full">
<canvas id="topProductsChart"></canvas> <canvas id="topProductsChart"></canvas>
</div> </div>
</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"> <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> <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"> <div class="relative h-96 w-full">
<canvas id="topSparesChart"></canvas> <canvas id="topSparesChart"></canvas> </div>
</div>
</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"> <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> <h3 class="text-lg font-bold text-neutral-900 dark:text-white mb-4">Mejores Clientes</h3>
<ul class="space-y-4"> <ul class="space-y-4">
@@ -121,7 +108,6 @@
</div> </div>
<!-- Cargar Chart.js desde CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
@@ -131,6 +117,27 @@
const textColor = isDark ? '#e5e7eb' : '#111827'; const textColor = isDark ? '#e5e7eb' : '#111827';
const gridColor = isDark ? '#374151' : '#d1d5db'; const gridColor = isDark ? '#374151' : '#d1d5db';
// NUEVA FUNCIÓN: Agrupa textos largos en múltiples líneas
const agruparTexto = (texto, maxCaracteres = 22) => {
if (!texto) return 'Eliminado';
if (texto.length <= maxCaracteres) return texto;
let palabras = texto.split(' ');
let lineas = [];
let lineaActual = '';
palabras.forEach(palabra => {
if ((lineaActual + palabra).length > maxCaracteres) {
lineas.push(lineaActual.trim());
lineaActual = palabra + ' ';
} else {
lineaActual += palabra + ' ';
}
});
if (lineaActual) lineas.push(lineaActual.trim());
return lineas;
};
// --- GRÁFICO DE BALANCE (Barras) --- // --- GRÁFICO DE BALANCE (Barras) ---
const ctxBalance = document.getElementById('balanceChart').getContext('2d'); const ctxBalance = document.getElementById('balanceChart').getContext('2d');
const months = @json($months); const months = @json($months);
@@ -269,7 +276,7 @@
new Chart(ctxTopProducts, { new Chart(ctxTopProducts, {
type: 'bar', type: 'bar',
data: { data: {
labels: rawTopProducts.map(i => i.product ? i.product.name : 'Eliminado'), labels: rawTopProducts.map(i => agruparTexto(i.product ? i.product.name : 'Eliminado')),
datasets: [{ datasets: [{
label: 'Cantidad Vendida', label: 'Cantidad Vendida',
data: rawTopProducts.map(i => i.total_qty), data: rawTopProducts.map(i => i.total_qty),
@@ -286,7 +293,7 @@
plugins: { legend: { display: false } }, plugins: { legend: { display: false } },
scales: { scales: {
x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } }, x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } },
y: { ticks: { color: textColor }, grid: { display: false } } y: { ticks: { color: textColor, autoSkip: false }, grid: { display: false } }
} }
} }
}); });
@@ -298,7 +305,7 @@
new Chart(ctxTopSpares, { new Chart(ctxTopSpares, {
type: 'bar', type: 'bar',
data: { data: {
labels: rawTopSpares.map(i => i.product ? i.product.name : 'Eliminado'), labels: rawTopSpares.map(i => agruparTexto(i.product ? i.product.name : 'Eliminado')),
datasets: [{ datasets: [{
label: 'Cantidad Usada/Vendida', label: 'Cantidad Usada/Vendida',
data: rawTopSpares.map(i => i.total_qty), data: rawTopSpares.map(i => i.total_qty),
@@ -315,7 +322,7 @@
plugins: { legend: { display: false } }, plugins: { legend: { display: false } },
scales: { scales: {
x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } }, x: { ticks: { color: textColor }, grid: { color: gridColor, drawBorder: false } },
y: { ticks: { color: textColor }, grid: { display: false } } y: { ticks: { color: textColor, autoSkip: false }, grid: { display: false } }
} }
} }
}); });