Rewrite on the Bennett platform: Spring Boot + Vite/React SPA

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
This commit is contained in:
2026-07-22 22:09:51 -05:00
parent 207415dbbe
commit f471462d05
164 changed files with 4223 additions and 14003 deletions
+40
View File
@@ -0,0 +1,40 @@
import { useEffect } from 'react';
import { Route, Routes, useLocation } from 'react-router-dom';
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import HomePage from '@/pages/Home';
import ProductsPage from '@/pages/Products';
import HistoryPage from '@/pages/History';
import ContactPage from '@/pages/Contact';
import NotFoundPage from '@/pages/NotFound';
/**
* Client-side navigation keeps the previous scroll position, which lands you halfway down a page you
* just opened. Anchors like /#visit still need to work, so only reset when there isn't one.
*/
const ScrollToTop = () => {
const { pathname, hash } = useLocation();
useEffect(() => {
if (!hash) window.scrollTo(0, 0);
}, [pathname, hash]);
return null;
};
const App = () => (
<div className="min-h-screen bg-bakery-50 flex flex-col">
<ScrollToTop />
<Header />
<main className="flex-grow">
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/products" element={<ProductsPage />} />
<Route path="/history" element={<HistoryPage />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</main>
<Footer />
</div>
);
export default App;