feat(ui): show server name on resource card (#7417)
This commit is contained in:
parent
8ba253dcd9
commit
cc3e39db1f
1 changed files with 253 additions and 105 deletions
|
|
@ -14,8 +14,8 @@
|
|||
@endcan
|
||||
@else
|
||||
@can('createAnyResource')
|
||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}" {{ wireNavigate() }}
|
||||
class="button">+
|
||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
||||
{{ wireNavigate() }} class="button">+
|
||||
New</a>
|
||||
@endcan
|
||||
@can('createAnyResource')
|
||||
|
|
@ -40,16 +40,18 @@ class="button">+
|
|||
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
|
||||
{{ $project->name }}</a>
|
||||
<button type="button" @click.stop="toggle()" class="px-1 text-warning">
|
||||
<svg class="w-3 h-3 transition-transform" :class="{ 'rotate-90': projectOpen }" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<svg class="w-3 h-3 transition-transform" :class="{ 'rotate-90': projectOpen }"
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div x-show="projectOpen" @click.outside="close()" x-transition:enter="transition ease-out duration-200"
|
||||
<div x-show="projectOpen" @click.outside="close()"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75" x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
|
||||
class="absolute z-20 top-full mt-1 w-56 -ml-2 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@foreach ($projects as $proj)
|
||||
<a href="{{ route('project.show', ['project_uuid' => $proj->uuid]) }}"
|
||||
|
|
@ -62,9 +64,27 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg
|
|||
</div>
|
||||
</li>
|
||||
@php
|
||||
$allEnvironments = $project->environments()->with(['applications', 'services'])->get();
|
||||
$allEnvironments = $project
|
||||
->environments()
|
||||
->with(['applications', 'services'])
|
||||
->get();
|
||||
@endphp
|
||||
<li class="inline-flex items-center" x-data="{ envOpen: false, activeEnv: null, envPositions: {}, activeRes: null, resPositions: {}, activeMenuEnv: null, menuPositions: {}, closeTimeout: null, envTimeout: null, resTimeout: null, menuTimeout: null, toggle() { this.envOpen = !this.envOpen; if (!this.envOpen) { this.activeEnv = null; this.activeRes = null; this.activeMenuEnv = null; } }, open() { clearTimeout(this.closeTimeout); this.envOpen = true }, close() { this.closeTimeout = setTimeout(() => { this.envOpen = false; this.activeEnv = null; this.activeRes = null; this.activeMenuEnv = null; }, 100) }, openEnv(id) { clearTimeout(this.closeTimeout); clearTimeout(this.envTimeout); this.activeEnv = id }, closeEnv() { this.envTimeout = setTimeout(() => { this.activeEnv = null; this.activeRes = null; this.activeMenuEnv = null; }, 100) }, openRes(id) { clearTimeout(this.envTimeout); clearTimeout(this.resTimeout); this.activeRes = id }, closeRes() { this.resTimeout = setTimeout(() => { this.activeRes = null; this.activeMenuEnv = null; }, 100) }, openMenu(id) { clearTimeout(this.resTimeout); clearTimeout(this.menuTimeout); this.activeMenuEnv = id }, closeMenu() { this.menuTimeout = setTimeout(() => { this.activeMenuEnv = null; }, 100) } }">
|
||||
<li class="inline-flex items-center" x-data="{ envOpen: false, activeEnv: null, envPositions: {}, activeRes: null, resPositions: {}, activeMenuEnv: null, menuPositions: {}, closeTimeout: null, envTimeout: null, resTimeout: null, menuTimeout: null, toggle() { this.envOpen = !this.envOpen; if (!this.envOpen) { this.activeEnv = null;
|
||||
this.activeRes = null;
|
||||
this.activeMenuEnv = null; } }, open() { clearTimeout(this.closeTimeout);
|
||||
this.envOpen = true }, close() { this.closeTimeout = setTimeout(() => { this.envOpen = false;
|
||||
this.activeEnv = null;
|
||||
this.activeRes = null;
|
||||
this.activeMenuEnv = null; }, 100) }, openEnv(id) { clearTimeout(this.closeTimeout);
|
||||
clearTimeout(this.envTimeout);
|
||||
this.activeEnv = id }, closeEnv() { this.envTimeout = setTimeout(() => { this.activeEnv = null;
|
||||
this.activeRes = null;
|
||||
this.activeMenuEnv = null; }, 100) }, openRes(id) { clearTimeout(this.envTimeout);
|
||||
clearTimeout(this.resTimeout);
|
||||
this.activeRes = id }, closeRes() { this.resTimeout = setTimeout(() => { this.activeRes = null;
|
||||
this.activeMenuEnv = null; }, 100) }, openMenu(id) { clearTimeout(this.resTimeout);
|
||||
clearTimeout(this.menuTimeout);
|
||||
this.activeMenuEnv = id }, closeMenu() { this.menuTimeout = setTimeout(() => { this.activeMenuEnv = null; }, 100) } }">
|
||||
<div class="flex items-center relative" @mouseenter="open()" @mouseleave="close()">
|
||||
<a class="text-xs truncate lg:text-sm hover:text-warning" {{ wireNavigate() }}
|
||||
href="{{ route('project.resource.index', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => $environment->uuid]) }}">
|
||||
|
|
@ -73,40 +93,61 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg
|
|||
<button type="button" @click.stop="toggle()" class="px-1 text-warning">
|
||||
<svg class="w-3 h-3 transition-transform" :class="{ 'rotate-90': envOpen }" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Environment Dropdown Container -->
|
||||
<div x-show="envOpen" @click.outside="close()" x-transition:enter="transition ease-out duration-200"
|
||||
<div x-show="envOpen" @click.outside="close()"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75" x-transition:leave-start="opacity-100 scale-100"
|
||||
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'; } })">
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100" 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'; } })">
|
||||
<!-- Environment List -->
|
||||
<div class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
<div
|
||||
class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@foreach ($allEnvironments as $env)
|
||||
@php
|
||||
$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(
|
||||
$env
|
||||
->databases()
|
||||
->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
)
|
||||
->merge(
|
||||
$env->services->map(
|
||||
fn($svc) => ['type' => 'service', 'resource' => $svc],
|
||||
),
|
||||
);
|
||||
@endphp
|
||||
<div @mouseenter="openEnv('{{ $env->uuid }}'); envPositions['{{ $env->uuid }}'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)" @mouseleave="closeEnv()">
|
||||
<div @mouseenter="openEnv('{{ $env->uuid }}'); envPositions['{{ $env->uuid }}'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
@mouseleave="closeEnv()">
|
||||
<a href="{{ route('project.resource.index', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => $env->uuid]) }}"
|
||||
class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200 {{ $env->uuid === $environment->uuid ? 'dark:text-warning font-semibold' : '' }}"
|
||||
title="{{ $env->name }}">
|
||||
<span class="truncate">{{ $env->name }}</span>
|
||||
@if ($envResources->count() > 0)
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
@endif
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
<div class="border-t border-neutral-200 dark:border-coolgray-200 mt-1 pt-1">
|
||||
<a href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}" {{ wireNavigate() }}
|
||||
<a href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}"
|
||||
{{ wireNavigate() }}
|
||||
class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
|
|
@ -122,23 +163,35 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover
|
|||
@foreach ($allEnvironments as $env)
|
||||
@php
|
||||
$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(
|
||||
$env
|
||||
->databases()
|
||||
->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
)
|
||||
->merge(
|
||||
$env->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc]),
|
||||
);
|
||||
@endphp
|
||||
@if ($envResources->count() > 0)
|
||||
<div x-show="activeEnv === '{{ $env->uuid }}'" x-cloak
|
||||
x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
||||
@mouseenter="openEnv('{{ $env->uuid }}')" @mouseleave="closeEnv()"
|
||||
:style="'position: absolute; left: 100%; top: ' + (envPositions['{{ $env->uuid }}'] || 0) + 'px; z-index: 30;'"
|
||||
:style="'position: absolute; left: 100%; top: ' + (envPositions[
|
||||
'{{ $env->uuid }}'] || 0) + 'px; z-index: 30;'"
|
||||
class="flex flex-col sm:flex-row items-start pl-1">
|
||||
<div class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
<div
|
||||
class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@foreach ($envResources as $envResource)
|
||||
@php
|
||||
$resType = $envResource['type'];
|
||||
$res = $envResource['resource'];
|
||||
$resRoute = match($resType) {
|
||||
$resRoute = match ($resType) {
|
||||
'application' => route('project.application.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_uuid' => $env->uuid,
|
||||
|
|
@ -155,16 +208,28 @@ class="flex flex-col sm:flex-row items-start pl-1">
|
|||
'database_uuid' => $res->uuid,
|
||||
]),
|
||||
};
|
||||
$resHasMultipleServers = $resType === 'application' && method_exists($res, 'additional_servers') && $res->additional_servers()->count() > 0;
|
||||
$resServerName = $resHasMultipleServers ? null : data_get($res, 'destination.server.name');
|
||||
$resHasMultipleServers =
|
||||
$resType === 'application' &&
|
||||
method_exists($res, 'additional_servers') &&
|
||||
$res->additional_servers()->count() > 0;
|
||||
$resServerName = $resHasMultipleServers
|
||||
? null
|
||||
: data_get($res, 'destination.server.name');
|
||||
@endphp
|
||||
<div @mouseenter="openRes('{{ $env->uuid }}-{{ $res->uuid }}'); resPositions['{{ $env->uuid }}-{{ $res->uuid }}'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)" @mouseleave="closeRes()">
|
||||
<div @mouseenter="openRes('{{ $env->uuid }}-{{ $res->uuid }}'); resPositions['{{ $env->uuid }}-{{ $res->uuid }}'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
@mouseleave="closeRes()">
|
||||
<a href="{{ $resRoute }}"
|
||||
class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200"
|
||||
title="{{ $res->name }}{{ $resServerName ? ' ('.$resServerName.')' : '' }}">
|
||||
<span class="truncate">{{ $res->name }}@if($resServerName) <span class="text-xs text-neutral-400">({{ $resServerName }})</span>@endif</span>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
title="{{ $res->name }}{{ $resServerName ? ' (' . $resServerName . ')' : '' }}">
|
||||
<span class="truncate">{{ $res->name }}@if ($resServerName)
|
||||
<span
|
||||
class="text-xs text-neutral-400">({{ $resServerName }})</span>
|
||||
@endif
|
||||
</span>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -190,112 +255,189 @@ class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutra
|
|||
$resKey = $env->uuid . '-' . $res->uuid;
|
||||
@endphp
|
||||
<div x-show="activeRes === '{{ $resKey }}'" x-cloak
|
||||
x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
@mouseenter="openRes('{{ $resKey }}')" @mouseleave="closeRes()"
|
||||
:style="'position: absolute; left: 100%; top: ' + (resPositions['{{ $resKey }}'] || 0) + 'px; z-index: 40;'"
|
||||
:style="'position: absolute; left: 100%; top: ' + (resPositions[
|
||||
'{{ $resKey }}'] || 0) + 'px; z-index: 40;'"
|
||||
class="flex flex-col sm:flex-row items-start pl-1">
|
||||
<!-- Main Menu List -->
|
||||
<div class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200">
|
||||
<div
|
||||
class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200">
|
||||
@if ($resType === 'application')
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)" @mouseleave="closeMenu()">
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
@mouseleave="closeMenu()">
|
||||
<a href="{{ route('project.application.configuration', $resParams) }}"
|
||||
class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">
|
||||
<span>Configuration</span>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="4"
|
||||
d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ route('project.application.deployment.index', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Deployments</a>
|
||||
<a href="{{ route('project.application.logs', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
<a href="{{ route('project.application.deployment.index', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Deployments</a>
|
||||
<a href="{{ route('project.application.logs', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
@can('canAccessTerminal')
|
||||
<a href="{{ route('project.application.command', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
<a href="{{ route('project.application.command', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
@endcan
|
||||
@elseif ($resType === 'service')
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)" @mouseleave="closeMenu()">
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
@mouseleave="closeMenu()">
|
||||
<a href="{{ route('project.service.configuration', $resParams) }}"
|
||||
class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">
|
||||
<span>Configuration</span>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="4"
|
||||
d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ route('project.service.logs', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
<a href="{{ route('project.service.logs', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
@can('canAccessTerminal')
|
||||
<a href="{{ route('project.service.command', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
<a href="{{ route('project.service.command', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
@endcan
|
||||
@else
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)" @mouseleave="closeMenu()">
|
||||
<div @mouseenter="openMenu('{{ $resKey }}-config'); menuPositions['{{ $resKey }}-config'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
@mouseleave="closeMenu()">
|
||||
<a href="{{ route('project.database.configuration', $resParams) }}"
|
||||
class="flex items-center justify-between gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">
|
||||
<span>Configuration</span>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M9 5l7 7-7 7"></path>
|
||||
<svg class="w-3 h-3 shrink-0" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="4"
|
||||
d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ route('project.database.logs', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
<a href="{{ route('project.database.logs', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Logs</a>
|
||||
@can('canAccessTerminal')
|
||||
<a href="{{ route('project.database.command', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
<a href="{{ route('project.database.command', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Terminal</a>
|
||||
@endcan
|
||||
@if (
|
||||
$res->getMorphClass() === 'App\Models\StandalonePostgresql' ||
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||
<a href="{{ route('project.database.backup.index', $resParams) }}" class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Backups</a>
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||
$res->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||
<a href="{{ route('project.database.backup.index', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-coolgray-200">Backups</a>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Configuration Sub-menu (4th level) -->
|
||||
<div x-show="activeMenuEnv === '{{ $resKey }}-config'" x-cloak
|
||||
x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
@mouseenter="openMenu('{{ $resKey }}-config')" @mouseleave="closeMenu()"
|
||||
:style="'position: absolute; left: 100%; top: ' + (menuPositions['{{ $resKey }}-config'] || 0) + 'px; z-index: 50;'"
|
||||
@mouseenter="openMenu('{{ $resKey }}-config')"
|
||||
@mouseleave="closeMenu()"
|
||||
:style="'position: absolute; left: 100%; top: ' + (menuPositions[
|
||||
'{{ $resKey }}-config'] || 0) + 'px; z-index: 50;'"
|
||||
class="pl-1">
|
||||
<div class="w-52 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@if ($resType === 'application')
|
||||
<a href="{{ route('project.application.configuration', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.application.environment-variables', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment Variables</a>
|
||||
<a href="{{ route('project.application.persistent-storage', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Persistent Storage</a>
|
||||
<a href="{{ route('project.application.source', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Source</a>
|
||||
<a href="{{ route('project.application.servers', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Servers</a>
|
||||
<a href="{{ route('project.application.scheduled-tasks.show', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Scheduled Tasks</a>
|
||||
<a href="{{ route('project.application.webhooks', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.application.preview-deployments', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Preview Deployments</a>
|
||||
<a href="{{ route('project.application.healthcheck', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Healthcheck</a>
|
||||
<a href="{{ route('project.application.rollback', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Rollback</a>
|
||||
<a href="{{ route('project.application.resource-limits', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource Limits</a>
|
||||
<a href="{{ route('project.application.resource-operations', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource Operations</a>
|
||||
<a href="{{ route('project.application.metrics', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Metrics</a>
|
||||
<a href="{{ route('project.application.tags', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.application.advanced', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Advanced</a>
|
||||
<a href="{{ route('project.application.danger', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger Zone</a>
|
||||
@elseif ($resType === 'service')
|
||||
<a href="{{ route('project.service.configuration', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.service.environment-variables', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment Variables</a>
|
||||
<a href="{{ route('project.service.storages', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Storages</a>
|
||||
<a href="{{ route('project.service.scheduled-tasks.show', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Scheduled Tasks</a>
|
||||
<a href="{{ route('project.service.webhooks', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.service.resource-operations', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource Operations</a>
|
||||
<a href="{{ route('project.service.tags', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.service.danger', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger Zone</a>
|
||||
@else
|
||||
<a href="{{ route('project.database.configuration', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.database.environment-variables', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment Variables</a>
|
||||
<a href="{{ route('project.database.servers', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Servers</a>
|
||||
<a href="{{ route('project.database.persistent-storage', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Persistent Storage</a>
|
||||
<a href="{{ route('project.database.webhooks', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.database.resource-limits', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource Limits</a>
|
||||
<a href="{{ route('project.database.resource-operations', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource Operations</a>
|
||||
<a href="{{ route('project.database.metrics', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Metrics</a>
|
||||
<a href="{{ route('project.database.tags', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.database.danger', $resParams) }}" class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger Zone</a>
|
||||
@endif
|
||||
<div
|
||||
class="w-52 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@if ($resType === 'application')
|
||||
<a href="{{ route('project.application.configuration', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.application.environment-variables', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment
|
||||
Variables</a>
|
||||
<a href="{{ route('project.application.persistent-storage', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Persistent
|
||||
Storage</a>
|
||||
<a href="{{ route('project.application.source', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Source</a>
|
||||
<a href="{{ route('project.application.servers', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Servers</a>
|
||||
<a href="{{ route('project.application.scheduled-tasks.show', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Scheduled
|
||||
Tasks</a>
|
||||
<a href="{{ route('project.application.webhooks', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.application.preview-deployments', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Preview
|
||||
Deployments</a>
|
||||
<a href="{{ route('project.application.healthcheck', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Healthcheck</a>
|
||||
<a href="{{ route('project.application.rollback', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Rollback</a>
|
||||
<a href="{{ route('project.application.resource-limits', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource
|
||||
Limits</a>
|
||||
<a href="{{ route('project.application.resource-operations', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource
|
||||
Operations</a>
|
||||
<a href="{{ route('project.application.metrics', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Metrics</a>
|
||||
<a href="{{ route('project.application.tags', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.application.advanced', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Advanced</a>
|
||||
<a href="{{ route('project.application.danger', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger
|
||||
Zone</a>
|
||||
@elseif ($resType === 'service')
|
||||
<a href="{{ route('project.service.configuration', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.service.environment-variables', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment
|
||||
Variables</a>
|
||||
<a href="{{ route('project.service.storages', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Storages</a>
|
||||
<a href="{{ route('project.service.scheduled-tasks.show', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Scheduled
|
||||
Tasks</a>
|
||||
<a href="{{ route('project.service.webhooks', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.service.resource-operations', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource
|
||||
Operations</a>
|
||||
<a href="{{ route('project.service.tags', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.service.danger', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger
|
||||
Zone</a>
|
||||
@else
|
||||
<a href="{{ route('project.database.configuration', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">General</a>
|
||||
<a href="{{ route('project.database.environment-variables', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Environment
|
||||
Variables</a>
|
||||
<a href="{{ route('project.database.servers', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Servers</a>
|
||||
<a href="{{ route('project.database.persistent-storage', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Persistent
|
||||
Storage</a>
|
||||
<a href="{{ route('project.database.webhooks', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Webhooks</a>
|
||||
<a href="{{ route('project.database.resource-limits', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource
|
||||
Limits</a>
|
||||
<a href="{{ route('project.database.resource-operations', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Resource
|
||||
Operations</a>
|
||||
<a href="{{ route('project.database.metrics', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Metrics</a>
|
||||
<a href="{{ route('project.database.tags', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200">Tags</a>
|
||||
<a href="{{ route('project.database.danger', $resParams) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 text-red-500">Danger
|
||||
Zone</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -311,8 +453,8 @@ class="pl-1">
|
|||
</div>
|
||||
@if ($environment->isEmpty())
|
||||
@can('createAnyResource')
|
||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}" {{ wireNavigate() }}
|
||||
class="items-center justify-center coolbox">+ Add Resource</a>
|
||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
||||
{{ wireNavigate() }} class="items-center justify-center coolbox">+ Add Resource</a>
|
||||
@else
|
||||
<div
|
||||
class="flex flex-col items-center justify-center p-8 text-center border border-dashed border-neutral-300 dark:border-coolgray-300 rounded-lg">
|
||||
|
|
@ -375,6 +517,8 @@ class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
|||
</div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
|
||||
<div class="max-w-full px-4 pt-1 truncate box-description">Server: <span
|
||||
x-text="item.destination?.server?.name || 'Unknown'"></span></div>
|
||||
<template x-if="item.server_status == false">
|
||||
<div class="px-4 text-xs font-bold text-error">Server is unreachable or
|
||||
misconfigured
|
||||
|
|
@ -425,6 +569,8 @@ class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
|||
</div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
|
||||
<div class="max-w-full px-4 pt-1 truncate box-description">Server: <span
|
||||
x-text="item.destination?.server?.name || 'Unknown'"></span></div>
|
||||
<template x-if="item.server_status == false">
|
||||
<div class="px-4 text-xs font-bold text-error">Server is unreachable or
|
||||
misconfigured
|
||||
|
|
@ -475,6 +621,8 @@ class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
|||
</div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
|
||||
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
|
||||
<div class="max-w-full px-4 pt-1 truncate box-description">Server: <span
|
||||
x-text="item.destination?.server?.name || 'Unknown'"></span></div>
|
||||
<template x-if="item.server_status == false">
|
||||
<div class="px-4 text-xs font-bold text-error">Server is unreachable or
|
||||
misconfigured
|
||||
|
|
|
|||
Loading…
Reference in a new issue