Merge: fix conflicts, add auth system with PostgreSQL, seed data, and 4 demo users

This commit is contained in:
2026-06-18 13:26:27 +02:00
8 changed files with 547 additions and 122 deletions
@@ -51,15 +51,16 @@ export function LeadStatusChart({ data }: LeadStatusChartProps) {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
className="h-full"
>
<Card>
<Card className="h-full">
<CardHeader>
<CardTitle>Lead Status</CardTitle>
</CardHeader>
<CardContent>
<div className="flex flex-col items-center">
<CardContent className="flex flex-1 flex-col">
<div className="flex flex-1 flex-col items-center justify-center">
{/* Donut */}
<svg width="280" height="280" viewBox="0 0 320 320" className="overflow-visible">
<svg width="100%" height="100%" viewBox="0 0 320 320" className="max-h-full overflow-visible" style={{ maxWidth: "280px" }}>
<defs>
{segs.map((s, i) => (
<radialGradient key={i} id={`chart-grad-${i}`} cx="50%" cy="50%" r="50%">
@@ -22,8 +22,8 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
const [hovered, setHovered] = useState<number | null>(null)
const width = 880
const height = 380
const padding = { top: 28, right: 24, bottom: 44, left: 44 }
const height = 620
const padding = { top: 128, right: 24, bottom: 48, left: 44 }
const chartW = width - padding.left - padding.right
const chartH = height - padding.top - padding.bottom
@@ -55,8 +55,9 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.3 }}
className="h-full"
>
<Card>
<Card className="h-full">
<CardHeader>
<div className="flex items-start justify-between">
<div>
@@ -77,12 +78,12 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
</div>
</div>
</CardHeader>
<CardContent>
<div className="relative">
<CardContent className="flex flex-1 flex-col">
<div className="relative flex-1">
<svg
viewBox={`0 0 ${width} ${height}`}
width="100%"
height="auto"
height="100%"
className="block overflow-visible"
>
<defs>
+1 -1
View File
@@ -32,7 +32,7 @@ export function StatCard({ title, value, icon: Icon, variant = "default", descri
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
>
<Card className="group hover:shadow-md transition-all duration-200">
<Card className="group hover:shadow-md transition-all duration-200 h-full">
<CardContent className="p-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
+8 -5
View File
@@ -1,6 +1,6 @@
"use client"
import { useState } from "react"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { useTheme } from "next-themes"
import { cn } from "@/lib/utils"
@@ -36,9 +36,12 @@ export function Topbar({ onMenuClick }: TopbarProps) {
const router = useRouter()
const { theme, setTheme } = useTheme()
const { user, logout } = useUser()
const [mounted, setMounted] = useState(false)
const [searchOpen, setSearchOpen] = useState(false)
useEffect(() => { setMounted(true) }, [])
if (!user) return null
const initials = user.name.split(" ").map((n) => n[0]).join("")
const initials = (user.firstName[0] + user.lastName[0]).toUpperCase()
return (
<header className="sticky top-0 z-20 flex h-16 items-center gap-4 border-b bg-background px-4 lg:px-6">
@@ -74,7 +77,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="text-muted-foreground"
>
{theme === "dark" ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
{mounted ? (theme === "dark" ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />) : <div className="h-5 w-5" />}
</Button>
{/* Notifications */}
@@ -116,13 +119,13 @@ export function Topbar({ onMenuClick }: TopbarProps) {
<AvatarImage src={user.avatar} />
<AvatarFallback>{initials}</AvatarFallback>
</Avatar>
<span className="hidden text-sm font-medium md:inline-block">{user.name}</span>
<span className="hidden text-sm font-medium md:inline-block">{user.firstName} {user.lastName}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium">{user.name}</p>
<p className="text-sm font-medium">{user.firstName} {user.lastName}</p>
<p className="text-xs text-muted-foreground">{user.email}</p>
<Badge variant="secondary" className="mt-1 w-fit text-xs capitalize">{user.role}</Badge>
</div>
+4 -4
View File
@@ -14,7 +14,7 @@ const ScrollArea = React.forwardRef<
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollBar forceMount />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
@@ -28,14 +28,14 @@ const ScrollBar = React.forwardRef<
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
"flex touch-none select-none transition-opacity",
orientation === "vertical" && "h-full w-3 border-l border-l-transparent p-[1px]",
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-muted-foreground/30" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName