docs: record 'Oops' crash fix (intl + PWA middleware) verified via Playwright
This commit is contained in:
@@ -6,6 +6,56 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ✅ "OOPS" CRASH FIXED — 2026-06-13 (update 3) — verified in a real browser
|
||||||
|
|
||||||
|
The previous "RESOLVED" was wrong: login was verified with **curl only**, which cannot
|
||||||
|
execute client-side JS. In a real browser **every page (including `/login`) crashed on
|
||||||
|
hydration** with the "Oops! Something went wrong" boundary. Diagnosed by installing a
|
||||||
|
**Playwright browser probe** (`scripts/debug/dashboard-probe.mjs`) that logs in and
|
||||||
|
captures real console/network errors + a screenshot. Three root causes, all fixed in
|
||||||
|
commit **`4be9c4c`** (frontend rebuilt + redeployed):
|
||||||
|
|
||||||
|
8. **App-wide intl hydration crash ("Oops" on every page).** Root `app/layout.tsx`
|
||||||
|
renders global client components (`PwaInstallPrompt` → `useTranslations`, `Toaster`,
|
||||||
|
`Sonner`) as siblings of `{children}` inside `<Providers>`, but only each route-group
|
||||||
|
layout wrapped *its own* children in `NextIntlClientProvider`. Those global components
|
||||||
|
mounted with **no intl context** → "No intl context found" → hydration crash.
|
||||||
|
**Fix:** root layout is now `async` and wraps the body in `NextIntlClientProvider`
|
||||||
|
via `getMessages()`. Nested route-group providers remain valid (next-intl nests).
|
||||||
|
|
||||||
|
9. **PWA assets intercepted by auth middleware** (manifest.json syntax error + stale cache).
|
||||||
|
The middleware `matcher` did not exclude `/manifest.json` or `/sw.js`, so unauthenticated
|
||||||
|
browsers got **307 → /login (HTML)** for both → browser parsed HTML as JSON
|
||||||
|
(`manifest.json:1 Syntax error`) and an HTML/old service worker kept serving **stale
|
||||||
|
bundles** ("website hasn't changed after redeploy"). **Fix:** matcher now excludes
|
||||||
|
`manifest.json`, `sw.js`, `icons`, `offline`. Verified: `manifest.json` → 200
|
||||||
|
`application/json`, `sw.js` → 200 `application/javascript`.
|
||||||
|
|
||||||
|
10. **Service-worker stale cache.** Bumped `CACHE_NAME` `v1`→`v2` in `public/sw.js` so the
|
||||||
|
`activate` handler purges old cached bundles from clients that loaded the broken build.
|
||||||
|
|
||||||
|
**Browser verification (Playwright):** login `admin@test.de`/`test123` → lands on
|
||||||
|
`/dashboard`, **no `pageerror`**, dashboard renders. The "Oops" is gone.
|
||||||
|
|
||||||
|
### ⚠️ Known follow-up (non-fatal — dashboard renders via mock fallback)
|
||||||
|
Dashboard API endpoints are **mis-wired**: frontend calls `/api/backend/dashboard/stats`,
|
||||||
|
`/distributions/recent`, `/consent/check` → proxy rewrites to `/api/v1/dashboard/stats`
|
||||||
|
etc., but the backend has **no such routes** (real stats route is `/api/v1/clubs/me/stats`
|
||||||
|
in `ClubController`; `/dashboard` only exists in `PortalController`) → HTTP 500/404.
|
||||||
|
The dashboard falls back to `mockClubStats`/`mockRecentDistributions` so it still renders.
|
||||||
|
Frontend `ClubStats` type also differs from backend `ClubStatsResponse`. Fix = align
|
||||||
|
service paths + DTO shape, or add `/api/v1/dashboard/*` endpoints to the backend.
|
||||||
|
|
||||||
|
### 🔧 New tooling installed on the Fedora 44 workstation
|
||||||
|
- **Node.js 22.22.2 + npm 10.9.7** via `sudo dnf install -y nodejs npm` (was MISSING).
|
||||||
|
- **`@playwright/mcp` v0.0.76** (run via `npx @playwright/mcp@latest`) + Chromium
|
||||||
|
headless-shell (`npx playwright install chromium`). Wired into Roo `mcp_settings.json`
|
||||||
|
(server name `playwright`). Chromium launches fine on Fedora 44 despite the
|
||||||
|
"OS not officially supported" warning.
|
||||||
|
- **Probe:** `scripts/debug/dashboard-probe.mjs` — reusable client-side debugger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ✅ LOGIN WORKING — 2026-06-13 (update 2) — full auth flow verified
|
## ✅ LOGIN WORKING — 2026-06-13 (update 2) — full auth flow verified
|
||||||
|
|
||||||
After deploy, login showed a client-side "Oops! Something went wrong" error boundary.
|
After deploy, login showed a client-side "Oops! Something went wrong" error boundary.
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
# 🌿 CannaManage — Local Test Tour Guide
|
||||||
|
|
||||||
|
**URL:** http://192.168.188.119:3000
|
||||||
|
**Stack:** Next.js frontend → Spring Boot backend → PostgreSQL
|
||||||
|
**Deployed on:** TrueNAS.local (`/mnt/VM_SSD_Pool/cannamanage/`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Login Credentials
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Email | `admin@test.de` |
|
||||||
|
| Password | `test123` |
|
||||||
|
| Role | `ROLE_ADMIN` |
|
||||||
|
| Club | Grüner Daumen e.V. (Berlin) |
|
||||||
|
|
||||||
|
Go to **http://192.168.188.119:3000** → you'll land on the login page → enter the credentials above.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗺️ What's Been Built — Where to Look
|
||||||
|
|
||||||
|
### 1. Marketing / Public Pages (no login required)
|
||||||
|
|
||||||
|
These are the public-facing pages Sprint 6 just shipped:
|
||||||
|
|
||||||
|
| URL | What you'll see |
|
||||||
|
|-----|-----------------|
|
||||||
|
| `/pricing` | 3-tier pricing: Starter €19, Pro €49, Enterprise |
|
||||||
|
| `/impressum` | Legal imprint placeholder |
|
||||||
|
| `/datenschutz` | Privacy policy placeholder |
|
||||||
|
| `/agb` | Terms of service placeholder |
|
||||||
|
|
||||||
|
> These pages use a separate layout (no sidebar, no auth). The footer links to them from login page and portal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. The Portal (after login)
|
||||||
|
|
||||||
|
Once you log in as `admin@test.de`, you're in the **club management portal** for *Grüner Daumen e.V.*
|
||||||
|
|
||||||
|
#### 📊 Dashboard
|
||||||
|
The main overview. Should show club stats, recent activity, quick links.
|
||||||
|
|
||||||
|
#### 👥 Members
|
||||||
|
5 test members pre-loaded:
|
||||||
|
|
||||||
|
| Name | Email | Age | Membership # | Notes |
|
||||||
|
|------|-------|-----|--------------|-------|
|
||||||
|
| Max Mustermann | max@test.de | Adult | M-001 | Regular member |
|
||||||
|
| Anna Schmidt | anna@test.de | Adult | M-002 | Regular member |
|
||||||
|
| Jonas Weber | jonas@test.de | **Under 21** | M-003 | ⚠️ THC restricted |
|
||||||
|
| Lisa Meyer | lisa@test.de | Adult | M-004 | Regular member |
|
||||||
|
| Tom Fischer | tom@test.de | Adult | M-005 | Regular member |
|
||||||
|
|
||||||
|
> **Try:** Click on Jonas Weber — his under-21 flag means he can only receive CBD strains and has a lower monthly limit (30g vs 50g).
|
||||||
|
|
||||||
|
#### 🌱 Strains / Batches
|
||||||
|
3 test strains and 3 available batches:
|
||||||
|
|
||||||
|
| Strain | THC | CBD | Batch Code | Stock |
|
||||||
|
|--------|-----|-----|------------|-------|
|
||||||
|
| Northern Lights | 18.5% | 0.8% | BATCH-2024-001 | 500g |
|
||||||
|
| Amnesia Haze | 22.0% | 1.2% | BATCH-2024-002 | 300g |
|
||||||
|
| CBD Critical Mass | 5.0% | 12.0% | BATCH-2024-003 | 200g |
|
||||||
|
|
||||||
|
> **Try:** Attempt to distribute Amnesia Haze (22% THC) to Jonas Weber — the compliance engine should **block it** (under-21 THC limit enforcement).
|
||||||
|
|
||||||
|
#### 📦 Distributions / POS
|
||||||
|
Try recording a distribution:
|
||||||
|
1. Select a member (e.g., Max Mustermann)
|
||||||
|
2. Select a batch (e.g., Northern Lights)
|
||||||
|
3. Enter quantity in grams (e.g., 5g)
|
||||||
|
4. Submit
|
||||||
|
|
||||||
|
The backend compliance check runs: member active? batch available? THC ok? daily limit (25g)? monthly limit (50g)?
|
||||||
|
|
||||||
|
> **Stress test:** Try distributing 30g in one go to Max — it should be blocked (daily limit is 25g per CanG).
|
||||||
|
|
||||||
|
#### 👔 Staff Management (Sprint 3)
|
||||||
|
Staff invite flow. You can invite staff members via email and assign roles. Check the staff list page.
|
||||||
|
|
||||||
|
#### ⚙️ Club Settings
|
||||||
|
Club details: name, address, license number, max members cap (500). Edit fields and save.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Compliance Engine — What to Test
|
||||||
|
|
||||||
|
These are the core legal compliance rules from CanG (Cannabis Act):
|
||||||
|
|
||||||
|
| Test | How | Expected result |
|
||||||
|
|------|-----|-----------------|
|
||||||
|
| Under-21 + high THC strain | Distribute Amnesia Haze to Jonas Weber | ❌ Blocked |
|
||||||
|
| Under-21 + CBD strain | Distribute CBD Critical Mass to Jonas | ✅ Allowed (≤10% THC) |
|
||||||
|
| Over daily limit | Try 26g for Max in one distribution | ❌ Blocked (max 25g/day) |
|
||||||
|
| Over monthly limit | Try distributing 50g total to one member | ❌ Blocked (max 50g/month, 30g for under-21) |
|
||||||
|
| Valid distribution | 5g Northern Lights to Anna Schmidt | ✅ Approved |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📑 API (for nerds)
|
||||||
|
|
||||||
|
The Spring Boot API is exposed at **http://192.168.188.119:8080**
|
||||||
|
|
||||||
|
Swagger UI (if enabled): http://192.168.188.119:8080/swagger-ui/index.html
|
||||||
|
Health check: http://192.168.188.119:8080/actuator/health
|
||||||
|
|
||||||
|
### Get a JWT token:
|
||||||
|
```bash
|
||||||
|
curl -X POST http://192.168.188.119:8080/api/v1/auth/login \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"email":"admin@test.de","password":"test123"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Container Management
|
||||||
|
|
||||||
|
All running on TrueNAS at `/mnt/VM_SSD_Pool/cannamanage/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
ssh truenas.local "docker ps --filter name=cannamanage"
|
||||||
|
|
||||||
|
# View backend logs
|
||||||
|
ssh truenas.local "docker logs cannamanage-backend -f --tail=50"
|
||||||
|
|
||||||
|
# View frontend logs
|
||||||
|
ssh truenas.local "docker logs cannamanage-frontend -f --tail=50"
|
||||||
|
|
||||||
|
# Restart everything
|
||||||
|
ssh truenas.local "cd /mnt/VM_SSD_Pool/cannamanage && docker compose -f docker-compose.yml -f docker-compose.truenas.yml restart"
|
||||||
|
|
||||||
|
# Stop everything
|
||||||
|
ssh truenas.local "cd /mnt/VM_SSD_Pool/cannamanage && docker compose -f docker-compose.yml -f docker-compose.truenas.yml down"
|
||||||
|
|
||||||
|
# Pull latest + rebuild
|
||||||
|
ssh truenas.local "cd /mnt/VM_SSD_Pool/cannamanage && git pull && docker compose -f docker-compose.yml -f docker-compose.truenas.yml up -d --build"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌍 i18n — Language Toggle
|
||||||
|
|
||||||
|
The app has German and English translations. Look for a language switcher in the UI — should be visible on login page and in the portal header. Marketing pages (`/pricing` etc.) are also bilingual.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Known Limits of the Test Environment
|
||||||
|
|
||||||
|
- Legal page texts (`/impressum`, `/datenschutz`, `/agb`) are **placeholder templates** — not real legal text
|
||||||
|
- Pricing page is a **mockup** — Stripe is not integrated yet
|
||||||
|
- Email sending (staff invites) is not wired to a real mail server in dev
|
||||||
|
- The "seed data" dates are from 2024 — the compliance quota engine uses current month/year, so the pre-seeded distributions won't count against current monthly limits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Generated by Lumen — Homelab Mode — 2026-06-13*
|
||||||
Reference in New Issue
Block a user