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: {
minify: "esbuild",
cssMinify: "esbuild",
chunkSizeWarningLimit: 500,
},
},
integrations: [

View File

@@ -1,5 +1,6 @@
{
"name": "fuz20",
"name": "fuz-site",
"type": "module",
"version": "1.0.0",
"private": true,
"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>
<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 }
});
if (title || desc) {
const { InfoWindow } = await google.maps.importLibrary("maps");
const info = new InfoWindow({
content: `
<div class="f-info-window">
<div class="f-info-header">
<div class="f-info-city">${title}</div>
<div class="f-info-street">${desc}</div>
</div>
</div>
`
});
// if (title || desc) {
// const { InfoWindow } = await google.maps.importLibrary("maps");
// const info = new InfoWindow({
// content: `
// <div class="f-info-window">
// <div class="f-info-header">
// <div class="f-info-city">${title}</div>
// <div class="f-info-street">${desc}</div>
// </div>
// </div>
// `
// });
info.open({ map, anchor: marker });
}
// info.open({ map, anchor: marker });
// }
}
} catch (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 isAboveFold = index === 0;
// Konfiguracja wyświetlania
// Konfiguracja wyświetlania
const showTitle = section.showTitle !== false; // domyślnie true
// Formatowanie listy miejscowości jako string
// Formatowanie listy miejscowości jako string
const citiesText = cities.join(", ");
---
@@ -66,33 +66,6 @@ const citiesText = cities.join(", ");
</a>
</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>
</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:
tytul: Pobierz
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
file: /public/files/EVIO TV.pdf
- nazwa: Lista kanałów JAMBOX TV
file: /public/files/EVIO TV.pdf
- nazwa: Lista kanałów coś
file: /public/files/EVIO TV.pdf
file: /public/files/JAMBOX TV.pdf
otworz:
tytul: Przeczytaj
pliki:
- nazwa: Polityka prywatności
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"
# opis wstawiany w <meta name="description" content=""
description: Polityka prywatności, opisuje zasady ochrony Twoich danych osobowych.
visible: true
intro: Polityka prywatności, opisuje zasady ochrony Twoich danych osobowych.
content: |
## §1. Informacje podstawowe.

View File

@@ -1,6 +1,6 @@
title: "Promocja świąteczna"
description: Przykładowo gdybysmy dodali promocję do dokumentów
visible: true
intro: Przykładowo gdybysmy dodali promocję do dokumentów
content: |
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żą:

View File

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

View File

@@ -4,11 +4,9 @@ opis: |
Wybierz rodzaj budynku i czas trwania umowy
uwaga: |
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
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
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
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).
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;
-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
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).
cena_opis: "zł/mies."
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:
- title: CatchUp - Archiwum TV
image: https://www.jambox.pl/sites/default/files/jambox-kyanit-catchup1.png

View File

@@ -1,7 +1,7 @@
import { useMemo } from "preact/hooks";
import { marked } from "marked";
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";
/**
@@ -46,6 +46,7 @@ export default function JamboxMozliwosciSearch({ items = [] }) {
<div className="f-chsearch__top">
<div className="f-chsearch__inputwrap">
<input
name="search"
className="f-chsearch__input"
type="search"
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="sitemap" type="application/xml" title="Sitemap" href="/sitemap.xml" />
<!-- OpenGraph -->
<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";
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">
<head>
<BaseHead seo={seo} />
</head>
<BaseHead seo={finalSeo} />
<body class="min-h-screen flex flex-col">
<Header />

View File

@@ -396,3 +396,90 @@ export function getRequiredEnv(key: string): string {
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 = {
title: string;
description: string;
visible?: boolean;
intro?: string;
content: string;
};
export type DocEntry = DocYaml & {
slug: string;
file: string;
description: string;
};
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.content || typeof data.content !== "string") continue;
if (!data.description || typeof data.description !== "string") continue;
items.push({
slug,
file,
title: data.title,
description: data.description,
visible: data.visible ?? false,
intro: data.intro ?? "",
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.content || typeof data.content !== "string") return null;
if (!data.description || typeof data.description !== "string") continue;
return {
slug,
file,
title: data.title,
visible: data.visible ?? false,
intro: data.intro ?? "",
content: data.content,
description: data.description,
};
}

View File

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

View File

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

View File

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

View File

@@ -42,17 +42,20 @@ const form = data.form;
<DefaultLayout seo={seo}>
<section class="f-section">
<!-- ✅ Zmieniona struktura - osobne bloki zamiast grida -->
<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-column">
<h1 class="f-section-title">{data.title}</h1>
<div class="f-contact-item">
<Markdown text={data.description} />
</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">
<div class="f-contact-form-inner">
@@ -135,6 +138,7 @@ const form = data.form;
</button>
</form>
</div>
</div>
<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)]
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">
Wybierz swoją miejscowość i ulicę oraz numer budynku, aby sprawdzić
dostępność usług światłowodowych FUZ.
@@ -49,7 +49,9 @@ const seo = loadYaml("./src/content/mapa-zasiegu/seo.yaml");
/>
</div>
</section>
<div class="container md:pl-16">
<SectionRenderer src="./src/content/site/area-section.yaml" />
</div>
<script is:inline>
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 {
background: var(--f-background) !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;
}
@@ -121,3 +121,30 @@
padding: 0 !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);
}