Przebudowa stron na indywidualne karty , pobierane z bazy danych

This commit is contained in:
dm
2025-12-11 15:25:00 +01:00
parent 49c5beb362
commit 0cf7c45131
27 changed files with 1133 additions and 420 deletions

71
src/pages/api/internet.js Normal file
View File

@@ -0,0 +1,71 @@
// src/pages/api/switches/internet.js
import Database from "better-sqlite3";
const DB_PATH = "./src/data/ServicesRange.db";
function getDb() {
return new Database(DB_PATH, { readonly: true });
}
export function GET() {
const db = getDb();
try {
const buildingTypes = db
.prepare("SELECT code, label FROM jambox_building_types ORDER BY code")
.all();
const contractTypes = db
.prepare(
"SELECT code, label FROM jambox_contract_types ORDER BY code"
)
.all();
const switches = [
{
id: "budynek",
etykieta: "Rodzaj budynku",
domyslny: buildingTypes[0]?.code ?? 1,
title: "Zmień rodzaj budynku by zobaczyć odpowiednie ceny",
opcje: buildingTypes.map((b) => ({
id: b.code, // 1,2,...
nazwa: b.label,
})),
},
{
id: "umowa",
etykieta: "Okres umowy",
domyslny: contractTypes[0]?.code ?? 1,
title: "Wybierz okres umowy by zobaczyć odpowiednie ceny",
opcje: contractTypes.map((c) => ({
id: c.code, // 1,2,...
nazwa: c.label,
})),
},
];
return new Response(
JSON.stringify({ ok: true, data: switches }),
{
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "public, max-age=60",
},
}
);
} catch (err) {
console.error("❌ Błąd w /api/switches/internet:", err);
return new Response(
JSON.stringify({ ok: false, error: err.message || "DB_ERROR" }),
{
status: 500,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
}
);
} finally {
db.close();
}
}

View File

@@ -0,0 +1,110 @@
// src/pages/api/internet/plans.js
import Database from "better-sqlite3";
const DB_PATH = "./src/data/ServicesRange.db";
function getDb() {
return new Database(DB_PATH, { readonly: true });
}
/**
* GET /api/internet/plans?building=1|2&contract=1|2
*/
export function GET({ url }) {
const buildingParam = url.searchParams.get("building");
const contractParam = url.searchParams.get("contract");
const building = buildingParam ? Number(buildingParam) : 1;
const contract = contractParam ? Number(contractParam) : 1;
const db = getDb();
try {
const stmt = db.prepare(
`
SELECT
p.id AS plan_id,
p.name AS plan_name,
p.popular AS plan_popular,
pr.price_monthly AS price_monthly,
pr.price_installation AS price_installation,
f.id AS feature_id,
f.label AS feature_label,
fv.value AS feature_value
FROM internet_plans p
LEFT JOIN internet_plan_prices pr
ON pr.plan_id = p.id
AND pr.building_type = ?
AND pr.contract_type = ?
LEFT JOIN internet_plan_feature_values fv
ON fv.plan_id = p.id
LEFT JOIN internet_features f
ON f.id = fv.feature_id
ORDER BY p.id ASC, f.id ASC;
`.trim()
);
const rows = stmt.all(building, contract);
// grupowanie do struktury: jeden plan = jedna karta
const byPlan = new Map();
for (const row of rows) {
if (!byPlan.has(row.plan_id)) {
byPlan.set(row.plan_id, {
id: row.plan_id,
code: row.plan_code,
name: row.plan_name,
popular: !!row.plan_popular,
price_monthly: row.price_monthly,
price_installation: row.price_installation,
features: [], // później wypełniamy
});
}
if (row.feature_id) {
byPlan.get(row.plan_id).features.push({
id: row.feature_id,
label: row.feature_label,
value: row.feature_value,
});
}
}
const data = Array.from(byPlan.values());
return new Response(
JSON.stringify({
ok: true,
building,
contract,
count: data.length,
data,
}),
{
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "public, max-age=30",
},
}
);
} catch (err) {
console.error("❌ Błąd w /api/internet/plans:", err);
return new Response(
JSON.stringify({ ok: false, error: "DB_ERROR" }),
{
status: 500,
headers: { "Content-Type": "application/json; charset=utf-8" },
}
);
} finally {
db.close();
}
}

View File

@@ -0,0 +1,152 @@
// src/pages/api/jambox/base-packages.js
import Database from "better-sqlite3";
const DB_PATH = "./src/data/ServicesRange.db"; // dostosuj, jeśli masz gdzie indziej
function getDb() {
return new Database(DB_PATH, { readonly: true });
}
/**
* GET /api/jambox/base-packages
* ?source=PLUS|EVIO
* &building=1|2 (1=jednorodzinny, 2=wielorodzinny)
* &contract=1|2 (1=24m, 2=bezterminowa)
*/
export function GET({ url }) {
const sourceParam = url.searchParams.get("source");
const source = sourceParam ? sourceParam.toUpperCase() : null;
const buildingParam = url.searchParams.get("building");
const contractParam = url.searchParams.get("contract");
const building = buildingParam ? Number(buildingParam) : null;
const contract = contractParam ? Number(contractParam) : null;
const db = getDb();
try {
let rows = [];
const hasVariant =
Number.isInteger(building) && Number.isInteger(contract);
if (source === "PLUS" || source === "EVIO") {
if (hasVariant) {
// pakiety + ceny dla danego budynku/umowy
const stmt = db.prepare(
`
SELECT
p.id,
p.source,
p.tid,
p.name,
p.slug,
p.sort_order,
p.updated_at,
pr.price_monthly,
pr.price_installation,
pr.currency
FROM jambox_base_packages p
LEFT JOIN jambox_base_package_prices pr
ON pr.package_id = p.id
AND pr.building_type = ?
AND pr.contract_type = ?
WHERE p.source = ?
ORDER BY p.sort_order ASC, p.name ASC;
`.trim()
);
rows = stmt.all(building, contract, source);
} else {
// tylko pakiety, bez cen
const stmt = db.prepare(
`
SELECT
p.id,
p.source,
p.tid,
p.name,
p.slug,
p.sort_order,
p.updated_at
FROM jambox_base_packages p
WHERE p.source = ?
ORDER BY p.sort_order ASC, p.name ASC;
`.trim()
);
rows = stmt.all(source);
}
} else {
// bez filtra source (raczej nie użyjesz, ale niech będzie poprawnie)
if (hasVariant) {
const stmt = db.prepare(
`
SELECT
p.id,
p.source,
p.tid,
p.name,
p.slug,
p.sort_order,
p.updated_at,
pr.price_monthly,
pr.price_installation,
pr.currency
FROM jambox_base_packages p
LEFT JOIN jambox_base_package_prices pr
ON pr.package_id = p.id
AND pr.building_type = ?
AND pr.contract_type = ?
ORDER BY p.source ASC, p.sort_order ASC, p.name ASC;
`.trim()
);
rows = stmt.all(building, contract);
} else {
const stmt = db.prepare(
`
SELECT
p.id,
p.source,
p.tid,
p.name,
p.slug,
p.sort_order,
p.updated_at
FROM jambox_base_packages p
ORDER BY p.source ASC, p.sort_order ASC, p.name ASC;
`.trim()
);
rows = stmt.all();
}
}
return new Response(
JSON.stringify({
ok: true,
source: source ?? "ALL",
building,
contract,
count: rows.length,
data: rows,
}),
{
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "public, max-age=60",
},
}
);
} catch (err) {
console.error("❌ Błąd odczytu z bazy jambox_base_packages:", err);
return new Response(
JSON.stringify({ ok: false, error: "DB_ERROR" }),
{
status: 500,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
}
);
} finally {
db.close();
}
}

View File

@@ -0,0 +1,85 @@
import Database from "better-sqlite3";
const DB_PATH = "./src/data/ServicesRange.db";
export async function GET() {
const db = new Database(DB_PATH, { readonly: true });
try {
const stmt = db.prepare(`
SELECT
p.id AS plan_id,
p.name AS plan_name,
IFNULL(p.popular, 0) AS plan_popular,
p.price_monthly AS price_monthly,
p.currency AS currency,
f.id AS feature_id,
f.label AS feature_label,
fv.value AS feature_value
FROM phone_plans p
LEFT JOIN phone_plan_feature_values fv
ON fv.plan_id = p.id
LEFT JOIN phone_features f
ON f.id = fv.feature_id
ORDER BY p.id ASC, f.id ASC
`);
const rows = stmt.all();
const byPlan = new Map();
for (const row of rows) {
if (!byPlan.has(row.plan_id)) {
byPlan.set(row.plan_id, {
id: row.plan_id,
code: row.plan_code,
name: row.plan_name,
popular: !!row.plan_popular,
price_monthly: row.price_monthly,
currency: row.currency || "PLN",
features: [],
});
}
if (row.feature_id) {
byPlan.get(row.plan_id).features.push({
id: row.feature_id,
label: row.feature_label,
value: row.feature_value,
});
}
}
const data = Array.from(byPlan.values());
return new Response(
JSON.stringify({
ok: true,
count: data.length,
data,
}),
{
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
}
);
} catch (err) {
console.error("❌ Błąd w /api/phone/plans:", err);
return new Response(
JSON.stringify({
ok: false,
error: err.message || "DB_ERROR",
}),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
} finally {
db.close();
}
}

View File

@@ -1,9 +1,8 @@
---
import DefaultLayout from "../../layouts/DefaultLayout.astro";
import Hero from "../../components/hero/Hero.astro";
import OffersSwitches from "../../islands/Offers/OffersSwitches.jsx";
import InternetDbOffersCards from "../../islands/OffersInternetCards.jsx";
import SectionRenderer from "../../components/sections/SectionRenderer.astro";
import Markdown from "../../islands/Markdown.jsx";
import OffersIsland from "../../islands/OffersIsland.jsx";
import yaml from "js-yaml";
import fs from "fs";
@@ -11,52 +10,19 @@ import fs from "fs";
const seo = yaml.load(
fs.readFileSync("./src/content/internet-swiatlowodowy/seo.yaml", "utf8"),
);
const hero = yaml.load(
fs.readFileSync("./src/content/internet-swiatlowodowy/hero.yaml", "utf8"),
);
const page = yaml.load(
fs.readFileSync("./src/content/internet-swiatlowodowy/page.yaml", "utf8"),
);
type Paragraph = {
title?: string;
content: string;
};
const data = yaml.load(
fs.readFileSync("./src/content/internet-swiatlowodowy/offers.yaml", "utf8"),
);
const first = page.paragraphs[0];
const rest = page.paragraphs.slice(1);
---
<DefaultLayout seo={seo}>
<Hero {...hero} />
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
{page.title.map((line: any) => <h1 class="f-section-title">{line}</h1>)}
{first.title && <h3>{first.title}</h3>}
<Markdown text={first.content} />
<h1 class="f-section-title">Internet światłowodowy</h1>
<div class="fuz-markdown max-w-none">
<p>Wybierz rodzaj budynku i czas trwania umowy</p>
</div>
<OffersSwitches client:load />
<InternetDbOffersCards client:load />
</div>
</section>
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
<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

@@ -6,6 +6,7 @@ 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 JamboxBasePackages from "../../islands/Offers/JamboxBasePackages.jsx";
import yaml from "js-yaml";
import fs from "fs";
@@ -48,12 +49,18 @@ const rest = page.paragraphs.slice(1);
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1 max-w-6xl mx-auto">
<JamboxBasePackages
client:load
source=""
title=""/>
<OffersIsland client:load data={data} />
</div>
</section>
<!-- <OffersIsland client:load data={data} /> -->
{
rest.map((p: Paragraph) => (
<section class="f-section">

View File

@@ -146,7 +146,7 @@ const mapStyleId = "8e0a97af9476f2d3";
<div class="f-info-header">
<div class="f-info-heading">
${
result.availableFiber
result.available
? `<span class="ok">✔</span> Internet światłowodowy dostępny`
: `<span class="no">✖</span> Światłowód niedostępny`
}

View File

@@ -1,9 +1,7 @@
---
import DefaultLayout from "../../layouts/DefaultLayout.astro";
import Hero from "../../components/hero/Hero.astro";
import SectionRenderer from "../../components/sections/SectionRenderer.astro";
import Markdown from "../../islands/Markdown.jsx";
import OffersIsland from "../../islands/OffersIsland.jsx";
import OffersPhoneCards from "../../islands/OffersPhoneCards.jsx";
import yaml from "js-yaml";
import fs from "fs";
@@ -11,44 +9,18 @@ import fs from "fs";
const seo = yaml.load(
fs.readFileSync("./src/content/telefon/seo.yaml", "utf8"),
);
const hero = yaml.load(
fs.readFileSync("./src/content/telefon/hero.yaml", "utf8"),
);
const page = yaml.load(
fs.readFileSync("./src/content/telefon/page.yaml", "utf8"),
);
type Paragraph = {
title?: string;
content: string;
};
const data = yaml.load(
fs.readFileSync("./src/content/telefon/offers.yaml", "utf8"),
);
const first = page.paragraphs[0];
const rest = page.paragraphs.slice(1);
---
<DefaultLayout seo={seo}>
<Hero {...hero} />
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
{page.title.map((line: any) => <h1 class="f-section-title">{line}</h1>)}
{first.title && <h3>{first.title}</h3>}
<Markdown text={first.content} />
</div>
</section>
<section class="f-section">
<div class="f-section-grid-single md:grid-cols-1">
<OffersIsland client:load data={data} />
<h1 class="f-section-title">Usługa telefonu</h1>
<OffersPhoneCards client:load />
</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">
@@ -57,7 +29,7 @@ const rest = page.paragraphs.slice(1);
</div>
</section>
))
}
} -->
<SectionRenderer src="./src/content/telefon/section.yaml" />
</DefaultLayout>