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:
@@ -0,0 +1,88 @@
|
||||
export interface ClubStats {
|
||||
totalMembers: number
|
||||
activeMembers: number
|
||||
distributionsToday: number
|
||||
gramsDistributedToday: number
|
||||
totalStockGrams: number
|
||||
monthlyQuotaUsagePercent: number
|
||||
}
|
||||
|
||||
export interface Distribution {
|
||||
id: string
|
||||
memberId: string
|
||||
memberName: string
|
||||
strainName: string
|
||||
amountGrams: number
|
||||
recordedBy: string
|
||||
recordedAt: string // ISO 8601
|
||||
}
|
||||
|
||||
export interface DistributionRecord {
|
||||
id: string
|
||||
memberId: string
|
||||
memberName: string
|
||||
batchId: string
|
||||
strainName: string
|
||||
amountGrams: number
|
||||
recordedBy: string
|
||||
recordedAt: string // ISO 8601
|
||||
status: "COMPLETED" // immutable
|
||||
}
|
||||
|
||||
export interface QuotaStatus {
|
||||
dailyUsedGrams: number
|
||||
dailyLimitGrams: number // always 25
|
||||
monthlyUsedGrams: number
|
||||
monthlyLimitGrams: number // 50 for ≥21, 30 for <21
|
||||
isUnder21: boolean
|
||||
}
|
||||
|
||||
export interface AvailableBatch {
|
||||
id: string
|
||||
strainName: string
|
||||
availableGrams: number
|
||||
thcPercent: number
|
||||
status: "AVAILABLE"
|
||||
}
|
||||
|
||||
export interface BatchSummary {
|
||||
id: string
|
||||
strainName: string
|
||||
availableGrams: number
|
||||
status: "AVAILABLE" | "RECALLED" | "DEPLETED"
|
||||
}
|
||||
|
||||
export interface Batch {
|
||||
id: string
|
||||
strainName: string
|
||||
thcPercent: number
|
||||
cbdPercent: number
|
||||
totalGrams: number
|
||||
availableGrams: number
|
||||
status: "AVAILABLE" | "RECALLED" | "DEPLETED"
|
||||
supplier: string
|
||||
harvestDate: string // ISO 8601
|
||||
receivedAt: string // ISO 8601
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export interface Strain {
|
||||
id: string
|
||||
name: string
|
||||
defaultThcPercent: number
|
||||
defaultCbdPercent: number
|
||||
}
|
||||
|
||||
export interface Member {
|
||||
id: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
dateOfBirth: string // ISO 8601
|
||||
phone?: string
|
||||
memberNumber: string
|
||||
status: "ACTIVE" | "SUSPENDED" | "EXPELLED"
|
||||
joinedAt: string // ISO 8601
|
||||
monthlyQuotaUsedPercent: number
|
||||
notes?: string
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import type { DefaultSession } from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
interface Session {
|
||||
user: {
|
||||
role: string
|
||||
clubId: string
|
||||
} & DefaultSession["user"]
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface User {
|
||||
role: string
|
||||
clubId: string
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
expiresAt: number
|
||||
}
|
||||
}
|
||||
|
||||
declare module "next-auth/jwt" {
|
||||
interface JWT {
|
||||
role?: string
|
||||
clubId?: string
|
||||
accessToken?: string
|
||||
refreshToken?: string
|
||||
expiresAt?: number
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user