Premium - poprawki stopka z rejestracją

This commit is contained in:
dm
2025-12-18 11:06:00 +01:00
parent a1740fcc31
commit 2920dc7c08
8 changed files with 336 additions and 8406 deletions

View File

@@ -25,11 +25,25 @@ type TvAddon = {
group_mode?: string;
};
// ✅ OPCJA A: metadane grupy + CTA
type GroupCta = {
label?: string;
href?: string;
title?: string;
opis?: string;
};
type GroupMeta = {
tytul?: string;
rejestracja?: GroupCta;
};
type TvAddonsDoc = {
tytul?: string;
opis?: string;
cena_opis?: string;
dodatki?: TvAddon[];
grupy?: Record<string, GroupMeta>;
};
const doc = yaml.load(
@@ -40,6 +54,9 @@ const pageTitle = doc?.tytul ?? "Dodatkowe pakiety TV";
const pageDesc = doc?.opis ?? "";
const addons: TvAddon[] = Array.isArray(doc?.dodatki) ? doc.dodatki : [];
// ✅ mapa meta grup
const groupMeta: Record<string, GroupMeta> = doc?.grupy ?? {};
// --- dynamic param ---
const tidParam = Astro.params.tid; // np. "49"
const tid = Number(tidParam);
@@ -63,95 +80,83 @@ if (pickedGroup) {
viewAddons = [picked];
}
// stabilne sortowanie (np. po tid, żeby HBO/Disney nie skakały)
// (jak miałeś) sortowanie w grupie — możesz zostawić albo wywalić
viewAddons = viewAddons
.slice()
.sort((a, b) => Number(a?.tid ?? 0) - Number(b?.tid ?? 0));
// tytuł/description strony
const singleTitle = pickedGroup ? (pickedGroup.toUpperCase() || pageTitle) : (String(picked?.nazwa ?? "").trim() || pageTitle);
const singleTitle = pickedGroup
? (groupMeta[pickedGroup]?.tytul ?? pickedGroup.toUpperCase() ?? pageTitle)
: (String(picked?.nazwa ?? "").trim() || pageTitle);
const singleDesc = pageDesc;
// ✅ CTA dla tej grupy (jeśli istnieje)
const footerCta: GroupCta | undefined =
pickedGroup ? groupMeta[pickedGroup]?.rejestracja : undefined;
---
<DefaultLayout title={singleTitle} description={singleDesc}>
<!-- (opcjonalnie) nagłówek strony -->
<!-- <section class="f-section">
<div class="f-section-grid-single">
<h1 class="f-section-title">{singleTitle}</h1>
{singleDesc && <Markdown text={singleDesc} />}
</div>
</section> -->
{/* Opcjonalnie: box jak na stronie ogólnej, tylko jeśli jesteśmy w grupie */}
<div class={pickedGroup ? "f-addon-group" : ""}>
{
viewAddons.map((addon: TvAddon, index: number) => {
const isAboveFold = index === 0;
{
viewAddons.map((addon: TvAddon, index: number) => {
const isAboveFold = index === 0;
const pkgName = String(addon?.nazwa ?? "").trim();
const hasYamlImage = !!String(addon?.image ?? "").trim();
const assumeHasMedia = pkgName ? true : hasYamlImage;
const pkgName = String(addon?.nazwa ?? "").trim();
const hasYamlImage = !!String(addon?.image ?? "").trim();
const assumeHasMedia = pkgName ? true : hasYamlImage;
const anchorId = addon?.tid != null ? `tid-${addon.tid}` : undefined;
const anchorId = addon?.tid != null ? `tid-${addon.tid}` : undefined;
return (
<section class="f-section" id={anchorId}>
<div
class={`f-section-grid f-addon-grid f-addon-section ${
assumeHasMedia ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
data-addon-section
data-has-media={assumeHasMedia ? "1" : "0"}
>
<div class="f-addon-text">
{pkgName && <h2 class="f-section-title">{pkgName}</h2>}
{addon?.opis && <Markdown text={addon.opis} />}
</div>
return (
<section class="f-section" id={anchorId}>
<div
class={`f-section-grid f-addon-grid f-addon-section ${
assumeHasMedia ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
data-addon-section
data-has-media={assumeHasMedia ? "1" : "0"}
>
<div class="f-addon-text">
{pkgName && <h2 class="f-section-title">{pkgName}</h2>}
{addon?.opis && <Markdown text={addon.opis} />}
<div class="f-addon-media">
{pkgName ? (
<AddonChannelsGrid
client:idle
packageName={pkgName}
fallbackImage={String(addon?.image ?? "")}
aboveFold={isAboveFold}
title={pkgName}
/>
) : null}
</div>
</div>
<div class="f-addon-media">
{pkgName ? (
<AddonChannelsGrid
client:idle
packageName={pkgName}
fallbackImage={String(addon?.image ?? "")}
aboveFold={isAboveFold}
title={pkgName}
/>
) : null}
</div>
</div>
</section>
);
})
}
<script is:inline>
function __scrollToHashWithOffset(behavior = "auto") {
if (!location.hash) return;
const id = decodeURIComponent(location.hash.slice(1));
const el = document.getElementById(id);
if (!el) return;
const nav =
document.querySelector(".f-navbar") ||
document.querySelector("header") ||
document.querySelector("[data-navbar]");
const navH = nav ? nav.getBoundingClientRect().height : 84;
const extra = 8;
const top = el.getBoundingClientRect().top + window.pageYOffset - navH - extra;
window.scrollTo({ top: Math.max(0, top), behavior });
</section>
);
})
}
function __runFix() {
__scrollToHashWithOffset("auto");
requestAnimationFrame(() => __scrollToHashWithOffset("auto"));
setTimeout(() => __scrollToHashWithOffset("auto"), 120);
setTimeout(() => __scrollToHashWithOffset("auto"), 600);
}
{/* ✅ STOPKA GRUPY: tylko na stronach grupowych */}
{pickedGroup && footerCta?.href && footerCta?.label ? (
<div class="f-addon-group-footer fuz-markdown max-w-none">
{footerCta.opis ? <p>{footerCta.opis}</p> : null}
<a
class="btn btn-primary"
href={footerCta.href}
title={footerCta.title ?? footerCta.label}
target="_blank"
rel="noopener noreferrer"
>
{footerCta.label}
</a>
</div>
) : null}
</div>
window.addEventListener("load", __runFix);
window.addEventListener("hashchange", () => __scrollToHashWithOffset("smooth"));
</script>
</DefaultLayout>

View File

@@ -6,7 +6,6 @@ import Markdown from "../../islands/Markdown.jsx";
import AddonChannelsGrid from "../../islands/jambox/AddonChannelsModal.jsx";
import "../../styles/jambox-tematyczne.css";
/** Typy minimalne */
type AddonPriceRow = {
pakiety?: string[] | any;
"12m"?: number | string;
@@ -21,6 +20,21 @@ type TvAddon = {
opis?: string;
image?: string;
cena?: AddonPriceRow[];
group?: string;
group_mode?: string;
};
// ✅ OPCJA A: metadane grupy + CTA
type GroupCta = {
label?: string;
href?: string;
title?: string;
opis?: string;
};
type GroupMeta = {
tytul?: string;
rejestracja?: GroupCta;
};
type TvAddonsDoc = {
@@ -28,6 +42,7 @@ type TvAddonsDoc = {
opis?: string;
cena_opis?: string;
dodatki?: TvAddon[];
grupy?: Record<string, GroupMeta>; // ✅
};
const doc = yaml.load(
@@ -38,8 +53,35 @@ const pageTitle = doc?.tytul ?? "Dodatkowe pakiety TV";
const pageDesc = doc?.opis ?? "";
const addons: TvAddon[] = Array.isArray(doc?.dodatki) ? doc.dodatki : [];
// ustaw tu bazowy URL dla stron pakietów
const detailsBase = "/pakiety-premium"; // => /pakiety-premium/{tid}
const detailsBase = "/pakiety-premium";
// ✅ mapa meta grup (np. hbo_max -> { rejestracja: {...} })
const groupMeta: Record<string, GroupMeta> = doc?.grupy ?? {};
type Group = {
key: string;
title?: string;
items: TvAddon[];
groupId?: string;
};
const groupsMap = new Map<string, Group>();
for (const a of addons) {
const g = String(a?.group ?? "").trim();
const key = g ? `g:${g}` : `s:${a?.tid ?? a?.id ?? a?.nazwa ?? ""}`;
if (!groupsMap.has(key)) {
groupsMap.set(key, {
key,
groupId: g || undefined,
title: g || undefined,
items: [],
});
}
groupsMap.get(key)!.items.push(a);
}
const groups: Group[] = Array.from(groupsMap.values());
---
<DefaultLayout title={pageTitle} description={pageDesc}>
@@ -51,43 +93,77 @@ const detailsBase = "/pakiety-premium"; // => /pakiety-premium/{tid}
</section>
{
addons.map((addon: TvAddon, index: number) => {
const isAboveFold = index === 0;
groups.map((group, groupIndex) => {
const isSingle = group.key.startsWith("s:");
const gId = String(group.groupId ?? "").trim(); // np. "hbo_max"
const meta = gId ? groupMeta[gId] : undefined;
const hasYamlImage = !!String(addon?.image ?? "").trim();
const pkgName = String(addon?.nazwa ?? "").trim();
const assumeHasMedia = pkgName ? true : hasYamlImage;
// link do strony pakietu (jeśli jest tid)
const href = addon?.tid != null ? `${detailsBase}/${addon.tid}` : null;
const footerCta = meta?.rejestracja;
const footerTitle =
meta?.tytul || (gId ? gId.replace(/_/g, " ") : undefined);
return (
<section class="f-section">
<div
class={`f-section-grid f-addon-grid f-addon-section ${
assumeHasMedia ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div class="f-addon-text">
{pkgName && <h2 class="f-section-title">{pkgName}</h2>}
<div class={`f-addon-group ${isSingle ? "f-addon-group--single" : ""}`}>
{addon?.opis && <Markdown text={addon.opis} />}
</div>
{group.items.map((addon: TvAddon, index: number) => {
const isAboveFold = groupIndex === 0 && index === 0;
<div class="f-addon-media">
{pkgName ? (
<AddonChannelsGrid
client:idle
packageName={pkgName}
fallbackImage={String(addon?.image ?? "")}
aboveFold={isAboveFold}
title={pkgName}
/>
) : null}
const pkgName = String(addon?.nazwa ?? "").trim();
const hasYamlImage = !!String(addon?.image ?? "").trim();
// ✅ zachowanie jak wcześniej (1 kolumna + ukrycie media gdy brak)
const assumeHasMedia = pkgName ? true : hasYamlImage;
const href =
addon?.tid != null ? `${detailsBase}/${addon.tid}` : null;
return (
<section class="f-section f-addon-group-item">
<div
class={`f-section-grid f-addon-grid f-addon-section ${
assumeHasMedia ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
data-addon-section
data-has-media={assumeHasMedia ? "1" : "0"}
>
<div class="f-addon-text">
{pkgName && <h3 class="f-section-title">{pkgName}</h3>}
{addon?.opis && <Markdown text={addon.opis} />}
</div>
<div class="f-addon-media">
{pkgName ? (
<AddonChannelsGrid
client:idle
packageName={pkgName}
fallbackImage={String(addon?.image ?? "")}
aboveFold={isAboveFold}
title={pkgName}
/>
) : null}
</div>
</div>
</section>
);
})}
{/* ✅ STOPKA GRUPY: przycisk rejestracji dla całej grupy */}
{!isSingle && footerCta?.href && footerCta?.label ? (
<div class="f-addon-group-footer fuz-markdown max-w-none">
{footerCta.opis ? <p>{footerCta.opis}</p> : null}
<a
class="btn btn-primary"
href={footerCta.href}
title={footerCta.title ?? footerCta.label}
target="_blank"
rel="noopener noreferrer"
>
{footerCta.label}
</a>
</div>
</div>
</section>
) : null}
</div>
);
})
}