fix(sf-fe): decode cad in Server Component (B5 400 regression after #356) #359

Merged
lekss361 merged 1 commit from fix/cad-decode-server-component into main 2026-05-18 04:07:04 +00:00
Owner

Summary

P0 hotfix — user reports API error 400: Неверный формат кадастрового номера на /site-finder/analysis/[cad].

Root cause

PR #351 первоначально decoded params.cad в Client Component. A7 split (PR #356) refactored на Server Component + AnalysisPageContent.tsx и забыл carry decode. URL /site-finder/analysis/66%3A41%3A0204016%3A10 сейчас:

  • params.cad = "66%3A41%3A0204016%3A10" (URL-encoded)
  • Hook encodeURIComponent(cad)66%253A41%253A0204016%253A10 (double-encoded)
  • FastAPI decodes one layer → 66%3A41%3A0204016%3A10
  • Regex NN:NN:NNNNNN(?::NN)? rejects → 400

Fix

decodeURIComponent(cadRaw) в обоих generateMetadata и page export [cad]/page.tsx. Downstream видит canonical 66:41:0204016:10 и encode'ит ровно один раз при build URL.

Verify

  • /site-finder/analysis/66:41:0204016:10 → POST /analyze → 200, Section 1 render'ит данные
  • Breadcrumb показывает 66:41:0204016:10, не 66%3A...

Closes hot 400 regression.

## Summary **P0 hotfix** — user reports `API error 400: Неверный формат кадастрового номера` на `/site-finder/analysis/[cad]`. ## Root cause PR #351 первоначально decoded `params.cad` в Client Component. A7 split (PR #356) refactored на Server Component + `AnalysisPageContent.tsx` и **забыл** carry decode. URL `/site-finder/analysis/66%3A41%3A0204016%3A10` сейчас: - `params.cad` = `"66%3A41%3A0204016%3A10"` (URL-encoded) - Hook `encodeURIComponent(cad)` → `66%253A41%253A0204016%253A10` (double-encoded) - FastAPI decodes one layer → `66%3A41%3A0204016%3A10` - Regex `NN:NN:NNNNNN(?::NN)?` rejects → 400 ## Fix `decodeURIComponent(cadRaw)` в обоих `generateMetadata` и page export `[cad]/page.tsx`. Downstream видит canonical `66:41:0204016:10` и encode'ит ровно один раз при build URL. ## Verify - `/site-finder/analysis/66:41:0204016:10` → POST `/analyze` → 200, Section 1 render'ит данные - Breadcrumb показывает `66:41:0204016:10`, не `66%3A...` Closes hot 400 regression.
lekss361 added 1 commit 2026-05-18 04:02:28 +00:00
Backend /api/v1/parcels/{cad}/analyze rejects URL-encoded cad with HTTP 400.
PR #351 originally fixed by decoding params.cad in the Client Component. A7
split (PR #356) refactored into Server Component + AnalysisPageContent and
forgot to carry the decode forward — every analysis page request through cad
URL bar (e.g. /site-finder/analysis/66%3A41%3A0204016%3A10) now passes the
encoded form to the hooks, which encodeURIComponent again, producing %253A,
which FastAPI decodes once to %3A, which the regex rejects.

Fix: decodeURIComponent(cadRaw) at the Server Component boundary (page.tsx)
in both generateMetadata and the page export. Downstream sees canonical
'66:41:0204016:10' and re-encodes exactly once when building API URLs.
Author
Owner

Deep Code Review — verdict

Summary

  • Status: APPROVE
  • Files reviewed: 1 (P2: frontend/src/app/site-finder/analysis/[cad]/page.tsx)
  • Lines: +10 / -2
  • PR: #359 (P0 prod 400 hotfix)

Verification

Decode in both Server Component boundaries — confirmed:

  • generateMetadata({params})const cad = decodeURIComponent(cadRaw); then used in title/description (line ~22)
  • AnalysisPage({params}) default export — same decode at top, passed to <AnalysisPageContent cad={cad}/> downstream (line ~33)

Idempotency checkdecodeURIComponent("66:41:0204016:10") returns the same string (no %XX sequences). Direct-typed canonical URL not affected; encoded URL /66%3A41%3A0204016%3A10 properly decoded to canonical form. No double-decode risk.

Blast radius — minimal:

  • AnalysisPageContent.tsx untouched (expects decoded cad prop, contract preserved)
  • Sections 1–5 wiring intact (single file change, no hook signatures or types modified)
  • Inline comment documents regression history (#351#356#359) — good for future-proofing

Edge cases:

  • Malformed % (e.g. 66%ZZ41...) → decodeURIComponent throws URIError. Next.js will surface as 500 page rather than 400 — acceptable, since malformed URL is user error, not regression. Not blocking; can be hardened later with try/catch if needed.

Critical / High / Medium

None.

Low / nits

  • None blocking. Optional future hardening: wrap decodeURIComponent in try/catch to return notFound() on URIError for hand-crafted malformed URLs.

Merging immediately

P0 prod regression — proceeding to squash-merge.

## Deep Code Review — verdict ### Summary - **Status**: APPROVE - **Files reviewed**: 1 (P2: `frontend/src/app/site-finder/analysis/[cad]/page.tsx`) - **Lines**: +10 / -2 - **PR**: #359 (P0 prod 400 hotfix) ### Verification **Decode in both Server Component boundaries** — confirmed: - `generateMetadata({params})` — `const cad = decodeURIComponent(cadRaw);` then used in title/description (line ~22) - `AnalysisPage({params})` default export — same decode at top, passed to `<AnalysisPageContent cad={cad}/>` downstream (line ~33) **Idempotency check** — `decodeURIComponent("66:41:0204016:10")` returns the same string (no `%XX` sequences). Direct-typed canonical URL not affected; encoded URL `/66%3A41%3A0204016%3A10` properly decoded to canonical form. No double-decode risk. **Blast radius** — minimal: - `AnalysisPageContent.tsx` untouched (expects decoded `cad` prop, contract preserved) - Sections 1–5 wiring intact (single file change, no hook signatures or types modified) - Inline comment documents regression history (#351 → #356 → #359) — good for future-proofing **Edge cases**: - Malformed `%` (e.g. `66%ZZ41...`) → `decodeURIComponent` throws `URIError`. Next.js will surface as 500 page rather than 400 — acceptable, since malformed URL is user error, not regression. Not blocking; can be hardened later with try/catch if needed. ### Critical / High / Medium None. ### Low / nits - None blocking. Optional future hardening: wrap `decodeURIComponent` in try/catch to return `notFound()` on `URIError` for hand-crafted malformed URLs. ### Merging immediately P0 prod regression — proceeding to squash-merge.
lekss361 merged commit 1bbd4e835d into main 2026-05-18 04:07:04 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#359
No description provided.