Rewrite on the Bennett platform: Spring Boot + Vite/React SPA
build-and-publish / build (push) Successful in 1m9s

Replaces the Next.js app. Same site, same look; the parts that were decisions rather
than markup now live in Java.

- catalogue, curated order, category filter and image URLs move from a TypeScript array
  into Postgres behind /api/products and /api/categories
- contact form uses the shared platform-starter-contact: validate, RECORD, send, then
  fan out to n8n. Recording first means a relay outage costs a notification, not an enquiry
- PageMetaController rewrites title/description/OG per route, replacing what Next's SSR
  gave crawlers and link-preview scrapers
- 50MB of photos leave the repo for the MinIO bucket, re-encoded to webp (14MB) with EXIF
  (including phone GPS) stripped
- fixes a catalogue typo: 'Strawberry Pie' was category 'Pies', which no filter matched, so
  it was unreachable unless browsing All

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XXKjx7FNyRVAjU8dgB5KhN
This commit is contained in:
2026-07-22 22:09:51 -05:00
co-authored by Claude Opus 4.8
parent 3f9efa178a
commit 11ae5f378d
164 changed files with 4223 additions and 14003 deletions
+23
View File
@@ -0,0 +1,23 @@
/**
* The catalogue, its ordering, its category filter and its image URLs are all decided by the
* backend — this file just fetches them. Same origin, so no base URL and no CORS.
*/
export interface Product {
id: number;
name: string;
category: string;
/** Absolute, ready to put in a src. Built server-side from the bucket config. */
images: string[];
}
async function get<T>(path: string): Promise<T> {
const res = await fetch(path, { headers: { Accept: 'application/json' } });
if (!res.ok) throw new Error(`${path} responded ${res.status}`);
return res.json() as Promise<T>;
}
export const fetchProducts = (category?: string) =>
get<Product[]>(category && category !== 'All' ? `/api/products?category=${encodeURIComponent(category)}` : '/api/products');
export const fetchCategories = () => get<string[]>('/api/categories');