up
This commit is contained in:
@@ -16,10 +16,8 @@ class GestionDashboard extends Page
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return [
|
||||
\App\Filament\Widgets\TelasStatsOverview::class,
|
||||
// \App\Filament\Widgets\TelasStatsOverview::class,
|
||||
// \App\Filament\Widgets\TelasEstadoOverview::class,
|
||||
// \App\Filament\Widgets\TelasChart::class,
|
||||
\App\Filament\Widgets\GestionKpisWidget::class,
|
||||
\App\Filament\Widgets\AlertasStockBodegasWidget::class,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ class OrdenProduccionResource extends Resource
|
||||
->default(0),
|
||||
|
||||
DatePicker::make('fecha_inicio')
|
||||
->required(),
|
||||
->required()
|
||||
->default(now()),
|
||||
|
||||
DatePicker::make('fecha_entrega_estimada')
|
||||
->required(),
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\OrdenProduccion;
|
||||
use App\Models\Tela;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class GestionKpisWidget extends BaseWidget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
try {
|
||||
$stats = [];
|
||||
|
||||
$total = OrdenProduccion::count();
|
||||
$stats[] = Stat::make('Órdenes totales', $total)
|
||||
->description('Total de órdenes de producción')
|
||||
->descriptionIcon('heroicon-m-clipboard-list')
|
||||
->color('primary');
|
||||
|
||||
$byState = OrdenProduccion::selectRaw('estado, count(*) as cnt')->groupBy('estado')->pluck('cnt', 'estado')->toArray();
|
||||
|
||||
$stats[] = Stat::make('En corte', $byState['en_corte'] ?? 0)
|
||||
->color('warning');
|
||||
$stats[] = Stat::make('En confección', $byState['en_confeccion'] ?? 0)
|
||||
->color('info');
|
||||
$stats[] = Stat::make('En tintorería', $byState['en_tintoreria'] ?? 0)
|
||||
->color('primary');
|
||||
$stats[] = Stat::make('En acabados', $byState['en_acabados'] ?? 0)
|
||||
->color('secondary');
|
||||
$stats[] = Stat::make('Finalizadas', $byState['finalizada'] ?? 0)
|
||||
->color('success');
|
||||
|
||||
$metros = Tela::sum('metros_disponibles');
|
||||
$stats[] = Stat::make('Metros disponibles', number_format($metros, 2) . ' m')
|
||||
->description('Total metros disponibles en stock')
|
||||
->descriptionIcon('heroicon-m-archive-box');
|
||||
|
||||
$atrasadas = OrdenProduccion::where('fecha_entrega_estimada', '<', now()->toDateString())
|
||||
->where('estado', '!=', 'finalizada')
|
||||
->count();
|
||||
$stats[] = Stat::make('Órdenes atrasadas', $atrasadas)
|
||||
->color($atrasadas > 0 ? 'danger' : 'success')
|
||||
->description('OPs con fecha de entrega vencida');
|
||||
|
||||
return $stats;
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
Stat::make('KPIs', 'Error')
|
||||
->description('Revisar logs')
|
||||
->color('danger'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected function getColumns(): int
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,11 @@ class OrdenProduccion extends Model
|
||||
|
||||
$orden->numero_orden = 'OP-' . str_pad($ultimoId, 6, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// Fecha de inicio por defecto hoy
|
||||
if (! isset($orden->fecha_inicio) || ! $orden->fecha_inicio) {
|
||||
$orden->fecha_inicio = now()->toDateString();
|
||||
}
|
||||
});
|
||||
|
||||
// Validar y reservar tela al cambiar de estado a 'en_corte'
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
@php
|
||||
use App\Models\OrdenProduccion;
|
||||
use App\Models\TrasladoPrenda;
|
||||
|
||||
$kanbanStates = ['en_corte','en_confeccion','en_tintoreria','en_acabados','finalizada'];
|
||||
$ordersByState = [];
|
||||
foreach($kanbanStates as $s) {
|
||||
$ordersByState[$s] = OrdenProduccion::where('estado', $s)->orderBy('fecha_entrega_estimada')->limit(6)->get();
|
||||
}
|
||||
|
||||
$recentTrans = TrasladoPrenda::latest()->limit(8)->get();
|
||||
@endphp
|
||||
|
||||
<div class="space-y-6 px-6">
|
||||
{{-- Header filters (simple) --}}
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold">Dashboard de Gestión</h1>
|
||||
<div class="flex gap-3">
|
||||
<a href="{{ route('filament.admin.resources.orden-produccions.create') }}" class="inline-flex items-center gap-2 px-3 py-2 bg-blue-600 text-white rounded">Crear OP</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- KPIs are rendered by widgets above --}}
|
||||
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
{{-- Kanban column (span 2) --}}
|
||||
<div class="col-span-2 bg-white rounded shadow p-4">
|
||||
<h2 class="text-lg font-semibold mb-3">Pipeline de Órdenes</h2>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
@foreach($ordersByState as $state => $orders)
|
||||
<div class="bg-gray-50 rounded p-3">
|
||||
<h3 class="text-sm font-semibold mb-2">{{ ucfirst(str_replace('_',' ', $state)) }} ({{ $orders->count() }})</h3>
|
||||
<div class="space-y-2">
|
||||
@forelse($orders as $op)
|
||||
<a href="{{ route('filament.admin.resources.orden-produccions.edit', ['record' => $op->id]) }}" class="block p-2 bg-white rounded border hover:shadow">
|
||||
<div class="text-sm font-semibold">#{{ $op->numero_orden }} — {{ $op->prenda_modelo }}</div>
|
||||
<div class="text-xs text-gray-500">Cant: {{ $op->cantidad_total }} · Metros: {{ $op->metros_requeridos }} · Entrega: {{ $op->fecha_entrega_estimada }}</div>
|
||||
</a>
|
||||
@empty
|
||||
<div class="text-sm text-gray-400">Sin órdenes</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Timeline of shipments --}}
|
||||
<div class="bg-white rounded shadow p-4">
|
||||
<h2 class="text-lg font-semibold mb-3">Últimos envíos (traslados)</h2>
|
||||
<ul class="space-y-2">
|
||||
@foreach($recentTrans as $t)
|
||||
<li class="p-2 border rounded flex items-start justify-between">
|
||||
<div>
|
||||
<div class="text-sm font-medium">Traslado #{{ $t->id }} — OP #{{ $t->orden_produccion_id }}</div>
|
||||
<div class="text-xs text-gray-500">{{ ucfirst($t->origen) }} → {{ ucfirst($t->destino) }} · Cant: {{ $t->cantidad_enviada }} · Estado: {{ ucfirst($t->estado) }}</div>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<a href="{{ route('filament.admin.resources.traslado-prendas.edit', ['record' => $t->id]) }}" class="text-blue-600 text-sm">Ver</a>
|
||||
@if($t->estado !== 'recibido')
|
||||
<form method="POST" action="{{ route('filament.traslados.mark-received', ['traslado' => $t->id]) }}">
|
||||
@csrf
|
||||
<button class="px-2 py-1 bg-green-600 text-white rounded text-sm">Marcar recibido</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Activity & Alerts row --}}
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<div class="col-span-2 bg-white rounded shadow p-4">
|
||||
<h3 class="text-lg font-semibold mb-2">Actividad reciente</h3>
|
||||
<p class="text-sm text-gray-500">Aquí verás eventos relevantes: creaciones, traslados, inventarios, etc.</p>
|
||||
{{-- Placeholder: implement live feed --}}
|
||||
</div>
|
||||
<div class="bg-white rounded shadow p-4">
|
||||
<h3 class="text-lg font-semibold mb-2">Alertas</h3>
|
||||
<p class="text-sm text-gray-500">Telas con stock bajo, órdenes atrasadas.</p>
|
||||
{{-- The Alerts widget will be shown in header; this is a placeholder for further details --}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user