"use client"; import Link from "next/link"; import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig, } from "@/components/ui/chart"; import { tr } from "@/i18n/tr"; import { cn } from "@/lib/utils"; import { formatDate, formatMoney } from "@/shared/format"; import { cumulative, OVERVIEW_PERIODS, type OverviewPeriod } from "@/shared/overview"; import type { OverviewData } from "../service"; const t = tr.dashboard.overview; export type OverviewView = "cumulative" | "periodic"; const compact = new Intl.NumberFormat("tr-TR", { notation: "compact", maximumFractionDigits: 1 }); const dayShort = new Intl.DateTimeFormat("tr-TR", { day: "numeric", month: "short" }); const weekdayShort = new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric" }); const monthShort = new Intl.DateTimeFormat("tr-TR", { month: "short", year: "2-digit" }); const monthLong = new Intl.DateTimeFormat("tr-TR", { month: "long", year: "numeric" }); const config = { plannedIncome: { label: t.series.plannedIncome, color: "var(--income)" }, budget: { label: t.series.budget, color: "var(--budget)" }, actual: { label: t.series.actual, color: "var(--expense)" }, } satisfies ChartConfig; const SERIES = ["plannedIncome", "budget", "actual"] as const; const asDate = (key: string) => new Date(key.length === 7 ? `${key}-15T12:00:00+03:00` : `${key}T12:00:00+03:00`); function bucketLabel(key: string, period: OverviewPeriod) { const d = asDate(key); if (key.length === 7) return monthShort.format(d); return period === "week" ? weekdayShort.format(d) : dayShort.format(d); } /** Bölüm 6.13 ana çizgi grafik: periyot seçici ve kümülatif / dönemsel görünüm (URL'de). */ export function OverviewChart({ data, view }: { data: OverviewData; view: OverviewView }) { const points = view === "cumulative" ? cumulative(data.points) : data.points; const chart = points.map((p) => ({ key: p.key, label: bucketLabel(p.key, data.period), plannedIncome: p.plannedIncome === null ? null : p.plannedIncome / 100, budget: p.budget === null ? null : p.budget / 100, actual: p.actual === null ? null : p.actual / 100, })); const hasGap = data.points.some((p) => p.budget === null); const href = (period: OverviewPeriod, v: OverviewView) => `?periyot=${period}&gorunum=${v === "cumulative" ? "kumulatif" : "donemsel"}`; const rangeText = data.range.granularity === "month" ? `${monthLong.format(asDate(data.range.buckets[0]!))} – ${monthLong.format(asDate(data.range.buckets.at(-1)!))}` : `${formatDate(asDate(data.range.from))} – ${formatDate(asDate(data.range.to))}`; const pill = (active: boolean) => cn( "rounded-md px-2.5 py-1 text-sm transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", active ? "bg-background font-medium shadow-sm" : "text-muted-foreground hover:text-foreground", ); return (
{t.title} {t.description(rangeText)}
compact.format(v)} /> { const key = payload?.[0]?.payload?.key as string | undefined; if (!key) return ""; return key.length === 7 ? monthLong.format(asDate(key)) : formatDate(asDate(key)); }} formatter={(value, name) => (
{config[name as keyof typeof config]?.label} {formatMoney(Math.round(Number(value) * 100))}
)} /> } /> } /> {SERIES.map((k) => ( ))}
{hasGap ?

{t.noBudget}

: null}
{t.table}
{SERIES.map((k) => ( ))} {points.map((p) => ( {SERIES.map((k) => ( ))} ))}
{t.bucket[data.range.granularity]} {t.series[k]}
{p.key.length === 7 ? monthLong.format(asDate(p.key)) : formatDate(asDate(p.key))} {p[k] === null ? "—" : formatMoney(p[k])}
); }