diff --git a/app/Livewire/Project/Resource/Index.php b/app/Livewire/Project/Resource/Index.php index be6e3e98f..094b61b28 100644 --- a/app/Livewire/Project/Resource/Index.php +++ b/app/Livewire/Project/Resource/Index.php @@ -13,33 +13,33 @@ class Index extends Component public Environment $environment; - public Collection $applications; - - public Collection $postgresqls; - - public Collection $redis; - - public Collection $mongodbs; - - public Collection $mysqls; - - public Collection $mariadbs; - - public Collection $keydbs; - - public Collection $dragonflies; - - public Collection $clickhouses; - - public Collection $services; - public Collection $allProjects; public Collection $allEnvironments; public array $parameters; - public function mount() + protected Collection $applications; + + protected Collection $postgresqls; + + protected Collection $redis; + + protected Collection $mongodbs; + + protected Collection $mysqls; + + protected Collection $mariadbs; + + protected Collection $keydbs; + + protected Collection $dragonflies; + + protected Collection $clickhouses; + + protected Collection $services; + + public function mount(): void { $this->applications = $this->postgresqls = $this->redis = $this->mongodbs = $this->mysqls = $this->mariadbs = $this->keydbs = $this->dragonflies = $this->clickhouses = $this->services = collect(); $this->parameters = get_route_parameters(); @@ -55,31 +55,23 @@ public function mount() $this->project = $project; - // Load projects and environments for breadcrumb navigation (avoids inline queries in view) + // Load projects and environments for breadcrumb navigation $this->allProjects = Project::ownedByCurrentTeamCached(); $this->allEnvironments = $project->environments() + ->select('id', 'uuid', 'name', 'project_id') ->with([ - 'applications.additional_servers', - 'applications.destination.server', - 'services', - 'services.destination.server', - 'postgresqls', - 'postgresqls.destination.server', - 'redis', - 'redis.destination.server', - 'mongodbs', - 'mongodbs.destination.server', - 'mysqls', - 'mysqls.destination.server', - 'mariadbs', - 'mariadbs.destination.server', - 'keydbs', - 'keydbs.destination.server', - 'dragonflies', - 'dragonflies.destination.server', - 'clickhouses', - 'clickhouses.destination.server', - ])->get(); + 'applications:id,uuid,name,environment_id', + 'services:id,uuid,name,environment_id', + 'postgresqls:id,uuid,name,environment_id', + 'redis:id,uuid,name,environment_id', + 'mongodbs:id,uuid,name,environment_id', + 'mysqls:id,uuid,name,environment_id', + 'mariadbs:id,uuid,name,environment_id', + 'keydbs:id,uuid,name,environment_id', + 'dragonflies:id,uuid,name,environment_id', + 'clickhouses:id,uuid,name,environment_id', + ]) + ->get(); $this->environment = $environment->loadCount([ 'applications', @@ -94,11 +86,9 @@ public function mount() 'services', ]); - // Eager load all relationships for applications including nested ones + // Eager load relationships for applications $this->applications = $this->environment->applications()->with([ 'tags', - 'additional_servers.settings', - 'additional_networks', 'destination.server.settings', 'settings', ])->get()->sortBy('name'); @@ -160,6 +150,49 @@ public function mount() public function render() { - return view('livewire.project.resource.index'); + return view('livewire.project.resource.index', [ + 'applications' => $this->applications, + 'postgresqls' => $this->postgresqls, + 'redis' => $this->redis, + 'mongodbs' => $this->mongodbs, + 'mysqls' => $this->mysqls, + 'mariadbs' => $this->mariadbs, + 'keydbs' => $this->keydbs, + 'dragonflies' => $this->dragonflies, + 'clickhouses' => $this->clickhouses, + 'services' => $this->services, + 'applicationsJs' => $this->toSearchableArray($this->applications), + 'postgresqlsJs' => $this->toSearchableArray($this->postgresqls), + 'redisJs' => $this->toSearchableArray($this->redis), + 'mongodbsJs' => $this->toSearchableArray($this->mongodbs), + 'mysqlsJs' => $this->toSearchableArray($this->mysqls), + 'mariadbsJs' => $this->toSearchableArray($this->mariadbs), + 'keydbsJs' => $this->toSearchableArray($this->keydbs), + 'dragonfliesJs' => $this->toSearchableArray($this->dragonflies), + 'clickhousesJs' => $this->toSearchableArray($this->clickhouses), + 'servicesJs' => $this->toSearchableArray($this->services), + ]); + } + + private function toSearchableArray(Collection $items): array + { + return $items->map(fn ($item) => [ + 'uuid' => $item->uuid, + 'name' => $item->name, + 'fqdn' => $item->fqdn ?? null, + 'description' => $item->description ?? null, + 'status' => $item->status ?? '', + 'server_status' => $item->server_status ?? null, + 'hrefLink' => $item->hrefLink ?? '', + 'destination' => [ + 'server' => [ + 'name' => $item->destination?->server?->name ?? 'Unknown', + ], + ], + 'tags' => $item->tags->map(fn ($tag) => [ + 'id' => $tag->id, + 'name' => $tag->name, + ])->values()->toArray(), + ])->values()->toArray(); } } diff --git a/resources/views/components/resources/breadcrumbs.blade.php b/resources/views/components/resources/breadcrumbs.blade.php index 135cad3a7..300a8d6e2 100644 --- a/resources/views/components/resources/breadcrumbs.blade.php +++ b/resources/views/components/resources/breadcrumbs.blade.php @@ -12,17 +12,18 @@ $projects = $projects ?? Project::ownedByCurrentTeamCached(); $environments = $environments ?? $resource->environment->project ->environments() + ->select('id', 'uuid', 'name', 'project_id') ->with([ - 'applications', - 'services', - 'postgresqls', - 'redis', - 'mongodbs', - 'mysqls', - 'mariadbs', - 'keydbs', - 'dragonflies', - 'clickhouses', + 'applications:id,uuid,name,environment_id', + 'services:id,uuid,name,environment_id', + 'postgresqls:id,uuid,name,environment_id', + 'redis:id,uuid,name,environment_id', + 'mongodbs:id,uuid,name,environment_id', + 'mysqls:id,uuid,name,environment_id', + 'mariadbs:id,uuid,name,environment_id', + 'keydbs:id,uuid,name,environment_id', + 'dragonflies:id,uuid,name,environment_id', + 'clickhouses:id,uuid,name,environment_id', ]) ->get(); $currentProjectUuid = data_get($resource, 'environment.project.uuid'); @@ -63,7 +64,7 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg -
  • +
  • + x-transition:leave-end="opacity-0 scale-95" + class="absolute z-20 top-full mt-1 left-0 sm:left-auto max-w-[calc(100vw-1rem)]" + x-init="$nextTick(() => { const rect = $el.getBoundingClientRect(); if (rect.right > window.innerWidth) { $el.style.left = 'auto'; $el.style.right = '0'; } })">
    @foreach ($environments as $environment) @php - // Use pre-loaded relations instead of databases() method to avoid N+1 queries $envDatabases = collect() ->merge($environment->postgresqls ?? collect()) ->merge($environment->redis ?? collect()) @@ -101,26 +103,17 @@ class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 bor ->merge($environment->dragonflies ?? collect()) ->merge($environment->clickhouses ?? collect()); $envResources = collect() - ->merge( - $environment->applications->map( - fn($app) => ['type' => 'application', 'resource' => $app], - ), - ) - ->merge( - $envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db]), - ) - ->merge( - $environment->services->map( - fn($svc) => ['type' => 'service', 'resource' => $svc], - ), - ); + ->merge($environment->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($environment->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])) + ->sortBy(fn($item) => strtolower($item['resource']->name)); @endphp
    $environment->uuid, + 'project_uuid' => $currentProjectUuid, + ]) }}" {{ wireNavigate() }} class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200 {{ $environment->uuid === $currentEnvironmentUuid ? 'dark:text-warning font-semibold' : '' }}" title="{{ $environment->name }}"> {{ $environment->name }} @@ -150,31 +143,29 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover @foreach ($environments as $environment) @php + $envDatabases = collect() + ->merge($environment->postgresqls ?? collect()) + ->merge($environment->redis ?? collect()) + ->merge($environment->mongodbs ?? collect()) + ->merge($environment->mysqls ?? collect()) + ->merge($environment->mariadbs ?? collect()) + ->merge($environment->keydbs ?? collect()) + ->merge($environment->dragonflies ?? collect()) + ->merge($environment->clickhouses ?? collect()); $envResources = collect() - ->merge( - $environment->applications->map( - fn($app) => ['type' => 'application', 'resource' => $app], - ), - ) - ->merge( - $environment - ->databases() - ->map(fn($db) => ['type' => 'database', 'resource' => $db]), - ) - ->merge( - $environment->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc]), - ); + ->merge($environment->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($environment->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])); @endphp @if ($envResources->count() > 0)
    - - - @foreach ($envResources as $envResource) - @php - $resType = $envResource['type']; - $res = $envResource['resource']; - $resParams = [ - 'project_uuid' => $currentProjectUuid, - 'environment_uuid' => $environment->uuid, - ]; - if ($resType === 'application') { - $resParams['application_uuid'] = $res->uuid; - } elseif ($resType === 'service') { - $resParams['service_uuid'] = $res->uuid; - } else { - $resParams['database_uuid'] = $res->uuid; - } - $resKey = $environment->uuid . '-' . $res->uuid; - @endphp -
    - -
    - @if ($resType === 'application') - - Deployments - Logs - @can('canAccessTerminal') - Terminal - @endcan - @elseif ($resType === 'service') - - Logs - @can('canAccessTerminal') - Terminal - @endcan - @else - - Logs - @can('canAccessTerminal') - Terminal - @endcan - @if ( - $res->getMorphClass() === 'App\Models\StandalonePostgresql' || - $res->getMorphClass() === 'App\Models\StandaloneMongodb' || - $res->getMorphClass() === 'App\Models\StandaloneMysql' || - $res->getMorphClass() === 'App\Models\StandaloneMariadb') - Backups - @endif - @endif -
    - - - -
    - @endforeach
    @endif @endforeach @@ -431,7 +210,6 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg $isApplication = $resourceType === 'App\Models\Application'; $isService = $resourceType === 'App\Models\Service'; $isDatabase = str_contains($resourceType, 'Database') || str_contains($resourceType, 'Standalone'); - // Use loaded relation count if available, otherwise check additional_servers_count attribute $hasMultipleServers = $isApplication && method_exists($resource, 'additional_servers') && ($resource->relationLoaded('additional_servers') ? $resource->additional_servers->count() > 0 : ($resource->additional_servers_count ?? 0) > 0); $serverName = $hasMultipleServers ? null : data_get($resource, 'destination.server.name'); @@ -447,221 +225,16 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg $routeParams['database_uuid'] = $resourceUuid; } @endphp -
  • -
    - - {{ data_get($resource, 'name') }}@if($serverName) ({{ $serverName }})@endif - - - - -
    - -
    - @if ($isApplication) - -
    - - Configuration - - - - -
    - - Deployments - - - Logs - - @can('canAccessTerminal') - - Terminal - - @endcan - @elseif ($isService) - -
    - - Configuration - - - - -
    - - Logs - - @can('canAccessTerminal') - - Terminal - - @endcan - @else - -
    - - Configuration - - - - -
    - - Logs - - @can('canAccessTerminal') - - Terminal - - @endcan - @if ( - $resourceType === 'App\Models\StandalonePostgresql' || - $resourceType === 'App\Models\StandaloneMongodb' || - $resourceType === 'App\Models\StandaloneMysql' || - $resourceType === 'App\Models\StandaloneMariadb') - - Backups - - @endif - @endif -
    - - - -
    -
    +
  • + + {{ data_get($resource, 'name') }}@if($serverName) ({{ $serverName }})@endif +
  • diff --git a/resources/views/livewire/project/resource/index.blade.php b/resources/views/livewire/project/resource/index.blade.php index f18df5061..a38a04043 100644 --- a/resources/views/livewire/project/resource/index.blade.php +++ b/resources/views/livewire/project/resource/index.blade.php @@ -60,36 +60,13 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg -
  • +
  • {{ $environment->name }} - -
    @foreach ($allEnvironments as $env) @php + $envDatabases = collect() + ->merge($env->postgresqls ?? collect()) + ->merge($env->redis ?? collect()) + ->merge($env->mongodbs ?? collect()) + ->merge($env->mysqls ?? collect()) + ->merge($env->mariadbs ?? collect()) + ->merge($env->keydbs ?? collect()) + ->merge($env->dragonflies ?? collect()) + ->merge($env->clickhouses ?? collect()); $envResources = collect() - ->merge( - $env->applications->map( - fn($app) => ['type' => 'application', 'resource' => $app], - ), - ) - ->merge( - $env - ->databases() - ->map(fn($db) => ['type' => 'database', 'resource' => $db]), - ) - ->merge( - $env->services->map( - fn($svc) => ['type' => 'service', 'resource' => $svc], - ), - ); + ->merge($env->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($env->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])) + ->sortBy(fn($item) => strtolower($item['resource']->name)); @endphp
    {{ $env->name }} @@ -153,7 +129,6 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover @foreach ($allEnvironments as $env) @php - // Use pre-loaded relations instead of databases() method to avoid N+1 queries $envDatabases = collect() ->merge($env->postgresqls ?? collect()) ->merge($env->redis ?? collect()) @@ -164,28 +139,19 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover ->merge($env->dragonflies ?? collect()) ->merge($env->clickhouses ?? collect()); $envResources = collect() - ->merge( - $env->applications->map( - fn($app) => ['type' => 'application', 'resource' => $app], - ), - ) - ->merge( - $envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db]), - ) - ->merge( - $env->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc]), - ); + ->merge($env->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($env->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])); @endphp @if ($envResources->count() > 0)
    - - - @foreach ($envResources as $envResource) - @php - $resType = $envResource['type']; - $res = $envResource['resource']; - $resParams = [ - 'project_uuid' => $project->uuid, - 'environment_uuid' => $env->uuid, - ]; - if ($resType === 'application') { - $resParams['application_uuid'] = $res->uuid; - } elseif ($resType === 'service') { - $resParams['service_uuid'] = $res->uuid; - } else { - $resParams['database_uuid'] = $res->uuid; - } - $resKey = $env->uuid . '-' . $res->uuid; - @endphp -
    - -
    - @if ($resType === 'application') - - Deployments - Logs - @can('canAccessTerminal') - Terminal - @endcan - @elseif ($resType === 'service') - - Logs - @can('canAccessTerminal') - Terminal - @endcan - @else - - Logs - @can('canAccessTerminal') - Terminal - @endcan - @if ( - $res->getMorphClass() === 'App\Models\StandalonePostgresql' || - $res->getMorphClass() === 'App\Models\StandaloneMongodb' || - $res->getMorphClass() === 'App\Models\StandaloneMysql' || - $res->getMorphClass() === 'App\Models\StandaloneMariadb') - Backups - @endif - @endif -
    - - - -
    - @endforeach
    @endif @endforeach @@ -656,16 +395,16 @@ function sortFn(a, b) { function searchComponent() { return { search: '', - applications: @js($applications), - postgresqls: @js($postgresqls), - redis: @js($redis), - mongodbs: @js($mongodbs), - mysqls: @js($mysqls), - mariadbs: @js($mariadbs), - keydbs: @js($keydbs), - dragonflies: @js($dragonflies), - clickhouses: @js($clickhouses), - services: @js($services), + applications: @js($applicationsJs), + postgresqls: @js($postgresqlsJs), + redis: @js($redisJs), + mongodbs: @js($mongodbsJs), + mysqls: @js($mysqlsJs), + mariadbs: @js($mariadbsJs), + keydbs: @js($keydbsJs), + dragonflies: @js($dragonfliesJs), + clickhouses: @js($clickhousesJs), + services: @js($servicesJs), filterAndSort(items) { if (this.search === '') { return Object.values(items).sort(sortFn);