Kolejne przeróbki,
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { Image } from "astro:assets";
|
||||
import type { ImageMetadata } from "astro";
|
||||
import Markdown from "../../islands/Markdown.jsx";
|
||||
import TvChannelsSearch from "../../islands/jambox/JamboxChannelsSearch.jsx";
|
||||
import JamboxChannelsSearch from "../../islands/jambox/JamboxChannelsSearch.jsx";
|
||||
|
||||
const props = Astro.props ?? {};
|
||||
const section = props.section ?? {};
|
||||
@@ -44,7 +44,7 @@ if (section.image) {
|
||||
{section.title && <h2 class="f-section-title">{section.title}</h2>}
|
||||
{section.content && <Markdown text={section.content} />}
|
||||
|
||||
<TvChannelsSearch client:load />
|
||||
<JamboxChannelsSearch client:load />
|
||||
|
||||
{section.button && (
|
||||
<div class="f-section-nav">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
title: SKONTAKTUJ SIĘ Z NAMI
|
||||
title: Skontaktuj się z nami
|
||||
description: |
|
||||
<h3>FUZ ADAM ROJEK</h3>
|
||||
<h4>ul. Świętojańska 46</h4>
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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,13 +157,11 @@ 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)
|
||||
class={`f-switch ${String(effectiveSelected[sw.id]) === String(op.id)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
@@ -179,7 +172,6 @@ export default function OffersSwitches(props) {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -3,7 +3,7 @@ import DefaultLayout from "../../layouts/DefaultLayout.astro";
|
||||
import OffersSwitches from "../../islands/OffersSwitches.jsx";
|
||||
import JamboxCards from "../../islands/jambox/JamboxCards.jsx";
|
||||
import SectionRenderer from "../../components/sections/SectionRenderer.astro";
|
||||
import SectionChannelsSearch from "../../components/sections/SectionChannelsSearch.astro"
|
||||
import SectionChannelsSearch from "../../components/sections/SectionChannelsSearch.astro";
|
||||
|
||||
import yaml from "js-yaml";
|
||||
import fs from "fs";
|
||||
@@ -24,7 +24,6 @@ const seo = yaml.load(
|
||||
<JamboxCards client:load />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<SectionRenderer src="./src/content/internet-telewizja/section.yaml" />
|
||||
<SectionChannelsSearch />
|
||||
<SectionRenderer src="./src/content/internet-telewizja/section.yaml" />
|
||||
</DefaultLayout>
|
||||
|
||||
@@ -18,16 +18,15 @@ const form = data.form;
|
||||
---
|
||||
|
||||
<DefaultLayout seo={seo}>
|
||||
<section id="kontakt" class="f-section">
|
||||
<div class="f-contact-grid">
|
||||
<!-- Kolumna lewa -->
|
||||
<div class="f-contact-col-1">
|
||||
<h2>{data.title}</h2>
|
||||
<section class="f-section">
|
||||
<div class="f-section-grid md:grid-cols-2 gap-10 items-start">
|
||||
<div>
|
||||
<h2 class="f-section-title">{data.title}</h2>
|
||||
<div class="f-contact-item" set:html={data.description} />
|
||||
</div>
|
||||
|
||||
<div class="f-contact-col-2">
|
||||
<h2>{data.contactFormTitle}</h2>
|
||||
<div>
|
||||
<h2 class="f-section-title">{data.contactFormTitle}</h2>
|
||||
<form id="contactForm" class="f-contact-form">
|
||||
<div class="f-contact-form-inner">
|
||||
<input
|
||||
@@ -46,7 +45,7 @@ const form = data.form;
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
@@ -84,17 +83,20 @@ const form = data.form;
|
||||
<input type="checkbox" name="rodo" required />
|
||||
<span>
|
||||
{form.rodo.label}
|
||||
<a href={form.rodo.policyLink} title={form.rodo.policyTitle}
|
||||
>{form.rodo.policyText}</a
|
||||
>.
|
||||
<a href={form.rodo.policyLink} title={form.rodo.policyTitle}>
|
||||
{form.rodo.policyText}
|
||||
</a>.
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<button title={form.submit.title}>{form.submit.label}</button>
|
||||
<button type="submit" class="btn btn-primary w-full py-3" title={form.submit.title}>
|
||||
{form.submit.label}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
<div class="f-contact-map">
|
||||
<MapGoogle
|
||||
apiKey={apiKey}
|
||||
@@ -108,6 +110,7 @@ const form = data.form;
|
||||
mapStyleId={data.maps.mapId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="f-toast"></div>
|
||||
</section>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
@import "./footer.css";
|
||||
@import "./cookie.css";
|
||||
@import "./contact.css";
|
||||
@import "./offers/offers-main.css";
|
||||
@import "./offers/offers-switches.css";
|
||||
|
||||
html {
|
||||
|
||||
@@ -1,56 +1,49 @@
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center font-semibold rounded-lg px-6 py-3 text-base transition-all duration-200 cursor-pointer select-none;
|
||||
@apply inline-flex items-center justify-center gap-2 font-semibold rounded-lg px-6 py-3 text-base transition-all duration-200 cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-[--f-header] focus-visible:ring-offset-2 focus-visible:ring-offset-[--f-background];
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply border-none bg-[--btn-background] text-[--btn-text];
|
||||
@apply border border-transparent bg-[--btn-background] text-[--btn-text];
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
@apply bg-[--btn-background-hover] text-[--btn-text-hover];
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
@apply opacity-60 cursor-not-allowed;
|
||||
}
|
||||
|
||||
.f-input {
|
||||
@apply w-full py-3 px-4 rounded-xl border border-[--f-input-border] bg-[--f-background] text-[--f-text] transition-all duration-200;
|
||||
}
|
||||
|
||||
.f-input:hover {
|
||||
@apply border-[--f-text] opacity-[0.9];
|
||||
@apply border-[--f-text] opacity-[0.4];
|
||||
}
|
||||
|
||||
.f-input:focus {
|
||||
@apply outline-none border-[--f-header];
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--f-header) 40%, transparent);
|
||||
}
|
||||
|
||||
.fuz-input::placeholder {
|
||||
.f-input::placeholder {
|
||||
color: color-mix(in srgb, var(--f-text) 40%, transparent);
|
||||
}
|
||||
|
||||
.fuz-input-error {
|
||||
border-color: #ff4d4f !important;
|
||||
box-shadow: 0 0 0 3px rgba(255, 77, 79, 0.3) !important;
|
||||
}
|
||||
|
||||
.fuz-input:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
background-color: color-mix(in srgb, var(--f-background) 80%, #888);
|
||||
.f-input-error {
|
||||
@apply border-red-500 ring-2 ring-red-500/30;
|
||||
}
|
||||
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.autocomplete-open {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
border-bottom-width: 0 !important;
|
||||
@apply rounded-b-none border-b-0;
|
||||
}
|
||||
|
||||
.autocomplete-list {
|
||||
@apply absolute left-0 right-0 z-50 bg-[--f-background] text-[--f-text] border border-gray-300 dark:border-slate-700 rounded-b-xl shadow-xl max-h-56 overflow-auto;
|
||||
@apply absolute left-0 right-0 z-50 bg-[--f-background] text-[--f-text] border border-[--f-input-border] rounded-b-xl shadow-xl max-h-56 overflow-auto;
|
||||
|
||||
border-top: none;
|
||||
animation: fadeIn 0.12s ease-out;
|
||||
|
||||
@@ -1,30 +1,5 @@
|
||||
@tailwind base;
|
||||
|
||||
.f-contact-grid {
|
||||
@apply grid md:grid-cols-2 gap-10 items-start max-w-7xl mx-auto;
|
||||
}
|
||||
|
||||
.f-contact-col-1 {
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
@apply f-section-header;
|
||||
}
|
||||
}
|
||||
|
||||
.f-contact-col-2 {
|
||||
@apply bg-[--f-background] text-[--f-text];
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
@apply f-section-header;
|
||||
}
|
||||
}
|
||||
|
||||
.f-contact-item {
|
||||
@apply space-y-1;
|
||||
|
||||
@@ -55,10 +30,6 @@
|
||||
@apply mt-2 h-4 w-4;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply btn btn-primary w-full py-3 text-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.f-contact-map {
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
.fuz-markdown button.modal-link {
|
||||
@apply no-underline hover:no-underline mt-2 bg-[--f-background] text-[--f-link-text];
|
||||
}
|
||||
|
||||
.fuz-markdown blockquote {
|
||||
@apply border-l-4 border-gray-300 dark:border-gray-700 pl-4 italic mb-4;
|
||||
}
|
||||
|
||||
@@ -15,21 +15,39 @@
|
||||
}
|
||||
|
||||
.f-mobile-toggle {
|
||||
@apply text-3xl p-2 text-[--f-text];
|
||||
@apply text-3xl p-2 text-[--f-text] relative z-[70];
|
||||
}
|
||||
|
||||
.f-mobile-menu {
|
||||
@apply fixed top-0 right-0 h-full w-64 bg-[--f-background] shadow-lg transform translate-x-full transition-transform duration-300 flex flex-col gap-4 p-6;
|
||||
@apply fixed right-4 bg-[--f-background] border border-[--f-border-color] p-4 flex flex-col gap-2 opacity-0 scale-95 -translate-y-2 pointer-events-none transition duration-200 divide-y divide-[--f-border-color];
|
||||
top: calc(var(--f-navbar-height, 64px) + 0.75rem);
|
||||
width: 18rem;
|
||||
max-width: calc(100vw - 2rem);
|
||||
z-index: 70;
|
||||
max-height: calc(100vh - var(--f-navbar-height, 64px) - 2rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.f-mobile-menu.open {
|
||||
@apply translate-x-0;
|
||||
@apply opacity-100 scale-100 translate-y-0 pointer-events-auto;
|
||||
}
|
||||
|
||||
.f-mobile-link {
|
||||
@apply text-lg py-2 border-b text-[--f-navbar-link] hover:text-[--f-navbar-link-hover] border-[--f-border-color];
|
||||
@apply text-base py-2 px-2
|
||||
text-[--f-navbar-link]
|
||||
hover:text-[--f-navbar-link-hover]
|
||||
hover:bg-black/5 dark:hover:bg-white/5
|
||||
transition;
|
||||
}
|
||||
|
||||
.f-navbar-logo {
|
||||
@apply w-[70] h-[36];
|
||||
}
|
||||
|
||||
.f-mobile-backdrop {
|
||||
@apply fixed inset-0 bg-black/30 opacity-0 pointer-events-none transition-opacity duration-200 z-[60];
|
||||
}
|
||||
|
||||
.f-mobile-backdrop.open {
|
||||
@apply opacity-100 pointer-events-auto;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
.f-extra-services {
|
||||
@apply mt-6 max-w-6xl mx-auto;
|
||||
}
|
||||
|
||||
.f-services-title {
|
||||
@apply text-2xl font-semibold mb-4 ml-10;
|
||||
}
|
||||
.f-services-body {
|
||||
@apply text-lg font-semibold mb-4 ml-10;
|
||||
}
|
||||
|
||||
.f-expand-details {
|
||||
@apply px-4 py-4;
|
||||
}
|
||||
|
||||
.f-feature-link {
|
||||
@apply cursor-pointer w-full h-full text-[--f-link-text] text-lg;
|
||||
}
|
||||
|
||||
.f-feature-link:hover {
|
||||
@apply text-[--f-link-text-hover];
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.fuz-offers-section {
|
||||
@apply py-6;
|
||||
}
|
||||
|
||||
.f-offers-container {
|
||||
@apply max-w-7xl mx-auto px-6;
|
||||
}
|
||||
|
||||
.f-offers-description {
|
||||
@apply mb-10 text-base leading-relaxed;
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.f-switch {
|
||||
@apply px-6 py-2 text-sm font-semibold cursor-pointer select-none transition-all;
|
||||
@apply px-6 py-3 text-sm font-semibold cursor-pointer select-none transition-all;
|
||||
}
|
||||
|
||||
.f-switch.active {
|
||||
|
||||
Reference in New Issue
Block a user