All changes
This commit is contained in:
parent
593f380581
commit
49f7ec2f5a
60 changed files with 1783 additions and 679 deletions
|
|
@ -119,7 +119,8 @@ Serves `frontend/dist/` as static fallback in production **only** when `--dist`
|
|||
React 18 + TypeScript. deck.gl `H3HexagonLayer` over MapLibre GL. TailwindCSS. No state management library — pure React hooks.
|
||||
|
||||
**Architecture:**
|
||||
- `App.tsx` — Minimal router: loads features/POI categories, handles page navigation (home/dashboard/data-sources/faq)
|
||||
- `App.tsx` — Minimal router: loads features/POI categories, handles page navigation. Page type is `'home' | 'dashboard' | 'learn' | 'pricing' | 'account' | 'saved' | 'invites' | 'invite'`. Auth-required pages (`account`, `saved`, `invites`) redirect to home with login modal when unauthenticated. `pageToPath()` / `pathToPage()` map between Page values and URL paths.
|
||||
- `AccountPage.tsx` — Exports three separate page components: `SavedPage` (`/saved` — saved searches + saved properties with sub-tabs), `InvitesPage` (`/invites` — invite link generation + history), and `AccountPage` (default export, `/account` — email, subscription, newsletter, support). Note: `'invite'` (singular, `/invite/:code`) is the invite *redemption* flow — distinct from `'invites'` (plural, `/invites`) which is the invite *management* page.
|
||||
- `MapPage.tsx` — Dashboard layout: composes map + left/right panes, uses custom hooks for all logic
|
||||
- Custom hooks in `hooks/` encapsulate stateful logic:
|
||||
- `useMapData` — Hexagon/postcode fetching, bounds, loading state, color range calculation
|
||||
|
|
@ -138,6 +139,8 @@ React 18 + TypeScript. deck.gl `H3HexagonLayer` over MapLibre GL. TailwindCSS. N
|
|||
- Viewport bounds computed via `getBoundsFromViewState()` in `map-utils.ts` — uses Web Mercator math with **TILE_SIZE=512** (MapLibre/deck.gl convention, NOT 256)
|
||||
- Properties pane uses feature names from API response (human-readable), not hardcoded field names
|
||||
- Proxy: dev server on :3001 proxies `/api` to :8001; also handles VS Code `/proxy/PORT` patterns
|
||||
- **Nav links must be `<a>` tags, not `<button>`**: All page navigation items in `Header.tsx` and `MobileMenu.tsx` use `<a href={PAGE_PATHS[page]}>` with an `onClick` that calls `e.preventDefault()` + client-side navigation for normal clicks, but lets CMD/Ctrl+click fall through to open in a new tab. `PAGE_PATHS` is exported from `Header.tsx`. Pattern: `if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;` before `preventDefault()`.
|
||||
- **Portal outside-click handlers must check both refs**: When a dropdown uses `createPortal(content, document.body)`, the portal DOM is outside the trigger's container. An outside-click handler using `container.contains(e.target)` will treat clicks on portal content as "outside" and close the dropdown. On mobile this breaks selection entirely because the native `mousedown` listener on `document` preempts React's synthetic event on the portal content. Fix: add a separate ref to the portal content and check both in the handler (`!containerRef.current.contains(target) && !dropdownRef.current?.contains(target)`). See `DestinationDropdown.tsx` for the pattern.
|
||||
|
||||
**Shared UI Components (`frontend/src/components/ui/`):**
|
||||
- `icons/` — One file per icon (CloseIcon, InfoIcon, EyeIcon, PlusIcon, ChevronIcon, FilterIcon, LightbulbIcon, DownloadIcon, MapPinIcon, CheckIcon, ClipboardIcon, SunIcon, MoonIcon, SpinnerIcon). All accept `className` prop. **Never inline SVGs** — always extract to this folder.
|
||||
|
|
@ -332,6 +335,7 @@ Follow these conventions in all Rust code:
|
|||
- **GridIndex returns slightly more than requested**: The 0.01° grid cells mean properties up to ~1km outside the viewport may be returned. The AABB filter in the route handlers catches these extras.
|
||||
- **POI proximity**: Uses 0.05° grid (~5km cells) to reduce candidates before haversine distance check
|
||||
- **OG tag injection**: Uses `<meta name="x-og-placeholder" content="__PERFECT_POSTCODES_OG_TAGS__"/>` placeholder in HTML, replaced at runtime by middleware
|
||||
- **Dev invite code**: The code `devdevdevdev` is recognized as a valid admin invite in dev mode only (`state.index_html.is_none()`, i.e., `--dist` not passed). Both `get_invite` and `post_redeem_invite` short-circuit for this code, returning a fake valid admin invite / no-op "licensed" response without hitting PocketBase. Preview at `http://localhost:3001/invite/devdevdevdev`.
|
||||
|
||||
## Rust Performance Patterns (server-rs)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue