@@ -50,6 +50,7 @@ import {
5050 TooltipTrigger ,
5151} from "~/components/ui/tooltip" ;
5252import { pathOptions } from "~/lib/channel-options" ;
53+ import { formatPathLabel } from "~/lib/request-policy" ;
5354import { cn , getErrorMessage } from "~/lib/utils" ;
5455
5556type SearchField = "any" | "title" | "artist" | "album" | "creator" ;
@@ -109,6 +110,7 @@ export function SongSearchPanel(props: {
109110 title : string ;
110111 eyebrow ?: string ;
111112 description ?: string ;
113+ infoNote ?: string ;
112114 placeholder ?: string ;
113115 className ?: string ;
114116} ) {
@@ -249,7 +251,7 @@ export function SongSearchPanel(props: {
249251
250252 const { data, error, isFetching, isLoading } = useQuery < SearchResponse > ( {
251253 queryKey : [ "song-search" , searchParams . toString ( ) ] ,
252- enabled : hasSearchInput && ! queryTooShort && ! requiresCoreSearchTerm ,
254+ enabled : ! queryTooShort && ! requiresCoreSearchTerm ,
253255 placeholderData : keepPreviousData ,
254256 queryFn : async ( ) : Promise < SearchResponse > => {
255257 const response = await fetch ( `/api/search?${ searchParams . toString ( ) } ` ) ;
@@ -271,9 +273,11 @@ export function SongSearchPanel(props: {
271273 } ) ;
272274
273275 const results =
274- hasSearchInput && ! queryTooShort && ! requiresCoreSearchTerm
275- ? ( data ?. results ?? [ ] )
276- : [ ] ;
276+ ! queryTooShort && ! requiresCoreSearchTerm ? ( data ?. results ?? [ ] ) : [ ] ;
277+ const resolvedInfoNote = props . infoNote ?. replace (
278+ "{count}" ,
279+ String ( data ?. total ?? 0 )
280+ ) ;
277281 const totalPages = Math . max (
278282 1 ,
279283 Math . ceil ( ( data ?. total ?? 0 ) / ( data ?. pageSize ?? 25 ) )
@@ -353,7 +357,7 @@ export function SongSearchPanel(props: {
353357 }
354358
355359 function renderPagination ( position : "top" | "bottom" ) {
356- if ( ! hasSearchInput || totalPages <= 1 ) {
360+ if ( totalPages <= 1 ) {
357361 return null ;
358362 }
359363
@@ -441,11 +445,19 @@ export function SongSearchPanel(props: {
441445 { props . description }
442446 </ p >
443447 ) : null }
448+ { resolvedInfoNote ? (
449+ < div className = "mt-4 rounded-[20px] border border-sky-400/30 bg-sky-500/10 px-4 py-3 text-sm text-sky-100" >
450+ < p className = "font-semibold uppercase tracking-[0.18em] text-sky-200" >
451+ Note:
452+ </ p >
453+ < p className = "mt-2" > { resolvedInfoNote } </ p >
454+ </ div >
455+ ) : null }
444456 </ div >
445- { hasSearchInput && ! queryTooShort && ! error ? (
457+ { ! queryTooShort && ! error ? (
446458 < div className = "rounded-[24px] border border-(--border) bg-(--panel-soft) px-4 py-3 text-right" >
447459 < p className = "text-lg font-semibold text-(--text)" >
448- { data ?. total ?? 0 } matching songs
460+ { data ?. total ?? 0 } songs
449461 </ p >
450462 { isFetching ? (
451463 < p className = "mt-1 text-xs font-medium text-(--muted)" >
@@ -605,15 +617,15 @@ export function SongSearchPanel(props: {
605617 < Label > Path</ Label >
606618 < MultiSelectSelect
607619 label = "Path"
608- options = { pathOptions . map (
609- ( part ) => part . charAt ( 0 ) . toUpperCase ( ) + part . slice ( 1 )
620+ options = { pathOptions . map ( ( part ) => formatPathLabel ( part ) ) }
621+ selectedValues = { advancedFilters . parts . map ( ( part ) =>
622+ formatPathLabel ( part )
610623 ) }
611- selectedValues = { advancedFilters . parts . map (
612- ( part ) => part . charAt ( 0 ) . toUpperCase ( ) + part . slice ( 1 )
613- ) }
614- onAdd = { ( value ) => toggleAdvancedPart ( value . toLowerCase ( ) ) }
624+ onAdd = { ( value ) =>
625+ toggleAdvancedPart ( getPathTokenFromLabel ( value ) )
626+ }
615627 onRemove = { ( value ) =>
616- toggleAdvancedPart ( value . toLowerCase ( ) )
628+ toggleAdvancedPart ( getPathTokenFromLabel ( value ) )
617629 }
618630 toneByValue = { getPathToneByValue }
619631 />
@@ -651,25 +663,25 @@ export function SongSearchPanel(props: {
651663 < CardContent className = "p-0" >
652664 { renderPagination ( "top" ) }
653665
654- { hasSearchInput ? (
655- < div className = "grid grid-cols-[minmax(0,2.1fr)_minmax(0,1.4fr)_minmax(0,1.2fr)_minmax(0,1fr)_72px] gap-4 border-b border-(--border) px-5 py-4 text-[11px] font-semibold uppercase tracking-[0.22em] text-(--muted)" >
656- < span > Song</ span >
657- < span > Details</ span >
658- < span > Paths / Tuning</ span >
659- < span > Stats</ span >
660- < span className = "text-right" > Copy</ span >
661- </ div >
662- ) : null }
666+ < div className = "grid grid-cols-[minmax(0,2.1fr)_minmax(0,1.4fr)_minmax(0,1.2fr)_minmax(0,1fr)_72px] gap-4 border-b border-(--border) px-5 py-4 text-[11px] font-semibold uppercase tracking-[0.22em] text-(--muted)" >
667+ < span > Song</ span >
668+ < span > Details</ span >
669+ < span > Paths / Tuning</ span >
670+ < span > Stats</ span >
671+ < span className = "text-right" > Copy</ span >
672+ </ div >
663673
664674 { isLoading && results . length === 0 ? (
665675 < div className = "px-5 py-8 text-sm text-(--muted)" >
666- Searching ...
676+ Loading songs ...
667677 </ div >
668678 ) : null }
669679
670680 { ! isLoading && queryTooShort && ! hasAdvancedFilter ? (
671- < div className = "px-5 py-8 text-sm text-(--muted)" >
672- Search terms must be at least 3 characters.
681+ < div className = "grid grid-cols-[minmax(0,2.1fr)_minmax(0,1.4fr)_minmax(0,1.2fr)_minmax(0,1fr)_72px] gap-4 border-b border-(--border) px-5 py-4 text-[11px] font-semibold uppercase tracking-[0.22em] text-(--muted)" >
682+ < span className = "col-span-full normal-case tracking-normal text-sm font-normal text-(--muted)" >
683+ Search terms must be at least 3 characters.
684+ </ span >
673685 </ div >
674686 ) : null }
675687
@@ -686,13 +698,13 @@ export function SongSearchPanel(props: {
686698 ) : null }
687699
688700 { ! isLoading &&
689- hasSearchInput &&
690701 ! queryTooShort &&
691702 ! requiresCoreSearchTerm &&
692703 results . length === 0 ? (
693704 < div className = "px-5 py-8 text-sm text-(--muted)" >
694- No songs matched those filters yet. Try broadening the search
695- field or clearing one of the advanced inputs.
705+ { hasSearchInput
706+ ? "No songs matched those filters yet. Try broadening the search field or clearing one of the advanced inputs."
707+ : "No songs are available in the demo catalog yet." }
696708 </ div >
697709 ) : null }
698710
@@ -760,7 +772,7 @@ export function SongSearchPanel(props: {
760772 { song . parts ?. includes ( "voice" ) ||
761773 song . parts ?. includes ( "vocals" ) ? (
762774 < Badge className = "border-violet-400/30 bg-violet-500/10 text-violet-300 hover:bg-violet-500/10" >
763- Voice
775+ Lyrics
764776 </ Badge >
765777 ) : null }
766778 </ div >
@@ -775,11 +787,6 @@ export function SongSearchPanel(props: {
775787 { song . durationText ? (
776788 < p className = "text-(--text)" > { song . durationText } </ p >
777789 ) : null }
778- { song . downloads != null ? (
779- < p className = "mt-1 text-(--muted)" >
780- { song . downloads . toLocaleString ( ) } downloads
781- </ p >
782- ) : null }
783790 { song . year ? (
784791 < p className = "mt-1 text-(--muted)" > { String ( song . year ) } </ p >
785792 ) : null }
@@ -836,6 +843,10 @@ function getPathToneByValue(value: string) {
836843 }
837844}
838845
846+ function getPathTokenFromLabel ( value : string ) {
847+ return value . toLowerCase ( ) === "lyrics" ? "voice" : value . toLowerCase ( ) ;
848+ }
849+
839850function MultiSelectSelect ( props : {
840851 label : string ;
841852 options : string [ ] ;
0 commit comments