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
+115
View File
@@ -0,0 +1,115 @@
import type { LucideIcon, icons } from "lucide-react"
import type { ComponentType, SVGAttributes } from "react"
export type LayoutType = "vertical" | "horizontal"
export type ModeType = "light" | "dark" | "system"
export type OrientationType = "vertical" | "horizontal"
export type DirectionType = "ltr" | "rtl"
export type LocaleType = "de" | "en"
export type ThemeType = string
export type RadiusType = number
export type SettingsType = {
theme: ThemeType
mode: ModeType
radius: RadiusType
layout: LayoutType
locale: LocaleType
}
export interface IconProps extends SVGAttributes<SVGElement> {
children?: never
color?: string
}
export type IconType = ComponentType<IconProps> | LucideIcon
export type DynamicIconNameType = keyof typeof icons
export interface UserType {
id: string
firstName: string
lastName: string
name: string
password: string
username: string
role: string
avatar: string
background: string
status: string
phoneNumber: string
email: string
state: string
country: string
address: string
zipCode: string
language: string
timeZone: string
currency: string
organization: string
twoFactorAuth: boolean
loginAlerts: boolean
accountReoveryOption?: "email" | "sms" | "codes"
connections: number
followers: number
}
export interface NavigationType {
title: string
items: NavigationRootItem[]
}
export type NavigationRootItem =
| NavigationRootItemWithHrefType
| NavigationRootItemWithItemsType
export interface NavigationRootItemBasicType {
title: string
label?: string
iconName: DynamicIconNameType
}
export interface NavigationRootItemWithHrefType
extends NavigationRootItemBasicType {
href: string
items?: never
}
export interface NavigationRootItemWithItemsType
extends NavigationRootItemBasicType {
items: (
| NavigationNestedItemWithHrefType
| NavigationNestedItemWithItemsType
)[]
href?: never
}
export interface NavigationNestedItemBasicType {
title: string
label?: string
}
export interface NavigationNestedItemWithHrefType
extends NavigationNestedItemBasicType {
href: string
items?: never
}
export interface NavigationNestedItemWithItemsType
extends NavigationNestedItemBasicType {
items: (
| NavigationNestedItemWithHrefType
| NavigationNestedItemWithItemsType
)[]
href?: never
}
export type NavigationNestedItem =
| NavigationNestedItemWithHrefType
| NavigationNestedItemWithItemsType