From 5c53fbc49772d5c7d4d5c00160e27b3ba6c5fad9 Mon Sep 17 00:00:00 2001 From: fullsizemalt <106900403+fullsizemalt@users.noreply.github.com> Date: Sun, 21 Dec 2025 01:53:44 -0800 Subject: [PATCH] feat(frontend): Spec out SearchDialog for maximum utility --- frontend/components/ui/search-dialog.tsx | 132 ++++++++++++++++------- 1 file changed, 91 insertions(+), 41 deletions(-) diff --git a/frontend/components/ui/search-dialog.tsx b/frontend/components/ui/search-dialog.tsx index e01dc9d..478e6fb 100644 --- a/frontend/components/ui/search-dialog.tsx +++ b/frontend/components/ui/search-dialog.tsx @@ -10,13 +10,18 @@ import { CommandList, CommandSeparator, } from "@/components/ui/command" -import { Search, Music, MapPin, Calendar, Users, Globe } from "lucide-react" +import { + Search, Music, MapPin, Calendar, Users, Globe, + LayoutDashboard, Library, Star, History, ArrowRight +} from "lucide-react" import { useRouter } from "next/navigation" import { getApiUrl } from "@/lib/api-config" +import { Badge } from "@/components/ui/badge" export function SearchDialog() { const [open, setOpen] = React.useState(false) const [query, setQuery] = React.useState("") + const [loading, setLoading] = React.useState(false) const [results, setResults] = React.useState({ songs: [], venues: [], @@ -24,7 +29,8 @@ export function SearchDialog() { groups: [], users: [], nicknames: [], - performances: [] + performances: [], + shows: [] // Ensure backend sends this or we default to empty }) const router = useRouter() @@ -42,15 +48,22 @@ export function SearchDialog() { React.useEffect(() => { if (query.length < 2) { - setResults({ songs: [], venues: [], tours: [], groups: [], users: [], nicknames: [], performances: [] }) + setResults({ songs: [], venues: [], tours: [], groups: [], users: [], nicknames: [], performances: [], shows: [] }) return } + setLoading(true) const timer = setTimeout(() => { fetch(`${getApiUrl()}/search/?q=${query}`) .then(res => res.json()) - .then(data => setResults(data)) - .catch(err => console.error(err)) + .then(data => { + setResults(data) + setLoading(false) + }) + .catch(err => { + console.error(err) + setLoading(false) + }) }, 300) return () => clearTimeout(timer) @@ -65,42 +78,80 @@ export function SearchDialog() { <> - - - No results found. + + + + {loading ? ( +
+ Searching... +
+ ) : ( + "No results found." + )} +
- {results.songs.length > 0 && ( + {query.length < 2 && ( + + handleSelect("/archive")}> + + Archive + + handleSelect("/leaderboards")}> + + Leaderboards + + handleSelect("/shows")}> + + All Shows + + handleSelect("/tours")}> + + Tours + + + )} + + {results.songs?.length > 0 && ( {results.songs.map((song: any) => ( handleSelect(`/songs/${song.id}`)}> - {song.title} +
+ {song.title} + {song.original_artist && ( + {song.original_artist} + )} +
))}
)} - {results.venues.length > 0 && ( + {results.venues?.length > 0 && ( {results.venues.map((venue: any) => ( handleSelect(`/venues/${venue.id}`)}> {venue.name} + + {venue.city}, {venue.state} + ))} )} - {results.tours.length > 0 && ( + {results.tours?.length > 0 && ( {results.tours.map((tour: any) => ( handleSelect(`/tours/${tour.id}`)}> @@ -111,45 +162,44 @@ export function SearchDialog() { )} - {results.groups.length > 0 && ( - - {results.groups.map((group: any) => ( - handleSelect(`/groups/${group.id}`)}> - - {group.name} - - ))} - - )} - - {results.nicknames && results.nicknames.length > 0 && ( - - {results.nicknames.map((nickname: any) => ( - handleSelect(`/performances/${nickname.performance_id}`)}> - - {nickname.nickname} - - ))} - - )} - - {results.performances && results.performances.length > 0 && ( + {results.performances?.length > 0 && ( {results.performances.map((perf: any) => ( - handleSelect(`/performances/${perf.id}`)}> + handleSelect(`/shows/${perf.show_id}`)}> - {perf.song?.title} - {perf.notes} +
+ {perf.song?.title || "Unknown Song"} + + {perf.notes} + {/* We rely on frontend resolving show date if available, or just link to show */} + +
+ Performance
))}
)} - {results.users.length > 0 && ( + {results.nicknames?.length > 0 && ( + + {results.nicknames.map((nickname: any) => ( + handleSelect(`/shows/${nickname.performance?.show_id}`)}> + + {nickname.nickname} + + ({nickname.performance?.song?.title}) + + + ))} + + )} + + {results.users?.length > 0 && ( {results.users.map((user: any) => ( handleSelect(`/profile/${user.id}`)}> - {user.email} + {user.username || user.email.split('@')[0]} ))}