id(); $table->string('type'); // nitro, emulator, sql, config $table->string('action'); // update, build, deploy, fix $table->string('item')->nullable(); // filename, version, etc $table->string('status'); // success, failed, pending $table->text('message')->nullable(); $table->string('user')->nullable(); $table->string('ip')->nullable(); $table->timestamp('created_at')->useCurrent(); $table->index(['type', 'created_at']); $table->index('status'); }); } } public function log(string $type, string $action, ?string $item = null, string $status = 'success', ?string $message = null): void { $this->ensureTableExists(); DB::table(self::TABLE)->insert([ 'type' => $type, 'action' => $action, 'item' => $item, 'status' => $status, 'message' => $message, 'user' => auth()->user()?->name ?? 'System', 'ip' => request()->ip(), 'created_at' => now(), ]); } public function getRecent(int $limit = 50): array { $this->ensureTableExists(); return DB::table(self::TABLE) ->orderBy('created_at', 'desc') ->limit($limit) ->get() ->toArray(); } public function getByType(string $type, int $limit = 50): array { $this->ensureTableExists(); return DB::table(self::TABLE) ->where('type', $type) ->orderBy('created_at', 'desc') ->limit($limit) ->get() ->toArray(); } public function getByStatus(string $status, int $limit = 50): array { $this->ensureTableExists(); return DB::table(self::TABLE) ->where('status', $status) ->orderBy('created_at', 'desc') ->limit($limit) ->get() ->toArray(); } public function getStats(): array { $this->ensureTableExists(); $total = DB::table(self::TABLE)->count(); $success = DB::table(self::TABLE)->where('status', 'success')->count(); $failed = DB::table(self::TABLE)->where('status', 'failed')->count(); $lastUpdate = DB::table(self::TABLE) ->orderBy('created_at', 'desc') ->first(); $byType = DB::table(self::TABLE) ->select('type', DB::raw('count(*) as count')) ->groupBy('type') ->pluck('count', 'type') ->toArray(); return [ 'total' => $total, 'success' => $success, 'failed' => $failed, 'success_rate' => $total > 0 ? round(($success / $total) * 100) : 100, 'last_update' => $lastUpdate, 'by_type' => $byType, ]; } public function getHtml(): string { $this->ensureTableExists(); $updates = $this->getRecent(20); $stats = $this->getStats(); $html = '
'; // Stats $html .= '
'; $html .= '
'; $html .= '
' . $stats['total'] . '
'; $html .= '
Totaal
'; $html .= '
'; $html .= '
' . $stats['success'] . '
'; $html .= '
Geslaagd
'; $html .= '
'; $html .= '
' . $stats['failed'] . '
'; $html .= '
Mislukt
'; $html .= '
'; $html .= '
' . $stats['success_rate'] . '%
'; $html .= '
Succes
'; $html .= '
'; // History list if ($updates === []) { $html .= '
'; $html .= '
📋
'; $html .= 'Nog geen update geschiedenis
'; } else { $html .= '
'; foreach ($updates as $update) { $icon = match ($update->status) { 'success' => '✅', 'failed' => '❌', 'pending' => '⏳', default => '⚪' }; $typeColor = match ($update->type) { 'nitro' => '#4ade80', 'emulator' => '#60a5fa', 'sql' => '#fbbf24', 'config' => '#a78bfa', default => '#94a3b8' }; $time = Carbon::parse($update->created_at)->diffForHumans(); $html .= '
'; $html .= '
' . $icon . '
'; $html .= '
'; $html .= '
'; $html .= '' . e($update->type) . ''; $html .= '' . e($update->action) . ''; if ($update->item) { $html .= '' . e($update->item) . ''; } $html .= '
'; if ($update->message) { $html .= '
' . e($update->message) . '
'; } $html .= '
'; $html .= '
'; $html .= '
' . e($update->user) . '
'; $html .= '
' . e($time) . '
'; $html .= '
'; $html .= '
'; } $html .= '
'; } return $html . '
'; } }