import { useChannelSearch } from "../../hooks/useChannelSearch.js"; import { useMemo, useState } from "preact/hooks"; import "../../styles/jambox-search.css"; export default function JamboxChannelsSearch() { // ✅ NOWY: Hook useChannelSearch zamiast ręcznego zarządzania const search = useChannelSearch('/api/jambox/jambox-channels-search', { debounceMs: 250, minQueryLength: 1, limit: 80 }); // Koszyk kanałów ("Chciałbym mieć te kanały") const [wanted, setWanted] = useState([]); const isWanted = (c) => wanted.some( (w) => String(w.name || "").toLowerCase() === String(c.name || "").toLowerCase() ); function addWanted(c) { setWanted((prev) => { const exists = prev.some( (w) => String(w.name || "").toLowerCase() === String(c.name || "").toLowerCase() ); if (exists) return prev; return [ ...prev, { name: c.name, logo_url: c.logo_url || "", packages: Array.isArray(c.packages) ? c.packages : [], thematic_packages: Array.isArray(c.thematic_packages) ? c.thematic_packages : [], }, ]; }); } function removeWantedByName(name) { setWanted((prev) => prev.filter( (w) => String(w.name || "").toLowerCase() !== String(name || "").toLowerCase() ) ); } function clearWanted() { setWanted([]); } // Sugestie pakietów na podstawie wybranych kanałów const packageSuggestions = useMemo(() => { if (!wanted.length) return { exact: [], ranked: [], thematic: [], baseWantedLen: 0, wantedLen: 0 }; // Kanały, które mają pakiety główne const baseWanted = wanted.filter((ch) => Array.isArray(ch.packages) && ch.packages.length > 0); const baseWantedLen = baseWanted.length; // Jeśli nie ma żadnego kanału "bazowego", zwracamy tylko tematyczne if (baseWantedLen === 0) { const thematicMap = new Map(); for (const ch of wanted) { const tp = Array.isArray(ch.thematic_packages) ? ch.thematic_packages : []; for (const p of tp) { const tid = String(p?.tid ?? "").trim(); const name = String(p?.name ?? "").trim(); if (!tid || !name) continue; if (!thematicMap.has(tid)) thematicMap.set(tid, { tid, name }); } } const thematic = Array.from(thematicMap.values()).sort((a, b) => a.name.localeCompare(b.name, "pl")); return { exact: [], ranked: [], thematic, baseWantedLen, wantedLen: wanted.length }; } // Zlicz pakiety const counts = new Map(); for (const ch of baseWanted) { const pkgs = Array.isArray(ch.packages) ? ch.packages : []; for (const p of pkgs) { const name = String(p?.name ?? "").trim(); if (!name) continue; const cur = counts.get(name) || { id: p?.id ?? name, name, count: 0 }; cur.count += 1; counts.set(name, cur); } } const all = Array.from(counts.values()); // Pakiety zawierające wszystkie wybrane kanały const exact = all .filter((p) => p.count === baseWantedLen) .sort((a, b) => a.name.localeCompare(b.name, "pl")); // Pakiety zawierające część kanałów (posortowane po ilości dopasowań) const ranked = all .filter((p) => p.count < baseWantedLen) .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name, "pl")) .slice(0, 12); // Pakiety tematyczne (dodatki) const thematicMap = new Map(); for (const ch of wanted) { const tp = Array.isArray(ch.thematic_packages) ? ch.thematic_packages : []; for (const p of tp) { const tid = String(p?.tid ?? "").trim(); const name = String(p?.name ?? "").trim(); if (!tid || !name) continue; if (!thematicMap.has(tid)) thematicMap.set(tid, { tid, name }); } } const thematic = Array.from(thematicMap.values()).sort((a, b) => a.name.localeCompare(b.name, "pl") ); return { exact, ranked, thematic, baseWantedLen, wantedLen: wanted.length }; }, [wanted]); function scrollToPackage(packageName) { const key = String(packageName || "").trim(); if (!key) return; const el = document.getElementById(`pkg-${key}`); if (!el) { console.warn("⚠ Nie znaleziono pakietu w DOM:", `pkg-${key}`); return; } el.scrollIntoView({ behavior: "smooth", block: "start" }); el.classList.add("is-target"); window.setTimeout(() => el.classList.remove("is-target"), 5400); } return (