fix: 'new image' quick action not progressing to resource selection

Fixed three issues preventing the "new image" quick action from working:

1. Frontend matching logic wasn't checking the quickcommand field
   - Added check for item.quickcommand in the matching logic
   - Now "new image" matches docker-image via its quickcommand "(type: new image)"

2. Search query remained populated after triggering selection flow
   - Clear searchQuery in navigateToResourceCreation() to show selection UI
   - This switches the UI from creatable items list to server selection

3. Redirect wasn't using Livewire's redirect method
   - Changed from redirect()->route() to $this->redirect(route())
   - Ensures proper Livewire component redirect behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai 2025-10-15 10:49:07 +02:00
parent 6d3c996ef3
commit 66cff9d9b8
3 changed files with 208 additions and 120 deletions

View file

@ -1147,6 +1147,9 @@ private function navigateToResourceCreation($type)
$this->selectedResourceType = $type; $this->selectedResourceType = $type;
$this->isSelectingResource = true; $this->isSelectingResource = true;
// Clear search query to show selection UI instead of creatable items
$this->searchQuery = '';
// Reset selections // Reset selections
$this->selectedServerId = null; $this->selectedServerId = null;
$this->selectedDestinationUuid = null; $this->selectedDestinationUuid = null;
@ -1316,10 +1319,10 @@ private function completeResourceCreation()
$queryParams['database_image'] = 'postgres:16-alpine'; $queryParams['database_image'] = 'postgres:16-alpine';
} }
return redirect()->route('project.resource.create', [ $this->redirect(route('project.resource.create', [
'project_uuid' => $this->selectedProjectUuid, 'project_uuid' => $this->selectedProjectUuid,
'environment_uuid' => $this->selectedEnvironmentUuid, 'environment_uuid' => $this->selectedEnvironmentUuid,
] + $queryParams); ] + $queryParams));
} }
} }

View file

@ -136,13 +136,17 @@
'new postgresql', 'new postgres', 'new mysql', 'new mariadb', 'new postgresql', 'new postgres', 'new mysql', 'new mariadb',
'new redis', 'new keydb', 'new dragonfly', 'new mongodb', 'new mongo', 'new clickhouse' 'new redis', 'new keydb', 'new dragonfly', 'new mongodb', 'new mongo', 'new clickhouse'
]; ];
if (exactMatchCommands.includes(trimmed)) { if (exactMatchCommands.includes(trimmed)) {
const matchingItem = this.creatableItems.find(item => { const matchingItem = this.creatableItems.find(item => {
const itemSearchText = `new ${item.name}`.toLowerCase(); const itemSearchText = `new ${item.name}`.toLowerCase();
const itemType = `new ${item.type}`.toLowerCase(); const itemType = `new ${item.type}`.toLowerCase();
return itemSearchText === trimmed || itemType === trimmed || const itemTypeWithSpaces = item.type ? `new ${item.type.replace(/-/g, ' ')}` : '';
(item.type && trimmed.includes(item.type.replace(/-/g, ' ')));
// Check if trimmed matches exactly or if the item's quickcommand includes this command
return itemSearchText === trimmed ||
itemType === trimmed ||
itemTypeWithSpaces === trimmed ||
(item.quickcommand && item.quickcommand.toLowerCase().includes(trimmed));
}); });
if (matchingItem) { if (matchingItem) {
@ -250,8 +254,7 @@
class="fixed top-0 left-0 z-99 flex items-start justify-center w-screen h-screen pt-[10vh]"> class="fixed top-0 left-0 z-99 flex items-start justify-center w-screen h-screen pt-[10vh]">
<div @click="closeModal()" class="absolute inset-0 w-full h-full bg-black/50 backdrop-blur-sm"> <div @click="closeModal()" class="absolute inset-0 w-full h-full bg-black/50 backdrop-blur-sm">
</div> </div>
<div x-show="modalOpen" x-trap.inert="modalOpen" <div x-show="modalOpen" x-trap.inert="modalOpen" x-init="$watch('modalOpen', value => { document.body.style.overflow = value ? 'hidden' : '' })"
x-init="$watch('modalOpen', value => { document.body.style.overflow = value ? 'hidden' : '' })"
x-transition:enter="ease-out duration-200" x-transition:enter-start="opacity-0 -translate-y-4 scale-95" x-transition:enter="ease-out duration-200" x-transition:enter-start="opacity-0 -translate-y-4 scale-95"
x-transition:enter-end="opacity-100 translate-y-0 scale-100" x-transition:leave="ease-in duration-150" x-transition:enter-end="opacity-100 translate-y-0 scale-100" x-transition:leave="ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0 scale-100" x-transition:leave-start="opacity-100 translate-y-0 scale-100"
@ -268,7 +271,8 @@ class="fixed top-0 left-0 z-99 flex items-start justify-center w-screen h-screen
</svg> </svg>
<svg x-show="isLoadingInitialData" x-cloak class="animate-spin h-5 w-5 text-warning" <svg x-show="isLoadingInitialData" x-cloak class="animate-spin h-5 w-5 text-warning"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4">
</circle> </circle>
<path class="opacity-75" fill="currentColor" <path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
@ -307,8 +311,8 @@ class="mt-2 bg-white dark:bg-coolgray-100 rounded-lg shadow-xl ring-1 ring-neutr
class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white"> class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none"
viewBox="0 0 24 24" stroke="currentColor"> viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M15 19l-7-7 7-7" /> stroke-width="2" d="M15 19l-7-7 7-7" />
</svg> </svg>
</button> </button>
<div> <div>
@ -323,11 +327,13 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
</div> </div>
@if ($loadingServers) @if ($loadingServers)
<div class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg"> <div
<svg class="animate-spin h-5 w-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg">
fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-yellow-500"
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="4"></circle> viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" <path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path> </path>
@ -337,7 +343,8 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
@elseif (count($availableServers) > 0) @elseif (count($availableServers) > 0)
@foreach ($availableServers as $index => $server) @foreach ($availableServers as $index => $server)
<button type="button" wire:click="selectServer({{ $server['id'] }}, true)" <button type="button"
wire:click="selectServer({{ $server['id'] }}, true)"
class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0"> class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0">
<div class="flex items-center justify-between gap-3 min-h-[2.5rem]"> <div class="flex items-center justify-between gap-3 min-h-[2.5rem]">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
@ -345,7 +352,8 @@ class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg
{{ $server['name'] }} {{ $server['name'] }}
</div> </div>
@if (!empty($server['description'])) @if (!empty($server['description']))
<div class="text-xs text-neutral-500 dark:text-neutral-400"> <div
class="text-xs text-neutral-500 dark:text-neutral-400">
{{ $server['description'] }} {{ $server['description'] }}
</div> </div>
@else @else
@ -355,10 +363,10 @@ class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg
@endif @endif
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400" fill="none" class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</button> </button>
@ -380,10 +388,10 @@ class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:bo
<button type="button" <button type="button"
@click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)" @click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)"
class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white"> class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M15 19l-7-7 7-7" /> stroke-width="2" d="M15 19l-7-7 7-7" />
</svg> </svg>
</button> </button>
<div> <div>
@ -398,11 +406,13 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
</div> </div>
@if ($loadingDestinations) @if ($loadingDestinations)
<div class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg"> <div
<svg class="animate-spin h-5 w-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg">
fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-yellow-500"
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="4"></circle> viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" <path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path> </path>
@ -412,22 +422,25 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
@elseif (count($availableDestinations) > 0) @elseif (count($availableDestinations) > 0)
@foreach ($availableDestinations as $index => $destination) @foreach ($availableDestinations as $index => $destination)
<button type="button" wire:click="selectDestination('{{ $destination['uuid'] }}', true)" <button type="button"
wire:click="selectDestination('{{ $destination['uuid'] }}', true)"
class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0"> class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0">
<div class="flex items-center justify-between gap-3 min-h-[2.5rem]"> <div
class="flex items-center justify-between gap-3 min-h-[2.5rem]">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="font-medium text-neutral-900 dark:text-white"> <div class="font-medium text-neutral-900 dark:text-white">
{{ $destination['name'] }} {{ $destination['name'] }}
</div> </div>
<div class="text-xs text-neutral-500 dark:text-neutral-400"> <div
class="text-xs text-neutral-500 dark:text-neutral-400">
Network: {{ $destination['network'] }} Network: {{ $destination['network'] }}
</div> </div>
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400" fill="none" class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</button> </button>
@ -449,10 +462,10 @@ class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:bo
<button type="button" <button type="button"
@click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)" @click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)"
class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white"> class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M15 19l-7-7 7-7" /> stroke-width="2" d="M15 19l-7-7 7-7" />
</svg> </svg>
</button> </button>
<div> <div>
@ -467,11 +480,13 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
</div> </div>
@if ($loadingProjects) @if ($loadingProjects)
<div class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg"> <div
<svg class="animate-spin h-5 w-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg">
fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-yellow-500"
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="4"></circle> viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" <path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path> </path>
@ -481,15 +496,18 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
@elseif (count($availableProjects) > 0) @elseif (count($availableProjects) > 0)
@foreach ($availableProjects as $index => $project) @foreach ($availableProjects as $index => $project)
<button type="button" wire:click="selectProject('{{ $project['uuid'] }}', true)" <button type="button"
wire:click="selectProject('{{ $project['uuid'] }}', true)"
class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0"> class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0">
<div class="flex items-center justify-between gap-3 min-h-[2.5rem]"> <div
class="flex items-center justify-between gap-3 min-h-[2.5rem]">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="font-medium text-neutral-900 dark:text-white"> <div class="font-medium text-neutral-900 dark:text-white">
{{ $project['name'] }} {{ $project['name'] }}
</div> </div>
@if (!empty($project['description'])) @if (!empty($project['description']))
<div class="text-xs text-neutral-500 dark:text-neutral-400"> <div
class="text-xs text-neutral-500 dark:text-neutral-400">
{{ $project['description'] }} {{ $project['description'] }}
</div> </div>
@else @else
@ -499,10 +517,10 @@ class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg
@endif @endif
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400" fill="none" class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</button> </button>
@ -524,10 +542,10 @@ class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:bo
<button type="button" <button type="button"
@click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)" @click="$wire.set('searchQuery', ''); setTimeout(() => $refs.searchInput.focus(), 100)"
class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white"> class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M15 19l-7-7 7-7" /> stroke-width="2" d="M15 19l-7-7 7-7" />
</svg> </svg>
</button> </button>
<div> <div>
@ -542,11 +560,13 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
</div> </div>
@if ($loadingEnvironments) @if ($loadingEnvironments)
<div class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg"> <div
<svg class="animate-spin h-5 w-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" class="flex items-center gap-3 p-3 bg-neutral-50 dark:bg-coolgray-200 rounded-lg">
fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-yellow-500"
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="4"></circle> viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" <path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path> </path>
@ -556,15 +576,18 @@ class="text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:
</div> </div>
@elseif (count($availableEnvironments) > 0) @elseif (count($availableEnvironments) > 0)
@foreach ($availableEnvironments as $index => $environment) @foreach ($availableEnvironments as $index => $environment)
<button type="button" wire:click="selectEnvironment('{{ $environment['uuid'] }}', true)" <button type="button"
wire:click="selectEnvironment('{{ $environment['uuid'] }}', true)"
class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0"> class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg-yellow-50 dark:hover:bg-yellow-900/20 transition-colors focus:outline-none focus:bg-yellow-100 dark:focus:bg-yellow-900/30 border-b border-neutral-100 dark:border-coolgray-300 last:border-0">
<div class="flex items-center justify-between gap-3 min-h-[2.5rem]"> <div
class="flex items-center justify-between gap-3 min-h-[2.5rem]">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="font-medium text-neutral-900 dark:text-white"> <div class="font-medium text-neutral-900 dark:text-white">
{{ $environment['name'] }} {{ $environment['name'] }}
</div> </div>
@if (!empty($environment['description'])) @if (!empty($environment['description']))
<div class="text-xs text-neutral-500 dark:text-neutral-400"> <div
class="text-xs text-neutral-500 dark:text-neutral-400">
{{ $environment['description'] }} {{ $environment['description'] }}
</div> </div>
@else @else
@ -574,10 +597,10 @@ class="search-result-item w-full text-left block px-4 py-3 min-h-[4rem] hover:bg
@endif @endif
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400" fill="none" class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</button> </button>
@ -616,7 +639,8 @@ class="search-result-item block px-4 py-3 hover:bg-neutral-50 dark:hover:bg-cool
<div class="flex items-center justify-between gap-3"> <div class="flex items-center justify-between gap-3">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-1"> <div class="flex items-center gap-2 mb-1">
<span class="font-medium text-neutral-900 dark:text-white truncate"> <span
class="font-medium text-neutral-900 dark:text-white truncate">
{{ $result['name'] }} {{ $result['name'] }}
</span> </span>
<span <span
@ -637,13 +661,15 @@ class="px-2 py-0.5 text-xs rounded-full bg-neutral-100 dark:bg-coolgray-300 text
</span> </span>
</div> </div>
@if (!empty($result['project']) && !empty($result['environment'])) @if (!empty($result['project']) && !empty($result['environment']))
<div class="text-xs text-neutral-500 dark:text-neutral-400 mb-1"> <div
class="text-xs text-neutral-500 dark:text-neutral-400 mb-1">
{{ $result['project'] }} / {{ $result['project'] }} /
{{ $result['environment'] }} {{ $result['environment'] }}
</div> </div>
@endif @endif
@if (!empty($result['description'])) @if (!empty($result['description']))
<div class="text-sm text-neutral-600 dark:text-neutral-400"> <div
class="text-sm text-neutral-600 dark:text-neutral-400">
{{ Str::limit($result['description'], 80) }} {{ Str::limit($result['description'], 80) }}
</div> </div>
@endif @endif
@ -651,8 +677,8 @@ class="px-2 py-0.5 text-xs rounded-full bg-neutral-100 dark:bg-coolgray-300 text
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-neutral-300 dark:text-neutral-600 self-center" class="shrink-0 h-5 w-5 text-neutral-300 dark:text-neutral-600 self-center"
fill="none" viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</a> </a>
@ -682,15 +708,16 @@ class="search-result-item w-full text-left block px-4 py-3 hover:bg-yellow-50 da
<div <div
class="flex-shrink-0 w-10 h-10 rounded-lg bg-yellow-100 dark:bg-yellow-900/40 flex items-center justify-center"> class="flex-shrink-0 w-10 h-10 rounded-lg bg-yellow-100 dark:bg-yellow-900/40 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-yellow-600 dark:text-yellow-400" fill="none" class="h-5 w-5 text-yellow-600 dark:text-yellow-400"
viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M12 4v16m8-8H4" /> stroke-width="2" d="M12 4v16m8-8H4" />
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-1"> <div class="flex items-center gap-2 mb-1">
<div class="font-medium text-neutral-900 dark:text-white truncate"> <div
class="font-medium text-neutral-900 dark:text-white truncate">
{{ $item['name'] }} {{ $item['name'] }}
</div> </div>
@if (isset($item['quickcommand'])) @if (isset($item['quickcommand']))
@ -698,7 +725,8 @@ class="h-5 w-5 text-yellow-600 dark:text-yellow-400" fill="none"
class="text-xs text-neutral-500 dark:text-neutral-400 shrink-0">{{ $item['quickcommand'] }}</span> class="text-xs text-neutral-500 dark:text-neutral-400 shrink-0">{{ $item['quickcommand'] }}</span>
@endif @endif
</div> </div>
<div class="text-sm text-neutral-600 dark:text-neutral-400 truncate"> <div
class="text-sm text-neutral-600 dark:text-neutral-400 truncate">
{{ $item['description'] }} {{ $item['description'] }}
</div> </div>
</div> </div>
@ -706,8 +734,8 @@ class="text-xs text-neutral-500 dark:text-neutral-400 shrink-0">{{ $item['quickc
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400 self-center" class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400 self-center"
fill="none" viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round"
d="M9 5l7 7-7 7" /> stroke-width="2" d="M9 5l7 7-7 7" />
</svg> </svg>
</div> </div>
</button> </button>
@ -792,7 +820,8 @@ class="search-result-item w-full text-left block px-4 py-3 hover:bg-yellow-50 da
class="flex-shrink-0 w-10 h-10 rounded-lg bg-yellow-100 dark:bg-yellow-900/40 flex items-center justify-center"> class="flex-shrink-0 w-10 h-10 rounded-lg bg-yellow-100 dark:bg-yellow-900/40 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-yellow-600 dark:text-yellow-400" class="h-5 w-5 text-yellow-600 dark:text-yellow-400"
fill="none" viewBox="0 0 24 24" stroke="currentColor"> fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" <path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M12 4v16m8-8H4" /> stroke-width="2" d="M12 4v16m8-8H4" />
</svg> </svg>
@ -860,10 +889,12 @@ class="shrink-0 h-5 w-5 text-yellow-500 dark:text-yellow-400 self-center"
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -876,8 +907,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New Project</h3> <h3 class="text-2xl font-bold">New Project</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -900,10 +931,12 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -916,8 +949,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New Server</h3> <h3 class="text-2xl font-bold">New Server</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -940,10 +973,12 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -956,8 +991,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New Team</h3> <h3 class="text-2xl font-bold">New Team</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -980,10 +1015,12 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -996,8 +1033,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New S3 Storage</h3> <h3 class="text-2xl font-bold">New S3 Storage</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -1020,10 +1057,12 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -1036,8 +1075,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New Private Key</h3> <h3 class="text-2xl font-bold">New Private Key</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -1060,10 +1099,12 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
if (firstInput) firstInput.focus(); if (firstInput) firstInput.focus();
}, 200); }, 200);
} }
})" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen"> })"
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" class="fixed top-0 left-0 lg:px-0 px-4 z-99 flex items-center justify-center w-screen h-screen">
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" <div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0" @click="modalOpen=false"
class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div> class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100" <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95" x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@ -1076,8 +1117,8 @@ class="relative w-full py-6 border rounded-sm drop-shadow-sm min-w-full lg:min-w
<h3 class="text-2xl font-bold">New GitHub App</h3> <h3 class="text-2xl font-bold">New GitHub App</h3>
<button @click="modalOpen=false" <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning"> class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-neutral-100 dark:hover:bg-coolgray-300 outline-0 focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke-width="1.5" stroke="currentColor"> viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
@ -1090,4 +1131,4 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
</template> </template>
</div> </div>
</div> </div>

View file

@ -0,0 +1,44 @@
<?php
/**
* Unit tests to verify that the "new image" quick action properly matches
* the docker-image type using the quickcommand field.
*
* This test verifies the fix for the issue where typing "new image" would
* not match because the frontend was only checking name and type fields,
* not the quickcommand field.
*/
it('ensures GlobalSearch blade template checks quickcommand field in matching logic', function () {
$bladeFile = file_get_contents(__DIR__.'/../../resources/views/livewire/global-search.blade.php');
// Check that the matching logic includes quickcommand check
expect($bladeFile)
->toContain('item.quickcommand')
->toContain('quickcommand.toLowerCase().includes(trimmed)');
});
it('ensures GlobalSearch clears search query when starting resource creation', function () {
$globalSearchFile = file_get_contents(__DIR__.'/../../app/Livewire/GlobalSearch.php');
// Check that navigateToResourceCreation clears the search query
expect($globalSearchFile)
->toContain('$this->searchQuery = \'\'');
});
it('ensures GlobalSearch uses Livewire redirect method', function () {
$globalSearchFile = file_get_contents(__DIR__.'/../../app/Livewire/GlobalSearch.php');
// Check that completeResourceCreation uses $this->redirect()
expect($globalSearchFile)
->toContain('$this->redirect(route(\'project.resource.create\'');
});
it('ensures docker-image item has quickcommand with new image', function () {
$globalSearchFile = file_get_contents(__DIR__.'/../../app/Livewire/GlobalSearch.php');
// Check that Docker Image has the correct quickcommand
expect($globalSearchFile)
->toContain("'name' => 'Docker Image'")
->toContain("'quickcommand' => '(type: new image)'")
->toContain("'type' => 'docker-image'");
});