Skip to content

Webový frontend ​

Webový frontend platformy Než zazvoní CMS je postaven na moderním aplikačním rámci Next.js 16 s architekturou App Router, React 19 a jazykem TypeScript. Aplikace se sestavuje do samostatného produkčního balíčku (standalone output) a běží v dockerovém kontejneru.

Jediná běžící instance aplikace dokáže obsluhovat neomezené množství škol. Každý příchozí požadavek dynamicky směruje na odpovídající školu podle domény nebo adresy v hlavičce Host. Vykreslování probíhá na straně serveru (Server-Side Rendering) s využitím datové mezipaměti Strapi REST API.

Kde co najdete ve struktuře kódu ​

Struktura aplikace ve složce web/ je členěna do přehledných vrstev:

  • app/: Stránky a šablony App Routeru (layout.tsx, page.tsx, [...path]/page.tsx, aktuality/page.tsx, hledat/page.tsx) a globální kaskádové styly globals.css a nz.css.
  • proxy.ts: Vstupní vrstva požadavků zajišťující bezpečnostní hlavičky, generování nonce pro Content Security Policy, omezení počtu požadavků i přesměrování starých adres.
  • lib/: Aplikační logika a pomocné funkce:
    • tenant.ts a school.ts: Rozpoznání a načtení školy podle hlavičky Host.
    • cms.ts a content.ts: Klientské funkce pro volání veřejného REST API a dotazy na stránky, články i kategorie.
    • theme.ts a fonts.ts: Výpočet CSS proměnných pro barvy a konfigurace písem.
    • types.ts: TypeScript rozhraní pro datový model Strapi a komponenty sekcí.
  • components/: Vizuální komponenty webu rozdělené na základní prvky, sekce klasického designu (sections/) a komponenty moderního designového jazyka Než zazvoní (nz/).

Výběr školy podle domény ​

Mechanismus rozpoznání školy v modulu lib/tenant.ts (funkce findSchool) vyhodnocuje hlavičku Host každého příchozího požadavku podle následujících pravidel:

  1. Vlastní doména školy: Nejprve se ověřuje přesná shoda se záznamem domain u některé ze škol v CMS (například oapv.cz i varianta www.oapv.cz).
  2. Subdoména platformy: Pokud vlastní doména neodpovídá, systém zkontroluje, zda hostitel odpovídá formátu <slug>.<SITE_BASE_DOMAIN> (například oapv.lab.navit.cz). Tento tvar slouží pro testovací provoz, náhledy a školy bez vlastní domény.
  3. Vývojové prostředí a místní provoz: Pro požadavky z adres localhost, holé IP adresy nebo interní jednoslovné názvy kontejnerů se použije výchozí škola definovaná v proměnné prostředí DEFAULT_SCHOOL_SLUG.

Z bezpečnostních důvodů systém odmítá jakékoli subdomény třetího řádu na cizích doménách (například oapv.cizidomena.cz). Tím je vyloučeno, aby kdokoli zobrazil web školy pod cizí doménou bez schválení v konfiguraci platformy.

Bezpečnostní vrstva a přesměrování adres ​

Vstupní brána proxy.ts zpracovává každý požadavek ještě před spuštěním samotného vykreslování:

  • Ochrana Content Security Policy (CSP): Pro každý požadavek vygeneruje unikátní kryptografický řetězec (nonce). Tento nonce předává inline skriptům frameworku Next.js. Politika přísně vymezuje zdroje médií na adresu CMS a povoluje vkládání externích rámců výhradně z Google Maps a domény YouTube bez cookies (youtube-nocookie.com).
  • Omezení počtu požadavků (Rate Limit): Chrání webový server před přetížením. Výchozí limit je 1200 požadavků za minutu na jednu klientskou IP adresu.
  • Trvalá přesměrování (HTTP 301) ze starého webu:
    • Odkazy na články ze starého WordPressu ve tvaru /slug-clanku/ automaticky přesměruje na novou adresu /aktuality/slug-clanku (za předpokladu, že článek v CMS existuje a neexistuje stránka se stejnou adresou).
    • Odkazy na kategorie /category/<slug>/ přesměruje na výpis aktualit s filtrem /aktuality?kategorie=<slug>.
    • Adresy zakončené lomítkem přesměruje na tvar bez lomítka v jediném kroku.
  • Zákaz indexace (Noindex): Při nastavení systémové proměnné SITE_NOINDEX=1 odesílá hlavičku X-Robots-Tag: noindex, nofollow, což zabraňuje indexaci testovacích a předváděcích webů vyhledávači.

Designové vrstvy a vizuální styl ​

Platforma podporuje dvě souběžné designové vrstvy, které se volí v administraci v záznamu Škola:

  • Klasický design (klasik): Původní šablona platformy. Využívá základní komponenty záhlaví a zápatí (SiteHeader, SiteFooter) a sekce ze složky components/sections/.
  • Design Než zazvoní (nz): Moderní vizuální styl navržený pro školy. Využívá specializované komponenty ze složky components/nz/ (NzHeader, NzFooter, NzSections, NzPageHeader, NzAnnouncement) a kaskádové styly nz.css. Umožňuje zvýrazňovat slova v nadpisech sekcí barvou školy pomocí hvězdiček (Škola, která *učí praxí*).

Na následujícím snímku vidíte úvodní stránku školy v moderním designu Než zazvoní:

Úvodní stránka webu v designu Než zazvoní

Pro srovnání si můžete prohlédnout stejnou školu v původním klasickém designu:

Web školy v klasickém designu

Nastavení barev a písem (Theming) ​

Grafický styl školy se neupravuje v kódu CSS, ale načítá se dynamicky ze záznamu školy v CMS:

  • Barvy školy (colorPrimary, colorAccent, colorDark, colorSurface) se aplikují jako CSS proměnné přímo na elementu <html>.
  • Skript lib/theme.ts automaticky dopočítává kontrastní odstíny, aby byla zajištěna přístupnost podle standardu WCAG 2.1 AA (kontrast textu na barevném pozadí i viditelnost linek tlačítek v poměru alespoň 3 : 1).
  • Písma nadpisů a textu se volí z 10 přednastavených rodin Google Fonts. Písma jsou uložena přímo na serveru (self-hosted) prostřednictvím balíčku next/font, takže prohlížeč nestahuje data z externích serverů Google.
  • Volba zaoblení rohů (radius: sharp, soft, round) ovlivňuje poloměr tlačítek, karet i vstupních polí.

Rozvržení stránek: běžná vs složená stránka ​

Aplikace rozlišuje dva typy rozvržení stránek:

  1. Složená stránka (composed): Pokud je úplně první sekcí na stránce komponenta section.hero, stránka se vykreslí jako složená. Hero sekce nese hlavní nadpis H1, sekce se roztahují přes celou šířku obrazovky (full-bleed) a standardní záhlaví stránky s drobečkovou navigací se potlačí. Tento styl se používá pro úvodní stránku a klíčové prezentace oborů.
  2. Běžná podstránka (page): Stránka má standardní záhlaví s drobečkovou navigací a případně bočním menu podstránek. Jednotlivé sekce se řadí do čitelného centrálního sloupce.

Postup krok za krokem: Přidání nového typu sekce ​

Přidání nového typu sekce do platformy vyžaduje zásah v CMS, schématu i webovém frontendu. Postupujte podle následujících pěti kroků:

1. Definice komponenty ve Strapi ​

V adresáři cms/src/components/section/ vytvořte nový soubor schématu, například banner.json, s definicí požadovaných polí (texty, odkazy, obrázky). Novou komponentu poté přidejte do seznamu dynamické zóny sections v souboru cms/src/api/page/content-types/page/schema.json.

2. Doplnění českých popisků v administraci ​

V souboru cms/src/cms-labels.ts přidejte záznam do objektu COMPONENTS:

typescript
'section.banner': {
  mainField: 'title',
  fields: {
    title: ['Nadpis', 'Hlavní sdělení banneru.'],
    text: ['Text', 'Doplňující informace.'],
    image: ['Obrázek'],
  },
},

3. Rozšíření schématu site.json a validátoru ​

V souboru tools/site.schema.json přidejte definici nové sekce do objektu $defs/sec/banner a zařaďte odkaz na ni do seznamu oneOf v objektu $defs/section. Importér import-site.js sekci zpracuje automaticky díky generickému překladu podle schématu Strapi.

4. Úprava API dotazů a typů v Next.js ​

V souboru web/lib/content.ts doplňte načítání vnořených dat v objektu PAGE_QUERY:

typescript
'populate[sections][on][section.banner][populate][image]': true,

V souboru web/lib/types.ts vytvořte TypeScript rozhraní SectionBanner a přidejte jej do sjednocení typů Section.

5. Vytvoření frontendového rendereru ​

Ve složce web/components/sections/ vytvořte komponentu Banner.tsx. Zaregistrujte ji v přepínači SectionBody v souboru web/components/Sections.tsx pro klasický vzhled a v souboru web/components/nz/NzSections.tsx pro design Než zazvoní. Případné kaskádové styly doplňte do souborů globals.css a nz.css.

Tipy a doporučení ​

Dodržujte pravidla přístupnosti

Každá sekce na webu musí být obalena sémantickým tagem <section> a mít jednoznačně přiřazený identifikátor nadpisu pomocí atributu aria-labelledby.

Využívejte CSS proměnné

Při tvorbě nových komponent nikdy nezapisujte barvy natvrdo v HEX kódech. Vždy používejte připravené tokeny motivu, například var(--color-primary) nebo var(--color-accent). Komponenta se tak automaticky přizpůsobí barvám jakékoli školy.

Zabezpečení externích odkazů

Všechny odkazy vedoucí mimo doménu školy musejí v HTML kódu obsahovat bezpečnostní atribut rel="noopener noreferrer".

Související ​