The deals tab was previously disabled when searching by block+plot
because find_recent_deals_for_address needs an address string. Govmap's
autocomplete in fact accepts the literal "גוש N חלקה M" and returns
the parcel coordinates, so we just compose that string client-side
and let the rest of the pipeline (coords → polygon → deals) run as-is.
Verified live: search "Block=6212 Plot=894" now returns the same two
deals on יהודה המכבי that an address search would.
Appraiser-name search still disables the deals tab — there's no
location to anchor a deals query on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five-layer defense so that no single library/browser quirk can flip the
layout back to LTR:
1. <body dir="rtl"> in addition to <html dir="rtl"> — some libs read
document.body.dir, not document.documentElement.dir.
2. Explicit `direction: rtl` on html, body in the @layer base CSS.
3. <DirectionProvider dir="rtl"> from @radix-ui/react-direction wrapping
the React tree, so every Radix primitive (Tabs, Dialog, Select,
Popover, Toast) gets RTL context — Radix's useDirection defaults to
"ltr" without it.
4. shadcn Tabs wrapper now defaults its dir prop to "rtl" — redundant
with the provider above, but defends against a future provider
removal silently breaking Tabs.
5. dir="rtl" set explicitly on each <table> element.
Fixes the symptom seen on the live site: deals/appraisals tables had
תאריך (the first <th> in the JSX) rendering on the LEFT instead of the
RIGHT, and Tabs flowed עסקאות→שמאי left-to-right instead of the
expected right-to-left.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The catch-all `lib/` pattern in both .gitignore and .dockerignore (intended
to exclude Python venv lib dirs) silently swallowed `web/src/lib/utils.ts`.
That's why every Docker build got "ENOENT /web/src/lib/utils" — the file
was never committed and never copied into the container, but local builds
worked because the file existed on disk.
Anchor `lib/` and `lib64/` to the repo root with `/lib/` and `/lib64/`,
then add the previously-ignored utils.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vite's path alias resolution worked locally but consistently failed in
the Docker stage with "Could not load /web/src/lib/utils ... ENOENT"
even after fixing __dirname for ESM. Some interaction between act/buildx
and Vite's resolver around tsconfig paths. Relative imports sidestep the
issue entirely and are explicit about the dependency direction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`__dirname` is undefined in ESM modules, so `path.resolve(__dirname, "./src")`
silently produced a bad path in Docker. Locally it happened to work (some
Node interop), but the Docker stage hit a clear error: "Could not load
/web/src/lib/utils ... ENOENT". Use fileURLToPath(import.meta.url).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Docker build was repeatedly failing on `@/*` path-alias resolution in
tsc, even though local builds (and the same script in the same Node 22
container) succeeded. Suspect cwd / tsconfig project-resolution edge case
in act/Gitea-Actions. Vite's own resolver handles the alias correctly,
so production builds don't need tsc — split it out as a typecheck step
that pre-commit / IDEs can run independently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The composite-mode + project-references setup broke `@/*` path alias
resolution inside the Docker stage 1 build (worked locally because tsc's
incremental cache was warm). Switch to a single tsconfig with
`tsc --noEmit && vite build` — Vite handles the alias via its own
resolver, so we only use tsc as a type-checker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a self-contained web interface that wraps the MCP tools so end users
don't need to issue MCP commands. Single Docker container serves:
/ → React SPA (RTL Hebrew, Heebo font, shadcn/ui components)
/api/* → FastAPI REST endpoints used by the SPA
/api/docs → OpenAPI / Swagger
/mcp → existing FastMCP streamable-http endpoint
/health → liveness probe
REST endpoints (in nadlan_mcp/web_app.py):
GET /api/search/address?q=... — autocomplete + best-effort gush/חלקה
via Govmap PARCEL_ALL layer
POST /api/search/deals — Govmap deals around an address
POST /api/search/appraisals — Decisive-appraiser search (gov.il)
GET /api/appraisals/pdf?url=.. — Stream PDFs through this server,
carrying the upstream x-client-id
header (a normal browser fetch fails)
UI flow:
- Address search → resolves gush/חלקה → shows BOTH deals (Govmap) AND
appraiser decisions (gov.il) for the same parcel side-by-side in tabs.
- Block+plot search → goes straight to appraisals.
- Appraiser-name search → all decisions by that appraiser.
Stack chosen for RTL-first usability and longevity: React 19, Vite 6,
TypeScript, Tailwind, shadcn/ui (Radix primitives), TanStack Query,
Lucide icons. ~295KB gzipped JS, no SSR overhead.
Multi-stage Dockerfile: Node 22 builds web/ in stage 1, Python 3.13
runtime serves the dist + the FastAPI app in stage 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>