feat: testimonial Monique Dubbelman, plekken 8→7, preflight checks

- 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 <noreply@anthropic.com>
This commit is contained in:
Frank Meeuwsen 2026-02-11 18:06:12 +01:00
parent 42c367f8ef
commit fe08b3c519
6 changed files with 129 additions and 5 deletions

View file

@ -15,6 +15,7 @@ npm run dev # Vite dev server on port 5173
npm run build # Production build to dist/ npm run build # Production build to dist/
npm run preview # Preview production build npm run preview # Preview production build
npm run lint # ESLint npm run lint # ESLint
./preflight.sh # Pre-deploy checks (lint + build + asset path validation)
``` ```
## Architecture ## Architecture
@ -58,10 +59,10 @@ Reusable component classes defined in `src/index.css` using `@layer components`:
## Deployment ## Deployment
```bash ```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` - **Server:** Hetzner (37.27.183.46) via SSH alias `coolify-deploy`
- **Container:** `wordpress-d0wko4gskokosssogcw8040g` - **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 - 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) - StickyBar has separate mobile (bottom) and desktop (top) layouts triggered by scroll position (600px threshold)
- Anchor links use `#inschrijven` for CTA scroll targets - 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

View file

@ -11,6 +11,9 @@ CONTAINER="wordpress-d0wko4gskokosssogcw8040g"
REMOTE_PATH="/var/www/html/workshopclaudecode" REMOTE_PATH="/var/www/html/workshopclaudecode"
TMP_PATH="/tmp/workshopclaudecode" TMP_PATH="/tmp/workshopclaudecode"
echo "0/4 - Preflight checks..."
./preflight.sh
echo ""
echo "1/4 - Building..." echo "1/4 - Building..."
npm run build --silent npm run build --silent

85
preflight.sh Executable file
View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -6,8 +6,9 @@
*/ */
function Hero() { function Hero() {
// Aantal beschikbare plaatsen - later dynamisch te maken // Aantal beschikbare plaatsen
const availableSpots = 8; const totalSpots = 8;
const availableSpots = 7;
return ( return (
<section className="section relative overflow-hidden"> <section className="section relative overflow-hidden">
@ -33,7 +34,7 @@ function Hero() {
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-coral-500 opacity-75"></span> <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-coral-500 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-coral-500"></span> <span className="relative inline-flex rounded-full h-2 w-2 bg-coral-500"></span>
</span> </span>
{availableSpots} plekken - kleine groep, persoonlijke aandacht <span className="line-through opacity-60">{totalSpots}</span>{' '}{availableSpots} plekken - kleine groep, persoonlijke aandacht
</span> </span>
</div> </div>

View file

@ -31,6 +31,14 @@ function Testimonials() {
url: "https://drawn.today/", url: "https://drawn.today/",
initials: "JB" 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.", 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", name: "Ewout Wolff",