From fe08b3c5198d4c0e9714571b29cdb81069a4eea8 Mon Sep 17 00:00:00 2001 From: Frank Meeuwsen Date: Wed, 11 Feb 2026 18:06:12 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20testimonial=20Monique=20Dubbelman,=20pl?= =?UTF-8?q?ekken=208=E2=86=927,=20preflight=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Testimonial Monique Dubbelman toegevoegd met lokale avatar - Hero: 8 doorgestreept, 7 plekken beschikbaar - preflight.sh: pre-deploy checks (lint, build, asset paden, public/ bestanden) - deploy.sh: draait preflight automatisch voor elke deploy - CLAUDE.md: verificatie werkwijze en static assets conventie gedocumenteerd Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 31 ++++++++- deploy.sh | 3 + preflight.sh | 85 ++++++++++++++++++++++++ public/20260211174-MoniqueDubbelman.jpg | Bin 0 -> 19850 bytes src/components/Hero.jsx | 7 +- src/components/Testimonials.jsx | 8 +++ 6 files changed, 129 insertions(+), 5 deletions(-) create mode 100755 preflight.sh create mode 100644 public/20260211174-MoniqueDubbelman.jpg diff --git a/CLAUDE.md b/CLAUDE.md index bab8374..0e21011 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,7 @@ npm run dev # Vite dev server on port 5173 npm run build # Production build to dist/ npm run preview # Preview production build npm run lint # ESLint +./preflight.sh # Pre-deploy checks (lint + build + asset path validation) ``` ## Architecture @@ -58,10 +59,10 @@ Reusable component classes defined in `src/index.css` using `@layer components`: ## Deployment ```bash -./deploy.sh # Build + deploy naar productie (geen passphrase nodig) +./deploy.sh # Preflight + build + deploy naar productie (geen passphrase nodig) ``` -Deployment flow: `npm run build` → rsync naar server → `docker cp` naar WordPress container → chown fix +Deployment flow: `./preflight.sh` → `npm run build` → rsync naar server → `docker cp` naar WordPress container → chown fix - **Server:** Hetzner (37.27.183.46) via SSH alias `coolify-deploy` - **Container:** `wordpress-d0wko4gskokosssogcw8040g` @@ -75,3 +76,29 @@ Deployment flow: `npm run build` → rsync naar server → `docker cp` naar Word - Inline SVG icons (Heroicons-style) rather than an icon library - StickyBar has separate mobile (bottom) and desktop (top) layouts triggered by scroll position (600px threshold) - Anchor links use `#inschrijven` for CTA scroll targets + +### Verificatie Werkwijze + +**Voor elke wijziging: check bestaande patronen** in de code voordat je iets nieuws toevoegt. Hoe doen vergelijkbare items het? Gebruik dezelfde aanpak. + +**Wanneer `./preflight.sh` draaien:** +- Nieuwe bestanden toevoegen (afbeeldingen, componenten) +- Nieuwe data-entries met paden of URLs +- Imports of exports wijzigen +- Structurele wijzigingen (nieuw component, nieuwe route) +- Configuratie aanpassen (vite.config, tailwind.config) + +**Niet nodig bij:** +- Tekst aanpassen (quote, titel, beschrijving) +- Getallen wijzigen (bijv. availableSpots) +- CSS tweaks (kleur, spacing, font-size) + +Vuistregel: als het kan breken, check het. Als het alleen tekst is, niet. + +### Static Assets (BELANGRIJK) +- Statische bestanden (afbeeldingen, etc.) staan in `public/` +- **Altijd** `${import.meta.env.BASE_URL}` als prefix gebruiken bij verwijzingen vanuit code +- Reden: productie draait op `/workshopclaudecode/`, niet op `/` +- Voorbeeld: `` src={`${import.meta.env.BASE_URL}foto.jpg`} `` +- NOOIT: `src="/foto.jpg"` (werkt lokaal maar breekt op productie) +- `./preflight.sh` detecteert hardcoded paden automatisch diff --git a/deploy.sh b/deploy.sh index fb960f8..1640469 100755 --- a/deploy.sh +++ b/deploy.sh @@ -11,6 +11,9 @@ CONTAINER="wordpress-d0wko4gskokosssogcw8040g" REMOTE_PATH="/var/www/html/workshopclaudecode" TMP_PATH="/tmp/workshopclaudecode" +echo "0/4 - Preflight checks..." +./preflight.sh +echo "" echo "1/4 - Building..." npm run build --silent diff --git a/preflight.sh b/preflight.sh new file mode 100755 index 0000000..102acdf --- /dev/null +++ b/preflight.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Preflight checks - draait voor elke deploy om veelvoorkomende fouten te vangen +# +# Checks: +# 1. ESLint +# 2. Vite build (syntax errors, missing imports) +# 3. Hardcoded asset paden die BASE_URL missen +# 4. Gerefereerde public/ bestanden bestaan daadwerkelijk + +set -e + +ERRORS=0 + +echo "=== Preflight Checks ===" +echo "" + +# 1. Lint check +echo "[1/4] ESLint..." +if npm run lint --silent 2>&1; then + echo " OK" +else + echo " WAARSCHUWING: lint errors gevonden (niet-blokkerend)" +fi +echo "" + +# 2. Build check +echo "[2/4] Vite build..." +npm run build --silent +echo " OK" +echo "" + +# 3. Check voor hardcoded asset paden zonder BASE_URL +# Zoekt naar src= of avatar: paden die beginnen met "/" gevolgd door een bestandsnaam +# maar NIET import.meta.env.BASE_URL gebruiken +echo "[3/4] Asset paden check (BASE_URL)..." +BAD_PATHS=$(grep -rn 'src=\s*"\/[a-zA-Z0-9]' src/ --include="*.jsx" --include="*.js" 2>/dev/null || true) +BAD_AVATAR=$(grep -rn "avatar:\s*[\"']/[a-zA-Z0-9]" src/ --include="*.jsx" --include="*.js" 2>/dev/null || true) + +if [ -n "$BAD_PATHS" ] || [ -n "$BAD_AVATAR" ]; then + echo " FOUT: Hardcoded asset paden gevonden zonder BASE_URL prefix!" + echo " Deze werken lokaal maar breken op productie (/workshopclaudecode/)." + echo "" + [ -n "$BAD_PATHS" ] && echo "$BAD_PATHS" | sed 's/^/ /' + [ -n "$BAD_AVATAR" ] && echo "$BAD_AVATAR" | sed 's/^/ /' + echo "" + echo " Fix: gebruik \${import.meta.env.BASE_URL}bestandsnaam.jpg" + ERRORS=$((ERRORS + 1)) +else + echo " OK" +fi +echo "" + +# 4. Check dat gerefereerde bestanden in public/ bestaan +echo "[4/4] Public assets bestaan..." +MISSING=0 +for file in public/*; do + [ -f "$file" ] || continue +done + +# Zoek alle BASE_URL referenties en check of het bestand bestaat in public/ +# Matcht alleen bestandsnamen (letters, cijfers, punt, streepje, underscore) +REFS=$(grep -roh 'BASE_URL}[a-zA-Z0-9._-]*' src/ --include="*.jsx" --include="*.js" 2>/dev/null | sed 's/BASE_URL}//' | grep -v '^$' || true) +for ref in $REFS; do + if [ ! -f "public/$ref" ]; then + echo " FOUT: public/$ref bestaat niet (gerefereerd in code)" + MISSING=$((MISSING + 1)) + fi +done + +if [ "$MISSING" -eq 0 ]; then + echo " OK" +else + ERRORS=$((ERRORS + MISSING)) +fi +echo "" + +# Resultaat +echo "=== Resultaat ===" +if [ "$ERRORS" -gt 0 ]; then + echo "GEFAALD: $ERRORS fout(en) gevonden. Fix deze voor deploy." + exit 1 +else + echo "ALLES OK - klaar voor deploy." + exit 0 +fi diff --git a/public/20260211174-MoniqueDubbelman.jpg b/public/20260211174-MoniqueDubbelman.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bb12869bf0dbbca757baefd64787ed965db16034 GIT binary patch literal 19850 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 literal 0 HcmV?d00001 diff --git a/src/components/Hero.jsx b/src/components/Hero.jsx index d4244f6..00de26a 100644 --- a/src/components/Hero.jsx +++ b/src/components/Hero.jsx @@ -6,8 +6,9 @@ */ function Hero() { - // Aantal beschikbare plaatsen - later dynamisch te maken - const availableSpots = 8; + // Aantal beschikbare plaatsen + const totalSpots = 8; + const availableSpots = 7; return (

@@ -33,7 +34,7 @@ function Hero() { - {availableSpots} plekken - kleine groep, persoonlijke aandacht + {totalSpots}{' '}{availableSpots} plekken - kleine groep, persoonlijke aandacht diff --git a/src/components/Testimonials.jsx b/src/components/Testimonials.jsx index debf4c3..6ea59d2 100644 --- a/src/components/Testimonials.jsx +++ b/src/components/Testimonials.jsx @@ -31,6 +31,14 @@ function Testimonials() { url: "https://drawn.today/", initials: "JB" }, + { + quote: "Frank leert je op een laagdrempelige en humoristische manier hoe je zelf in een paar uur je eigen app bouwt, zonder technische kennis. Je leert dingen waarvan je niet wist dat je ze wilde leren", + name: "Monique Dubbelman", + role: "Community Manager", + avatar: `${import.meta.env.BASE_URL}20260211174-MoniqueDubbelman.jpg`, + url: "https://modub.nl/", + initials: "MD" + }, { quote: "Kan een niet-developer apps, websites, games maken? Het lijkt te mooi om waar te zijn, totdat Frank je met een glimlach en geduld gerust stelt. Voor mij was deze sessie zeer inspirerend en zorgde ervoor dat ik vier apps heb gemaakt in korte tijd.", name: "Ewout Wolff",