feat: Sprint 4 complete — frontend MVP (admin dashboard + member portal)

Shadboard starter-kit (Next.js 15 + React 19 + shadcn/ui + Tailwind 4)

Sprint 4.a — Admin Dashboard:
- Auth: NextAuth.js v5, login page, middleware, token rotation
- Dashboard: KPI cards, Recharts stock chart, quick actions
- Members: TanStack Table (search/sort/paginate), add/edit forms
- Distributions: multi-step form, real-time quota check, history
- Stock: batch management, recall dialog, bar chart
- Reports: monthly/member-list/recall, PDF/CSV download, preview

Sprint 4.b — Member Portal:
- Separate route group with top-nav layout (mobile-first)
- Quota dashboard with radial SVG progress indicators
- Distribution history with month filter
- Profile/settings with password change

Cross-cutting:
- i18n: German (default) + English via next-intl
- Dark + light mode (next-themes, user-togglable)
- Playwright E2E tests (6/6 green)
- Docker multi-stage build (node:22-alpine)
- API proxy via Next.js rewrites

Tech: Next.js 15.2.8, React 19, Tailwind 4, NextAuth v5,
TanStack Table, Recharts, Zod, React Hook Form, Playwright
This commit is contained in:
Patrick Plate
2026-06-12 17:18:38 +02:00
parent a1d4ba44e3
commit fe6e96dd3f
143 changed files with 23568 additions and 0 deletions
@@ -0,0 +1,30 @@
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 175 175" width="175" height="175">
<title>male-01</title>
<style>
.s0 { fill: #ffffff }
.s1 { fill: #000000 }
</style>
<g id="Introduction">
<g id="variations">
<g id="images">
<g id="a-person/bust">
<g id="body/Tee-1">
</g>
<g id="head/Bun-2">
<path id="🎨-Background" fill-rule="evenodd" class="s0" d="m127.1 88c-0.5-6.5 0.3-13.3-2.2-19.5-4.4-10.2-16.6-13.6-26.1-17.5-3.8-1.2-16.6-1.3-21.1-2.8-10.1-3.4-12.5-7.4-20.5-1-1.3 1.1-2.2 2.3-2.8 3.9v0.1c-0.1 0.6 0.1 1.3 0.5 1.8-3.5 3.6-5 8.9-3.2 13.6 5.8 12.3 5.5 19 3.7 32.2-2.5 12.3 3.1 16.8 12.4 23.6-0.4 0.7-0.5 1.7 0 2.3 10.7 15.1 30.9 22.2 48.2 14.6 7-3.1 10.1-10.6 11.9-17.4 2.1-7.5 2.8-15.5 1.1-23.2-0.7-3.5-1.6-7.1-1.9-10.7z"/>
<path id="🖍-Ink" fill-rule="evenodd" class="s1" d="m65.4 16c1.3-0.2 2.2 0.6 2.7 1.8q0.3 0.6 0.5 1.2c3.3-1.6 7.9-1.2 10 2 1.4 2.3 0.6 5.1 0 7.5l-0.4 1.6c0.7 0.5 1.3 1.3 1.2 2.1v0.1l-0.1 2.2q0.7-0.7 1.3-1.5 0.4-0.8 0.8-1.5c1.8-2.7 5.9-0.3 4.4 2.6q-0.4 0.8-1 1.6-0.5 1.1-0.9 2.3c0.7 0.3 1.2 1 1.3 2 0.1 2.4-1.1 4.4-3.1 5.7-0.6 0.4-1.2 0.4-1.8 0.3q-0.1 0.2-0.3 0.4 1.2 1 2.2 2.2 2.8-0.4 5.6-0.4c2.3-1.1 5-1.1 7.5-0.7 1.5 0.3 3.1 0.8 4.6 1.2 1.7 0.4 3.3 0.4 5 0.7 2.1 0.3 3.9 1.3 5.1 2.8 1.3 0.1 2.6 0.6 3.7 1.3q3.9 2.5 7.7 5c1 0.6 1.4 1.6 1.2 2.7v0.1l-0.1 0.5 0.2 0.3c0.8 1.1 1.6 2.2 2.1 3.5q1.5 2 2.6 4.2c1 2 1.8 4.1 2.4 6.2q0.2 0.8 0.4 1.7c0.1 0.8-0.1 1.4-0.2 2.1-0.1 1-0.2 1.9-0.3 2.9-0.1 0.6-0.7 0.9-1.2 0.9q0.3 2.5 0.5 4.9 0.3 3.4 0.4 6.9c0.7 0.9 1.9 1.6 2.9 1.8 1.2 0.4 2.6 0 3.8-0.4 0.7-0.3 1.3 0.7 1 1.3-1.2 2-3.7 2-5.5 1.1-0.8-0.4-1.5-0.9-2-1.6q0 1 0.1 2v0.6c0.4 7.1 1.2 14.4-0.1 21.4-2.3 12.2-12.4 21.4-24.7 22.2-6.4 0.3-11.8-1.6-14.5-2.6-2.6-1.1-1.6-3.3 0.3-2.6 3.2 1 9 2.6 14.3 1.9 12.3-1.6 22.8-7.8 21.7-34.4-0.1-3.8-0.6-7.6-0.7-11.3-0.1-3.7 0-7.4-0.2-11.1l-0.1-1.3q0-2.2-0.2-4.4c-0.1-0.6-0.2-1.2-0.4-1.7q-0.7-2.3-1.7-4.4-0.3 0.2-0.6 0.2c-1.4 0.3-2.8 0.2-4.2-0.4q-0.9-0.4-1.7-1.1-0.4-0.4-0.5-0.7c-0.7 0-1.4-0.2-1.8-0.6-0.4-0.6-0.5-1.1-0.3-1.7h-0.1c-0.5 1.4-1.6 2.7-2.9 3.5-1.9 1.4-4.3 1.8-6.6 1.8-2.6 0.1-5.3-0.3-7.8-0.7q-1-0.2-1.9-0.4-0.1 0-0.1 0.1c-2.2 2-5.2 2.6-8 1.4-1.6-0.7-2.9-1.8-4.1-3.1q0 1-0.1 2v0.3c-0.1 1.2-0.3 2.5-1.2 3.4-1.5 1.4-3.7 0.8-5.4-0.1q0 0.1 0 0.1 0.7 2.4 1.4 4.7l0.1 0.4c0.3 1.1 0.7 2.2 0.8 3.4 0.2 2.2-1.3 4.4-3.8 3.7-1.3-0.4-2.3-1.5-3.3-2.4l-0.7-0.5c1.7 5.3 2.8 10.8 2.9 16.4 0 0.3-0.2 0.5-0.5 0.6-0.7 0-1.4-0.1-2.1-0.4q0 0-0.1 0c-1.2-0.2-2.2-0.6-3.2-1.2q0.4 1 0.3 2.2c0 0.2-0.3 0.2-0.4 0-0.5-2.7-3.7-4.7-6.4-4.2-2.9 0.6-5 3.1-5.5 5.9-0.6 3.1 0.5 6.6 2.5 9 0.9 1.1 2.1 2 3.5 2.2 0.7 0.1 1.4 0.1 2-0.1q0.4-0.2 0.8-0.3c0.4-0.2 0.8-0.2 1.2-0.4 0.8-0.3 1.6 0.4 1.2 1.2-0.4 0.8-0.7 1.3-1.3 1.6 0.5 0.6 0.9 1.4 1.1 1.6 0.8 1.1 1.6 2.2 2.6 3.2q1.4 1.5 3 2.8l0.5 0.4c1.1 0.8 2 1.6 2.7 2.8 0.6 1-0.1 2.3-1.2 2.2-3.5 0-6-3-7.8-5.7q-0.4-0.5-0.7-1.2-0.1 0.2-0.3 0.4l0.2 0.5c0.6 1.6-1.9 2.6-2.6 1.1q-0.8-1.5-1.8-3c-0.5 0-0.8-0.3-1.1-0.6-0.5-0.8-1.1-1.6-1.7-2.5q-0.4-0.4-0.9-0.8-0.1 0.2-0.2 0.3-0.6 1.3-1.5 2.4l-0.1 0.2c-1.1 1.4-2.6 2.9-4.4 3.2-0.6 0.1-1.1-0.6-0.6-1 1.2-1.2 2.8-2 3.9-3.2q0.9-1 1.6-2.1l0.1-0.1q0.1-0.2 0.2-0.4-0.8-0.6-1.6-1.1c-1.4 1.2-3.3 1.8-4.9 2.8l-0.2 0.1c-1.5 1-3-1.1-1.8-2.3 1.3-1.3 2.3-2.9 3.5-4.3-0.6-1-0.8-2.2-0.9-3.4q-0.1-0.5-0.1-1c-0.8 0-1.6-0.2-2.3-0.7l-0.3-0.2c-0.8 0.3-1.7 0.4-2.6 0.4-2-0.1-2.8-2.4-1.6-3.8 1.3-1.5 3.6-1.8 5.3-1.1q0.1-0.1 0.2-0.1-0.1-0.6 0-1.1v-0.2q-0.6-0.9-0.7-2-0.2-0.8 0-1.7c0.1-0.1 0.2-0.3 0.2-0.5q-0.2-0.6-0.2-1.2-0.1 0-0.1-0.1c-0.9-1.2-0.7-2.7 0.1-3.7q0-0.1 0-0.2-0.1-0.2-0.1-0.4c-0.8-0.8-1.4-1.8-1.3-3 0-0.7 0.3-1.4 0.8-1.9-0.1-0.2-0.1-0.5 0-0.7-0.7-1.9-0.6-4.2-0.1-6.4q-0.2-0.4-0.2-1c0.1-1.6 1.5-3.1 3.1-3q0.3 0 0.5 0 0.1-0.7 0.4-1.3-0.1-0.1-0.2-0.2-1.6 0.9-3.3 1.3c-0.9 0.2-1.8-0.8-1.2-1.6q0.3-0.5 0.8-1-0.6 0-1.3 0.1c-0.7 0.1-1.6-0.4-2.1-1.1q-0.9 0.9-1.8 1.8c-1.6 1.5-4 0.4-4.2-1.6q-0.7-0.2-1.3-0.6c-2.5 1-5.1 1.6-7.9 1.7-1.9 0.1-3.1-2.2-2.1-3.8q0.9-1.6 2.2-2.9-2 0.7-4.2 1c-0.8 0.1-1.9-0.6-2.2-1.3-1.2-2.7-1.1-5.6 0.3-8.3q0.1-0.1 0.2-0.3 0.2-0.5 0.4-1c-1.6-1.8-1.8-4.4-0.9-6.6-0.3-0.5-0.5-1.3-0.2-1.9 0.7-1.9 1.7-3.6 3-5q-0.2-0.4-0.4-0.8c-0.7-2.2 0.1-4.8 1.8-6.3 1.1-1 2.3-1.6 3.6-1.9q0.8-1.5 2.1-2.6 0-0.1 0.1-0.2c1.3-2.4 3.6-3.7 6.1-4 0.1-1 0.7-1.9 1.6-2.1q0.4-1.3 1.2-2.3c0.8-1.2 2.1-1.7 3.4-0.9q1.1 0.6 2 1.3 0.5-0.2 1-0.2h0.1l2.1-0.1q1.3-0.6 2.6-1.2 0.8-0.7 1.7-1.1 1.1-0.4 2.2-0.5 0.9-0.3 1.9-0.5c1.4-0.3 2.9-0.5 4.4-0.5q0.1-0.6 0.1-1.2v-0.2c-0.1-1.3 1-2.3 2.2-2.5zm-1.5 102.3q-0.3 0.1-0.6 0.1 0.1 0.3 0.2 0.5 0.2 0.2 0.5 0.3-0.1-0.5-0.1-0.9zm-10.9-3.2l-0.1 0.3q0 0-0.1 0.1 0.5 0 0.9 0 0 0-0.1-0.1-0.3-0.1-0.6-0.3zm5.9-11.8c1.9 0.1 3.5 0.4 5.2 1.2l0.1 0.1c1.5 0.7 4 2.1 3.8 4.1-0.1 0.5-0.5 1.1-1.1 1.1-1.8-0.1-2.8-1.9-4-3-1.2-1.1-2.7-2.3-4.1-3-0.2-0.1-0.1-0.5 0.1-0.5zm-8.5-26c-0.4 0-0.3 0.4-0.1 0.1q0.1-0.1 0.1-0.1zm24.2-29.1c-4.3 0.1-8.5 2.6-11.7 5.2-2.8 2.2-5.2 4.8-7.3 7.6q-0.8 2.1-1.4 4.3c-0.2 0.9-0.4 1.9-0.6 2.9l-0.1 0.3q-0.2 0.5-0.4 1c0.4 0.4 0.9 0.7 1.5 0.8q0.2 0.1 0.4 0.2c0.5-0.7 0.8-1.5 1.1-2.3q1.2-2.1 2.7-4.1c2.8-3.6 5.8-6.9 9.4-9.6 2.7-2.1 5.8-3.7 9.1-4.8q-1.2-0.9-2.7-1.5z"/>
</g>
<g id="face/Smiling">
<path id="🖍-Ink" fill-rule="evenodd" class="s1" d="m107.7 126.7c-1.4-0.5-2.7-1.5-3.6-2.7-0.3-0.5-0.4-0.9-0.4-1.3 0.1-0.5 0.4-0.9 0.8-1.2 0.3-0.3 0.8-0.4 1.3-0.4 0.4 0.1 0.8 0.3 1.1 0.8q0.9 1.2 2.1 1.8c0.8 0.4 1.7 0.6 2.6 0.5 1.7-0.2 2.9-1.2 4.2-2.5q0.2-0.2 0.5-0.5 0.1-0.2 0.4-0.3 0.2 0 0.5 0 0.3 0.1 0.5 0.4 0.1 0.2 0.1 0.5c-0.1 1.3-0.8 2.5-1.8 3.4-1.1 1.1-2.5 1.8-3.9 2-1.5 0.3-3 0.1-4.4-0.5zm5.9-21.5c8.1 1.4 3.9 15.4-6.4 7.7-1.3-1 0.2-3.2 1.6-2.3 1.4 1.1 4.3 3.5 6.3 1.4 1.7-2.4-0.7-4.7-2.3-5.7-0.4-0.3-0.1-1.3 0.8-1.1zm-19.4-7.8c0.2-2.2 3.6-2.7 3.9-0.5 0.3 1.9-0.1 3.7-0.9 5.5-0.9 1.6-3 0.8-3.1-0.7-0.2-2 0-2.9 0.1-4.3zm25.4-1c0.5-0.6 1.1-1 2-0.8 2.5 0.8 1.7 4.3 0.8 6.1-0.4 0.9-2 1.2-2.7 0.3-1.1-1.3-1.7-4-0.2-5.4q0-0.1 0.1-0.2zm-22.5-8.6h0.4c1.2-0.3 2.8-0.5 3.6 0.5 0.5 0.5 0.7 1.5 0 2-1 0.9-2.3 0.6-3.6 0.6q-1.6 0.1-3.1 0.6c-2.3 0.6-4.1 1.8-6.1 3.1-0.2 0.2-0.5-0.1-0.4-0.4 1.2-2.2 3.3-4 5.5-5.1 1.2-0.6 2.4-1.1 3.7-1.3zm20.2-1l0.3 0.1c3.3 0.7 7.6 2.1 9.2 5.3 0 0.5 0 0.6-0.1 0.6v0.1c0 0-0.1 0.1-0.3 0.2q0-0.1-0.1-0.1c-0.7-0.2-1.3-0.4-1.9-0.7l-0.6-0.3c-0.6-0.3-1.3-0.7-2.1-0.9q-2.4-0.8-4.8-1.1-0.6 0-1-0.3-0.4-0.3-0.5-0.9c-0.2-0.4-0.1-0.8 0.1-1.2q0.3-0.5 0.8-0.7 0.5-0.2 1-0.1z"/>
</g>
<g id="facial-hair/Full-3">
<path id="🖍-Ink" fill-rule="evenodd" class="s1" d="m130.3 153.1c-13.5 12.2-42.8 7.5-54.5-5.7-9-6-8.1-19.5-7.2-29.2 0.6-4.9 0.5-10.2 2.7-14.6-0.2-1.1 1.4-2 1.9-0.8 2.4 5.4 4.9 10.9 8.7 15.5 3.6 5 14.5 11.7 16.7 2.1 2.7-6 13.2-7.1 19-6.2 7.4 1.9 3.8 9.7 11.2 8.9q0.1 0 0.3 0c0.4-0.6 0.7-1.4 1.1-2.1q-0.1-0.3-0.1-0.5c0.4-0.6 1.1-1 1.9-0.6q0.1 0.1 0.1 0.2c2.3 10.7 2.9 22.9-1.8 33zm-10.5-25.4c-0.1-1.9-1.7-5.7-4-6.6-4.7-1.1-12.9 0.7-10.9 7.1 0.5 1.2 1.9 2.7 3.3 2.2 1.1-0.4 1.4-2.1 2.1-2.9 2.8-2.8 5 0.7 7 2.5 1.5 1 2.6-0.9 2.5-2.3z"/>
</g>
<g id="accessories/Glasses-3">
<path id="🖍-Ink" fill-rule="evenodd" class="s1" d="m137.7 93.1c-0.9 0.5-1.4 1.4-1.2 2.4 0.2 0.7-0.1 1.3-0.7 1.6-0.1 4.8-0.9 10.1-4.6 13.6-8.6 5.7-17-1.2-15.2-12.1q-0.2-0.1-0.4-0.4-0.1-0.1-0.1-0.2c-0.2-0.2-1.6-0.7-3.4-0.4-1.8 0.2-2.3 1-3.3 1.9-0.1 0.3-0.3 0.5-0.7 0.6-0.7 4-1.5 8.5-4.5 11.5-7.7 7.2-21 1.2-20.5-9.7-2.7-0.5-0.5-3.6-4.7-4.3-0.8-0.3-1.5-0.8-2-1.4-6.3-0.6-12.6-0.3-18.7 1.9-2.4 1-5.6 1.4-4.2 5-0.1 1-1.3 1.6-2 0.9-4.3-7.6 4-8.7 10.1-9.7 3.2-0.7 6.5-1 9.8-0.9 1.6 0.1 3.6 0 4.9 0.4 1.4-4.3 30.7-5.3 32.4 1.8 2.2-0.1 3.9 0.5 5.8-0.1 0.6-5.2 14.5-7.1 20.6-5.7q0.6 0 1.2 0.2 0.3 0 0.5 0.2 0.3 0 0.5 0c1.4 0.3 1.6 2.4 0.4 2.9zm-31.2 6c-2.4-2.1-3.8-4.1-8.5-4.4-3.7-0.1-7.7-0.2-11 1.9-2 1.1-1.6 3.9-3.2 5.1 1.8 13.1 18.7 16.5 21.8 2 0.4-1.5 0.8-3.2 0.9-4.6zm27.2-2.4c-1.8-1.9-0.7-4.4-4.9-4.1-12 0.8-8.2 7.8-11.7 6.8 0.1 4.7 1.8 10.6 7 11.1 2.5 0.2 5.1-0.4 6.5-2.3 2.4-3.3 3.1-7.6 3.1-11.5z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,3 @@
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path d="M160,100 A60,60 0 1,1 100,40" fill="none" stroke="currentColor" stroke-width="40"/>
</svg>

After

Width:  |  Height:  |  Size: 190 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB