Kolejne poprawki

This commit is contained in:
dm
2025-12-11 07:06:32 +01:00
parent 1bdffb1003
commit 49c5beb362
19 changed files with 565 additions and 421 deletions

View File

@@ -0,0 +1,67 @@
---
import Markdown from "../../islands/Markdown.jsx";
// Pobranie XML Jambox
const url = "https://www.jambox.pl/xml/mozliwosci.xml";
const xmlText: string = await fetch(url).then(r => r.text());
// Parser wszystkich <node>
function parseNodes(xml: string) {
return [...xml.matchAll(/<node>([\s\S]*?)<\/node>/g)].map((match) => {
const block = match[1];
const get = (tag: string) => {
const m = block.match(new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`));
return m ? m[1].trim() : "";
};
return {
title: get("title"),
teaser: get("teaser"),
description: get("description"),
icon: get("icon"),
};
});
}
const nodes = parseNodes(xmlText);
---
<section class="f-section">
<h2 class="f-section-title mb-10">Dodatkowe możliwości telewizji JAMBOX</h2>
{nodes.map((item, i) => {
const reverse = i % 2 === 1;
return (
<div class={`f-section-item py-14`}>
<div class={`f-section-grid md:grid-cols-2`}>
<!-- OBRAZ -->
<div class={`${reverse ? "md:order-2" : "md:order-1"}`}>
<img
src={item.icon}
alt={item.title}
loading="lazy"
class="f-section-image rounded-xl shadow-lg"
/>
</div>
<!-- TEKST -->
<div class={`f-section-grid ${reverse ? "md:order-1" : "md:order-2"}`}>
<h3 class="f-section-title text-2xl mb-4">{item.title}</h3>
{item.teaser && (
<p class="text-lg font-medium opacity-80 mb-4">
{item.teaser}
</p>
)}
<Markdown text={item.description} />
</div>
</div>
</div>
);
})}
</section>

View File

@@ -19,7 +19,7 @@ ctas:
title: "Przejdź do oferty Internet + Telewizja w FUZ"
primary: false
- label: "Sprawdź dostępność usługi"
href: "/mapa-zasiegu"
title: "Sprawdź zasięg Internetu światłowodowego FUZ"
primary: false
# - label: "Sprawdź dostępność usługi"
# href: "/mapa-zasiegu"
# title: "Sprawdź zasięg Internetu światłowodowego FUZ"
# primary: false

View File

@@ -21,7 +21,7 @@ ctas:
primary: false
title: "Przejdź do oferty telefonu"
- label: "Sprawdź dostępność usługi"
href: "/mapa-zasiegu"
title: "Sprawdź zasięg Internetu światłowodowego FUZ"
primary: false
# - label: "Sprawdź dostępność usługi"
# href: "/mapa-zasiegu"
# title: "Sprawdź zasięg Internetu światłowodowego FUZ"
# primary: false

View File

@@ -2,132 +2,152 @@ przelaczniki:
- id: "budynek"
etykieta: "Rodzaj budynku"
domyslny: "jednorodzinny"
title: Zmień rodzaj budynku by zobaczyć odpowiednie ceny
title: "Zmień rodzaj budynku by zobaczyć odpowiednie ceny"
opcje:
- id: "jednorodzinny"
nazwa: "Jednorodzinny"
- id: "wielorodzinny"
nazwa: "Wielorodzinny"
- id: "umowa"
etykieta: "Okres umowy"
domyslny: "24m"
title: Wybierz okres umowy by zobaczyć odpowiednie ceny
title: "Wybierz okres umowy by zobaczyć odpowiednie ceny"
opcje:
- id: "24m"
nazwa: "24 miesiące"
- id: "bezterminowa"
nazwa: "Bezterminowa"
# WIERSZE TABELI
funkcje:
- id: "pobieranie"
etykieta: "Prędkość pobierania"
- id: "wysylanie"
etykieta: "Prędkość wysyłania"
- id: "router"
etykieta: "Router Wi-Fi"
# - id: "tv"
# etykieta: "Pakiet TV"
- id: "umowa_info"
etykieta: "Umowa"
- id: "instalacja"
etykieta: "Aktywacja"
- id: "adres_ip"
etykieta: "Adres IP"
plany_title:
- id: "umowa_info"
etykieta: "Umowa"
- id: "instalacja"
etykieta: "Aktywacja"
plany:
- id: "fiber100"
nazwa: "FIBER 100"
predkosc: "100 Mb/s"
popularny: false
ceny:
jednorodzinny:
# 12m: "59 zł/mc"
24m: "64 zł/mc"
bezterminowa: "84 zł/mc"
24m: 64
bezterminowa: 84
wielorodzinny:
# 12m: "55 zł/mc"
24m: "54 zł/mc"
bezterminowa: "74 zł/mc"
24m: 54
bezterminowa: 74
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "do 100 Mb/s"
wysylanie: "do 50 Mb/s"
router: true
adres_ip: "Dynamiczny"
tv: false
umowa_info: "12 / 24 / bez umowy"
instalacja: "149 zł"
- id: "fiber300"
nazwa: "FIBER 300"
predkosc: "300 Mb/s"
popularny: true
ceny:
jednorodzinny:
# 12m: "79 zł/mc"
24m: "75 zł/mc"
bezterminowa: "95 zł/mc"
24m: 75
bezterminowa: 95
wielorodzinny:
# 12m: "69 zł/mc"
24m: "65 zł/mc"
bezterminowa: "85 zł/mc"
24m: 65
bezterminowa: 85
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "do 300 Mb/s"
wysylanie: "do 150 Mb/s"
router: true
umowa_info: "12 / 24 / bez umowy"
instalacja: "149,00 zł"
adres_ip: "Dynamiczny"
umowa_info: "12 / 24 / bez umowy"
- id: "fiber600"
nazwa: "FIBER 600"
predkosc: "600 Mb/s"
popularny: false
ceny:
jednorodzinny:
24m: "85 zł/mc"
bezterminowa: "105 zł/mc"
24m: 85
bezterminowa: 105
wielorodzinny:
24m: "75 zł/mc"
bezterminowa: "95 zł/mc"
24m: 75
bezterminowa: 95
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "do 600 Mb/s"
wysylanie: "do 300 Mb/s"
router: true
umowa_info: "24 / bez umowy"
instalacja: "149 zł"
adres_ip: "Dynamiczny"
umowa_info: "24 / bez umowy"
uslugi:
title: Usługi dodatkowe
description: Do wybranej opcji możesz dokupić dotakowe usługi.
items:
- Nazwa usługi
- Cena
- Szczegóły
uslugi_dodatkowe:
addons:
- id: "public_ip"
nazwa: "Publiczny adres IP"
cena: "18,45 zł miesięcznie."
opis: |
Otrzymujesz unikalny, publiczny adres przypisany na stałe do Twojego łącza, który pozwala na:
- Monitoring domu
- Dostęp zdalny do urządzeń
- Hostowanie serwera lub aplikacji
typ: "checkbox"
cena: 18.45
Jest to przydatne dla firm, graczy i zaawansowanych użytkowników, którzy potrzebują stabilnej identyfikacji swojej sieci w internecie.
- id: telefon
nazwa: Telefon
cena: od wybranej opcji
opis: |
Profesjonalna telefonia VoIP jako dodatek do internetu
- Niższe koszty połączeń - szczególnie na komórki i za granicę
- Zachowaj dotychczasowy numer lub otrzymaj nowy lokalny
- Bez dodatkowych kabli - działa przez internet
- Jeden numer dostępny na wielu urządzeniach jednocześnie
- Krystalicznie czysty dźwięk HD
[Poznaj szczegóły oferty telefonii →](/telefon "Poznaj szczegóły oferty telefonii")
- id: "telefon"
nazwa: "Telefon VoIP"
typ: "select"
opis: "Wybierz pakiet telefonii"
opcje:
- id: "brak"
nazwa: "Bez telefonu"
cena: 0
- id: "tele30"
nazwa: "TELE 30"
cena: 9.90
- id: "tele100"
nazwa: "TELE 100"
cena: 15.00
- id: "tele300"
nazwa: "TELE 300"
cena: 29.00
- id: "tele500"
nazwa: "TELE 500"
cena: 44.00

View File

@@ -3,8 +3,8 @@ title:
paragraphs:
- title:
# content: |
# Światłowody to najbardziej zaawansowana technologia przesyłu danych.
content: |
Wybierz rodzaj budynku i czas trwania umowy
# Gwarantują błyskawiczną prędkość, stabilność i niezawodność bez względu na warunki.
@@ -18,11 +18,5 @@ paragraphs:
# Sprawdź naszą pełną ofertę i wybierz rozwiązanie dopasowane do Twoich potrzeb.
- title:
content:
Router WiFi AC1200 w cenie instalacji, zapewnia jeszcze większą moc, szybkość, zasięg i bezpieczeństwo dla Twoich stale rosnących potrzeb sieciowych.
Dzięki funkcji sieci gościnnej Twoi goście mają dostęp do internetu, a Twoje urządzenia i dane pozostają bezpieczne.
Technologia dwuzakresowa eliminuje zakłócenia i gwarantuje płynne działanie sieci dla całej rodziny.
# Kolejne sekcje mozna dodawać poja wiać się bedą pod tabela produktów
# - title:
# content:

View File

@@ -20,7 +20,7 @@ ctas:
title: "Przejdź do oferty telefonu"
primary: false
- label: "Sprawdź dostępność usługi"
href: "/mapa-zasiegu"
title: "Sprawdź zasięg Internetu światłowodowego FUZ"
primary: false
# - label: "Sprawdź dostępność usługi"
# href: "/mapa-zasiegu"
# title: "Sprawdź zasięg Internetu światłowodowego FUZ"
# primary: false

View File

@@ -2,7 +2,7 @@ przelaczniki:
- id: "budynek"
etykieta: "Rodzaj budynku"
domyslny: "jednorodzinny"
title: Zmień rodzaj budynku by zobaczyć odpowiednie ceny
title: "Zmień rodzaj budynku by zobaczyć odpowiednie ceny"
opcje:
- id: "jednorodzinny"
nazwa: "Jednorodzinny"
@@ -12,7 +12,7 @@ przelaczniki:
- id: "umowa"
etykieta: "Okres umowy"
domyslny: "24m"
title: Wybierz okres umowy by zobaczyć odpowiednie ceny
title: "Wybierz okres umowy by zobaczyć odpowiednie ceny"
opcje:
- id: "24m"
nazwa: "24 miesiące"
@@ -35,18 +35,28 @@ funkcje:
- id: "instalacja"
etykieta: "Aktywacja"
plany_title:
plany:
- id: "pakiet-1"
nazwa: "SMART"
popularny: false
ceny:
jednorodzinny:
24m: "109 zł/mc"
bezterminowa: "129 zł/mc"
24m: 109
bezterminowa: 129
wielorodzinny:
24m: "99 zł/mc"
bezterminowa: "119 zł/mc"
24m: 99
bezterminowa: 119
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -54,18 +64,28 @@ plany:
kanaly: "127"
hd: "99"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
- id: "pakiet-2"
nazwa: "OPTIMUM"
popularny: false
ceny:
jednorodzinny:
24m: "125 zł/mc"
bezterminowa: "145 zł/mc"
24m: 125
bezterminowa: 145
wielorodzinny:
24m: "115 zł/mc"
bezterminowa: "135 zł/mc"
24m: 115
bezterminowa: 135
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -73,18 +93,28 @@ plany:
kanaly: "171"
hd: "129"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
- id: "pakiet-3"
nazwa: "PLATINUM"
popularny: true
ceny:
jednorodzinny:
24m: "158 zł/mc"
bezterminowa: "178 zł/mc"
24m: 158
bezterminowa: 178
wielorodzinny:
24m: "148 zł/mc"
bezterminowa: "168 zł/mc"
24m: 148
bezterminowa: 168
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -92,18 +122,28 @@ plany:
kanaly: "207"
hd: "157"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
- id: "pakiet-4"
nazwa: "PODSTAWOWY"
popularny: false
ceny:
jednorodzinny:
24m: "88 zł/mc"
bezterminowa: "108 zł/mc"
24m: 88
bezterminowa: 108
wielorodzinny:
24m: "78 zł/mc"
bezterminowa: "98 zł/mc"
24m: 78
bezterminowa: 98
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -111,18 +151,28 @@ plany:
kanaly: "83"
hd: "63"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
- id: "pakiet-5"
nazwa: "KORZYSTNY"
popularny: false
ceny:
jednorodzinny:
24m: "110 zł/mc"
bezterminowa: "130 zł/mc"
24m: 110
bezterminowa: 130
wielorodzinny:
24m: "100 zł/mc"
bezterminowa: "120 zł/mc"
24m: 100
bezterminowa: 120
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -130,18 +180,28 @@ plany:
kanaly: "142"
hd: "107"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
- id: "pakiet-6"
nazwa: "BOGATY"
popularny: false
ceny:
jednorodzinny:
24m: "120 zł/mc"
bezterminowa: "140 zł/mc"
24m: 120
bezterminowa: 140
wielorodzinny:
24m: "110 zł/mc"
bezterminowa: "130 zł/mc"
24m: 110
bezterminowa: 130
koszty:
instalacja:
jednorodzinny:
24m: 149
bezterminowa: 199
wielorodzinny:
24m: 99
bezterminowa: 149
funkcje:
pobieranie: "300 Mb/s"
wysylanie: "150 Mb/s"
@@ -149,87 +209,3 @@ plany:
kanaly: "184"
hd: "139"
umowa_info: "24 / Bezterminowa"
instalacja: "99 zł"
uslugi:
title: Usługi dodatkowe
description: Do wybranej opcji możesz dokupić dotakowe usługi.
items:
- Nazwa usługi
- Cena
- Szczegóły
uslugi_dodatkowe:
- id: "public_ip"
nazwa: "Publiczny adres IP"
cena: "18,45 zł miesięcznie."
opis: |
Otrzymujesz unikalny, publiczny adres przypisany na stałe do Twojego łącza, który pozwala na:
- Monitoring domu
- Dostęp zdalny do urządzeń
- Hostowanie serwera lub aplikacji
Jest to przydatne dla firm, graczy i zaawansowanych użytkowników, którzy potrzebują stabilnej identyfikacji swojej sieci w internecie.
- id: telefon
nazwa: Telefon
cena: od wybranej opcji
opis: |
Profesjonalna telefonia VoIP jako dodatek do internetu
- Niższe koszty połączeń - szczególnie na komórki i za granicę
- Zachowaj dotychczasowy numer lub otrzymaj nowy lokalny
- Bez dodatkowych kabli - działa przez internet
- Jeden numer dostępny na wielu urządzeniach jednocześnie
- Krystalicznie czysty dźwięk HD
[Poznaj szczegóły oferty telefonii →](/telefon)
- id: "canal_plus"
nazwa: "Canal+ Seriale i Filmy"
cena: "od 25,00 zł miesięcznie"
opis: |
<div class="w-full max-w-xl mx-auto aspect-video">
<iframe
title="Pakiet Canal+"
src="https://www.jambox.pl/iframe-pakiet-logo?p=49"
class="w-full h-full rounded-lg shadow"
style="w-full h-full rounded-lg shadow bg-transparent"
></iframe>
</div>
- id: "canal_sport"
nazwa: "Canal+ Super Sport"
cena: "od 65,00 zł miesięcznie"
opis: |
<div class="w-full max-w-xl mx-auto aspect-video" style="aspect-ratio: 6 / 1;">
<iframe
title="Pakiet Canal+"
src="https://www.jambox.pl/iframe-pakiet-logo?p=48"
class="w-full h-full rounded-lg shadow bg-transparent">
</iframe>
</div>
- id: "canal_hbo"
nazwa: "HBO +MAX"
cena: "od 27,90 zł miesięcznie"
opis: |
<div class="w-full max-w-xl mx-auto aspect-video">
<iframe
title="Pakiet Canal+"
src="https://www.jambox.pl/iframe-pakiet-logo?p=20"
class="w-full h-full rounded-lg shadow"
style="w-full h-full rounded-lg shadow bg-transparent"
></iframe>
</div>
- id: "canal_cinemax"
nazwa: "Cinemax"
cena: "od 14,90 zł miesięcznie"
opis: |
<div class="w-full max-w-xl mx-auto aspect-video">
<iframe
title="Pakiet Canal+"
src="https://www.jambox.pl/iframe-pakiet-logo?p=18"
class="w-full h-full rounded-lg shadow"
style="w-full h-full rounded-lg shadow bg-transparent"
></iframe>
</div>

View File

@@ -13,9 +13,3 @@ paragraphs:
# Oszczędzaj czas i ciesz się prostotą, wszystko czego potrzebujesz, w jednym miejscu.
# Kolejne sekcje mozna dodawać poja wiać się bedą pod tabela produktów
- title:
content:
Telewizja dostępna jest wyłącznie jako element pakietów.
Liczba kanałów zależy od wybranego pakietu.

View File

@@ -20,7 +20,7 @@ ctas:
title: "Przejdź do oferty Internet + Telewizja w FUZ"
primary: false
- label: "Sprawdź dostępność usługi "
href: "/mapa-zasiegu"
title: "Sprawdź zasięg Internetu światłowodowego FUZ"
primary: false
# - label: "Sprawdź dostępność usługi "
# href: "/mapa-zasiegu"
# title: "Sprawdź zasięg Internetu światłowodowego FUZ"
# primary: false

View File

@@ -1,19 +1,21 @@
funkcje:
- id: "minuty_darmowe"
etykieta: "Darmowe minuty"
- id: "cena_minuta_stacjonarna"
etykieta: "Połączenia do krajowych sieci stacjonarnych"
- id: "cena_minuta_komorkowe"
etykieta: "Połączenia do krajowych sieci komórkowych"
- id: "instalacja"
etykieta: "Aktywacja"
plany_title:
plany:
- id: "tele30"
nazwa: "TELE 30"
popularny: false
cena: "9,90 zł/mc"
cena: 9.90
funkcje:
minuty_darmowe: "30"
cena_minuta_stacjonarna: "0,07 zł / min."
@@ -23,7 +25,7 @@ plany:
- id: "tele100"
nazwa: "TELE 100"
popularny: false
cena: "15,00 zł/mc"
cena: 15.00
funkcje:
minuty_darmowe: "100"
cena_minuta_stacjonarna: "0,07 zł / min."
@@ -33,7 +35,7 @@ plany:
- id: "tele300"
nazwa: "TELE 300"
popularny: false
cena: "29,00 zł/mc"
cena: 29.00
funkcje:
minuty_darmowe: "300"
cena_minuta_stacjonarna: "0,07 zł / min."
@@ -43,9 +45,11 @@ plany:
- id: "tele500"
nazwa: "TELE 500"
popularny: false
cena: "44,00 zł/mc"
cena: 44.00
funkcje:
minuty_darmowe: "500"
cena_minuta_stacjonarna: "0,07 zł / min."
cena_minuta_komorkowe: "0,19 zł / min"
instalacja: "1,23 zł"
addons: []

View File

@@ -0,0 +1,22 @@
export function getInstallationPrice(plan, switches, selected) {
// Dla telefonii instalacja to po prostu funkcja wpisana w plan
if (!plan.koszty) {
// instalacja jest zapisana jako tekst "1,23 zł"
const raw = plan.funkcje?.instalacja;
if (!raw) return 0;
const num = parseFloat(raw.replace(",", "."));
return isNaN(num) ? 0 : num;
}
// Internet / TV
const budynek = selected.budynek;
const umowa = selected.umowa;
return (
plan.koszty?.instalacja?.[budynek]?.[umowa] ??
plan.koszty?.instalacja?.[budynek]?.default ??
plan.koszty?.instalacja?.default ??
0
);
}

View File

@@ -1,83 +1,75 @@
import { getPrice } from "../../helpers/getPrice";
import { getActiveLabel } from "../../helpers/getActiveLabel";
import { getInstallationPrice } from "../../helpers/getInstallationPrice";
import "../../styles/offers/offers-table.css";
export default function OffersCards({ data, switches, selected }) {
const plans = data.plany;
export default function OffersTable({ switches, selected, plans, features, plan_title }) {
return (
<div class="f-table-wrapper">
<table class="f-table">
<thead class="f-table-head">
<tr>
<th class="f-table-heading">{plan_title}</th>
{plans.map((plan) => (
<th
class={`f-plan-heading ${plan.popularny ? "is-popular f-popular-top" : ""
}`}
>
{/* {plan.popularny && (<div class="f-popular-badge">Popularny</div>)} */}
<div class="f-plan-title">{plan.nazwa}</div>
<div class="f-plan-price">
{getPrice(plan, switches, selected)}
</div>
{/* <div class="f-plan-speed">{plan.predkosc}</div> */}
</th>
))}
</tr>
</thead>
<section class="f-offers">
{data.plany_title && (
<h2 class="f-offers-title">{data.plany_title}</h2>
)}
<tbody>
{features.map((f, rowIndex) => (
<tr class={rowIndex % 2 === 0 ? "f-row-even" : "f-row-odd"}>
<td class="f-feature-name">{f.etykieta}</td>
<div class={`f-offers-grid f-count-${plans.length}`}>
{plans.map((plan) => (
<OfferCard
key={plan.id}
plan={plan}
features={data.funkcje}
switches={switches}
selected={selected}
/>
))}
</div>
</section>
);
}
{plans.map((plan) => {
const isPopular = plan.popularny;
const isLastRow = rowIndex === features.length - 1;
function OfferCard({ plan, features, switches, selected }) {
const basePrice = getPrice(plan, switches, selected);
if (f.id === "umowa_info") {
return (
<td
class={`f-feature-cell ${isPopular
? `is-popular ${isLastRow ? "f-popular-bottom" : ""
}`
: ""
}`}
>
{getActiveLabel(switches, selected, "umowa")}
</td>
);
}
// NOWE: cena instalacji dynamiczna
const installPrice = getInstallationPrice(plan, switches, selected);
const val = plan.funkcje?.[f.id];
return (
<div class={`f-card ${plan.popularny ? "f-card-popular" : ""}`}>
{plan.popularny && (
<div class="f-card-badge">Najczęściej wybierany</div>
)}
const baseClass =
val === true
? "f-feature-yes"
: val === false || val == null
? "f-feature-no"
: "f-feature-cell";
<div class="f-card-header">
<div class="f-card-name">{plan.nazwa}</div>
<div class="f-card-price">{basePrice} /mies.</div>
</div>
return (
<td
class={`${baseClass} ${isPopular
? `is-popular ${isLastRow ? "f-popular-bottom" : ""
}`
: ""
}`}
>
{val === true
? "✓"
: val === false || val == null
? "✕"
: val}
</td>
);
})}
</tr>
))}
</tbody>
</table>
<ul class="f-card-features">
{features.map((f) => {
let val =
f.id === "umowa_info"
? getActiveLabel(switches, selected, "umowa")
: plan.funkcje?.[f.id];
// PODMIANA pola instalacji — nie ze statycznego YAML tylko dynamicznie
if (f.id === "instalacja") {
val = installPrice + " zł";
}
return (
<li class="f-card-row">
<span class="f-card-label">{f.etykieta}</span>
<span class="f-card-value">
{val === true
? "✓"
: val === false || val == null
? "✕"
: val}
</span>
</li>
);
})}
</ul>
</div>
);
}

View File

@@ -1,69 +1,59 @@
import { useState } from "preact/hooks";
import FuzMarkdown from "./Markdown.jsx";
import OffersSwitches from "./Offers/OffersSwitches.jsx";
import OffersTable from "./Offers/OffersTable.jsx";
import OffersCards from "./Offers/OffersTable.jsx"; // <-- WAŻNE!!
import OffersExtraServices from "./Offers/OffersExtraServices.jsx";
// import "../styles/offers/offers-main.css";
export default function InternetOffersIsland({ data }) {
export default function OffersIsland({ data }) {
const switches = data.przelaczniki ?? [];
const features = data.funkcje ?? [];
const plans = data.plany ?? [];
const plan_title = data.plany_title ?? "";
const extraServices = data.uslugi_dodatkowe ?? [];
const services = data.uslugi ?? [];
const initialSelected = {};
switches.forEach((sw) => {
initialSelected[sw.id] = sw.domyslny;
// selected state
const [selected, setSelected] = useState(() => {
const init = {};
switches.forEach((sw) => (init[sw.id] = sw.domyslny));
return init;
});
const [selected, setSelected] = useState(initialSelected);
const [openServiceId, setOpenServiceId] = useState(null);
// services accordion (jeśli potrzebne)
const [openId, setOpenId] = useState(null);
const toggleService = (id) =>
setOpenServiceId((prev) => (prev === id ? null : id));
const handleSwitch = (switchId, value) => {
setSelected((prev) => ({
...prev,
[switchId]: value
}));
};
const handleSwitchClick = (switchId, optionId) =>
setSelected((prev) => ({ ...prev, [switchId]: optionId }));
const toggleExtra = (id) => {
setOpenId((prev) => (prev === id ? null : id));
};
return (
<section class="f-offers-section">
<div class="f-offers-container">
{data.opis_gorny && (
<div class="f-offers-description">
<FuzMarkdown text={data.opis_gorny} />
</div>
)}
<div class="f-offers-wrapper">
<OffersSwitches
switches={switches}
selected={selected}
onSwitch={handleSwitchClick}
/>
{/* SWITCHERY */}
<OffersSwitches
switches={switches}
selected={selected}
onSwitch={handleSwitch}
/>
<OffersTable
switches={switches}
selected={selected}
plans={plans}
features={features}
plan_title={plan_title}
/>
{/* KARTY OFERT */}
<OffersCards
data={data}
switches={switches}
selected={selected}
/>
{/* USŁUGI DODATKOWE */}
{data.uslugi_dodatkowe && (
<OffersExtraServices
extraServices={extraServices}
openId={openServiceId}
toggle={toggleService}
services={services}
extraServices={data.uslugi_dodatkowe}
services={data.uslugi}
openId={openId}
toggle={toggleExtra}
/>
{data.opis_dolny && (
<div class="fuz-offers-description">
<FuzMarkdown text={data.opis_dolny} />
</div>
)}
</div>
</section>
)}
</div>
);
}

View File

@@ -41,17 +41,22 @@ const rest = page.paragraphs.slice(1);
</div>
</section>
<OffersIsland client:load data={data} />
{rest.map((p: Paragraph) => (
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
{p.title && <h3 class="f-section-title">{p.title}</h3>}
<Markdown text={p.content.replace(/\n/g, "\n\n")} />
<OffersIsland client:load data={data} />
</div>
</section>
))}
{
rest.map((p: Paragraph) => (
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
{p.title && <h3 class="f-section-title">{p.title}</h3>}
<Markdown text={p.content.replace(/\n/g, "\n\n")} />
</div>
</section>
))
}
<SectionRenderer src="./src/content/internet-swiatlowodowy/section.yaml" />
</DefaultLayout>

View File

@@ -5,6 +5,7 @@ import SectionRenderer from "../../components/sections/SectionRenderer.astro";
import Markdown from "../../islands/Markdown.jsx";
import Modal from "../../islands/Modal.jsx";
import OffersIsland from "../../islands/OffersIsland.jsx";
import JamboxMozliwosci from "../../components/sections/SectionJamboxMozliwosci.astro";
import yaml from "js-yaml";
import fs from "fs";
@@ -45,7 +46,14 @@ const rest = page.paragraphs.slice(1);
</div>
</section>
<OffersIsland client:load data={data} />
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1 max-w-6xl mx-auto">
<OffersIsland client:load data={data} />
</div>
</section>
<!-- <OffersIsland client:load data={data} /> -->
{
rest.map((p: Paragraph) => (
<section class="f-section">
@@ -59,6 +67,9 @@ const rest = page.paragraphs.slice(1);
<SectionRenderer src="./src/content/internet-telewizja/section.yaml" />
<JamboxMozliwosci />
<Modal client:load modalData={modalData} />
</DefaultLayout>

View File

@@ -41,7 +41,13 @@ const rest = page.paragraphs.slice(1);
</div>
</section>
<OffersIsland client:load data={data} />
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
<OffersIsland client:load data={data} />
</div>
</section>
<!-- <OffersIsland client:load data={data} /> -->
{
rest.map((p: Paragraph) => (
<section class="f-section">

View File

@@ -1,9 +1,9 @@
.f-switches-wrapper {
@apply flex flex-wrap justify-center gap-6 mb-10;
@apply flex flex-wrap justify-center gap-6;
}
.f-switch-group {
@apply inline-flex overflow-hidden relative bg-[--f-background-switch] mt-8;
@apply inline-flex overflow-hidden relative bg-[--f-background-switch];
}
.f-switch {

View File

@@ -1,89 +1,146 @@
.f-table-wrapper {
@apply overflow-x-auto rounded-3xl shadow-lg mb-0 border border-[--f-offers-border];
/* GŁÓWNE SEKCJE */
.f-offers {
@apply my-6;
}
.f-table {
@apply min-w-full border-collapse;
/* GRID FLEX — zawsze centrowany */
.f-offers-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 2rem;
padding: 0 1rem;
}
.f-table-head {
background: color-mix(in srgb, var(--f-text) 6%, transparent);
/* KARTA POPRAWIONA, BEZ width/max-width/flex 1 1 300px */
.f-card {
flex: 0 1 320px;
@apply bg-[--f-bg] text-[--f-text] border border-[--f-offers-border]
rounded-2xl shadow-md p-6 relative flex flex-col gap-4;
}
.f-table-heading {
@apply text-center font-semibold text-xl py-4 px-4 text-wrap;
.f-card:hover {
@apply shadow-lg;
transform: translateY(-3px);
}
.f-plan-heading {
@apply text-center py-4 px-4 align-bottom relative;
}
.f-plan-title {
@apply text-xl font-semibold mb-1;
}
.f-plan-price {
@apply text-2xl font-extrabold mb-1 text-[--f-offers-price];
}
/* Na ten moment ukryte */
.f-plan-speed {
@apply text-xs opacity-[0.7];
}
.f-row-even {
background: transparent;
}
.f-row-odd {
background: color-mix(in srgb, var(--f-text) 4%, transparent);
}
.f-feature-name {
@apply py-3 px-4 text-lg font-medium ;
}
.f-feature-cell {
@apply py-3 px-4 text-center text-lg;
}
.f-feature-yes {
@apply py-3 px-4 text-center font-bold text-base;
}
.f-feature-no {
@apply py-3 px-4 text-center font-bold text-base opacity-[0.45];
}
/* Popularny */
.is-popular,
.f-popular-col {
/* POPULARNY PLAN */
.f-card-popular {
border: 2px solid var(--f-offers-popular);
background: var(--f-offers-popular-bg);
/* color-mix(in srgb, var(--f-offers-popular) 22%, transparent) !important; */
border-left: 2px solid var(--f-offers-popular);
border-right: 2px solid var(--f-offers-popular);
position: relative;
z-index: 0;
}
.f-popular-top {
border-top: 2px solid var(--f-offers-popular);
.f-card-badge {
@apply absolute top-3 right-3 bg-[--btn-background]
text-[--btn-text] text-sm font-semibold px-3 py-1 rounded-full;
}
.f-popular-bottom {
border-bottom: 2px solid var(--f-offers-popular);
/* HEADER */
.f-card-header {
@apply flex flex-col items-start gap-1;
}
.f-popular-badge {
@apply bg-blue-500 text-white text-xs font-semibold px-3 py-1 rounded-full inline-block mb-2 uppercase tracking-wide;
/* background: #3b82f6;
color: white;
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
display: inline-block;
margin-bottom: 0.5rem;
text-transform: uppercase;
letter-spacing: 0.025em; */
.f-card-name {
@apply text-xl font-semibold;
}
.f-card-price {
@apply text-3xl font-extrabold text-[--f-offers-price];
}
/* FEATURES */
.f-card-features {
@apply list-none p-0 m-0;
}
.f-card-row {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 0.75rem;
padding: 0.5rem 0;
border-bottom: 1px solid var(--f-offers-border);
align-items: center;
}
.f-card-row:last-child {
border-bottom: none;
}
.f-card-label {
@apply text-base font-medium opacity-80;
}
.f-card-value {
@apply text-base font-semibold text-right;
}
.f-card-value.yes {
@apply text-green-600;
}
.f-card-value.no {
@apply text-red-600 opacity-60;
}
/* -----------------------------
INTELIGENTNY UKŁAD KART
------------------------------ */
/* DOMYŚLNE: responsywnie */
.f-card {
flex: 1 1 300px;
max-width: 360px;
}
/* LICZNIK KART */
.f-offers-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 2rem;
}
/* --- UKŁAD SPECJALNY --- */
/* 4 karty → 2 + 2 */
.f-count-4 .f-card {
flex: 0 1 calc(50% - 2rem);
max-width: 420px;
}
/* 5 kart → 3 + 2 */
.f-count-5 .f-card {
flex: 0 1 calc(33% - 2rem);
}
.f-count-5 .f-card:nth-child(n+4) {
flex: 0 1 calc(50% - 2rem);
max-width: 420px;
}
/* 7 kart → 3 + 3 + 1 (środek) */
.f-count-7 .f-card {
flex: 0 1 calc(33% - 2rem);
}
.f-count-7 .f-card:last-child {
flex: 0 1 calc(33% - 2rem);
margin-left: auto;
margin-right: auto;
}
/* 8 kart → 3 + 3 + 2 */
.f-count-8 .f-card {
flex: 0 1 calc(33% - 2rem);
}
.f-count-8 .f-card:nth-child(n+7) {
flex: 0 1 calc(50% - 2rem);
max-width: 420px;
}
/* 10 kart → 3 + 3 + 3 + 1 */
.f-count-10 .f-card:last-child {
flex: 0 1 calc(33% - 2rem);
margin-left: auto;
margin-right: auto;
}

View File

@@ -66,10 +66,16 @@
--f-link-text: var(--link-color-light);
--f-link-text-hover: var(--link-hover-light);
/* Buttons Input */
/*
--btn-background: var(--link-color-light);
--btn-text: var(--surface4-light);
--btn-background-hover: var(--surface4-light);
--btn-text-hover: var(--link-color-light);
*/
--btn-background: var(--surface4-light);
--btn-text: var(--link-color-light);
--btn-background-hover: var(--link-color-light);
--btn-text-hover: var(--surface4-light);
--f-background-toast: var(--surface2-dark);