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/:slug — RegionDetailIntro, RegionDetailHerbGrid (app/components/region/); /symptomy/:slug — SymptomyDetailIntro, 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/:slug — RitualyDetailIntro, 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} má id="main-content" a tabIndex={-1}.
2.2 Dekor a layout bloky
Hotovo (minimální krok): app/components/page-decor.tsx — PageDecor 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í)
Stabilizovat otevřené body P2.x(P2.1–P2.2 hotovo; P2.3 krok 1 — builder + Vitest; zbytek P2.3 v sekci výše).Jedna bezpečná extrakce z.sezona.$month.tsxPrvní bezsqlářský split z.herb-detail.server.tsMalý helper pro NavLink třídy v.root.tsxSamostatný PR: ESLint základ.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).