mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 11:55:23 +02:00
feat: enhance PricingView search bar with a searchable dropdown list
This commit is contained in:
@@ -63,6 +63,29 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
const [outerHFilter, setOuterHFilter] = useState<string[]>([]);
|
||||
const [dynamicColFilters, setDynamicColFilters] = useState<Record<number, string[]>>({});
|
||||
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const searchDropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Close search dropdown on click outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (searchDropdownRef.current && !searchDropdownRef.current.contains(event.target as Node)) {
|
||||
setIsSearchOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const searchSuggestions = useMemo(() => {
|
||||
if (!search) return data.slice(0, 50); // Show first 50 as default
|
||||
const s = search.toLowerCase();
|
||||
return data.filter(r =>
|
||||
String(r[COLUMNS.ARTICLE_NO] || '').toLowerCase().includes(s) ||
|
||||
String(r[COLUMNS.ARTICLE_NAME] || '').toLowerCase().includes(s)
|
||||
).slice(0, 50);
|
||||
}, [data, search]);
|
||||
|
||||
// ── Dynamic column detection ──────────────────────────────────────────────
|
||||
const { uvpIdx, srpCols, containerCols } = useMemo(() => {
|
||||
const uvpIdx = findCol(headers, 'uvp');
|
||||
@@ -407,25 +430,57 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
{/* ── Search bar ── */}
|
||||
<div className="flex-1 max-w-md relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search SKU or Name..."
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
className="w-full pl-4 pr-10 py-2 bg-slate-800 border border-slate-700 rounded-lg text-sm text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all"
|
||||
/>
|
||||
{search ? (
|
||||
<button
|
||||
onClick={() => setSearch('')}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
) : (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500">
|
||||
<Package className="w-4 h-4" />
|
||||
{/* ── Search bar with Dropdown ── */}
|
||||
<div className="flex-1 max-w-md relative" ref={searchDropdownRef}>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search SKU or Name..."
|
||||
value={search}
|
||||
onChange={e => {
|
||||
setSearch(e.target.value);
|
||||
setIsSearchOpen(true);
|
||||
}}
|
||||
onFocus={() => setIsSearchOpen(true)}
|
||||
className="w-full pl-4 pr-10 py-2 bg-slate-800 border border-slate-700 rounded-lg text-sm text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all"
|
||||
/>
|
||||
{search ? (
|
||||
<button
|
||||
onClick={() => { setSearch(''); setIsSearchOpen(false); }}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
) : (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500">
|
||||
<Package className="w-4 h-4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Search Dropdown Panel */}
|
||||
{isSearchOpen && searchSuggestions.length > 0 && (
|
||||
<div className="absolute top-full left-0 right-0 mt-1 bg-slate-800 border border-slate-700 rounded-lg shadow-2xl z-[150] overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
<div className="max-h-64 overflow-y-auto">
|
||||
{searchSuggestions.map((row, idx) => (
|
||||
<button
|
||||
key={`${row[COLUMNS.ARTICLE_NO]}-${idx}`}
|
||||
onClick={() => {
|
||||
setSearch(String(row[COLUMNS.ARTICLE_NO]));
|
||||
setIsSearchOpen(false);
|
||||
}}
|
||||
className="w-full text-left px-4 py-2.5 hover:bg-slate-700/50 flex flex-col gap-0.5 border-b border-slate-700/30 last:border-0 transition-colors"
|
||||
>
|
||||
<span className="text-xs font-mono font-bold text-blue-400">{row[COLUMNS.ARTICLE_NO]}</span>
|
||||
<span className="text-xs text-slate-300 truncate">{row[COLUMNS.ARTICLE_NAME]}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{searchSuggestions.length === 50 && (
|
||||
<div className="px-4 py-1.5 bg-slate-900/50 text-[10px] text-slate-500 border-t border-slate-700 italic">
|
||||
Showing first 50 results...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user