Add docker build
This commit is contained in:
parent
c84af213e2
commit
627ba5496a
4 changed files with 184 additions and 0 deletions
13
.dockerignore
Normal file
13
.dockerignore
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
data/
|
||||
data_sources/
|
||||
.venv
|
||||
**/node_modules
|
||||
**/dist
|
||||
server-rs/target
|
||||
.git
|
||||
.task
|
||||
.claude
|
||||
__pycache__
|
||||
*.parquet
|
||||
analyses/
|
||||
*.log
|
||||
49
.github/workflows/docker.yml
vendored
Normal file
49
.github/workflows/docker.yml
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=sha,prefix=sha-,format=short
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
97
CLAUDE.md
97
CLAUDE.md
|
|
@ -117,6 +117,103 @@ React 18 + TypeScript. deck.gl `H3HexagonLayer` over MapLibre GL. TailwindCSS. N
|
|||
- Properties pane uses feature names from API response (human-readable), not hardcoded field names
|
||||
- Proxy: dev server on :3030 proxies `/api` to :8001; also handles VS Code `/proxy/PORT` patterns
|
||||
|
||||
## Frontend Design Guide (STRICT — must be followed for all UI changes)
|
||||
|
||||
The frontend uses Tailwind's `darkMode: 'class'` strategy. The `dark` class is toggled on `<html>`. Every visible element must have both light and dark styles. **Never add a light-only color class without its `dark:` counterpart.** Run `task build:frontend` after any UI change to verify.
|
||||
|
||||
### Theme System
|
||||
|
||||
- **State**: `App.tsx` owns a `theme` state (`'light' | 'dark' | 'system'`), persisted in `localStorage` under the key `theme`, default `'system'`.
|
||||
- **Effective theme**: When `'system'`, resolved via `window.matchMedia('(prefers-color-scheme: dark)')`. A `change` listener re-renders on OS preference flip.
|
||||
- **Toggle cycle**: light → dark → system → light. Three-way, not binary.
|
||||
- **Flash prevention**: `index.html` contains an inline `<script>` that applies the `dark` class before first paint. If the localStorage/matchMedia logic in that script changes, update it to match `App.tsx`.
|
||||
- **Prop plumbing**: `effectiveTheme` (`'light' | 'dark'`) is passed as a prop to `<Map>` and `<HomePage>`. Components that need the resolved theme must receive it as a prop — do not read localStorage or matchMedia inside child components.
|
||||
|
||||
### Color Token Reference
|
||||
|
||||
Every UI element must use the correct token from this table. Do not invent new pairings.
|
||||
|
||||
| Role | Light class | Dark class | Hex (dark) |
|
||||
|------|------------|------------|------------|
|
||||
| **Page / pane background** | `bg-warm-50` or `bg-white` | `dark:bg-warm-900` | #1c1917 |
|
||||
| **Card / elevated surface** | `bg-white` | `dark:bg-warm-800` | #292524 |
|
||||
| **Inset / recessed surface** | `bg-warm-100` or `bg-warm-50` | `dark:bg-warm-800` | #292524 |
|
||||
| **Input / select background** | `bg-white` | `dark:bg-warm-800` or `dark:bg-warm-900` | |
|
||||
| **Primary border** | `border-warm-200` | `dark:border-warm-700` | #44403c |
|
||||
| **Subtle border (dividers)** | `border-warm-100` | `dark:border-warm-800` | #292524 |
|
||||
| **Primary text (headings)** | `text-navy-950` or implicit dark | `dark:text-warm-100` | #f5f5f4 |
|
||||
| **Body text** | `text-warm-700` | `dark:text-warm-300` | #d6d3d1 |
|
||||
| **Secondary text (labels, hints)** | `text-warm-500` or `text-warm-600` | `dark:text-warm-400` | #a8a29e |
|
||||
| **Disabled / placeholder text** | `text-warm-400` / `placeholder-warm-400` | `dark:text-warm-500` / `dark:placeholder-warm-500` | #78716c |
|
||||
| **Accent text (links, actions)** | `text-teal-600` | `dark:text-teal-400` | #1de4c3 |
|
||||
| **Accent hover text** | `hover:text-teal-800` | `dark:hover:text-teal-300` | #51f7d9 |
|
||||
| **Accent background (highlights)** | `bg-teal-50` | `dark:bg-teal-900/30` | |
|
||||
| **Active ring / focus ring** | `ring-teal-400` | same — works in both | |
|
||||
| **Price / key metric text** | `text-teal-700` | `dark:text-teal-400` | |
|
||||
| **Remove / close button** | `text-warm-400 hover:text-warm-700` | `dark:hover:text-warm-300` | |
|
||||
| **Checkbox accent** | `accent-teal-600` | same — works in both | |
|
||||
| **Header (unchanged both modes)** | `bg-navy-900 text-white` | same | |
|
||||
|
||||
### Mapping Rules for Specific Contexts
|
||||
|
||||
**Sidebars (Filters, POIPane, PropertiesPane, right-pane tabs):**
|
||||
- Container: `bg-white dark:bg-warm-900`
|
||||
- Inner cards / dropdown menus: `bg-white dark:bg-warm-800`
|
||||
- Borders: `border-warm-200 dark:border-warm-700`
|
||||
- Tab text (active): add `dark:text-warm-100`
|
||||
- Tab text (inactive): `text-warm-600 dark:text-warm-400`
|
||||
|
||||
**Map overlays (PostcodeSearch, MapLegend, POI popup, loading indicator):**
|
||||
- Background: `bg-white dark:bg-warm-800`
|
||||
- Text: `dark:text-warm-200`
|
||||
- Semi-transparent variants: use `/90` opacity suffix (e.g. `dark:bg-warm-800/90`)
|
||||
- Deck.gl tooltip (inline styles, not Tailwind): use `#292524` bg / `#e7e5e4` text / `rgba(0,0,0,0.5)` shadow in dark.
|
||||
- Deck.gl postcode labels (RGB arrays): `[220,220,220,220]` text / `[30,30,30,200]` outline in dark; inverse in light.
|
||||
|
||||
**Map basemaps:**
|
||||
- Light: `https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json`
|
||||
- Dark: `https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json`
|
||||
- `handleMapLoad` must only apply label/water tweaks in light mode. Dark Matter has good defaults.
|
||||
|
||||
**HomePage (landing page):**
|
||||
- Page bg: `bg-warm-50 dark:bg-warm-900`
|
||||
- Cards: `bg-white dark:bg-warm-800` with `border-warm-200 dark:border-warm-700`
|
||||
- Backdrop-blur panels: use `/60` or `/40` opacity on both `bg-warm-50` and `dark:bg-warm-900`
|
||||
- HexCanvas: reads `isDark` ref; uses dimmer fill (`#058172`) and stroke (`#0a665b`) at 60% opacity multiplier.
|
||||
- All headings: `dark:text-warm-100`. All body: `dark:text-warm-300` or `dark:text-warm-400`.
|
||||
|
||||
**DataSourcesPage:**
|
||||
- Same card pattern as above. Footer is already dark (`bg-navy-900`) — no changes needed.
|
||||
- License badges: `bg-warm-100 dark:bg-warm-700 text-warm-600 dark:text-warm-300`
|
||||
- Links: `text-teal-600 dark:text-teal-400`
|
||||
|
||||
**DataSources floating button (on map):**
|
||||
- `bg-white/90 dark:bg-warm-800/90` with `text-teal-600 dark:text-teal-400`
|
||||
|
||||
### Rules for New Components
|
||||
|
||||
1. **Every `bg-white` needs `dark:bg-warm-800` or `dark:bg-warm-900`.** Pane-level = warm-900, card-level = warm-800.
|
||||
2. **Every `border-warm-200` needs `dark:border-warm-700`.**
|
||||
3. **Every `text-warm-*` needs a `dark:text-warm-*` counterpart.** Follow the token table — don't guess.
|
||||
4. **Every `text-teal-600` needs `dark:text-teal-400`.** Every `hover:text-teal-800` needs `dark:hover:text-teal-300`.
|
||||
5. **Every `bg-teal-50` needs `dark:bg-teal-900/30`.**
|
||||
6. **Every `hover:bg-warm-50` needs `dark:hover:bg-warm-700` or `dark:hover:bg-warm-800`.**
|
||||
7. **Inputs and selects**: always add `dark:bg-warm-800 dark:text-warm-200 dark:border-warm-700`. Placeholders get `dark:placeholder-warm-500`.
|
||||
8. **Checkboxes**: always include `accent-teal-600 rounded`.
|
||||
9. **Do not use Tailwind `dark:` classes inside deck.gl layers or canvas code.** Use the `theme` prop / ref and conditional JS values.
|
||||
10. **Do not add `transition-*` classes for theme switching.** The global CSS rule in `index.css` handles transitions for `background-color`, `border-color`, and `color` on all standard HTML elements. Adding per-element transition classes will conflict.
|
||||
11. **Never hardcode hex colors in JSX `style=` props for themed elements** (except deck.gl tooltip and canvas, which can't use Tailwind). Use the Tailwind classes from the token table instead.
|
||||
12. **The header (`bg-navy-900`) is identical in both themes.** Do not add dark variants to it.
|
||||
|
||||
### Verification Checklist (for any UI PR)
|
||||
|
||||
- [ ] `task build:frontend` passes with no errors
|
||||
- [ ] Every new `bg-*`, `text-*`, `border-*` class has a `dark:` counterpart (search your diff)
|
||||
- [ ] Toggle through all three modes (light → dark → system) with no flash
|
||||
- [ ] Map basemap switches when theme changes
|
||||
- [ ] Sidebars, dropdowns, and popups are readable in both modes
|
||||
- [ ] HomePage and DataSourcesPage adapt correctly
|
||||
|
||||
## Key Implementation Details
|
||||
|
||||
- **Spatial sort**: Rows sorted by 0.01° grid cell at load time for cache-friendly sequential access
|
||||
|
|
|
|||
25
Dockerfile
Normal file
25
Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Stage 1: Build frontend
|
||||
FROM node:20-slim AS frontend
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Build Rust server
|
||||
FROM rust:1.83-bookworm AS server
|
||||
WORKDIR /app
|
||||
COPY server-rs/ server-rs/
|
||||
WORKDIR /app/server-rs
|
||||
RUN cargo build --release
|
||||
|
||||
# Stage 3: Runtime
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /app
|
||||
COPY --from=server /app/server-rs/target/release/property-map-server ./
|
||||
COPY --from=frontend /app/frontend/dist ./dist/
|
||||
|
||||
EXPOSE 8001
|
||||
ENTRYPOINT ["./property-map-server"]
|
||||
CMD ["--data", "/data/wide.parquet", "--pois", "/data/filtered_uk_pois.parquet"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue