diff --git a/app/Livewire/Header.php b/app/Livewire/Header.php
index f849e850..a5577314 100644
--- a/app/Livewire/Header.php
+++ b/app/Livewire/Header.php
@@ -7,6 +7,7 @@
use App\Enums\UserRole;
use App\Models\Project;
use Livewire\Component;
+use Livewire\Attributes\On;
use Filament\Actions\Action;
use App\Rules\ProfanityCheck;
use App\Settings\GeneralSettings;
@@ -44,29 +45,23 @@ public function mount()
->get();
}
- public function render()
+ #[On('create-item-from-search')]
+ public function openSubmitItemWithQuery(string $query): void
{
- return view('livewire.header');
+ // Mount the submit item action with the query as an argument
+ $this->mountAction('submitItem', ['prefilledTitle' => $query]);
}
- public function searchItemAction(): Action
+ #[On('open-submit-item-modal')]
+ public function openSubmitItemModal(): void
{
- return Action::make('searchItem')
- ->link()
- ->color('gray')
- ->label(function () {
- return view('components.search-button-label');
- })
- ->icon('heroicon-o-magnifying-glass')
- ->extraAttributes(['class' => '!px-3 !py-1.5 rounded-lg text-white hover:text-white hover:bg-white/10 focus:ring-white/20 [&_svg]:text-white'])
- ->modalWidth('4xl')
- ->modalFooterActions([
- Action::make('👀')->hidden()
- ])
- ->modalHeading($this->getRandomFunnyPlaceholder())
- ->modalIcon('heroicon-o-magnifying-glass')
- ->modalAlignment(Alignment::Left)
- ->modalContent(view('modals.search'));
+ // Mount the submit item action without pre-filled data
+ $this->mountAction('submitItem');
+ }
+
+ public function render()
+ {
+ return view('livewire.header');
}
public function submitItemAction(): Action
@@ -101,8 +96,23 @@ public function submitItemAction(): Action
->modalIcon('heroicon-o-plus-circle')
->modalWidth('3xl')
->modalSubmitActionLabel('Confirm')
- ->fillForm(function () {
- return $this->currentProjectId ? ['project_id' => $this->currentProjectId] : [];
+ ->mountUsing(function ($form, array $arguments) {
+ // Fill the form with prefilled data if available
+ $data = [];
+
+ // Add project_id if currentProjectId is set
+ if ($this->currentProjectId) {
+ $data['project_id'] = $this->currentProjectId;
+ }
+
+ // Add prefilled title if provided from search
+ if ($prefilledTitle = $arguments['prefilledTitle'] ?? null) {
+ $data['title'] = $prefilledTitle;
+ // Also set similar items for the prefilled title
+ $this->setSimilarItems($prefilledTitle);
+ }
+
+ $form->fill($data);
})
->schema(function () {
$inputs = [];
@@ -215,28 +225,4 @@ public function setSimilarItems($state): void
return $query;
})->get(['title', 'slug']) : collect([]);
}
-
- protected function getRandomFunnyPlaceholder(): string
- {
- $placeholders = [
- "Type here to find your lost keys... or your sanity",
- "Searching for Wi-Fi signals and lost socks",
- "Type something brilliant or your cat will judge you",
- "Looking for answers, memes, and cat videos",
- "Search for unicorns, we might find one",
- "Finding a needle in a digital haystack",
- "Type your thoughts, we'll pretend to understand",
- "Searching for dinosaurs in the digital age",
- "Lost in the web? We've got a virtual map",
- "Hunting for pixels and easter eggs",
- "Type something epic or order pizza, your choice",
- "Lost in the code? We'll be your debugger",
- "Looking for a shortcut to success",
- "Searching for the meaning of life... or cute cat videos"
- ];
-
- shuffle($placeholders);
-
- return $placeholders[0];
- }
}
diff --git a/app/Livewire/SpotlightSearch.php b/app/Livewire/SpotlightSearch.php
new file mode 100644
index 00000000..0d527b0c
--- /dev/null
+++ b/app/Livewire/SpotlightSearch.php
@@ -0,0 +1,117 @@
+isOpen = true;
+ $this->query = '';
+ $this->items = [];
+ $this->projects = [];
+ $this->totalResults = 0;
+
+ // Dispatch browser event for Alpine.js
+ $this->dispatch('spotlight-opened');
+ }
+
+ public function close(): void
+ {
+ $this->isOpen = false;
+ $this->query = '';
+ $this->items = [];
+ $this->projects = [];
+ $this->totalResults = 0;
+ }
+
+ public function updatedQuery(): void
+ {
+ if (empty(trim($this->query))) {
+ $this->items = [];
+ $this->projects = [];
+ $this->totalResults = 0;
+ return;
+ }
+
+ $this->searchItems();
+ $this->searchProjects();
+ $this->totalResults = count($this->items) + count($this->projects);
+ }
+
+ protected function searchItems(): void
+ {
+ $query = Item::query()
+ ->visibleForCurrentUser()
+ ->with(['project', 'board', 'user'])
+ ->where(function ($q) {
+ $q->where('title', 'like', '%' . $this->query . '%')
+ ->orWhere('content', 'like', '%' . $this->query . '%');
+ })
+ ->orderByDesc('created_at')
+ ->limit(8);
+
+ $this->items = $query->get()->map(function (Item $item) {
+ return [
+ 'id' => $item->id,
+ 'title' => $item->title,
+ 'slug' => $item->slug,
+ 'project_title' => $item->project?->title,
+ 'board_title' => $item->board?->title,
+ 'votes_count' => $item->total_votes ?? 0,
+ 'created_at' => $item->created_at?->diffForHumans(),
+ 'url' => route('items.show', $item),
+ ];
+ })->toArray();
+ }
+
+ protected function searchProjects(): void
+ {
+ $query = Project::query()
+ ->visibleForCurrentUser()
+ ->with(['boards'])
+ ->where(function ($q) {
+ $q->where('title', 'like', '%' . $this->query . '%')
+ ->orWhere('description', 'like', '%' . $this->query . '%');
+ })
+ ->orderBy('title')
+ ->limit(8);
+
+ $this->projects = $query->get()->map(function (Project $project) {
+ return [
+ 'id' => $project->id,
+ 'title' => $project->title,
+ 'slug' => $project->slug,
+ 'description' => $project->description,
+ 'icon' => $project->icon,
+ 'boards_count' => $project->boards->count(),
+ 'url' => route('projects.show', $project),
+ ];
+ })->toArray();
+ }
+
+ public function createNewItem(): void
+ {
+ $query = $this->query;
+ $this->close();
+ $this->dispatch('create-item-from-search', query: $query);
+ }
+
+ public function render()
+ {
+ return view('livewire.spotlight-search');
+ }
+}
diff --git a/lang/en/spotlight.php b/lang/en/spotlight.php
index efe117fb..ce2edac5 100644
--- a/lang/en/spotlight.php
+++ b/lang/en/spotlight.php
@@ -2,6 +2,7 @@
return [
'search' => 'Search',
+ 'search_button' => 'Search',
'create-item' => [
'name' => 'Create item',
'description' => 'Create an item',
@@ -18,4 +19,26 @@
'name' => 'Logout',
'description' => 'Logout out of your account',
],
+
+ // Search Interface
+ 'search_placeholder' => 'Search items and projects...',
+ 'section_items' => 'Items',
+ 'section_projects' => 'Projects',
+ 'section_actions' => 'Actions',
+ 'quick_actions' => 'Quick Actions',
+ 'create_new_item' => 'Create new item',
+ 'create_new_item_desc' => 'Create a new roadmap item',
+ 'profile_title' => 'Profile',
+ 'profile_desc' => 'View and edit your profile',
+ 'activity' => 'Activity',
+ 'activity_desc' => 'View recent activity and updates',
+ 'admin_panel' => 'Admin Panel',
+ 'admin_panel_desc' => 'Manage your roadmap settings',
+ 'no_results' => 'No results found for',
+ 'initial_message' => 'Search for items and projects',
+ 'keyboard_hint' => 'Use arrow keys to navigate',
+ 'keyboard_navigate' => 'to navigate',
+ 'keyboard_open' => 'to open',
+ 'results_count' => ':count result|:count results',
+ 'board' => 'board|boards',
];
diff --git a/lang/nl/spotlight.php b/lang/nl/spotlight.php
index c5bd457e..b15b1214 100644
--- a/lang/nl/spotlight.php
+++ b/lang/nl/spotlight.php
@@ -2,6 +2,7 @@
return [
'search' => 'Zoeken',
+ 'search_button' => 'Zoeken',
'create-item' => [
'name' => 'Item aanmaken',
'description' => 'Maak een item aan',
@@ -18,4 +19,26 @@
'name' => 'Uitloggen',
'description' => 'Log jezelf uit',
],
+
+ // Search Interface
+ 'search_placeholder' => 'Zoek items en projecten...',
+ 'section_items' => 'Items',
+ 'section_projects' => 'Projecten',
+ 'section_actions' => 'Acties',
+ 'quick_actions' => 'Snelle Acties',
+ 'create_new_item' => 'Nieuw item aanmaken',
+ 'create_new_item_desc' => 'Maak een nieuw roadmap item aan',
+ 'profile_title' => 'Profiel',
+ 'profile_desc' => 'Bekijk en bewerk je profiel',
+ 'activity' => 'Activiteit',
+ 'activity_desc' => 'Bekijk recente activiteit en updates',
+ 'admin_panel' => 'Beheerpaneel',
+ 'admin_panel_desc' => 'Beheer je roadmap instellingen',
+ 'no_results' => 'Geen resultaten gevonden voor',
+ 'initial_message' => 'Zoek op items en projecten',
+ 'keyboard_hint' => 'Gebruik de pijltjestoetsen om te navigeren',
+ 'keyboard_navigate' => 'om te navigeren',
+ 'keyboard_open' => 'om te openen',
+ 'results_count' => ':count resultaat|:count resultaten',
+ 'board' => 'board|boards',
];
diff --git a/resources/css/app.css b/resources/css/app.css
index 4236955d..967b5a52 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -134,3 +134,42 @@
.fi-ta {
@apply w-full;
}
+
+/* Spotlight Search Styles */
+.spotlight-container {
+ box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
+}
+
+@media (prefers-color-scheme: dark) {
+ .spotlight-container {
+ box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
+ }
+}
+
+/* Custom scrollbar for spotlight results */
+.spotlight-container .max-h-\[60vh\]::-webkit-scrollbar {
+ width: 8px;
+}
+
+.spotlight-container .max-h-\[60vh\]::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.spotlight-container .max-h-\[60vh\]::-webkit-scrollbar-thumb {
+ background: rgba(156, 163, 175, 0.3);
+ border-radius: 4px;
+}
+
+.spotlight-container .max-h-\[60vh\]::-webkit-scrollbar-thumb:hover {
+ background: rgba(156, 163, 175, 0.5);
+}
+
+@media (prefers-color-scheme: dark) {
+ .spotlight-container .max-h-\[60vh\]::-webkit-scrollbar-thumb {
+ background: rgba(75, 85, 99, 0.5);
+ }
+
+ .spotlight-container .max-h-\[60vh\]::-webkit-scrollbar-thumb:hover {
+ background: rgba(75, 85, 99, 0.7);
+ }
+}
diff --git a/resources/views/components/app.blade.php b/resources/views/components/app.blade.php
index 2e44aadb..5c8ade63 100644
--- a/resources/views/components/app.blade.php
+++ b/resources/views/components/app.blade.php
@@ -97,6 +97,7 @@ function updateTheme() {
@livewireScriptConfig
@filamentScripts
@livewire('notifications')
+@livewire('spotlight-search')
@stack('javascript')