Poprawki w switcherach i internet Card

This commit is contained in:
dm
2025-12-12 08:32:53 +01:00
parent b53bbf2a5e
commit 0f17daee17
4 changed files with 66 additions and 34 deletions

Binary file not shown.

View File

@@ -1,9 +1,8 @@
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import InternetAddonsModal from "./InternetAddonsModal.jsx";
import "../../styles/offers/offers-table.css"; import "../../styles/offers/offers-table.css";
import InternetAddonsModal from "./InternetAddonsModal.jsx"; // 🔹 dostosuj ścieżkę, jeśli inna
export default function InternetDbOffersCards({ export default function InternetDbOffersCards({
title = "Oferty Internetu FUZ",
}) { }) {
const [selected, setSelected] = useState({}); const [selected, setSelected] = useState({});
const [labels, setLabels] = useState({}); const [labels, setLabels] = useState({});
@@ -11,12 +10,16 @@ export default function InternetDbOffersCards({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
// 🔹 stan modala z dodatkami
const [addonsModalOpen, setAddonsModalOpen] = useState(false); const [addonsModalOpen, setAddonsModalOpen] = useState(false);
const [activePlan, setActivePlan] = useState(null); const [activePlan, setActivePlan] = useState(null);
// nasłuchuj globalnego eventu z OffersSwitches
useEffect(() => { 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) { function handler(e) {
const detail = e.detail || {}; const detail = e.detail || {};
if (detail.selected) { if (detail.selected) {
@@ -31,6 +34,7 @@ export default function InternetDbOffersCards({
return () => window.removeEventListener("fuz:switch-change", handler); return () => window.removeEventListener("fuz:switch-change", handler);
}, []); }, []);
const buildingCode = Number(selected.budynek) || 1; const buildingCode = Number(selected.budynek) || 1;
const contractCode = Number(selected.umowa) || 1; const contractCode = Number(selected.umowa) || 1;
@@ -94,7 +98,6 @@ export default function InternetDbOffersCards({
</div> </div>
)} )}
{/* 🔹 Modal z usługami dodatkowymi (internet + telefon + addony) */}
<InternetAddonsModal <InternetAddonsModal
isOpen={addonsModalOpen} isOpen={addonsModalOpen}
onClose={() => setAddonsModalOpen(false)} onClose={() => setAddonsModalOpen(false)}
@@ -108,32 +111,24 @@ function OfferCard({ plan, contractLabel, onConfigureAddons }) {
const basePrice = plan.price_monthly; const basePrice = plan.price_monthly;
const installPrice = plan.price_installation; const installPrice = plan.price_installation;
const allFeatures = plan.features || []; const featureRows = plan.features || [];
// 🔹 to są inne cechy (bez umowy i instalacji) const effectiveContract = contractLabel || "—";
const featureRows = allFeatures.filter(
(f) => f.id !== "umowa_info" && f.id !== "instalacja"
);
// 🔹 cecha opisująca umowę (z backendu)
const contractFeature = allFeatures.find((f) => f.id === "umowa_info");
// 🔹 tekst, który faktycznie pokażemy w wierszu "Umowa"
const effectiveContract =
contractLabel || contractFeature?.value || contractFeature?.label || "—";
return ( return (
<div class={`f-card ${plan.popular ? "f-card-popular" : ""}`}> <div class={`f-card ${plan.popular ? "f-card-popular" : ""}`}>
<div class="f-card-header"> <div class="f-card-header">
<div class="f-card-name">{plan.name}</div> <div class="f-card-name">{plan.name}</div>
<div class="f-card-price">{basePrice} /mies.</div> <div class="f-card-price">
{basePrice != null ? `${basePrice} zł/mies.` : "—"}
</div>
</div> </div>
<ul class="f-card-features"> <ul class="f-card-features">
{featureRows.map((f) => { {featureRows.map((f) => {
let val = f.value; let val = f.value;
let display; let display;
if (val === true || val === "true") display = "✓"; if (val === true || val === "true") display = "✓";
else if (val === false || val === "false" || val == null) display = "✕"; else if (val === false || val === "false" || val == null) display = "✕";
else display = val; else display = val;
@@ -146,11 +141,13 @@ function OfferCard({ plan, contractLabel, onConfigureAddons }) {
); );
})} })}
{/* Umowa już tylko z przełącznika */}
<li class="f-card-row"> <li class="f-card-row">
<span class="f-card-label">Umowa</span> <span class="f-card-label">Umowa</span>
<span class="f-card-value">{effectiveContract}</span> <span class="f-card-value">{effectiveContract}</span>
</li> </li>
{/* Aktywacja z pola plan.price_installation */}
<li class="f-card-row"> <li class="f-card-row">
<span class="f-card-label">Aktywacja</span> <span class="f-card-label">Aktywacja</span>
<span class="f-card-value"> <span class="f-card-value">

View File

@@ -1,5 +1,4 @@
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
// import "../../styles/offers/offers-switches.css";
function buildLabels(switches, selected) { function buildLabels(switches, selected) {
const out = {}; const out = {};
@@ -24,7 +23,7 @@ export default function OffersSwitches(props) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
// AUTO: pobieramy konfigurację z API // 🔹 AUTO mode sam pobieram /api/switches + wysyłam event
useEffect(() => { useEffect(() => {
if (isControlled) return; if (isControlled) return;
@@ -35,7 +34,7 @@ export default function OffersSwitches(props) {
setError(""); setError("");
try { try {
const res = await fetch("/api/internet"); const res = await fetch("/api/switches");
if (!res.ok) throw new Error(`HTTP ${res.status}`); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json(); const json = await res.json();
@@ -53,18 +52,24 @@ export default function OffersSwitches(props) {
setAutoSwitches(sws); setAutoSwitches(sws);
setAutoSelected(initial); setAutoSelected(initial);
// 🔥 zapisz globalny stan
window.fuzSwitchState = {
selected: initial,
labels,
};
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("fuz:switch-change", { new CustomEvent("fuz:switch-change", {
detail: { detail: {
id: null, id: null,
value: null, value: null,
selected: initial, selected: initial,
labels, // tu lecą etykiety z DB labels,
}, },
}), }),
); );
} catch (err) { } catch (err) {
console.error("Błąd pobierania switchy:", err); console.error("Błąd pobierania switchy:", err);
if (!cancelled) setError("Nie udało się załadować przełączników."); if (!cancelled) setError("Nie udało się załadować przełączników.");
} finally { } finally {
if (!cancelled) setLoading(false); if (!cancelled) setLoading(false);
@@ -88,13 +93,19 @@ export default function OffersSwitches(props) {
const next = { ...prev, [id]: value }; const next = { ...prev, [id]: value };
const labels = buildLabels(autoSwitches, next); const labels = buildLabels(autoSwitches, next);
// 🔥 aktualizuj globalny stan
window.fuzSwitchState = {
selected: next,
labels,
};
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("fuz:switch-change", { new CustomEvent("fuz:switch-change", {
detail: { detail: {
id, id,
value, value,
selected: next, selected: next,
labels, // etykiety po kliknięciu labels,
}, },
}), }),
); );
@@ -104,6 +115,32 @@ export default function OffersSwitches(props) {
} }
}; };
// 🔥 CONTROLLED: zsynchronizuj globalny stan + event
useEffect(() => {
if (!isControlled) return;
if (!Array.isArray(switches) || !switches.length) return;
const safeSelected = selected || {};
const labels = buildLabels(switches, safeSelected);
// 🔥 globalny stan
window.fuzSwitchState = {
selected: safeSelected,
labels,
};
window.dispatchEvent(
new CustomEvent("fuz:switch-change", {
detail: {
id: null,
value: null,
selected: safeSelected,
labels,
},
}),
);
}, [isControlled, switches, selected]);
if (!isControlled && loading) { if (!isControlled && loading) {
return ( return (
<div class="f-switches-wrapper"> <div class="f-switches-wrapper">

View File

@@ -1,4 +1,4 @@
// src/pages/api/switches/internet.js // src/pages/api/switches.js
import Database from "better-sqlite3"; import Database from "better-sqlite3";
const DB_PATH = const DB_PATH =
@@ -13,13 +13,11 @@ export function GET() {
try { try {
const buildingTypes = db const buildingTypes = db
.prepare("SELECT code, label FROM jambox_building_types ORDER BY code") .prepare("SELECT code, label FROM jambox_building_types ORDER BY is_default DESC, code")
.all(); .all();
const contractTypes = db const contractTypes = db
.prepare( .prepare("SELECT code, label FROM jambox_contract_types ORDER BY is_default DESC, code")
"SELECT code, label FROM jambox_contract_types ORDER BY code"
)
.all(); .all();
const switches = [ const switches = [
@@ -29,7 +27,7 @@ export function GET() {
domyslny: buildingTypes[0]?.code ?? 1, domyslny: buildingTypes[0]?.code ?? 1,
title: "Zmień rodzaj budynku by zobaczyć odpowiednie ceny", title: "Zmień rodzaj budynku by zobaczyć odpowiednie ceny",
opcje: buildingTypes.map((b) => ({ opcje: buildingTypes.map((b) => ({
id: b.code, // 1,2,... id: b.code,
nazwa: b.label, nazwa: b.label,
})), })),
}, },
@@ -39,7 +37,7 @@ export function GET() {
domyslny: contractTypes[0]?.code ?? 1, domyslny: contractTypes[0]?.code ?? 1,
title: "Wybierz okres umowy by zobaczyć odpowiednie ceny", title: "Wybierz okres umowy by zobaczyć odpowiednie ceny",
opcje: contractTypes.map((c) => ({ opcje: contractTypes.map((c) => ({
id: c.code, // 1,2,... id: c.code,
nazwa: c.label, nazwa: c.label,
})), })),
}, },
@@ -56,7 +54,7 @@ export function GET() {
} }
); );
} catch (err) { } catch (err) {
console.error("Błąd w /api/switches/internet:", err); console.error("Błąd w /api/switches:", err);
return new Response( return new Response(
JSON.stringify({ ok: false, error: err.message || "DB_ERROR" }), JSON.stringify({ ok: false, error: err.message || "DB_ERROR" }),
{ {