feat: enhance global search UI and positioning
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m55s

- Moved search bar to the left/center for better accessibility.

- Increased search bar width to max-w-2xl.

- Refined search results dropdown layout and styling.
This commit is contained in:
Cauê Faleiros
2026-03-09 15:54:49 -03:00
parent c07967188a
commit 13bcfc1314

View File

@@ -194,45 +194,35 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
{/* Main Content */} {/* Main Content */}
<div className="flex-1 flex flex-col min-w-0"> <div className="flex-1 flex flex-col min-w-0">
{/* Header */} {/* Header */}
<header className="h-20 bg-white dark:bg-dark-header border-b border-zinc-200 dark:border-dark-border px-4 sm:px-8 flex items-center justify-between z-10 sticky top-0 transition-colors"> <header className="h-20 bg-white dark:bg-dark-header border-b border-zinc-200 dark:border-dark-border px-4 sm:px-8 flex items-center gap-4 sm:gap-8 z-10 sticky top-0 transition-colors">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4 shrink-0">
<button onClick={() => setIsMobileMenuOpen(true)} className="lg:hidden text-zinc-500 hover:text-zinc-900 dark:hover:text-white"> <button onClick={() => setIsMobileMenuOpen(true)} className="lg:hidden text-zinc-500 hover:text-zinc-900 dark:hover:text-white">
<Menu size={24} /> <Menu size={24} />
</button> </button>
</div> </div>
<div className="flex items-center gap-4 sm:gap-6"> {/* Search Bar - Moved to left/center and made wider */}
{/* Dark Mode Toggle */} <div className="hidden md:block relative flex-1 max-w-2xl">
<button <div className="flex items-center bg-zinc-100 dark:bg-dark-bg rounded-xl px-4 py-2.5 w-full border border-transparent focus-within:bg-white dark:focus-within:bg-dark-card focus-within:border-brand-yellow focus-within:ring-2 focus-within:ring-brand-yellow/20 dark:focus-within:ring-brand-yellow/10 transition-all">
onClick={toggleDarkMode}
className="p-2.5 text-zinc-500 dark:text-dark-muted hover:bg-zinc-100 dark:hover:bg-dark-border rounded-xl transition-all"
title={isDark ? "Mudar para Modo Claro" : "Mudar para Modo Escuro"}
>
{isDark ? <Sun size={20} /> : <Moon size={20} />}
</button>
{/* Search Bar */}
<div className="hidden md:block relative">
<div className="flex items-center bg-zinc-100 dark:bg-dark-bg rounded-full px-4 py-2 w-64 border border-transparent focus-within:bg-white dark:focus-within:bg-dark-card focus-within:border-brand-yellow focus-within:ring-2 focus-within:ring-brand-yellow/20 dark:focus-within:ring-brand-yellow/10 transition-all">
{isSearching ? <Loader2 size={18} className="text-brand-yellow animate-spin" /> : <Search size={18} className="text-zinc-400 dark:text-dark-muted" />} {isSearching ? <Loader2 size={18} className="text-brand-yellow animate-spin" /> : <Search size={18} className="text-zinc-400 dark:text-dark-muted" />}
<input <input
type="text" type="text"
placeholder="Buscar..." placeholder="Buscar membros, equipes ou atendimentos..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
onFocus={() => searchQuery.length >= 2 && setShowSearchResults(true)} onFocus={() => searchQuery.length >= 2 && setShowSearchResults(true)}
className="bg-transparent border-none outline-none text-sm ml-2 w-full text-zinc-700 dark:text-dark-text placeholder-zinc-400 dark:placeholder-dark-muted" className="bg-transparent border-none outline-none text-sm ml-3 w-full text-zinc-700 dark:text-dark-text placeholder-zinc-400 dark:placeholder-dark-muted"
/> />
</div> </div>
{/* Search Results Dropdown */} {/* Search Results Dropdown */}
{showSearchResults && ( {showSearchResults && (
<div className="absolute top-full mt-2 left-0 right-0 w-[400px] bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl shadow-2xl overflow-hidden z-50 animate-in fade-in slide-in-from-top-2 duration-200"> <div className="absolute top-full mt-2 left-0 w-full bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl shadow-2xl overflow-hidden z-50 animate-in fade-in slide-in-from-top-2 duration-200">
<div className="max-h-[480px] overflow-y-auto p-2"> <div className="max-h-[480px] overflow-y-auto p-2">
{/* Members Section */} {/* Members Section */}
{searchResults.members.length > 0 && ( {searchResults.members.length > 0 && (
<div className="mb-4"> <div className="mb-4">
<div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest">Membros</div> <div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest border-b border-zinc-50 dark:border-dark-border/50 mb-1">Membros</div>
{searchResults.members.map(m => ( {searchResults.members.map(m => (
<button <button
key={m.id} key={m.id}
@@ -246,7 +236,7 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
<img <img
src={m.avatar_url?.startsWith('http') ? m.avatar_url : `${import.meta.env.PROD ? '' : 'http://localhost:3001'}${m.avatar_url || ''}`} src={m.avatar_url?.startsWith('http') ? m.avatar_url : `${import.meta.env.PROD ? '' : 'http://localhost:3001'}${m.avatar_url || ''}`}
alt={m.name} alt={m.name}
className="w-8 h-8 rounded-full border border-zinc-100 dark:border-dark-border" className="w-9 h-9 rounded-full border border-zinc-100 dark:border-dark-border object-cover"
onError={(e) => { (e.target as HTMLImageElement).src = `https://ui-avatars.com/api/?name=${encodeURIComponent(m.name)}&background=random`; }} onError={(e) => { (e.target as HTMLImageElement).src = `https://ui-avatars.com/api/?name=${encodeURIComponent(m.name)}&background=random`; }}
/> />
<div> <div>
@@ -261,19 +251,19 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
{/* Teams Section */} {/* Teams Section */}
{searchResults.teams.length > 0 && ( {searchResults.teams.length > 0 && (
<div className="mb-4"> <div className="mb-4">
<div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest">Equipes</div> <div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest border-b border-zinc-50 dark:border-dark-border/50 mb-1">Equipes</div>
{searchResults.teams.map(t => ( {searchResults.teams.map(t => (
<button <button
key={t.id} key={t.id}
onClick={() => { onClick={() => {
navigate(`/admin/teams`); // For now just to teams list navigate(`/admin/teams`);
setShowSearchResults(false); setShowSearchResults(false);
setSearchQuery(''); setSearchQuery('');
}} }}
className="w-full flex items-center gap-3 p-2 hover:bg-zinc-50 dark:hover:bg-dark-border rounded-xl transition-colors text-left" className="w-full flex items-center gap-3 p-2 hover:bg-zinc-50 dark:hover:bg-dark-border rounded-xl transition-colors text-left"
> >
<div className="w-8 h-8 rounded-lg bg-zinc-100 dark:bg-dark-bg flex items-center justify-center text-zinc-500 dark:text-dark-muted"> <div className="w-9 h-9 rounded-lg bg-zinc-100 dark:bg-dark-bg flex items-center justify-center text-zinc-500 dark:text-dark-muted border border-zinc-200 dark:border-dark-border">
<Building2 size={16} /> <Building2 size={18} />
</div> </div>
<div> <div>
<div className="text-sm font-semibold text-zinc-900 dark:text-dark-text">{t.name}</div> <div className="text-sm font-semibold text-zinc-900 dark:text-dark-text">{t.name}</div>
@@ -287,7 +277,7 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
{/* Attendances Section */} {/* Attendances Section */}
{searchResults.attendances.length > 0 && ( {searchResults.attendances.length > 0 && (
<div className="mb-2"> <div className="mb-2">
<div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest">Atendimentos</div> <div className="px-3 py-2 text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest border-b border-zinc-50 dark:border-dark-border/50 mb-1">Atendimentos</div>
{searchResults.attendances.map(a => ( {searchResults.attendances.map(a => (
<button <button
key={a.id} key={a.id}
@@ -298,13 +288,13 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
}} }}
className="w-full flex items-center gap-3 p-2 hover:bg-zinc-50 dark:hover:bg-dark-border rounded-xl transition-colors text-left" className="w-full flex items-center gap-3 p-2 hover:bg-zinc-50 dark:hover:bg-dark-border rounded-xl transition-colors text-left"
> >
<div className="w-8 h-8 rounded-lg bg-zinc-100 dark:bg-dark-bg flex items-center justify-center text-zinc-500 dark:text-dark-muted text-[10px] font-bold"> <div className="w-9 h-9 rounded-lg bg-zinc-100 dark:bg-dark-bg flex items-center justify-center text-zinc-500 dark:text-dark-muted text-[10px] font-bold border border-zinc-200 dark:border-dark-border">
KPI KPI
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="text-sm font-semibold text-zinc-900 dark:text-dark-text truncate">{a.summary}</div> <div className="text-sm font-semibold text-zinc-900 dark:text-dark-text truncate">{a.summary}</div>
<div className="text-[10px] text-zinc-500 dark:text-dark-muted flex justify-between"> <div className="text-[10px] text-zinc-500 dark:text-dark-muted flex justify-between mt-0.5">
<span>{a.user_name}</span> <span className="font-medium">{a.user_name}</span>
<span>{new Date(a.created_at).toLocaleDateString()}</span> <span>{new Date(a.created_at).toLocaleDateString()}</span>
</div> </div>
</div> </div>
@@ -323,8 +313,15 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
)} )}
</div> </div>
{/* Close results when clicking outside */} <div className="flex items-center gap-4 sm:gap-6 ml-auto shrink-0">
{showSearchResults && <div className="fixed inset-0 z-40" onClick={() => setShowSearchResults(false)} />} {/* Dark Mode Toggle */}
<button
onClick={toggleDarkMode}
className="p-2.5 text-zinc-500 dark:text-dark-muted hover:bg-zinc-100 dark:hover:bg-dark-border rounded-xl transition-all"
title={isDark ? "Mudar para Modo Claro" : "Mudar para Modo Escuro"}
>
{isDark ? <Sun size={20} /> : <Moon size={20} />}
</button>
{/* Notifications */} {/* Notifications */}
<div className="relative"> <div className="relative">