From b2f2343592092e40b2df7a974f1a0802d38783ed Mon Sep 17 00:00:00 2001 From: Bryam105 Date: Fri, 23 Jan 2026 11:56:27 -0330 Subject: [PATCH] NUEVO: Implementado PROTOTIPO (v0.1) de taller, creado a partir de modelo kanban con columnas de estados. PENDIENTE DE MUCHOS CAMBIOS... --- app/Http/Controllers/TallerController.php | 77 +++++++++++++++++++ app/Models/Appointment.php | 3 + ...dd_taller_fields_to_appointments_table.php | 35 +++++++++ .../views/components/taller-card.blade.php | 72 +++++++++++++++++ resources/views/dashboard.blade.php | 2 +- resources/views/taller/create.blade.php | 72 +++++++++++++++++ resources/views/taller/index.blade.php | 54 +++++++++++++ routes/web.php | 9 ++- 8 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/TallerController.php create mode 100644 database/migrations/2026_01_23_135512_add_taller_fields_to_appointments_table.php create mode 100644 resources/views/components/taller-card.blade.php create mode 100644 resources/views/taller/create.blade.php create mode 100644 resources/views/taller/index.blade.php diff --git a/app/Http/Controllers/TallerController.php b/app/Http/Controllers/TallerController.php new file mode 100644 index 0000000..bdaf41a --- /dev/null +++ b/app/Http/Controllers/TallerController.php @@ -0,0 +1,77 @@ +whereIn('status', ['pending', 'in_progress', 'ready']) // Entregados/historial no se muestra, implementar vista aparte + ->orderBy('scheduled_at', 'asc') + ->get(); + + // Agrupado por estado para las columnas + $pending = $jobs->where('status', 'pending'); + $inProgress = $jobs->where('status', 'in_progress'); + $ready = $jobs->where('status', 'ready'); + + return view('taller.index', compact('pending', 'inProgress', 'ready')); + } + + /** + * Formulario para nuevo servicio + */ + public function create() + { + $clients = Client::orderBy('name')->get(); + return view('taller.create', compact('clients')); + } + + /** + * Guardar el servicio + */ + public function store(Request $request) + { + $request->validate([ + 'client_id' => 'required|exists:clients,id', + 'bike_model' => 'required|string', + 'problem_description' => 'required|string', + 'scheduled_at' => 'required|date', + 'estimated_cost' => 'nullable|numeric', + ]); + + // Buscamos el cliente para copiar su teléfono + $client = Client::find($request->client_id); + + Appointment::create([ + 'client_id' => $request->client_id, + 'contact_phone' => $request->contact_phone ?? $client->phone, + 'bike_model' => $request->bike_model, + 'problem_description' => $request->problem_description, + 'parts_needed' => $request->parts_needed, + 'estimated_cost' => $request->estimated_cost, + 'scheduled_at' => $request->scheduled_at, + 'status' => 'pending', // Estado inicial: Pendiente + ]); + + return redirect()->route('taller.index')->with('success', 'Bicicleta ingresada al taller correctamente.'); + } + + /** + * Mover de estado + */ + public function updateStatus(Request $request, Appointment $appointment) + { + $appointment->update(['status' => $request->status]); + return back()->with('success', 'Estado actualizado.'); + } +} \ No newline at end of file diff --git a/app/Models/Appointment.php b/app/Models/Appointment.php index 85d1752..87a42b2 100644 --- a/app/Models/Appointment.php +++ b/app/Models/Appointment.php @@ -11,9 +11,12 @@ class Appointment extends Model protected $fillable = [ 'client_id', + 'contact_phone', 'scheduled_at', 'bike_model', 'problem_description', + 'parts_needed', + 'estimated_cost', 'status', 'notes' ]; diff --git a/database/migrations/2026_01_23_135512_add_taller_fields_to_appointments_table.php b/database/migrations/2026_01_23_135512_add_taller_fields_to_appointments_table.php new file mode 100644 index 0000000..5f1690e --- /dev/null +++ b/database/migrations/2026_01_23_135512_add_taller_fields_to_appointments_table.php @@ -0,0 +1,35 @@ +text('parts_needed')->nullable()->after('problem_description'); + + // Costo Aproximado (Presupuesto) + $table->decimal('estimated_cost', 10, 2)->nullable()->after('parts_needed'); + + // Teléfono de contacto rápido (por si es distinto al del cliente registrado) + $table->string('contact_phone')->nullable()->after('client_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('appointments', function (Blueprint $table) { + $table->dropColumn(['parts_needed', 'estimated_cost', 'contact_phone']); + }); + } +}; diff --git a/resources/views/components/taller-card.blade.php b/resources/views/components/taller-card.blade.php new file mode 100644 index 0000000..757920e --- /dev/null +++ b/resources/views/components/taller-card.blade.php @@ -0,0 +1,72 @@ +@props(['job', 'color']) + +@php + $borderColor = match($color) { + 'red' => 'border-l-red-500', + 'yellow' => 'border-l-yellow-500', + 'neon' => 'border-l-neon-lime', + default => 'border-l-gray-500' + }; +@endphp + +
+ + +
+ {{ $job->client->name }} + {{ $job->scheduled_at->format('d/m H:i') }} +
+ +

{{ $job->bike_model }}

+

{{ $job->problem_description }}

+ + @if($job->estimated_cost) +
+ Presupuesto: + ${{ number_format($job->estimated_cost, 0) }} +
+ @endif + + +
+ + + @if($job->status !== 'pending') +
+ @csrf @method('PATCH') + + +
+ @else +
+ @endif + + + + + Avisar + + + + @if($job->status !== 'ready') +
+ @csrf @method('PATCH') + + +
+ @elseif($job->status === 'ready') + +
+ @csrf @method('PATCH') + + +
+ @endif +
+
\ No newline at end of file diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index ec8aef8..b786324 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -16,7 +16,7 @@ - + diff --git a/resources/views/taller/create.blade.php b/resources/views/taller/create.blade.php new file mode 100644 index 0000000..94797f5 --- /dev/null +++ b/resources/views/taller/create.blade.php @@ -0,0 +1,72 @@ + + + + +
+
+ @csrf + + +
+

1. Cliente y Contacto

+
+
+ + + + Nuevo Cliente +
+
+ + +
+
+
+ + +
+

2. Diagnóstico Inicial

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

3. Acuerdo

+
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/resources/views/taller/index.blade.php b/resources/views/taller/index.blade.php new file mode 100644 index 0000000..f7dc5f2 --- /dev/null +++ b/resources/views/taller/index.blade.php @@ -0,0 +1,54 @@ + + + + + +
+ + +
+
+

🔴 Pendientes / A Revisar

+ {{ $pending->count() }} +
+
+ @foreach($pending as $job) + + @endforeach +
+
+ + +
+
+

🟡 En Reparación

+ {{ $inProgress->count() }} +
+
+ @foreach($inProgress as $job) + + @endforeach +
+
+ + +
+
+

🟢 Listas para Retirar

+ {{ $ready->count() }} +
+
+ @foreach($ready as $job) + + @endforeach +
+
+ +
+ +
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 5e6ba69..61e25b2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ use App\Http\Controllers\HomeController; use App\Http\Controllers\LoginController; use App\Http\Controllers\ProductosController; use App\Http\Controllers\RegisterController; +use App\Http\Controllers\TallerController; use App\Models\Product; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Session; @@ -40,7 +41,13 @@ Route::middleware(['auth'])->group(function () { 'productos' => 'product' ]); Route::resource('sales', SaleController::class)->only(['index', 'create', 'store', 'show']); - + // Rutas de Taller + Route::controller(TallerController::class)->prefix('taller')->name('taller.')->group(function () { + Route::get('/', 'index')->name('index'); + Route::get('/create', 'create')->name('create'); + Route::post('/', 'store')->name('store'); + Route::patch('/{appointment}/status', 'updateStatus')->name('updateStatus'); + }); });