with('establishment:id,name,uuid'); $this->scopeEstablishmentQuery($query); if ($request->filled('status')) { $query->where('status', $request->string('status')); } if ($request->filled('type')) { $query->where('type', $request->string('type')); } if ($request->filled('search')) { $search = $request->string('search'); $query->where(function ($query) use ($search) { $query->where('name', 'like', "%{$search}%") ->orWhere('qr_code', 'like', "%{$search}%"); }); } $machines = $query ->orderBy('name') ->paginate(20) ->withQueryString() ->through(fn (Machine $machine) => $this->formatMachineListItem($machine)); return Inertia::render('Supervisor/Machines/Index', [ 'machines' => $machines, 'filters' => [ 'status' => $request->string('status')->toString() ?: null, 'type' => $request->string('type')->toString() ?: null, 'search' => $request->string('search')->toString() ?: null, ], 'statusOptions' => $this->statusOptions(), 'typeOptions' => $this->typeOptions(), ]); } public function show(string $uuid): Response { $query = Machine::query()->where('uuid', $uuid); $this->scopeEstablishmentQuery($query); $machine = $query ->with(['establishment:id,name,uuid,address,city', 'machineIntegration', 'currentUser:id,first_name,last_name,email']) ->firstOrFail(); $recentEvents = MachineEvent::query() ->where('machine_id', $machine->id) ->orderByDesc('occurred_at') ->limit(20) ->get() ->map(fn (MachineEvent $event) => [ 'event_type' => $event->event_type, 'event_type_label' => $this->eventTypeLabel($event->event_type), 'occurred_at' => $event->occurred_at?->toIso8601String(), 'processing_status' => $event->processing_status, ]); return Inertia::render('Supervisor/Machines/Show', [ 'machine' => [ 'uuid' => $machine->uuid, 'name' => $machine->name, 'type' => $machine->type, 'type_label' => $this->typeLabel($machine->type), 'qr_code' => $machine->qr_code, 'status' => $machine->status, 'status_label' => $this->statusLabel($machine->status), 'cycle_started_at' => $machine->cycle_started_at?->toIso8601String(), 'cycle_ends_at' => $machine->cycle_ends_at?->toIso8601String(), 'last_heartbeat_at' => $machine->last_heartbeat_at?->toIso8601String(), 'establishment' => $machine->establishment ? [ 'uuid' => $machine->establishment->uuid, 'name' => $machine->establishment->name, 'address' => $machine->establishment->address, 'city' => $machine->establishment->city, ] : null, 'current_user' => $machine->currentUser ? [ 'name' => trim($machine->currentUser->first_name.' '.$machine->currentUser->last_name), 'email' => $machine->currentUser->email, ] : null, 'integration' => $machine->machineIntegration ? [ 'provider' => $machine->machineIntegration->provider, 'mode' => $machine->machineIntegration->mode, 'is_active' => $machine->machineIntegration->is_active, ] : null, ], 'recentEvents' => $recentEvents, ]); } /** * @return array */ private function formatMachineListItem(Machine $machine): array { return [ 'uuid' => $machine->uuid, 'name' => $machine->name, 'type' => $machine->type, 'type_label' => $this->typeLabel($machine->type), 'status' => $machine->status, 'status_label' => $this->statusLabel($machine->status), 'establishment_name' => $machine->establishment?->name, 'last_heartbeat_at' => $machine->last_heartbeat_at?->toIso8601String(), ]; } /** * @return array */ private function statusOptions(): array { return [ 'available' => 'Disponible', 'reserved' => 'Réservée', 'running' => 'En cours', 'maintenance' => 'Maintenance', 'offline' => 'Hors ligne', 'error' => 'Erreur', ]; } /** * @return array */ private function typeOptions(): array { return [ 'washer_small' => 'Lave-linge petit', 'washer_large' => 'Lave-linge grand', 'dryer_small' => 'Sèche-linge petit', 'dryer_large' => 'Sèche-linge grand', ]; } private function statusLabel(string $status): string { return $this->statusOptions()[$status] ?? $status; } private function typeLabel(string $type): string { return $this->typeOptions()[$type] ?? $type; } private function eventTypeLabel(string $type): string { return match ($type) { 'heartbeat' => 'Signal de vie', 'machine_online' => 'En ligne', 'machine_offline' => 'Hors ligne', 'cycle_started' => 'Cycle démarré', 'cycle_completed' => 'Cycle terminé', 'cycle_failed' => 'Cycle échoué', 'error_reported' => 'Erreur signalée', 'status_changed' => 'Changement de statut', default => $type, }; } }