211 lines
5.8 KiB
JavaScript
211 lines
5.8 KiB
JavaScript
import { useEffect, useState } from "preact/hooks";
|
|
import Markdown from "../Markdown.jsx";
|
|
import OffersSwitches from "../Switches.jsx";
|
|
import InternetAddonsModal from "./InternetAddonsModal.jsx";
|
|
import "../../styles/cards.css";
|
|
|
|
function formatMoney(amount, currency = "PLN") {
|
|
if (typeof amount !== "number" || Number.isNaN(amount)) return "";
|
|
try {
|
|
return new Intl.NumberFormat("pl-PL", {
|
|
style: "",
|
|
currency,
|
|
maximumFractionDigits: 0,
|
|
}).format(amount);
|
|
} catch {
|
|
return String(amount);
|
|
}
|
|
}
|
|
|
|
// ✅ mapper: InternetCard(YAML) + match + labels -> plan (dla modala)
|
|
function mapCardToPlan(card, match, labels, waluta) {
|
|
const baseParams = Array.isArray(card?.parametry) ? card.parametry : [];
|
|
|
|
const features = baseParams.map((p) => ({
|
|
label: p.label,
|
|
value: p.value,
|
|
}));
|
|
|
|
// na końcu jak parametry:
|
|
features.push({ label: "Umowa", value: labels?.umowa || "—" });
|
|
features.push({
|
|
label: "Aktywacja",
|
|
value:
|
|
typeof match?.aktywacja === "number" ? formatMoney(match.aktywacja, waluta) : "—",
|
|
});
|
|
|
|
return {
|
|
name: card?.nazwa || "—",
|
|
price_monthly: typeof match?.miesiecznie === "number" ? match.miesiecznie : 0,
|
|
price_installation: typeof match?.aktywacja === "number" ? match.aktywacja : 0,
|
|
features,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {{
|
|
* title?: string,
|
|
* description?: string,
|
|
* cards?: any[],
|
|
* waluta?: string,
|
|
* cenaOpis?: string,
|
|
* phoneCards?: any[],
|
|
* addons?: any[],
|
|
* addonsCenaOpis?: string,
|
|
* switches?: any[] // ✅ NOWE: przełączniki z YAML
|
|
* }} props
|
|
*/
|
|
export default function InternetCards({
|
|
title = "",
|
|
description = "",
|
|
cards = [],
|
|
waluta = "PLN",
|
|
cenaOpis = "zł/mies.",
|
|
phoneCards = [],
|
|
addons = [],
|
|
addonsCenaOpis = "zł/mies.",
|
|
switches = [], // ✅ NOWE
|
|
}) {
|
|
const visibleCards = Array.isArray(cards) ? cards : [];
|
|
|
|
// switch state (teraz idzie z OffersSwitches na podstawie YAML)
|
|
const [selected, setSelected] = useState({});
|
|
const [labels, setLabels] = useState({});
|
|
|
|
// modal
|
|
const [addonsModalOpen, setAddonsModalOpen] = useState(false);
|
|
const [activePlan, setActivePlan] = useState(null);
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== "undefined" && window.fuzSwitchState) {
|
|
const { selected: sel, labels: labs } = window.fuzSwitchState;
|
|
if (sel) setSelected(sel);
|
|
if (labs) setLabels(labs);
|
|
}
|
|
|
|
function handler(e) {
|
|
const detail = e?.detail || {};
|
|
if (detail.selected) setSelected(detail.selected);
|
|
if (detail.labels) setLabels(detail.labels);
|
|
}
|
|
|
|
window.addEventListener("fuz:switch-change", handler);
|
|
return () => window.removeEventListener("fuz:switch-change", handler);
|
|
}, []);
|
|
|
|
return (
|
|
<section class="f-offers">
|
|
{title && <h1 class="f-section-header">{title}</h1>}
|
|
|
|
{description && (
|
|
<div class="mb-4">
|
|
<Markdown text={description} />
|
|
</div>
|
|
)}
|
|
|
|
{/* ✅ TERAZ switcher dostaje dane z YAML */}
|
|
<OffersSwitches switches={switches} />
|
|
|
|
{visibleCards.length === 0 ? (
|
|
<p class="opacity-80">Brak dostępnych pakietów.</p>
|
|
) : (
|
|
<div class={`f-offers-grid f-count-${visibleCards.length || 1}`}>
|
|
{visibleCards.map((card) => (
|
|
<OfferCard
|
|
key={card.nazwa}
|
|
card={card}
|
|
selected={selected}
|
|
labels={labels}
|
|
waluta={waluta}
|
|
cenaOpis={cenaOpis}
|
|
onConfigureAddons={(plan) => {
|
|
setActivePlan(plan);
|
|
setAddonsModalOpen(true);
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
<InternetAddonsModal
|
|
isOpen={addonsModalOpen}
|
|
onClose={() => setAddonsModalOpen(false)}
|
|
plan={activePlan}
|
|
phoneCards={phoneCards}
|
|
addons={addons}
|
|
cenaOpis={addonsCenaOpis || cenaOpis}
|
|
/>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function OfferCard({ card, selected, labels, waluta, cenaOpis, onConfigureAddons }) {
|
|
const baseParams = Array.isArray(card?.parametry) ? card.parametry : [];
|
|
const ceny = Array.isArray(card?.ceny) ? card.ceny : [];
|
|
|
|
const budynek = selected?.budynek;
|
|
const umowa = selected?.umowa;
|
|
|
|
const match = ceny.find(
|
|
(c) => String(c?.budynek) === String(budynek) && String(c?.umowa) === String(umowa),
|
|
);
|
|
|
|
const mies = match?.miesiecznie;
|
|
const akt = match?.aktywacja;
|
|
|
|
// na końcu jako parametry
|
|
const params = [
|
|
...baseParams,
|
|
{ klucz: "umowa", label: "Umowa", value: labels?.umowa || "—" },
|
|
{
|
|
klucz: "aktywacja",
|
|
label: "Aktywacja",
|
|
value: typeof akt === "number" ? formatMoney(akt, waluta) : "—",
|
|
},
|
|
];
|
|
|
|
const canConfigureAddons = !!match;
|
|
|
|
return (
|
|
<div class={`f-card ${card.popularny ? "f-card-popular" : ""}`}>
|
|
{card.popularny && <div class="f-card-badge">Najczęściej wybierany</div>}
|
|
|
|
<div class="f-card-header">
|
|
<div class="f-card-name">{card.nazwa}</div>
|
|
|
|
<div class="f-card-price">
|
|
{typeof mies === "number" ? (
|
|
<>
|
|
{formatMoney(mies, waluta)} <span class="opacity-80">{cenaOpis}</span>
|
|
</>
|
|
) : (
|
|
<span class="opacity-70">Wybierz opcje</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="f-card-features">
|
|
{params.map((p) => (
|
|
<li class="f-card-row" key={p.klucz || p.label}>
|
|
<span class="f-card-label">{p.label}</span>
|
|
<span class="f-card-value">{p.value}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary mt-4"
|
|
disabled={!canConfigureAddons}
|
|
onClick={() => {
|
|
const plan = mapCardToPlan(card, match, labels, waluta);
|
|
onConfigureAddons(plan);
|
|
}}
|
|
title={!canConfigureAddons ? "Wybierz typ budynku i umowę" : ""}
|
|
>
|
|
Skonfiguruj usługi dodatkowe
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|