Přeskočit na obsah

Refaktor v3 — další vrstva kvality (mimo jádro v2)

Stav dokumentu: Hotovo (bezpečný rozsah v3, 2026) — v hlavní větvi platí npm run verify (typecheck, ESLint, Vitest, build; CI volá verify). Dokument dál slouží jako archiv provedených kroků a jako zápis backlogu mimo uzavřený sprint: dokončení P2.3 (rizikové SQL — zbývá případná matice na D1 a vlastní přepisy), volitelný §2.2 PageDecor, §6 výkon, případné další dělení DB. Archiv v2: refaktor-v2.md.

Cíl: shrnout další směry — větší soubory mimo kartu byliny, DB vrstvu kolem detailu, opakující se UI vzory, tooling (lint/test/CI), přístupnost, výkon a provoz workeru — tak, aby šly brát po jednom malém diffu s npm run verify.

Zásada: stejně jako u archivu refaktoring-100-safe.md a v2 — žádné „big bang“ commity; u vizuálu a SQL vždy explicitní akceptace a kontrola regrese.

Uzavření úkolu (bezpečný rozsah v3): Plánované extrakce rout, meta, katalogu /byliny, sezóny, přehledů a tooling §4 jsou v kódu dokončené; průběžná kontrola zůstává npm run verify. Sekce P2.3 a zmínky §2.2 / §6 níže jsou zápis budoucí práce, nikoli rozpracovaný checklist tohoto úkolu.


0. Vztah k předchozím refaktorům

  • v1 (100% safe): sdílené helpery, labely, parsery — hotové v archivu refaktoring-100-safe.md; neměnit zpětně bez důvodu.
  • v2 (archiv): mechanický cleanup §1, infrastruktura §3, bezpečná část §2.3 (preview) — refaktor-v2.md. Otevřený backlog z původního §2 je v sekci „Převzatý backlog…“ níže.
  • v3: bezpečný rozsah (routy, meta, /byliny, sezona, přehledy, lint/test/CI) byl v roce 2026 v hlavní větvi dokončen; viz stavový řádek nahoře a sekci Uzavření úkolu.

Převzatý backlog z archivu refaktor v2 (§2 — /byliny a herbs.server.ts)

Zdroj archivu: refaktor-v2.md. Hotový bezpečný přesun preview helperů (herb-catalog-preview.server.ts) zůstává popsán v archivu v §2.3; níže je přenesený zápis z v2 (P2.1–P2.2 dokončeny v kódu; P2.3 — první krok regresních testů hotový, viz sekce).

P2.1 Rozdělit app/routes/byliny.$slug.tsx po sekcích

Hotovo: sekce v app/components/herb-detail/ (hero, harvest, storage, processing, spiritual, science, images, safety, …); složení stránky v herb-detail-page.tsx (HerbDetailPage); route skládá meta, loader a předá data do HerbDetailPage; meta karty byliny (article OG, volitelný náhled) v app/lib/herb-detail-route-meta.ts (herbDetailRouteMeta) + herb-detail-route-meta.test.ts.

Bezpečnostní pravidla (splněno v implementaci):

  • Postupně extrahované sekce; props podle loader dat.
  • Neměněny texty ani pořadí sekcí v rámci refaktoru.

P2.2 Rozdělit app/routes/byliny.tsx na filtr panel a výsledky

Hotovo: app/components/catalog-filter-form.tsx, catalog-intro.tsx, catalog-herb-results.tsx; složení stránky /byliny v app/components/catalog/catalog-byliny-page.tsx (CatalogBylinyPage; CatalogBylinyPageLoaderData = CatalogLoaderFilterSnapshot + výsledky a metadata); výpočet „jsou aktivní filtry“ v app/lib/catalog-page-helpers.ts (catalogHasActiveFilters, typ CatalogLoaderFilterSnapshot) + Vitest catalog-page-helpers.test.ts; meta katalogu v app/lib/byliny-catalog-meta.ts (bylinyCatalogRouteMeta) + byliny-catalog-meta.test.ts; parsování query a mapování na HerbListParams / řádek filtrů loaderu v app/lib/catalog-byliny-search-params.ts (parseBylinyCatalogSearchParams, bylinyParsedToHerbListParams, bylinyParsedToLoaderFilterRow) + catalog-byliny-search-params.test.ts.

P2.3 Rizikové úpravy app/db/herbs.server.ts (SQL / filtry) — backlog s prvním krokem

Hotový krok 1 (bez změny chování dotazu): sestavení SQL a binds pro strukturované filtry katalogu je v app/db/herb-catalog-filter-ids-query.server.ts jako buildHerbCatalogFilterIdsQuery; měsíc a region sdílejí fragmenty harvestCoversMonthSql a herbInRegionSql ze sezona-harvest-sql.server.ts (shodně se sezona.server.ts). EXISTS pro sběr (měsíc / část / kombinace přes herbCatalogHarvest*ExistsSql a harvestCoversMonthSql), zpracování, téma, vědu (substantial / none / samostatný studyType), spirituály (publikované řádky + facety z JSON), predikát safety (herbCatalogSafetyLevelEqualsSql) jsou v herb-catalog-filter-sql-fragments.server.ts (herbCatalogProcessingMethodsInExistsSql, herbCatalogTopicLinkExistsSql) + Vitest herb-catalog-filter-sql-fragments.server.test.ts. herbIdsMatchingCatalogFilters v herbs.server.ts jen připraví dotaz a zavolá D1. Regresní matice na lokální D1 (HTTP po buildu): p2-3-katalog-filtry-d1-matice.md, skript npm run smoke:byliny-matrix. Regresní Vitest herb-catalog-filter-ids-query.server.test.ts pokrývá prázdný stav (null), region, zpracování, měsíc / část / kombinaci, téma + topicScope, vědu (substantial + scienceMin, none, studyType u any / u none), spirituály, facet z JSON, safety_level. Normalizace: normalizeCatalogEnumSlug, normalizeHerbListMonth (viz §1.3 / herb-list-month.ts); komentáře v sezona-harvest-sql.server.ts odkazují na builder.

Zbývá (stále rizikové, až po potřebě):

  • Přepis nebo přeskupení logiky za použití výše uvedených testů (nebo rozšíření testů před změnou).
  • Skládání SQL fragmentů jinak než dnes (udržet shodu se sezónními fragmenty / matice na D1).
  • Změna logiky science, studyType, month + part — vždy s rozšířenými testy a průchodem matice na D1.

Parsery URL (parseMonthParam, parseCatalogPmSlugs, parseBylinyCatalogSearchParams, …) mají Vitest v app/lib/*.test.ts.


1. Velké soubory a routy (kromě přeneseného backlogu P2.x)

1.1 app/routes/sezona.$month.tsx (~300+ řádků)

Hotovo: tabulka bylin v app/components/sezona/sezona-month-herbs-table.tsx (SezonaMonthHerbsTable); měsíční chipy, regiony a části v app/components/sezona/sezona-month-filters.tsx (SezonaMonthFilters); dekor (SoftBlob/LeafSprig), drobečky a úvodní blok v app/components/sezona/sezona-month-intro.tsx (SezonaMonthIntro); horní blok (obal relative + intro + filtry) v sezona-month-top.tsx (SezonaMonthTop); prázdný stav bez řádků v app/components/sezona/sezona-month-empty.tsx (SezonaMonthEmpty); meta měsíce v app/lib/sezona-month-meta.ts (sezonaMonthRouteMeta a dílčí helpery) + sezona-month-meta.test.ts; meta přehledu /sezona v app/lib/sezona-index-meta.ts (sezonaIndexRouteMeta a dílčí helpery) + sezona-index-meta.test.ts; hlavička přehledu /sezona v app/components/sezona/sezona-index-header.tsx (SezonaIndexHeader); mřížka měsíců přehledu v app/components/sezona/sezona-index-month-grid.tsx (SezonaIndexMonthGrid); odkazy do katalogu (?region, /byliny?month + part) v app/lib/sezona-month-catalog-links.ts + sezona-month-catalog-links.test.ts — beze změny URL, query, textů a Tailwindu v extrahovaných blocích.

1.2 app/db/herb-detail.server.ts (~470 řádků)

Hotovo (bezsqlářský krok): čisté helpery resolveHerbImageSrc a parseRecipeStepsJson v app/lib/herb-detail-helpers.ts (receptové kroky přes sdílené parseJsonStringArray v app/lib/json-string-array.ts); server modul je importuje. SQL texty a bindy beze změny.

1.3 Další DB moduly střední velikosti

Částečně: sdílená normalizace slugů v app/lib/catalog-slug-normalize.ts (normalizeCatalogSlug, normalizeCatalogEnumSlug pro DB enumy / katalog) — používá topics.server.ts přes normalizeTopicSlug v topic-slug.ts, processing-methods.server.ts, aliasová routa byliny-na-zpracovani.$slug a builder filtrů v herb-catalog-filter-ids-query.server.ts. Vitest catalog-slug-normalize.test.ts. EXISTS fragmenty zpracování/tématu katalogu: herb-catalog-filter-sql-fragments.server.ts. Parsování JSON polí řetězců v app/lib/json-string-array.ts (parseJsonStringArray) — spiritual-guides.server.ts a receptové kroky přes herb-detail-helpers / omitEmptyTrimmed; Vitest json-string-array.test.ts. Fragmenty SQL pro měsíc/region sezóny přesunuty do app/db/sezona-harvest-sql.server.ts (harvestCoversMonthSql, herbInRegionSql) — import v sezona.server.ts; texty SQL beze změny. Další rozdělení herbs.server.ts / sjednocení s katalogem — backlog; zásada „žádný přepis WHERE bez matice“ platí.

1.4 Komponenty s vlastní logikou

Hotovo (drobně): app/components/ritual-guide-steps.tsx — počáteční hydrated pro jednokrokové návody bez zbytečného setState v efektu; u více kroků zůstává obnova z sessionStorage v useEffect (s vědomým eslint-disable pro pravidlo set-state-in-effect v tomto souboru).

1.5 app/routes/home.tsx

Hotovo: blok „Sběr právě teď“ v app/components/home/home-sezona-teaser.tsx (HomeSezonaTeaser); celá úvodní karta (dekor, nadpis, lead, teaser, CTA katalog) v app/components/home/home-hero-card.tsx (HomeHeroCard) — beze změny textů, URL a Tailwindu; meta domovské stránky v app/lib/home-route-meta.ts (homeRouteMeta) + home-route-meta.test.ts.

1.6 Přehledové katalogové routy

Hotovo: úvodní bloky a mřížky karet mimo loader/meta u /ritualy (RitualyIndexIntro, RitualyIndexGuideGrid), /region (RegionIndexIntro, RegionIndexGrid), /symptomy (SymptomyIndexIntro včetně GET formuláře q, SymptomyIndexTopicGrid), /zpracovani (ZpracovaniIndexIntro, ZpracovaniIndexMethodGrid), /recepty (ReceptyIndexIntro, ReceptyIndexRecipeGrid). Routy jen skládají meta, loader a layout main. Statické meta těchto přehledů (+ /roadmap, /dokumentace) v app/lib/overview-routes-meta.ts + overview-routes-meta.test.ts.

1.7 Roadmap a dokumentace

Hotovo: obsah /roadmap v app/components/roadmap/roadmap-content.tsx (RoadmapContent); přehled /dokumentace v app/components/dokumentace/dokumentace-index-content.tsx (DokumentaceIndexContent); detail /dokumentace/:slug (drobečky + karta Markdown) v dokumentace-doc-view.tsx (DokumentaceDocView). Statické meta přehledů /roadmap a /dokumentace jsou v overview-routes-meta.ts (viz §1.6); meta stránky dokumentu (dokumentaceDocRouteMeta) v app/lib/detail-pages-route-meta.ts + detail-pages-route-meta.test.ts; loader a registry dokumentů zůstávají v routách.

1.8 Detail regionu a tématu (symptomu)

Hotovo: /region/:slugRegionDetailIntro, RegionDetailHerbGrid (app/components/region/); /symptomy/:slugSymptomyDetailIntro, SymptomyDetailHerbSection (app/components/symptomy/). Štítek regions.type sjednocen v app/lib/region-type-label.ts (regionTypeLabel) — používá RegionIndexGrid a RegionDetailIntro. meta: regionDetailRouteMeta, symptomyDetailRouteMeta v detail-pages-route-meta.ts.

1.9 Detail rituálu a způsobu zpracování

Hotovo: /ritualy/:slugRitualyDetailIntro, RitualyDetailBody (app/components/ritualy/); /zpracovani/:slug — celý obsah stránky (včetně jednoho obalu relative) v zpracovani-detail-content.tsx (ZpracovaniDetailContent). meta: ritualyDetailRouteMeta, zpracovaniDetailRouteMeta v detail-pages-route-meta.ts (sdílený modul s dokumentaceDocRouteMeta a region/symptom — viz §§1.7–1.8) + detail-pages-route-meta.test.ts.


2. Opakující se UI a kořen aplikace

2.1 app/root.tsx — navigace a šablona

Hotovo: helper mainNavLinkClass(isActive); odkaz Přeskočit na obsah (#main-content); obal {children}id="main-content" a tabIndex={-1}.

2.2 Dekor a layout bloky

Hotovo (minimální krok): app/components/page-decor.tsxPageDecor obaluje {children} v root.tsx uvnitř #main-content s className="contents min-h-0" (display: contents), tedy bez nové layoutové hloubky; připraveno pro budoucí dekorativní vrstvy (aria-hidden) dle potřeby.


3. Meta a obsah — konzistence (bez velkých SEO featur)

Hotovo: v app/lib/site-meta.ts konstanty SITE_BRAND_NAME, SITE_TITLE_SUFFIX, SITE_TITLE_PIPE_BRAND ( | …); websiteSocialMeta používá značku z konstanty. Titulky a popisy v meta u hlavních rout (/, /byliny, /sezona, /symptomy, /ritualy, /region, /zpracovani, /recepty, /roadmap, /dokumentace) sjednoceny přes tyto konstanty (beze změny zobrazovaného textu oproti předchozím řetězcům); logika meta pro / a /byliny je v home-route-meta.ts a byliny-catalog-meta.ts, pro přehledové indexy a roadmapu/dokumentaci v overview-routes-meta.ts, pro dynamické detaily (ritualy, region, symptomy, zpracovani, dokumentace podle slug) v detail-pages-route-meta.ts, pro kartu byliny v herb-detail-route-meta.ts. Vitest pro meta moduly: viz §4.2.


4. Tooling: lint, formát, testy, CI

4.1 ESLint (+ TypeScript plugin)

Hotovo: eslint.config.mjs@eslint/js, typescript-eslint, eslint-plugin-react-hooks; ignorované generované a build cesty.

4.2 Automatické testy

Hotovo: Vitest (vitest.config.ts), testy app/lib/month-param.test.ts, catalog-filter-params.test.ts, catalog-byliny-search-params.test.ts, region-param.test.ts, region-type-label.test.ts, site-meta.test.ts, topic-slug.test.ts, sezona-month-meta.test.ts, sezona-index-meta.test.ts, sezona-month-catalog-links.test.ts, catalog-page-helpers.test.ts, home-route-meta.test.ts, byliny-catalog-meta.test.ts, overview-routes-meta.test.ts, detail-pages-route-meta.test.ts, herb-detail-route-meta.test.ts, catalog-slug-normalize.test.ts, herb-list-month.test.ts, json-string-array.test.ts, app/db/herb-catalog-filter-ids-query.server.test.ts, app/db/herb-catalog-filter-sql-fragments.server.test.ts (regrese SQL builderu katalogu / P2.3).

4.3 CI

Hotovo: .github/workflows/ci.yml spouští npm run verify (včetně cache npm dle setup-node).


5. Worker a provoz (workers/)

Hotovo (minimum): v workers/app.ts u scheduled runHerbMaintenance strukturovaný jednořádkový JSON log (level, component, trigger, ok, …) místo volného výpisu objektu. HTTP endpoint /__internal/herb-maintenance nadále vrací JSON tělo.


6. Výkon a assety

Částečně: Google Fonts v root.tsx používají display=swap a parametr subset=latin,latin-ext (menší stahovaný CSS/fontové soubory pro češtinu v rámci Google CDN). Širší self-host / vlastní subsetting — samostatný úkol. Obrázky R2 / CLS — navázat na bezpecnost-a-media-stitky.md.


7. Repozitář a generované soubory

Beze změny kódu: pravidlo z v2 §3.3 platí (build/, .react-router/types, .wrangler/, worker-configuration.d.ts dle politiky — typicky necommitovat). Po merge konfliktech ověřit git ls-files.


8. Co zatím nedělat jako součást v3 refaktoru

  • Neměnit veřejné URL, slugy dokumentace ani sitemap logiku bez tasku a datové kontroly.
  • Nezasahovat do obsahu Markdownu v docs/ kvůli „úklidu“ — může změnit veřejné /dokumentace/:slug.
  • Neinstalovat lint + přepsat celý app/ v jednom PR s DB refaktorem.

Doporučené pořadí (indikativní)

  1. Stabilizovat otevřené body P2.x (P2.1–P2.2 hotovo; P2.3 krok 1 — builder + Vitest; zbytek P2.3 v sekci výše).
  2. Jedna bezpečná extrakce z sezona.$month.tsx.
  3. První bezsqlářský split z herb-detail.server.ts.
  4. Malý helper pro NavLink třídy v root.tsx.
  5. Samostatný PR: ESLint základ.
  6. Samostatný PR: Vitest + testy parserů.

Akceptace dokumentu samotného

  • Po implementaci doplněn stavový řádek a checklisty výše.
  • Při zařazení do rozcestníku aktualizovat README.md v docs/tasks/ (řádek refaktor-v3).
  • Úkol bezpečného rozsahu v3 v dokumentaci uzavřen (stav Hotovo; P2.3 — krok 1 v kódu, zbytek jen zápis backlogu).