"use client"
import { useCallback, useMemo, useState } from "react"
import { useRouter } from "next/navigation"
import { useTranslations } from "next-intl"
import { toast } from "sonner"
import {
AlertCircle,
ArrowLeft,
Check,
ChevronsUpDown,
Info,
Leaf,
ShieldAlert,
User,
} from "lucide-react"
import type { AvailableBatch, Member, QuotaStatus } from "@/types/api"
import { getMockQuota, mockAvailableBatches } from "@/data/mock/distributions"
import { mockMembers } from "@/data/mock/members"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Progress } from "@/components/ui/progress"
// Step indicator component
function StepIndicator({
currentStep,
steps,
}: {
currentStep: number
steps: string[]
}) {
return (
{steps.map((step, i) => (
{i < currentStep ? : i + 1}
{step}
{i < steps.length - 1 && (
)}
))}
)
}
// Quota bar with color coding
function QuotaBar({
label,
used,
limit,
unit,
}: {
label: string
used: number
limit: number
unit: string
}) {
const percent = (used / limit) * 100
const colorClass =
percent >= 80
? "bg-red-500"
: percent >= 50
? "bg-amber-500"
: "bg-green-500"
return (
{label}
{used} / {limit}
{unit}
)
}
export default function NewDistributionPage() {
const t = useTranslations("distributions")
const router = useRouter()
const [step, setStep] = useState(0)
const [selectedMember, setSelectedMember] = useState(null)
const [quota, setQuota] = useState(null)
const [selectedBatch, setSelectedBatch] = useState(
null
)
const [amount, setAmount] = useState("")
const [memberSearch, setMemberSearch] = useState("")
const [showMemberList, setShowMemberList] = useState(false)
const steps = [t("step1"), t("step2"), t("step3"), t("step4")]
// Filter active members for the combobox
const activeMembers = useMemo(
() => mockMembers.filter((m) => m.status === "ACTIVE"),
[]
)
const filteredMembers = useMemo(() => {
if (!memberSearch) return activeMembers
const search = memberSearch.toLowerCase()
return activeMembers.filter(
(m) =>
`${m.firstName} ${m.lastName}`.toLowerCase().includes(search) ||
m.memberNumber.toLowerCase().includes(search)
)
}, [memberSearch, activeMembers])
// Check if member is blocked
const isMemberBlocked = useCallback((member: Member) => {
return member.status === "SUSPENDED" || member.status === "EXPELLED"
}, [])
// Check if member is under 21
const isUnder21 = useCallback((member: Member) => {
const birthDate = new Date(member.dateOfBirth)
const today = new Date()
const age = today.getFullYear() - birthDate.getFullYear()
const monthDiff = today.getMonth() - birthDate.getMonth()
if (
monthDiff < 0 ||
(monthDiff === 0 && today.getDate() < birthDate.getDate())
) {
return age - 1 < 21
}
return age < 21
}, [])
// Handle member selection
const handleSelectMember = useCallback(
(member: Member) => {
setSelectedMember(member)
setShowMemberList(false)
if (isMemberBlocked(member)) {
return // Stay on step 0, show error
}
// Load quota
const q = getMockQuota(member.id)
setQuota(q)
setStep(1)
},
[isMemberBlocked]
)
// Validation for amount
const amountNum = parseFloat(amount) || 0
const validationErrors = useMemo(() => {
const errors: string[] = []
if (!selectedBatch || amountNum <= 0) return errors
if (amountNum > selectedBatch.availableGrams) {
errors.push(t("exceedsBatch"))
}
if (quota && amountNum > quota.dailyLimitGrams - quota.dailyUsedGrams) {
errors.push(t("exceedsDaily", { limit: quota.dailyLimitGrams }))
}
if (quota && amountNum > quota.monthlyLimitGrams - quota.monthlyUsedGrams) {
errors.push(t("exceedsMonthly", { limit: quota.monthlyLimitGrams }))
}
return errors
}, [amountNum, selectedBatch, quota, t])
const canProceedToConfirm =
selectedBatch && amountNum > 0 && validationErrors.length === 0
// Confirm distribution
const handleConfirm = () => {
// Mock: log + toast + redirect
console.log("Distribution recorded:", {
memberId: selectedMember?.id,
memberName: `${selectedMember?.firstName} ${selectedMember?.lastName}`,
batchId: selectedBatch?.id,
strainName: selectedBatch?.strainName,
amountGrams: amountNum,
recordedBy: "Maria Schulz",
recordedAt: new Date().toISOString(),
status: "COMPLETED",
})
toast.success(t("success"))
router.push("/distributions")
}
return (
{/* Header */}
{t("newDistribution")}
{/* Step indicator */}
{/* Step 1: Member Selection */}
{step === 0 && (
{t("step1")}
{t("selectMember")}
{/* Member search combobox */}
setShowMemberList(!showMemberList)}
>
{selectedMember ? (
{selectedMember.firstName} {selectedMember.lastName} (
{selectedMember.memberNumber})
) : (
{t("selectMember")}
)}
{showMemberList && (
Kein Mitglied gefunden.
{filteredMembers.slice(0, 8).map((member) => (
handleSelectMember(member)}
className="cursor-pointer"
>
{member.firstName} {member.lastName}
{member.memberNumber}
{member.status !== "ACTIVE" && (
{member.status}
)}
))}
)}
{/* Selected member card */}
{selectedMember && (
{selectedMember.firstName} {selectedMember.lastName}
{selectedMember.memberNumber} · Mitglied seit{" "}
{new Date(selectedMember.joinedAt).toLocaleDateString(
"de-DE"
)}
{selectedMember.status === "ACTIVE"
? "Aktiv"
: selectedMember.status}
{/* Blocked member warning */}
{isMemberBlocked(selectedMember) && (
)}
{/* Under 21 info */}
{!isMemberBlocked(selectedMember) &&
isUnder21(selectedMember) && (
)}
)}
{/* If member is active and selected, show "Next" button */}
{selectedMember &&
!isMemberBlocked(selectedMember) &&
step === 0 && (
)}
)}
{/* Step 2: Quota Check */}
{step === 1 && quota && (
{t("step2")}
{selectedMember?.firstName} {selectedMember?.lastName}
{quota.isUnder21 && " (unter 21)"}
{quota.isUnder21 && (
)}
)}
{/* Step 3: Batch Selection & Amount */}
{step === 2 && (
{t("step3")}
{/* Batch selection */}
{mockAvailableBatches.map((batch) => (
setSelectedBatch(batch)}
className={`flex cursor-pointer items-center justify-between rounded-lg border p-3 transition-colors ${
selectedBatch?.id === batch.id
? "border-primary bg-primary/5"
: "border-border hover:bg-muted/50"
}`}
>
{batch.strainName}
THC: {batch.thcPercent}%
{batch.availableGrams}g {t("available")}
))}
{/* Amount input */}
{selectedBatch && (
setAmount(e.target.value)}
placeholder="0.0"
className="font-mono text-lg"
/>
{/* Validation errors */}
{validationErrors.length > 0 && (
{validationErrors.map((error) => (
))}
)}
{/* Show remaining after this distribution */}
{amountNum > 0 && validationErrors.length === 0 && quota && (
Tagesrest danach:{" "}
{(
quota.dailyLimitGrams -
quota.dailyUsedGrams -
amountNum
).toFixed(1)}
g
Monatsrest danach:{" "}
{(
quota.monthlyLimitGrams -
quota.monthlyUsedGrams -
amountNum
).toFixed(1)}
g
)}
)}
)}
{/* Step 4: Confirmation */}
{step === 3 && selectedMember && selectedBatch && (
{t("step4")}
{t("summary")}
{/* Summary card */}
{t("member")}
{selectedMember.firstName} {selectedMember.lastName}
{t("strain")}
{selectedBatch.strainName}
{t("amount")}
{amountNum}g
{t("staff")}
Maria Schulz
)}
)
}