Kolejne przeróbki,

This commit is contained in:
dm
2025-12-13 10:13:20 +01:00
parent 5822237745
commit 4655554ff2
17 changed files with 197 additions and 218 deletions

View File

@@ -1,31 +1,75 @@
import { useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
/**
* @typedef {{ name: string; href: string }} MenuLink
* @param {{ links: MenuLink[] }} props
*/
export default function MobileMenu({ links = [] }) {
const [open, setOpen] = useState(false);
const menuRef = useRef(null);
const btnRef = useRef(null);
const toggle = () => setOpen(o => !o);
const close = () => setOpen(false);
const toggle = (e) => {
e?.stopPropagation?.();
setOpen((o) => !o);
};
useEffect(() => {
const onKey = (e) => e.key === "Escape" && close();
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, []);
useEffect(() => {
if (!open) return;
const onDocClick = (e) => {
const menuEl = menuRef.current;
const btnEl = btnRef.current;
if (menuEl && menuEl.contains(e.target)) return;
if (btnEl && btnEl.contains(e.target)) return;
close();
};
document.addEventListener("mousedown", onDocClick);
document.addEventListener("touchstart", onDocClick, { passive: true });
return () => {
document.removeEventListener("mousedown", onDocClick);
document.removeEventListener("touchstart", onDocClick);
};
}, [open]);
return (
<>
<button
ref={btnRef}
onClick={toggle}
class="f-mobile-toggle md:hidden"
aria-label="Menu mobilne"
aria-expanded={open}
aria-controls="mobile-menu"
>
{open ? "✖" : "☰"}
</button>
<div class={`f-mobile-menu ${open ? "open" : ""}`}>
{links.map(link => (
<a
href={link.href}
class="f-mobile-link"
onClick={() => setOpen(false)}
>
<div
class={`f-mobile-backdrop ${open ? "open" : ""}`}
onClick={close}
aria-hidden="true"
/>
<div
id="mobile-menu"
ref={menuRef}
class={`f-mobile-menu ${open ? "open" : ""}`}
onClick={(e) => e.stopPropagation()}
>
{links.map((link) => (
<a href={link.href} class="f-mobile-link" onClick={close}>
{link.name}
</a>
))}

View File

@@ -23,7 +23,6 @@ export default function OffersSwitches(props) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
// 🔹 AUTO mode sam pobieram /api/switches + wysyłam event
useEffect(() => {
if (isControlled) return;
@@ -52,7 +51,6 @@ export default function OffersSwitches(props) {
setAutoSwitches(sws);
setAutoSelected(initial);
// 🔥 zapisz globalny stan
window.fuzSwitchState = {
selected: initial,
labels,
@@ -69,7 +67,7 @@ export default function OffersSwitches(props) {
}),
);
} catch (err) {
console.error("Błąd pobierania switchy:", err);
console.error("Błąd pobierania przełączników:", err);
if (!cancelled) setError("Nie udało się załadować przełączników.");
} finally {
if (!cancelled) setLoading(false);
@@ -93,7 +91,6 @@ export default function OffersSwitches(props) {
const next = { ...prev, [id]: value };
const labels = buildLabels(autoSwitches, next);
// 🔥 aktualizuj globalny stan
window.fuzSwitchState = {
selected: next,
labels,
@@ -115,7 +112,6 @@ export default function OffersSwitches(props) {
}
};
// 🔥 CONTROLLED: zsynchronizuj globalny stan + event
useEffect(() => {
if (!isControlled) return;
if (!Array.isArray(switches) || !switches.length) return;
@@ -123,7 +119,6 @@ export default function OffersSwitches(props) {
const safeSelected = selected || {};
const labels = buildLabels(switches, safeSelected);
// 🔥 globalny stan
window.fuzSwitchState = {
selected: safeSelected,
labels,
@@ -162,23 +157,20 @@ export default function OffersSwitches(props) {
return (
<div class="f-switches-wrapper">
{effectiveSwitches.map((sw) => (
<div class="f-switch-box">
<div class="f-switch-group">
{sw.opcje.map((op) => (
<button
type="button"
class={`f-switch ${
String(effectiveSelected[sw.id]) === String(op.id)
? "active"
: ""
<div class="f-switch-group">
{sw.opcje.map((op) => (
<button
type="button"
class={`f-switch ${String(effectiveSelected[sw.id]) === String(op.id)
? "active"
: ""
}`}
onClick={() => handleClick(sw.id, op.id)}
title={sw.title}
>
{op.nazwa}
</button>
))}
</div>
onClick={() => handleClick(sw.id, op.id)}
title={sw.title}
>
{op.nazwa}
</button>
))}
</div>
))}
</div>

View File

@@ -79,20 +79,15 @@ export default function JamboxChannelsModal({ isOpen, onClose, pkg }) {
<div class="max-w-8xl mx-auto px-3 md:px-6">
<h2 class="fuz-modal-title">Kanały w pakiecie {pkg.name}</h2>
<div class="jmb-search">
<div class="fuz-chsearch__top">
<input
class="jmb-search-input"
class="fuz-chsearch__input"
type="search"
value={query}
onInput={(e) => setQuery(e.currentTarget.value)}
placeholder="Szukaj kanału po nazwie…"
aria-label="Szukaj kanału po nazwie"
/>
{query && (
<button class="jmb-search-clear" type="button" onClick={() => setQuery("")}>
Wyczyść
</button>
)}
</div>
{!loading && !error && (
@@ -112,35 +107,32 @@ export default function JamboxChannelsModal({ isOpen, onClose, pkg }) {
<div class="">
<div class="f-section-channel">
{filtered.map((ch) => (
<div class="jmb-channel-card" key={ch.number}>
<div
class="jmb-channel-card"
key={ch.number}
onClick={(e) => {
if (e.target.closest("a, button")) return;
e.currentTarget.classList.toggle("is-flipped");
}}
>
<div class="jmb-channel-inner">
{/* FRONT */}
<div class="jmb-channel-face jmb-channel-front">
{ch.logo_url && (
<img
src={ch.logo_url}
alt={ch.name}
class="jmb-channel-logo"
loading="lazy"
/>
<img src={ch.logo_url} alt={ch.name} class="jmb-channel-logo" loading="lazy" />
)}
<div class="jmb-channel-name">{ch.name}</div>
<div class="jmb-channel-number">kanał {ch.number}</div>
</div>
{/* BACK */}
<div class="jmb-channel-face jmb-channel-back">
<div class="jmb-channel-back-title">{ch.name}</div>
<div
class="jmb-channel-desc"
dangerouslySetInnerHTML={{
__html: ch.description || "<em>Brak opisu kanału.</em>",
}}
dangerouslySetInnerHTML={{ __html: ch.description || "<em>Brak opisu kanału.</em>" }}
/>
</div>
</div>
</div>
))}
</div>
</div>

View File

@@ -97,9 +97,9 @@ export default function JamboxChannelsSearch() {
{c.name}
</div>
<div class="fuz-chsearch__channel-number">
{/* <div class="fuz-chsearch__channel-number">
kanał {c.min_number || "—"}
</div>
</div> */}
</div>
@@ -116,7 +116,7 @@ export default function JamboxChannelsSearch() {
{c.packages.map((p, i) => (
<span class="fuz-chsearch__pkg" key={p.id}>
{p.name}{" "}
<span class="fuz-chsearch__pkgnum">({p.number})</span>
<span class="fuz-chsearch__pkgnum">(kanał {p.number})</span>
{i < c.packages.length - 1 ? ", " : ""}
</span>
))}