diff --git a/app/Livewire/GlobalSearch.php b/app/Livewire/GlobalSearch.php index 33366f418..98571b87f 100644 --- a/app/Livewire/GlobalSearch.php +++ b/app/Livewire/GlobalSearch.php @@ -22,6 +22,8 @@ class GlobalSearch extends Component { public $searchQuery = ''; + private $previousTrimmedQuery = ''; + public $isModalOpen = false; public $searchResults = []; @@ -86,6 +88,7 @@ public function closeSearchModal() { $this->isModalOpen = false; $this->searchQuery = ''; + $this->previousTrimmedQuery = ''; $this->searchResults = []; } @@ -101,25 +104,49 @@ public static function clearTeamCache($teamId) public function updatedSearchQuery() { - $query = strtolower(trim($this->searchQuery)); + $trimmedQuery = trim($this->searchQuery); + + // If only spaces were added/removed, don't trigger a search + if ($trimmedQuery === $this->previousTrimmedQuery) { + return; + } + + $this->previousTrimmedQuery = $trimmedQuery; + + // If search query is empty, just clear results without processing + if (empty($trimmedQuery)) { + $this->searchResults = []; + $this->isCreateMode = false; + $this->creatableItems = []; + $this->autoOpenResource = null; + $this->isSelectingResource = false; + $this->cancelResourceSelection(); + + return; + } + + $query = strtolower($trimmedQuery); // Reset keyboard navigation index $this->dispatch('reset-selected-index'); - if (str_starts_with($query, 'new')) { + // Only enter create mode if query is exactly "new" or starts with "new " (space after) + if ($query === 'new' || str_starts_with($query, 'new ')) { $this->isCreateMode = true; $this->loadCreatableItems(); - $this->searchResults = []; // Check for sub-commands like "new project", "new server", etc. - // Use original query (not trimmed) to ensure exact match without trailing spaces - $detectedType = $this->detectSpecificResource(strtolower($this->searchQuery)); + $detectedType = $this->detectSpecificResource($query); if ($detectedType) { $this->navigateToResource($detectedType); } else { // If no specific resource detected, reset selection state $this->cancelResourceSelection(); } + + // Also search for existing resources that match the query + // This allows users to find resources with "new" in their name + $this->search(); } else { $this->isCreateMode = false; $this->creatableItems = []; @@ -624,6 +651,8 @@ private function search() // Search for matching creatable resources to show as suggestions (if no priority item) if (! $priorityCreatableItem) { $this->loadCreatableItems(); + + // Search in regular creatable items (apps, databases, quick actions) $creatableSuggestions = collect($this->creatableItems) ->filter(function ($item) use ($query) { $searchText = strtolower($item['name'].' '.$item['description'].' '.($item['type'] ?? '')); @@ -648,7 +677,37 @@ private function search() $item['is_creatable_suggestion'] = true; return $item; + }); + + // Also search in services (loaded on-demand) + $serviceSuggestions = collect($this->services) + ->filter(function ($item) use ($query) { + $searchText = strtolower($item['name'].' '.$item['description'].' '.($item['type'] ?? '')); + + return preg_match('/\b'.preg_quote($query, '/').'/i', $searchText); }) + ->map(function ($item) use ($query) { + // Calculate match priority: name > type > description + $name = strtolower($item['name']); + $type = strtolower($item['type'] ?? ''); + $description = strtolower($item['description']); + + if (preg_match('/\b'.preg_quote($query, '/').'/i', $name)) { + $item['match_priority'] = 1; + } elseif (preg_match('/\b'.preg_quote($query, '/').'/i', $type)) { + $item['match_priority'] = 2; + } else { + $item['match_priority'] = 3; + } + + $item['is_creatable_suggestion'] = true; + + return $item; + }); + + // Merge and sort all suggestions + $creatableSuggestions = $creatableSuggestions + ->merge($serviceSuggestions) ->sortBy('match_priority') ->take(10) ->values() @@ -914,31 +973,18 @@ private function loadCreatableItems() ]); } - // === Services Category === - - if ($user->can('createAnyResource')) { - // Load all services - $allServices = get_service_templates(); - - foreach ($allServices as $serviceKey => $service) { - $items->push([ - 'name' => str($serviceKey)->headline()->toString(), - 'description' => data_get($service, 'slogan', 'Deploy '.str($serviceKey)->headline()), - 'type' => 'one-click-service-'.$serviceKey, - 'category' => 'Services', - 'resourceType' => 'service', - ]); - } - } - $this->creatableItems = $items->toArray(); } public function navigateToResource($type) { - // Find the item by type + // Find the item by type - check regular items first, then services $item = collect($this->creatableItems)->firstWhere('type', $type); + if (! $item) { + $item = collect($this->services)->firstWhere('type', $type); + } + if (! $item) { return; } @@ -1227,12 +1273,52 @@ public function getSelectedResourceNameProperty() $this->loadCreatableItems(); } - // Find the item by type + // Find the item by type - check regular items first, then services $item = collect($this->creatableItems)->firstWhere('type', $this->selectedResourceType); + if (! $item) { + $item = collect($this->services)->firstWhere('type', $this->selectedResourceType); + } + return $item ? $item['name'] : null; } + public function getServicesProperty() + { + // Cache services in a static property to avoid reloading on every access + static $cachedServices = null; + + if ($cachedServices !== null) { + return $cachedServices; + } + + $user = auth()->user(); + + if (! $user->can('createAnyResource')) { + $cachedServices = []; + + return $cachedServices; + } + + // Load all services + $allServices = get_service_templates(); + $items = collect(); + + foreach ($allServices as $serviceKey => $service) { + $items->push([ + 'name' => str($serviceKey)->headline()->toString(), + 'description' => data_get($service, 'slogan', 'Deploy '.str($serviceKey)->headline()), + 'type' => 'one-click-service-'.$serviceKey, + 'category' => 'Services', + 'resourceType' => 'service', + ]); + } + + $cachedServices = $items->toArray(); + + return $cachedServices; + } + public function render() { return view('livewire.global-search'); diff --git a/resources/views/livewire/global-search.blade.php b/resources/views/livewire/global-search.blade.php index 0bd833d8e..ad9a9f6ad 100644 --- a/resources/views/livewire/global-search.blade.php +++ b/resources/views/livewire/global-search.blade.php @@ -1,14 +1,20 @@
+ Please wait while we fetch your data +
+- Searching... -
+ +