Compare commits

...

16 Commits

Author SHA1 Message Date
dm
6b73e91ddb Files ponowne dodanie plików, z małych liter
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 1m52s
2025-12-22 18:04:07 +01:00
dm
abc3bd4d41 Files usuniecie plików
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 2m0s
2025-12-22 18:00:06 +01:00
dm
e0095ee10f Dokumenty scieżki do plików
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 2m16s
2025-12-22 17:18:13 +01:00
dm
bbb6742849 Dokumnety - poprawa nazw plików
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 1m56s
2025-12-22 17:07:45 +01:00
dm
2c30704a11 Dokumenty poprawka scieżki do plików
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 1m59s
2025-12-22 16:56:58 +01:00
dm
95b308455c Dokumenty - usuniecie smiec dodanie nowych plików
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 2m9s
2025-12-22 16:51:25 +01:00
dm
146e738c09 Kontakt - poprawka nagłowka
All checks were successful
Deploy FUZ 2.0 / deploy (push) Successful in 1m56s
2025-12-21 18:57:46 +01:00
dm
92edbad2c2 Poprawki w konfiguracji 2025-12-21 18:52:49 +01:00
dm
b065db4faf Poprawka w rozszerzeniu pliku highlightUtils 2025-12-21 18:32:32 +01:00
dm
ed513957c3 Ulepszona konfiguracja 2025-12-21 18:26:20 +01:00
dm
12be46d038 Sitemap - przywrócenie 2025-12-21 18:17:14 +01:00
dm
0d967ea6c8 Style inline usuniecie i wstawienie do css 2025-12-21 17:28:47 +01:00
dm
259ee007db Dodanie Title i Description strony do yamli oraz jezeli nie ma seo to do strony 2025-12-21 17:24:09 +01:00
dm
e9f440353d Robot.txt 2025-12-21 15:18:02 +01:00
dm
f91b557efd Mapa zasięgu kontener na listę miejscowości 2025-12-21 14:05:17 +01:00
dm
832ee2e796 Kontakt ukruty dymek, Lista miejscowości wyrównywanie pod mapą 2025-12-21 13:00:32 +01:00
37 changed files with 371 additions and 171 deletions

View File

@@ -17,6 +17,7 @@ export default defineConfig({
build: { build: {
minify: "esbuild", minify: "esbuild",
cssMinify: "esbuild", cssMinify: "esbuild",
chunkSizeWarningLimit: 500,
}, },
}, },
integrations: [ integrations: [

View File

@@ -1,5 +1,6 @@
{ {
"name": "fuz20", "name": "fuz-site",
"type": "module",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/files/umowa_tv.pdf Normal file

Binary file not shown.

15
public/robots.txt Normal file
View File

@@ -0,0 +1,15 @@
# robots.txt dla FUZ Adam Rojek
# https://www.fuz.pl/robots.txt
User-agent: *
Allow: /
# Sitemap
Sitemap: https://www.fuz.dariuszm.eu/sitemap.xml
# Crawl-delay (opcjonalnie)
# Crawl-delay: 1
# Zablokuj crawlowanie zbędnych ścieżek (jeśli są)
Disallow: /pages/api/
Disallow: /_astro/

View File

@@ -12,31 +12,3 @@ const sorted = cities.sort((a: string, b: any) => a.localeCompare(b, "pl"));
))} ))}
</div> </div>
</div> </div>
<style>
.fuz-cities-box {
background: var(--f-background);
color: var(--f-text);
padding: 16px;
}
.fuz-cities-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 12px;
}
.fuz-cities-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 6px 14px;
font-size: 0.95rem;
line-height: 1.4;
}
.fuz-city-item {
color: var(--f-text);
}
</style>

View File

@@ -102,21 +102,21 @@ const domId = `fuz-map-${Math.random().toString(36).slice(2)}`;
position: { lat, lng: lon } position: { lat, lng: lon }
}); });
if (title || desc) { // if (title || desc) {
const { InfoWindow } = await google.maps.importLibrary("maps"); // const { InfoWindow } = await google.maps.importLibrary("maps");
const info = new InfoWindow({ // const info = new InfoWindow({
content: ` // content: `
<div class="f-info-window"> // <div class="f-info-window">
<div class="f-info-header"> // <div class="f-info-header">
<div class="f-info-city">${title}</div> // <div class="f-info-city">${title}</div>
<div class="f-info-street">${desc}</div> // <div class="f-info-street">${desc}</div>
</div> // </div>
</div> // </div>
` // `
}); // });
info.open({ map, anchor: marker }); // info.open({ map, anchor: marker });
} // }
} }
} catch (error) { } catch (error) {
console.error("Error initializing map:", error); console.error("Error initializing map:", error);

View File

@@ -21,10 +21,10 @@ const sectionImages = import.meta.glob<{ default: ImageMetadata }>(
const sectionImage = section.image ? findSectionImage(sectionImages, section.image) : null; const sectionImage = section.image ? findSectionImage(sectionImages, section.image) : null;
const isAboveFold = index === 0; const isAboveFold = index === 0;
// Konfiguracja wyświetlania // Konfiguracja wyświetlania
const showTitle = section.showTitle !== false; // domyślnie true const showTitle = section.showTitle !== false; // domyślnie true
// Formatowanie listy miejscowości jako string // Formatowanie listy miejscowości jako string
const citiesText = cities.join(", "); const citiesText = cities.join(", ");
--- ---
@@ -66,33 +66,6 @@ const citiesText = cities.join(", ");
</a> </a>
</p> </p>
)} )}
<!-- {
section.button && (
<div class="f-section-nav mt-6">
<a href={section.button.url}
class="btn btn-primary"
title={section.button.title}
>
{section.button.text}
</a>
</div>
)
} -->
</div> </div>
</div> </div>
</section> </section>
<!-- <style>
.f-cities-paragraph {
margin-top: 1.5rem;
font-size: 1rem;
line-height: 1.8;
color: var(--f-text, #212529);
}
:global(.dark) .f-cities-paragraph {
color: var(--f-text-dark, #f7fafc);
}
</style> -->

View File

@@ -5,17 +5,39 @@ grupy:
pobierz: pobierz:
tytul: Pobierz tytul: Pobierz
pliki: pliki:
- nazwa: Umowa Internet
file: /public/files/umowa_internet.pdf
- nazwa: Informacje przedumowne Internet
file: /public/files/informacje_przedumowne_net.pdf
- nazwa: Podsumowanie warunków umowy Internet
file: /public/files/podsumowanie_warunków_umowy_net.pdf
- nazwa: Oświadczenia Internet
file: /public/files/oświadczenia_net.pdf
- nazwa: Informacje urządzenia Internet
file: /public/files/informacje_urządzenia_net.pdf
- nazwa: Formularz odstąpienia Internet
file: /public/files/formularz_odstapienia_net.pdf
- nazwa: Umowa TV
file: /public/files/umowa_tv.pdf
- nazwa: Podsumowanie warunków umowy TV
file: /public/files/podsumowanie_warunków_umowy_tv.pdf
- nazwa: Informacje przedumowne TV
file: /public/files/informacje_przedumowne_tv.pdf
- nazwa: Cennik usług TV
file: /public/files/cennik_uslug_tv.pdf
- nazwa: Cennik usług dodatkowych
file: /public/files/cennik_uslug_dodatkowych.pdf
- nazwa: Lista kanałów EVIO TV - nazwa: Lista kanałów EVIO TV
file: /public/files/EVIO TV.pdf file: /public/files/EVIO TV.pdf
- nazwa: Lista kanałów JAMBOX TV - nazwa: Lista kanałów JAMBOX TV
file: /public/files/EVIO TV.pdf file: /public/files/JAMBOX TV.pdf
- nazwa: Lista kanałów coś
file: /public/files/EVIO TV.pdf
otworz: otworz:
tytul: Przeczytaj tytul: Przeczytaj
pliki: pliki:
- nazwa: Polityka prywatności - nazwa: Polityka prywatności
slug: polityka-prywatnosci slug: polityka-prywatnosci
- nazwa: Promocja przykład do wyswietlenia
slug: promocja

View File

@@ -1,6 +1,8 @@
# tytuł dokumentu jednoczesnie tytułem strony <title></title>
title: "Polityka Prywatności" title: "Polityka Prywatności"
# opis wstawiany w <meta name="description" content=""
description: Polityka prywatności, opisuje zasady ochrony Twoich danych osobowych.
visible: true visible: true
intro: Polityka prywatności, opisuje zasady ochrony Twoich danych osobowych.
content: | content: |
## §1. Informacje podstawowe. ## §1. Informacje podstawowe.

View File

@@ -1,6 +1,6 @@
title: "Promocja świąteczna" title: "Promocja świąteczna"
description: Przykładowo gdybysmy dodali promocję do dokumentów
visible: true visible: true
intro: Przykładowo gdybysmy dodali promocję do dokumentów
content: | content: |
Jeśli kupujesz w sklepach internetowych, prawdopodobnie co pewien czas natykasz się na opisy, które nie zachęcają do zakupów. Jeśli kupujesz w sklepach internetowych, prawdopodobnie co pewien czas natykasz się na opisy, które nie zachęcają do zakupów.
Do najczęściej powtarzanych błędów opisów produktów należą: Do najczęściej powtarzanych błędów opisów produktów należą:

View File

@@ -1,6 +1,6 @@
page: page:
title: "FUZ Adam Rojek - Internet Światłowodowy Wyszków | Szybki i Stabilny" title: "FUZ Adam Rojek - Internet Światłowodowy Wyszków"
description: "Internet światłowodowy w Wyszkowie i okolicach. Lokalny operator z doświadczeniem - stabilne łącze, profesjonalny serwis, konkurencyjne ceny. Sprawdź dostępność!" description: "Internet światłowodowy w Wyszkowie i okolicach. Lokalny operator z doświadczeniem - stabilne łącze, profesjonalny serwis, konkurencyjne ceny."
image: "/og/home-og.png" image: "/og/home-og.png"
url: "/" url: "/"
keywords: keywords:

View File

@@ -4,11 +4,9 @@ opis: |
Wybierz rodzaj budynku i czas trwania umowy Wybierz rodzaj budynku i czas trwania umowy
uwaga: | uwaga: |
Powyższe „ceny brutto z Rabatami 15zł” Powyższe „ceny brutto z Rabatami 15zł”
uwzględniają rabat -15 zł (z czego -5 zł - Rabat za wyrażenie zgody na otrzymywanie Rachunków/faktur VAT za świadczone uwzględniają rabat -15 zł (z czego -5 zł - Rabat za wyrażenie zgody na otrzymywanie Rachunków/faktur VAT za świadczone przez Dostawcę Usług usługi telekomunikacyjne drogą elektroniczną na wskazany w umowie adres e-mail oraz za pośrednictwem EBOK;
przez Dostawcę Usług usługi telekomunikacyjne drogą elektroniczną na wskazany w umowie adres mail oraz za pośrednictwem EBOK; -10 zł - Rabat pod warunkiem -10 zł - Rabat pod warunkiem złożenia wniosku o dostarczanie przez Dostawcę Usług treści każdej proponowanej zmiany warunków Umowy, w tym określonych w Umowie, Informacjach Przedumownych oraz danych Dostawcy Usług (chyba że przepisy powszechnie
złożenia wniosku o dostarczanie przez Dostawcę Usług treści każdej proponowanej zmiany warunków Umowy, w tym określonych w Umowie, Informacjach Przedumownych oraz danych Dostawcy Usług (chyba że przepisy powszechnie obowiązującego prawa przewidują wyłącznie zawiadomienia poprzez publiczne ogłoszenie), jak obowiązującego prawa przewidują wyłącznie zawiadomienia poprzez publiczne ogłoszenie), jak również kontakt w ramach procedur reklamacyjnych, w tym w szczególności przesłania odpowiedzi na reklamację, na podany w Umowie adres poczty elektronicznej).
również kontaktowanie się ze mną w ramach procedur reklamacyjnych, w tym w szczególności przesłania odpowiedzi na reklamację, na podany w Umowie adres poczty
elektronicznej).
cena_opis: "zł/mies." cena_opis: "zł/mies."
cards: cards:

View File

@@ -1,3 +1,5 @@
title: Możliwości telewizji JAMBOX
description: Poznaj funkcje i udogodnienia dostępne na dekoderach telewizji JAMBOX.
sections: sections:
- title: CatchUp - Archiwum TV - title: CatchUp - Archiwum TV
image: https://www.jambox.pl/sites/default/files/jambox-kyanit-catchup1.png image: https://www.jambox.pl/sites/default/files/jambox-kyanit-catchup1.png

View File

@@ -1,7 +1,7 @@
import { useMemo } from "preact/hooks"; import { useMemo } from "preact/hooks";
import { marked } from "marked"; import { marked } from "marked";
import { useLocalSearch } from "../../hooks/useLocalSearch.js"; import { useLocalSearch } from "../../hooks/useLocalSearch.js";
import { highlightText, highlightHtml } from "../../lib/highlightUtils.js"; import { highlightText, highlightHtml } from "../../lib/highlightUtils.jsx";
import "../../styles/jambox-search.css"; import "../../styles/jambox-search.css";
/** /**
@@ -46,6 +46,7 @@ export default function JamboxMozliwosciSearch({ items = [] }) {
<div className="f-chsearch__top"> <div className="f-chsearch__top">
<div className="f-chsearch__inputwrap"> <div className="f-chsearch__inputwrap">
<input <input
name="search"
className="f-chsearch__input" className="f-chsearch__input"
type="search" type="search"
value={search.query} value={search.query}

View File

@@ -52,6 +52,7 @@ const jsonExtra = meta.extraSchema ? JSON.stringify(meta.extraSchema) : null;
/> />
<link rel="canonical" href={meta.canonical} /> <link rel="canonical" href={meta.canonical} />
<link rel="sitemap" type="application/xml" title="Sitemap" href="/sitemap.xml" />
<!-- OpenGraph --> <!-- OpenGraph -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />

View File

@@ -11,13 +11,44 @@ import Cookie from "../islands/Cookie.jsx";
import rawCookie from "../content/site/cookie.yaml?raw"; import rawCookie from "../content/site/cookie.yaml?raw";
const cookieCfg = yaml.parse(rawCookie); const cookieCfg = yaml.parse(rawCookie);
const { seo } = Astro.props;
// ✅ Pobierz wszystkie możliwe props
const {
seo, // Pełny obiekt SEO (stary sposób)
title, // Indywidualny title (nowy sposób)
description, // Indywidualny description (nowy sposób)
image, // Opcjonalny image
keywords, // Opcjonalne keywords
url, // Opcjonalny url
} = Astro.props;
// ✅ PRIORYTET: title/description → seo → undefined
let finalSeo;
// Jeśli mamy indywidualne pola (title lub description) - użyj ich
if (title || description) {
finalSeo = {
page: {
title,
description,
image,
keywords,
url,
},
};
}
// Jeśli nie ma indywidualnych, ale jest seo object - użyj go
else if (seo) {
finalSeo = seo;
}
// Jeśli nic nie ma - undefined (użyje global defaults z BaseHead)
else {
finalSeo = undefined;
}
--- ---
<html lang="pl" class="scroll-smooth"> <html lang="pl" class="scroll-smooth">
<head> <BaseHead seo={finalSeo} />
<BaseHead seo={seo} />
</head>
<body class="min-h-screen flex flex-col"> <body class="min-h-screen flex flex-col">
<Header /> <Header />

View File

@@ -396,3 +396,90 @@ export function getRequiredEnv(key: string): string {
return value; return value;
} }
// ==================== SITEMAP HELPERS ====================
export type SitemapUrl = {
loc: string;
lastmod?: string;
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
priority?: number;
};
/**
* Generuje sitemap XML z listy URL-i
* @param urls - Tablica URL-i do sitemap
* @returns String z XML sitemap
*
* @example
* const urls = [
* { loc: 'https://example.com/', priority: 1.0, changefreq: 'daily' },
* { loc: 'https://example.com/about', priority: 0.8, changefreq: 'monthly' }
* ];
* const xml = generateSitemapXml(urls);
*/
export function generateSitemapXml(urls: SitemapUrl[]): string {
const urlEntries = urls.map(url => {
const parts = [
' <url>',
` <loc>${url.loc}</loc>`,
];
if (url.lastmod) {
parts.push(` <lastmod>${url.lastmod}</lastmod>`);
}
if (url.changefreq) {
parts.push(` <changefreq>${url.changefreq}</changefreq>`);
}
if (url.priority !== undefined) {
parts.push(` <priority>${url.priority.toFixed(1)}</priority>`);
}
parts.push(' </url>');
return parts.join('\n');
}).join('\n');
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlEntries}
</urlset>`;
}
/**
* Automatyczne określenie changefreq na podstawie ścieżki URL
* @param path - Ścieżka URL (np. "/internet-swiatlowodowy")
* @returns Częstotliwość zmian
*
* @example
* inferChangeFreq('/') // => 'daily'
* inferChangeFreq('/dokumenty/regulamin') // => 'yearly'
*/
export function inferChangeFreq(path: string): SitemapUrl['changefreq'] {
if (path === '/') return 'daily';
if (path.includes('/internet-') || path.includes('/telefon')) return 'weekly';
if (path.includes('/mapa-zasiegu') || path.includes('/kontakt')) return 'monthly';
if (path.includes('/dokumenty')) return 'yearly';
if (path.includes('/premium')) return 'monthly';
return 'weekly';
}
/**
* Automatyczne określenie priority na podstawie ścieżki URL
* @param path - Ścieżka URL
* @returns Priorytet (0.0 - 1.0)
*
* @example
* inferPriority('/') // => 1.0
* inferPriority('/dokumenty/polityka-prywatnosci') // => 0.5
*/
export function inferPriority(path: string): number {
if (path === '/') return 1.0;
if (path.includes('/internet-') || path.includes('/telefon')) return 0.9;
if (path.includes('/mapa-zasiegu') || path.includes('/kontakt')) return 0.8;
if (path.includes('/premium')) return 0.7;
if (path.includes('/telewizja-mozliwosci')) return 0.7;
if (path.includes('/dokumenty')) return 0.5;
return 0.7;
}

View File

@@ -4,14 +4,15 @@ import yaml from "js-yaml";
export type DocYaml = { export type DocYaml = {
title: string; title: string;
description: string;
visible?: boolean; visible?: boolean;
intro?: string;
content: string; content: string;
}; };
export type DocEntry = DocYaml & { export type DocEntry = DocYaml & {
slug: string; slug: string;
file: string; file: string;
description: string;
}; };
const DOCS_DIR = path.join(process.cwd(), "src", "content", "document"); const DOCS_DIR = path.join(process.cwd(), "src", "content", "document");
@@ -34,13 +35,14 @@ export function listDocuments(): DocEntry[] {
if (!data.title || typeof data.title !== "string") continue; if (!data.title || typeof data.title !== "string") continue;
if (!data.content || typeof data.content !== "string") continue; if (!data.content || typeof data.content !== "string") continue;
if (!data.description || typeof data.description !== "string") continue;
items.push({ items.push({
slug, slug,
file, file,
title: data.title, title: data.title,
description: data.description,
visible: data.visible ?? false, visible: data.visible ?? false,
intro: data.intro ?? "",
content: data.content, content: data.content,
}); });
} }
@@ -60,14 +62,15 @@ export function getDocumentBySlug(slug: string): DocEntry | null {
if (!data.title || typeof data.title !== "string") return null; if (!data.title || typeof data.title !== "string") return null;
if (!data.content || typeof data.content !== "string") return null; if (!data.content || typeof data.content !== "string") return null;
if (!data.description || typeof data.description !== "string") continue;
return { return {
slug, slug,
file, file,
title: data.title, title: data.title,
visible: data.visible ?? false, visible: data.visible ?? false,
intro: data.intro ?? "",
content: data.content, content: data.content,
description: data.description,
}; };
} }

View File

@@ -15,7 +15,7 @@ if (!doc || doc.visible !== true) {
const html = marked.parse(doc.content); const html = marked.parse(doc.content);
--- ---
<DefaultLayout title={doc.title}> <DefaultLayout title={doc.title} description={doc.description}>
<section class="f-section"> <section class="f-section">
<div class="f-section-grid-single"> <div class="f-section-grid-single">
<a href="/dokumenty" class="f-document-link"> <a href="/dokumenty" class="f-document-link">

View File

@@ -49,7 +49,6 @@ const right = groups["pobierz"] ?? {};
<div> <div>
<h3 class="f-section-title">{right.tytul ?? "Pobierz"}</h3> <h3 class="f-section-title">{right.tytul ?? "Pobierz"}</h3>
{ {
!right.pliki?.length ? ( !right.pliki?.length ? (
<p class="opacity-70 mt-4">Brak plików.</p> <p class="opacity-70 mt-4">Brak plików.</p>

View File

@@ -12,6 +12,8 @@ type YamlSection = {
}; };
type YamlData = { type YamlData = {
title?: string;
description?: string;
sections?: YamlSection[]; sections?: YamlSection[];
}; };
@@ -22,10 +24,16 @@ let items: Array<{
content: string; content: string;
}> = []; }> = [];
let pageTitle = "";
let pageDescription = "";
let err = ""; let err = "";
try { try {
const data = loadYaml<YamlData>("./src/content/internet-telewizja/telewizja-mozliwosci.yaml"); const data = loadYaml<YamlData>(
"./src/content/internet-telewizja/telewizja-mozliwosci.yaml",
);
pageTitle = data?.title || pageTitle;
pageDescription = data?.description || pageDescription;
const sections = safeArray<YamlSection>(data?.sections); const sections = safeArray<YamlSection>(data?.sections);
items = sections items = sections
@@ -42,7 +50,7 @@ try {
} }
--- ---
<DefaultLayout title="Możliwości JAMBOX"> <DefaultLayout title={pageTitle} description={pageDescription}>
<section class="f-section" id="top"> <section class="f-section" id="top">
<div class="f-section-grid-single"> <div class="f-section-grid-single">
<h1 class="f-section-title">Możliwości JAMBOX</h1> <h1 class="f-section-title">Możliwości JAMBOX</h1>

View File

@@ -42,17 +42,20 @@ const form = data.form;
<DefaultLayout seo={seo}> <DefaultLayout seo={seo}>
<section class="f-section"> <section class="f-section">
<!-- ✅ Zmieniona struktura - osobne bloki zamiast grida -->
<div class="f-contact-grid"> <div class="f-contact-grid">
{/* row 1: tytuły */}
<h1 class="f-section-title m-0">{data.title}</h1>
<h1 class="f-section-title m-0">{data.contactFormTitle}</h1>
{/* row 2: treść */} {/* Lewa kolumna: Kontakt */}
<div class="f-contact-item"> <div class="f-contact-column">
<Markdown text={data.description} /> <h1 class="f-section-title">{data.title}</h1>
<div class="f-contact-item">
<Markdown text={data.description} />
</div>
</div> </div>
<div id="form"> {/* Prawa kolumna: Formularz */}
<div class="f-contact-column" id="form">
<h2 class="f-section-title">{data.contactFormTitle}</h2>
<form id="contactForm" class="f-contact-form"> <form id="contactForm" class="f-contact-form">
<div class="f-contact-form-inner"> <div class="f-contact-form-inner">
@@ -135,6 +138,7 @@ const form = data.form;
</button> </button>
</form> </form>
</div> </div>
</div> </div>
<div class="mt-10"> <div class="mt-10">

View File

@@ -28,7 +28,7 @@ const seo = loadYaml("./src/content/mapa-zasiegu/seo.yaml");
class="w-full md:w-[340px] bg-[var(--f-background)] text-[var(--f-text)] class="w-full md:w-[340px] bg-[var(--f-background)] text-[var(--f-text)]
pt-6 px-6 flex flex-col gap-6 overflow-y-auto z-40" pt-6 px-6 flex flex-col gap-6 overflow-y-auto z-40"
> >
<h3 class="text-3xl">Sprawdź dostępność usług</h3> <h1 class="text-3xl">Sprawdź dostępność usług</h1>
<p class="text-sm"> <p class="text-sm">
Wybierz swoją miejscowość i ulicę oraz numer budynku, aby sprawdzić Wybierz swoją miejscowość i ulicę oraz numer budynku, aby sprawdzić
dostępność usług światłowodowych FUZ. dostępność usług światłowodowych FUZ.
@@ -49,7 +49,9 @@ const seo = loadYaml("./src/content/mapa-zasiegu/seo.yaml");
/> />
</div> </div>
</section> </section>
<SectionRenderer src="./src/content/site/area-section.yaml" /> <div class="container md:pl-16">
<SectionRenderer src="./src/content/site/area-section.yaml" />
</div>
<script is:inline> <script is:inline>
let fiberLayer = null; let fiberLayer = null;

View File

@@ -1,49 +0,0 @@
import { globby } from 'globby';
export async function GET() {
const base = "https://www.fuz.pl";
// Pobieramy wszystkie pliki .astro
const files = await globby([
"src/pages/**/*.astro",
"!src/pages/_*.astro", // pomiń pliki zaczynające się od _
"!src/pages/**/[...*", // pomiń catch-all
"!src/pages/**/[**", // pomiń dynamiczne parametry
"!src/pages/sitemap.xml.js", // pomiń samą sitemapę
"!src/pages/api/**" // pomiń API endpoints
]);
// Konwersja ścieżek plikowych → URL-e
const urls = files.map((file) => {
let url = file
.replace("src/pages", "")
.replace(".astro", "");
// obsługa index: /index.astro → /
if (url.endsWith("/index")) {
url = url.replace("/index", "");
}
return url;
});
const body = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls
.map((url) => {
return `
<url>
<loc>${base}${url}</loc>
<changefreq>weekly</changefreq>
<priority>${url === "/" ? "1.0" : "0.8"}</priority>
</url>`;
})
.join("")}
</urlset>`;
return new Response(body, {
headers: {
"Content-Type": "application/xml",
},
});
}

100
src/pages/sitemap.xml.ts Normal file
View File

@@ -0,0 +1,100 @@
import type { APIRoute } from 'astro';
import {
getEnv,
generateSitemapXml,
inferChangeFreq,
inferPriority,
type SitemapUrl
} from '../lib/astro-helpers';
import { listDocuments } from '../lib/documents';
/**
* Dynamiczny sitemap generator
* GET /sitemap.xml
*/
export const GET: APIRoute = async ({ site }) => {
const base = site?.toString().replace(/\/$/, '') ||
getEnv('PUBLIC_SITE_URL') ||
"https://www.fuz.pl";
const now = new Date().toISOString();
const urls: SitemapUrl[] = [];
// ========================================
// STATYCZNE STRONY
// ========================================
const staticPages = [
'/',
'/internet-swiatlowodowy',
'/internet-telewizja',
'/telefon',
'/mapa-zasiegu',
'/kontakt',
'/dokumenty',
'/premium',
'/telewizja-mozliwosci',
];
staticPages.forEach(path => {
urls.push({
loc: `${base}${path}`,
lastmod: now,
changefreq: inferChangeFreq(path),
priority: inferPriority(path),
});
});
// ========================================
// DYNAMICZNE STRONY: Dokumenty
// ========================================
try {
const docs = listDocuments();
docs
.filter(d => d.visible === true)
.forEach(d => {
const path = `/dokumenty/${d.slug}`;
urls.push({
loc: `${base}${path}`,
lastmod: now,
changefreq: 'yearly',
priority: 0.5,
});
});
} catch (e) {
console.warn('⚠️ Sitemap: Could not load documents:', e);
}
// ========================================
// DYNAMICZNE STRONY: Premium packages (opcjonalnie)
// ========================================
// TODO: Jeśli masz dynamiczne pakiety premium, dodaj tutaj:
/*
try {
const packages = await loadPremiumPackages();
packages.forEach(p => {
urls.push({
loc: `${base}/premium/${p.tid}`,
lastmod: now,
changefreq: 'monthly',
priority: 0.7,
});
});
} catch (e) {
console.warn('⚠️ Sitemap: Could not load premium packages:', e);
}
*/
// ========================================
// GENERUJ XML
// ========================================
const sitemap = generateSitemapXml(urls);
return new Response(sitemap, {
status: 200,
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'Cache-Control': 'public, max-age=3600', // 1h cache
},
});
};

View File

@@ -112,7 +112,7 @@
.gm-style-iw-c { .gm-style-iw-c {
background: var(--f-background) !important; background: var(--f-background) !important;
border-radius: 14px !important; border-radius: 14px !important;
box-shadow: 0 4px 18px rgba(0,0,0,0.12) !important; box-shadow: 0 4px 18px rgba(0, 0, 0, 0.12) !important;
padding: 0 !important; padding: 0 !important;
} }
@@ -121,3 +121,30 @@
padding: 0 !important; padding: 0 !important;
background: transparent !important; background: transparent !important;
} }
.fuz-cities-box {
background: var(--f-background);
color: var(--f-text);
padding: 16px;
}
.fuz-cities-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 12px;
}
.fuz-cities-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 6px 14px;
font-size: 0.95rem;
line-height: 1.4;
}
.fuz-city-item {
color: var(--f-text);
}