fix(sf-fe): decode cad param (B5 400) + hide entry sidebar when drawer open #351

Merged
lekss361 merged 1 commit from fix/sf-cad-decode-drawer-overlay-v2 into main 2026-05-17 23:37:46 +00:00
Owner

Recreated from #350 (closed — branched off stale fix/sf-entry-parcel-adapter-cadinput HEAD before #349 merged squash → conflict). This branch is rebased onto current main (f87a352 after #349 merge), single commit.

Summary

2 prod регрессии (user screenshot):

Bug A — /parcels/{cad}/analyze всегда 400

API error 400: Неверный формат кадастрового номера.
Ожидается NN:NN:NNNNNN или NN:NN:NNNNNN:NN

Next.js dynamic route доставляет params.cad URL-encoded (:%3A). Хуки потом делали encodeURIComponent на уже-encoded строке → /api/v1/parcels/66%253A41%253A0204016%253A10/analyze. FastAPI decoded one layer → 66%3A41%3A0204016%3A10, regex rejected → 400. Section 1 вечный error state.

Fix: decodeURIComponent(cadRaw) один раз на границе page → downstream хуки / breadcrumb / Section1 видят canonical 66:41:0204016:10 и encode ровно один раз при build URL. Заодно закрывает breadcrumb double-encode bug из QA PR #346.

Bug B — sidebar и drawer наложены

При click на парцель <ParcelDrawer/> slide-in из правого края наезжал на правый sidebar (CadInput + RecentParcels + ParcelLegend) — оба anchored right.

Fix: conditional render — sidebar mounts только когда selectedParcel == null.

Verify post-deploy

  • /site-finder/analysis/66:41:0204016:10 — Section 1 НЕ показывает «не удалось загрузить»; KPI + EGRN + POI рендерятся.
  • Network: GET /api/v1/parcels/66%3A41%3A0204016%3A10/analyze → 200 (single-encode).
  • /site-finder → click на marker → drawer открыт, sidebar исчез; close → sidebar возвращается.
  • Breadcrumb на analysis — 66:41:0204016:10, не 66%3A41%3A0204016%3A10.

Closes #350.

Recreated from #350 (closed — branched off stale `fix/sf-entry-parcel-adapter-cadinput` HEAD before #349 merged squash → conflict). This branch is rebased onto current `main` (`f87a352` after #349 merge), single commit. ## Summary 2 prod регрессии (user screenshot): ### Bug A — `/parcels/{cad}/analyze` всегда 400 ``` API error 400: Неверный формат кадастрового номера. Ожидается NN:NN:NNNNNN или NN:NN:NNNNNN:NN ``` Next.js dynamic route доставляет `params.cad` **URL-encoded** (`:` → `%3A`). Хуки потом делали `encodeURIComponent` на уже-encoded строке → `/api/v1/parcels/66%253A41%253A0204016%253A10/analyze`. FastAPI decoded one layer → `66%3A41%3A0204016%3A10`, regex rejected → 400. Section 1 вечный error state. **Fix**: `decodeURIComponent(cadRaw)` один раз на границе page → downstream хуки / breadcrumb / Section1 видят canonical `66:41:0204016:10` и encode ровно один раз при build URL. Заодно закрывает breadcrumb double-encode bug из QA PR #346. ### Bug B — sidebar и drawer наложены При click на парцель `<ParcelDrawer/>` slide-in из правого края наезжал на правый sidebar (CadInput + RecentParcels + ParcelLegend) — оба anchored right. **Fix**: conditional render — sidebar mounts только когда `selectedParcel == null`. ## Verify post-deploy - [ ] `/site-finder/analysis/66:41:0204016:10` — Section 1 НЕ показывает «не удалось загрузить»; KPI + EGRN + POI рендерятся. - [ ] Network: `GET /api/v1/parcels/66%3A41%3A0204016%3A10/analyze` → 200 (single-encode). - [ ] `/site-finder` → click на marker → drawer открыт, sidebar исчез; close → sidebar возвращается. - [ ] Breadcrumb на analysis — `66:41:0204016:10`, не `66%3A41%3A0204016%3A10`. Closes #350.
lekss361 added 1 commit 2026-05-17 23:35:09 +00:00
Two prod issues:

1. Analysis page B5 endpoint returned HTTP 400 'Неверный формат
   кадастрового номера' for any cad. Next.js dynamic route delivers
   `params.cad` URL-encoded (':' -> '%3A'). Hooks then ran
   encodeURIComponent on the already-encoded string, producing
   /api/v1/parcels/66%253A41%253A0204016%253A10/analyze; FastAPI
   decoded one layer to '66%3A41%3A0204016%3A10', which the regex
   rejected. Fix: decodeURIComponent the param once at the page
   boundary so downstream consumers (hooks + breadcrumb + Section1)
   work with the canonical '66:41:0204016:10' and encode exactly
   once when building URLs. Also resolves the breadcrumb double-
   encode noted in PR #346 QA.

2. Entry page right-sidebar (CadInput + RecentParcels + ParcelLegend)
   visually overlapped the ParcelDrawer slide-in: both were anchored
   to the right edge with the drawer rendered above as an overlay
   without an opaque backdrop on the sidebar area. Conditional render:
   sidebar only mounts when no parcel is selected, so opening a parcel
   gives the drawer a clean right column.
Author
Owner

Deep Code Review — verdict APPROVE

Files reviewed: 2 (P2) · +25/-18 · mergeable=true · base=main@f87a3522

Bug A — decodeURIComponent at page boundary

  • decodeURIComponent(cadRaw) once on params; downstream hooks/breadcrumb/Section1 receive canonical 66:41:0204016:10 and re-encode exactly once when building API URL. Symmetric, correct fix.
  • Idempotent: applying to already-decoded 66:41:0204016:10 is no-op (no % chars) — safe under any nav order.
  • Malformed %XX would throw URIError — acceptable, Next.js router won't deliver malformed segments; synthetic edge case falls back to app error boundary. Not worth try/catch.

Bug B — conditional sidebar render

  • {selectedParcel == null && <Sidebar/>} cleanly hides right rail while drawer is open. No more overlap.
  • State tradeoff: CadInput local input state resets on remount, RecentParcels re-mounts but TanStack Query cache survives → no refetch cost. UX-acceptable.
  • CSS-hide alternative would preserve input field but adds DOM weight; conditional render is cleaner.

Conventions

  • TS strict, no any, no XSS surface, == null consistent with surrounding code.
  • No regressions in #343 ParcelDrawer / #349 adapter+CadInput chains.

Verify post-deploy (from PR body)

  • GET /api/v1/parcels/66%3A41%3A0204016%3A10/analyze → 200
  • /site-finder marker click → drawer open, sidebar hidden; close → sidebar back
  • Breadcrumb shows 66:41:0204016:10

Merging via squash.

## Deep Code Review — verdict APPROVE **Files reviewed**: 2 (P2) · +25/-18 · mergeable=true · base=main@f87a3522 ### Bug A — decodeURIComponent at page boundary - `decodeURIComponent(cadRaw)` once on params; downstream hooks/breadcrumb/Section1 receive canonical `66:41:0204016:10` and re-encode exactly once when building API URL. Symmetric, correct fix. - Idempotent: applying to already-decoded `66:41:0204016:10` is no-op (no `%` chars) — safe under any nav order. - Malformed `%XX` would throw `URIError` — acceptable, Next.js router won't deliver malformed segments; synthetic edge case falls back to app error boundary. Not worth try/catch. ### Bug B — conditional sidebar render - `{selectedParcel == null && <Sidebar/>}` cleanly hides right rail while drawer is open. No more overlap. - State tradeoff: `CadInput` local input state resets on remount, `RecentParcels` re-mounts but TanStack Query cache survives → no refetch cost. UX-acceptable. - CSS-hide alternative would preserve input field but adds DOM weight; conditional render is cleaner. ### Conventions - TS strict, no `any`, no XSS surface, `== null` consistent with surrounding code. - No regressions in #343 ParcelDrawer / #349 adapter+CadInput chains. ### Verify post-deploy (from PR body) - [ ] `GET /api/v1/parcels/66%3A41%3A0204016%3A10/analyze` → 200 - [ ] `/site-finder` marker click → drawer open, sidebar hidden; close → sidebar back - [ ] Breadcrumb shows `66:41:0204016:10` Merging via squash.
lekss361 merged commit ac86b75115 into main 2026-05-17 23:37:46 +00:00
Author
Owner

Closing — superseded by ongoing UI alignment PR (frontend-engineer worker a5cc7a063e4c5b18e). User feedback: sidebar hide approach из #351 неверное решение («при выборе ... закрывается блок Анализировать»). Новый PR: drawer positioned over map area only, sidebar (CadInput + Recent + Legend) всегда visible + decode cad сохраняется + UI alignment с макетом.

Closing — superseded by ongoing UI alignment PR (frontend-engineer worker `a5cc7a063e4c5b18e`). User feedback: sidebar hide approach из #351 неверное решение («при выборе ... закрывается блок Анализировать»). Новый PR: drawer positioned over map area only, sidebar (CadInput + Recent + Legend) всегда visible + decode cad сохраняется + UI alignment с макетом.
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#351
No description provided.