feat: add system monitor component and API for performance metrics

feat: implement conversations API with message retrieval and posting

feat: add avatar URL handling for users and update user role definitions

feat: create chat system tables in the database with initial seed data

fix: update user roles from "sales_user" to "sales" for consistency

chore: add middleware for system API route access

fixed fuckups on john's side, added a benchmark

Made Graphs actualy load from database and realtime data, graphs will update and percentages will be mathematically made,  and made realtime

added chatcart edits, made graphs glow.

added in some little small home feeling fuctions

added in a slide show, in login with qoutes from creators because i want to leave a print on it saying we made this shit, we built it brick for brick

login page added qoutes some random, and made them shuffle from left to right one dissapears other come in

adding shape to the textbox for the qoutes

Fixed my chat fuckups when it comes to chats
This commit is contained in:
Ace
2026-06-18 22:48:03 +02:00
parent f5d09298a2
commit adbcc4b9af
38 changed files with 2321 additions and 741 deletions
@@ -1,8 +1,10 @@
"use client"
import { useState, useMemo } from "react"
import { useState, useMemo, useEffect } from "react"
import { motion } from "framer-motion"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { ChevronLeft, ChevronRight } from "lucide-react"
interface IntervalData {
label: string
@@ -17,9 +19,35 @@ interface LeadsPerMonthChartProps {
const NEW_LEADS = "#0d9488"
const CLOSED = "#c9a96e"
export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
export function LeadsPerMonthChart({ data: initialData }: LeadsPerMonthChartProps) {
const [year, setYear] = useState(new Date().getFullYear())
const [chartData, setChartData] = useState<IntervalData[]>(initialData)
const [hovered, setHovered] = useState<number | null>(null)
useEffect(() => {
setChartData(initialData)
}, [initialData])
useEffect(() => {
async function fetchYearData() {
try {
const res = await fetch(`/api/dashboard?year=${year}`)
if (res.ok) {
const data = await res.json()
setChartData(data.leadsPerMonth || [])
}
} catch {
// ignore
}
}
const thisYear = new Date().getFullYear()
if (year !== thisYear || initialData.length === 0) {
fetchYearData()
} else {
setChartData(initialData)
}
}, [year, initialData])
const width = 880
const height = 620
const padding = { top: 128, right: 24, bottom: 48, left: 44 }
@@ -27,38 +55,51 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
const chartH = height - padding.top - padding.bottom
const maxVal = useMemo(() => {
if (data.length === 0) return 1
const max = Math.max(...data.map((d) => Math.max(d.leads, d.closed)))
if (chartData.length === 0) return 1
const max = Math.max(...chartData.map((d) => Math.max(d.leads, d.closed)))
return Math.max(Math.ceil(max / 7) * 7, 1)
}, [data])
}, [chartData])
const ticks = useMemo(() => {
const steps = 4
return Array.from({ length: steps + 1 }, (_, i) => Math.round((maxVal / steps) * i))
}, [maxVal])
const groupW = chartW / data.length
const groupW = chartW / Math.max(chartData.length, 1)
const barW = groupW * 0.28
const gap = groupW * 0.06
const yScale = (v: number) => chartH - (v / maxVal) * chartH
const active = hovered
const activeDatum = active !== null ? data[active] : null
const activeDatum = active !== null ? chartData[active] : null
const totalNew = data.reduce((s, d) => s + d.leads, 0)
const totalClosed = data.reduce((s, d) => s + d.closed, 0)
const totalNew = chartData.reduce((s, d) => s + d.leads, 0)
const totalClosed = chartData.reduce((s, d) => s + d.closed, 0)
const closeRate = totalNew > 0 ? Math.round((totalClosed / totalNew) * 100) : 0
if (data.length === 0) {
const currentYear = new Date().getFullYear()
if (chartData.length === 0) {
return (
<Card className="h-full">
<CardHeader>
<CardTitle>Leads Per Month</CardTitle>
<div className="flex items-center justify-between">
<CardTitle>Leads Per Month</CardTitle>
<div className="flex items-center gap-1">
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={() => setYear((y) => y - 1)}>
<ChevronLeft className="h-4 w-4" />
</Button>
<span className="min-w-[60px] text-center text-sm font-medium">{year}</span>
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={() => setYear((y) => Math.min(y + 1, currentYear))} disabled={year >= currentYear}>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</CardHeader>
<CardContent>
<div className="flex items-center justify-center h-[400px] text-sm text-muted-foreground">
No data for this period
No data for {year}
</div>
</CardContent>
</Card>
@@ -90,6 +131,15 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
<span className="inline-block h-2.5 w-2.5 rounded-sm" style={{ background: CLOSED }} />
<span className="text-xs text-muted-foreground">Closed</span>
</div>
<div className="ml-2 flex items-center gap-1 rounded-lg border px-2 py-1">
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => setYear((y) => y - 1)}>
<ChevronLeft className="h-3.5 w-3.5" />
</Button>
<span className="min-w-[50px] text-center text-sm font-semibold">{year}</span>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => setYear((y) => Math.min(y + 1, currentYear))} disabled={year >= currentYear}>
<ChevronRight className="h-3.5 w-3.5" />
</Button>
</div>
</div>
</div>
</CardHeader>
@@ -145,7 +195,7 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
))}
{/* Bars */}
{data.map((d, i) => {
{chartData.map((d, i) => {
const groupX = i * groupW
const isActive = active === i
const newH = chartH - yScale(d.leads)
@@ -158,7 +208,6 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
onMouseLeave={() => setHovered(null)}
style={{ cursor: "pointer" }}
>
{/* Hover highlight band */}
<rect
x={groupX}
y={0}
@@ -168,7 +217,6 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
rx={6}
/>
{/* New Leads bar */}
<rect
x={groupX + groupW / 2 - barW - gap / 2}
y={yScale(d.leads)}
@@ -181,7 +229,6 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
style={{ transition: "opacity 0.15s ease" }}
/>
{/* Closed bar */}
<rect
x={groupX + groupW / 2 + gap / 2}
y={yScale(d.closed)}
@@ -194,7 +241,6 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
style={{ transition: "opacity 0.15s ease" }}
/>
{/* Month label */}
<text
x={groupX + groupW / 2}
y={chartH + 26}
@@ -211,13 +257,12 @@ export function LeadsPerMonthChart({ data }: LeadsPerMonthChartProps) {
</g>
</svg>
{/* Tooltip */}
{activeDatum && (
<div
className="pointer-events-none absolute top-0"
style={{
left: `${((active! + 0.5) / data.length) * 100}%`,
transform: active! > data.length - 2
left: `${((active! + 0.5) / chartData.length) * 100}%`,
transform: active! > chartData.length - 2
? "translate(-100%, 0)"
: "translate(-12%, 0)",
transition: "left 0.15s ease",