diff --git a/app/(dashboard)/analytics/analytics-client.tsx b/app/(dashboard)/analytics/analytics-client.tsx new file mode 100644 index 0000000..2c1230f --- /dev/null +++ b/app/(dashboard)/analytics/analytics-client.tsx @@ -0,0 +1,166 @@ +"use client"; + +import { useState } from "react"; +import { Card, CardContent } from "poyraz-ui/atoms"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "poyraz-ui/molecules"; +import { + Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, + PieChart, Pie, Cell, Legend +} from "recharts"; +import { BarChart3, Filter } from "lucide-react"; + +export type AnalyticsData = { + tasks: { id: string; status: string; created_at: string; due_at?: string }[]; + projects: { id: string; name: string }[]; + finances: { id: string; type: string; amount: number; transaction_date: string; project_id?: string }[]; +}; + +type AnalyticsClientProps = { + data: AnalyticsData; +}; + +const COLORS = ["hsl(var(--primary))", "hsl(var(--destructive))", "#eab308", "#3b82f6", "#8b5cf6"]; + +export function AnalyticsClient({ data }: AnalyticsClientProps) { + const [dateRange, setDateRange] = useState("this_month"); + + const now = new Date(); + + const filterByDate = (dateStr: string | null) => { + if (!dateStr) return false; + const date = new Date(dateStr); + if (dateRange === "this_week") { + const firstDay = new Date(now.setDate(now.getDate() - now.getDay() + (now.getDay() === 0 ? -6 : 1))); + return date >= firstDay; + } + if (dateRange === "this_month") { + return date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); + } + if (dateRange === "this_year") { + return date.getFullYear() === now.getFullYear(); + } + return true; + }; + + const filteredFinances = data.finances.filter(f => filterByDate(f.transaction_date)); + const filteredTasks = data.tasks.filter(t => filterByDate(t.created_at || t.due_at)); + + // Project-based income + const projectIncomeMap = new Map(); + filteredFinances.filter(f => f.type === "income" && f.project_id).forEach(f => { + const project = data.projects.find(p => p.id === f.project_id); + const name = project ? project.name : "Bilinmeyen"; + if (!projectIncomeMap.has(name)) { + projectIncomeMap.set(name, { name, value: 0 }); + } + projectIncomeMap.get(name).value += Number(f.amount); + }); + const projectIncomeData = Array.from(projectIncomeMap.values()); + + // Task completion stats + const completedTasks = filteredTasks.filter(t => t.status === "completed").length; + const activeTasks = filteredTasks.filter(t => t.status !== "completed" && t.status !== "cancelled").length; + + const taskStatusData = [ + { name: "Tamamlanan", value: completedTasks }, + { name: "Devam Eden", value: activeTasks } + ]; + + return ( +
+
+
+
+ + Analizler +
+
+

+ Performans ve Finans Analizi +

+

+ Müşteri bazlı gelirler, görev tamamlama oranları ve proje ilerleme grafikleri. +

+
+
+ +
+ +
+
+ +
+ + +

Proje Bazlı Gelir Dağılımı

+
+ {projectIncomeData.length > 0 ? ( + + + + {projectIncomeData.map((entry, index) => ( + + ))} + + `₺${value}`} + contentStyle={{ + backgroundColor: 'hsl(var(--background))', + borderColor: 'hsl(var(--border))', + borderRadius: '0.375rem', + }} + /> + + + + ) : ( +
Veri bulunamadı.
+ )} +
+
+
+ + + +

Görev Durumu Analizi

+
+ + + + + + + + + +
+
+
+
+
+ ); +} diff --git a/app/(dashboard)/analytics/page.tsx b/app/(dashboard)/analytics/page.tsx index 0d2348e..e6fd239 100644 --- a/app/(dashboard)/analytics/page.tsx +++ b/app/(dashboard)/analytics/page.tsx @@ -1,255 +1,39 @@ -"use client"; +import { createClient } from "@/lib/supabase/server"; +import { AnalyticsClient } from "./analytics-client"; +import { redirect } from "next/navigation"; -import { useState } from "react"; -import { - ArrowUpRight, Brain, TrendingUp, Zap, Target, ArrowDownRight, - BarChart3, Filter, Download, Info, CheckCircle2, AlertCircle -} from "lucide-react"; -import { - Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, - Tooltip, XAxis, YAxis, Radar, RadarChart, PolarGrid, PolarAngleAxis, Cell -} from "recharts"; -import { motion, AnimatePresence } from "framer-motion"; +export const metadata = { + title: "Analizler - Cognis", +}; -// Mock Data -const productivityTrend = [ - { date: "May 1", focus: 65, energy: 40 }, - { date: "May 4", focus: 75, energy: 55 }, - { date: "May 8", focus: 60, energy: 50 }, - { date: "May 12", focus: 85, energy: 70 }, - { date: "May 16", focus: 72, energy: 65 }, - { date: "May 20", focus: 95, energy: 85 }, - { date: "May 24", focus: 88, energy: 80 }, -]; +export default async function AnalyticsPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); -const habitCompletion = [ - { name: "Reading", completed: 85, missed: 15, color: "#6C5BB0" }, - { name: "Workout", completed: 60, missed: 40, color: "#a798e8" }, - { name: "Meditation", completed: 90, missed: 10, color: "#10b981" }, - { name: "Coding", completed: 75, missed: 25, color: "#3b82f6" }, -]; + if (!user) { + redirect("/login"); + } -const focusRadarData = [ - { subject: "Deep Work", A: 120, fullMark: 150 }, - { subject: "Learning", A: 98, fullMark: 150 }, - { subject: "Health", A: 86, fullMark: 150 }, - { subject: "Networking", A: 65, fullMark: 150 }, - { subject: "Admin", A: 40, fullMark: 150 }, -]; + // 1. Fetch tasks + const { data: tasks } = await supabase + .from("tasks") + .select("*"); -export default function AnalyticsPage() { - const [isExporting, setIsExporting] = useState(false); - const [activeInsight, setActiveInsight] = useState(null); + // 2. Fetch projects + const { data: projects } = await supabase + .from("projects") + .select("*"); - const handleExport = () => { - setIsExporting(true); - setTimeout(() => setIsExporting(false), 3000); + // 3. Fetch finances + const { data: finances } = await supabase + .from("finance_transactions") + .select("*"); + + const analyticsData = { + tasks: tasks || [], + projects: projects || [], + finances: finances || [], }; - return ( -
- - {/* Top Header */} -
-

- Analytics / Performance Metrics -

-
-
- - -
- -
-
- - {/* KPI Row */} -
- - - - - {/* Dynamic AI Score Ring */} -
-
-

Strategic Readiness

-
94%
-

Optimal alignment with goals.

-
-
- - - - -
- -
-
-
-
- - {/* Main Analysis Area */} -
- - {/* Productivity Chart with Hotspots */} -
-
-
-

Correlation: Focus vs Energy

-

Detailed visual mapping of biological energy impact on focus output.

-
-
-
Focus
-
Energy
-
-
- - {/* Chart Interaction Layer */} -
- -
- - - {activeInsight && ( - -
- AI Observation -
- {activeInsight} -
- )} -
- -
- - - - - - - - - - - - - - - - - - - - -
-
- - {/* Sidebar Insights */} -
- -
-

Effort Distribution

-
- - - - - - - - -
-
- -
-

Strategic Alerts

-
-
- -
-
Habit Streak Maintained
-
Reading streak is now at 12 days. Energy levels are correlating positively.
-
-
-
- -
-
Admin Overload
-
Admin tasks have increased by 15%. Consider automating these via Cognis AI.
-
-
-
-
- -
-
-
- ); -} - -function KpiCard({ title, value, change, icon: Icon, trend, color = "primary" }: any) { - const isUp = trend === "up"; - const trendColor = isUp ? "text-emerald-500" : "text-rose-500"; - - return ( -
-
- -
-

{title}

-
- {value} - - {isUp ? : } {change} - -
-
vs previous 30 days
-
- ); + return ; } diff --git a/app/(dashboard)/dashboard-client.tsx b/app/(dashboard)/dashboard-client.tsx new file mode 100644 index 0000000..49bfe80 --- /dev/null +++ b/app/(dashboard)/dashboard-client.tsx @@ -0,0 +1,299 @@ +"use client"; + +import { useState } from "react"; +import { Badge, Button, Card, CardContent } from "poyraz-ui/atoms"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "poyraz-ui/molecules"; +import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, Line, LineChart } from "recharts"; +import { CheckCircle2, Wallet, FolderKanban, Activity, CalendarDays, Plus } from "lucide-react"; +import { QuickActionsSheet } from "@/components/dashboard/quick-actions-sheet"; + +export type DashboardData = { + tasks: { id: string; status: string; created_at: string; updated_at?: string; due_at?: string }[]; + projects: { id: string; status: string; name: string }[]; + finances: { id: string; type: string; amount: number; transaction_date: string }[]; + logs: { id: string; log_date: string; mood_score: number; energy_score: number }[]; + events: { id: string; title: string; type: string; starts_at: string }[]; +}; + +type DashboardClientProps = { + data: DashboardData; +}; + +export function DashboardClient({ data }: DashboardClientProps) { + const [dateRange, setDateRange] = useState("this_month"); + const [isQuickActionOpen, setIsQuickActionOpen] = useState(false); + + // Calculate real metrics from the `data` prop depending on `dateRange` + const now = new Date(); + + // Helper to filter by date + const filterByDate = (dateStr: string | null) => { + if (!dateStr) return false; + const date = new Date(dateStr); + if (dateRange === "today") { + return date.toDateString() === now.toDateString(); + } + if (dateRange === "this_week") { + const firstDay = new Date(now.setDate(now.getDate() - now.getDay() + (now.getDay() === 0 ? -6 : 1))); // Monday + return date >= firstDay; + } + if (dateRange === "this_month") { + return date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); + } + return true; + }; + + // KPIs + const filteredFinances = data.finances.filter(f => filterByDate(f.transaction_date)); + const income = filteredFinances.filter(f => f.type === "income").reduce((acc, curr) => acc + Number(curr.amount), 0); + const expense = filteredFinances.filter(f => f.type === "expense").reduce((acc, curr) => acc + Number(curr.amount), 0); + const netProfit = income - expense; + + const activeProjectsCount = data.projects.filter(p => p.status === "active").length; + + const completedTasksCount = data.tasks.filter(t => t.status === "completed" && filterByDate(t.updated_at || t.created_at)).length; + + const filteredLogs = data.logs.filter(l => filterByDate(l.log_date)); + const avgMood = filteredLogs.length > 0 + ? (filteredLogs.reduce((acc, curr) => acc + curr.mood_score, 0) / filteredLogs.length).toFixed(1) + : "0.0"; + + // Recharts Data Prep + // Group finances by date + const financeMap = new Map(); + filteredFinances.forEach(f => { + const d = new Date(f.transaction_date).toLocaleDateString("tr-TR", { month: "short", day: "numeric" }); + if (!financeMap.has(d)) { + financeMap.set(d, { name: d, income: 0, expense: 0 }); + } + const entry = financeMap.get(d); + if (f.type === "income") entry.income += Number(f.amount); + if (f.type === "expense") entry.expense += Number(f.amount); + }); + const incomeTrendData = Array.from(financeMap.values()); + + // Group logs by date + const moodTrendData = filteredLogs.map(l => ({ + date: new Date(l.log_date).toLocaleDateString("tr-TR", { month: "short", day: "numeric" }), + mood: l.mood_score, + energy: l.energy_score, + })); + + // Format currency + const formatCurrency = (val: number) => { + return new Intl.NumberFormat("tr-TR", { + style: "currency", + currency: "USD", + maximumFractionDigits: 0, + }).format(val); + }; + + return ( +
+ {/* Header */} +
+
+
+ + Genel Bakış +
+
+

+ Dashboard +

+

+ İş performansını, gelirlerini ve günlük durumunu takip et. +

+
+
+ +
+ + + + +
+
+ + {/* KPI Cards */} +
+ + + + +
+ + {/* Charts */} +
+ + +

Gelir / Gider Özeti

+
+ {incomeTrendData.length > 0 ? ( + + + + + + + + + + + ) : ( +
Bu tarih aralığında finansal veri yok.
+ )} +
+
+
+ + + +

Mood & Enerji Trendi

+
+ {moodTrendData.length > 0 ? ( + + + + + + + + + + + ) : ( +
Bu tarih aralığında günlük verisi yok.
+ )} +
+
+
+
+ + {/* Upcoming & Tasks List */} +
+ + +
+

Yaklaşan Etkinlikler ve Deadlinelar

+ +
+
+ {data.events.length > 0 ? data.events.map((event) => ( +
+
+
+

{event.title}

+

+ {new Date(event.starts_at).toLocaleDateString("tr-TR", { month: "long", day: "numeric", hour: "2-digit", minute: "2-digit" })} +

+
+
+ )) : ( +
Yaklaşan etkinlik yok.
+ )} +
+ + +
+
+ ); +} + +function StatCard({ + label, + value, + icon: Icon, + tone, +}: { + label: string; + value: string; + icon: typeof FolderKanban; + tone: "green" | "blue" | "amber" | "red"; +}) { + const toneClass = { + green: "bg-emerald-50 text-emerald-700", + blue: "bg-blue-50 text-blue-700", + amber: "bg-amber-50 text-amber-700", + red: "bg-primary/10 text-primary", + }[tone]; + + return ( + + +
+

{label}

+

{value}

+
+
+ +
+
+
+ ); +} diff --git a/app/(dashboard)/page.tsx b/app/(dashboard)/page.tsx index 266c540..fd00340 100644 --- a/app/(dashboard)/page.tsx +++ b/app/(dashboard)/page.tsx @@ -1,395 +1,69 @@ -"use client"; +import { createClient } from "@/lib/supabase/server"; +import { DashboardClient } from "./dashboard-client"; +import { redirect } from "next/navigation"; -import { useState } from "react"; -import { - Bar, - BarChart, - CartesianGrid, - Cell, - Line, - LineChart, - Pie, - PieChart, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from "recharts"; -import { - CheckSquare2, Droplets, Moon, Target, Activity, - MoreHorizontal, PenTool, Plus, GripVertical, Maximize2 -} from "lucide-react"; -import { motion, Reorder } from "framer-motion"; -import { QuickActionsSheet } from "@/components/dashboard/quick-actions-sheet"; -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +export const metadata = { + title: "Dashboard - Cognis", +}; -// Mock Data -const focusTrendData = [ - { date: "May 12", value: 55 }, - { date: "May 13", value: 42 }, - { date: "May 14", value: 60 }, - { date: "May 15", value: 85 }, - { date: "May 16", value: 72 }, - { date: "May 17", value: 87 }, - { date: "May 18", value: 100 }, -]; +export default async function DashboardPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); -const sleepData = [ - { day: "M", value: 6.5 }, - { day: "T", value: 7.2 }, - { day: "W", value: 5.8 }, - { day: "T", value: 6.1 }, - { day: "F", value: 8.0 }, - { day: "S", value: 7.5 }, - { day: "S", value: 8.5 }, -]; + if (!user) { + redirect("/login"); + } -const waterData = [ - { day: "M", value: 1.5 }, - { day: "T", value: 2.1 }, - { day: "W", value: 1.8 }, - { day: "T", value: 2.5 }, - { day: "F", value: 2.0 }, - { day: "S", value: 3.0 }, - { day: "S", value: 2.8 }, -]; - -const moodData = [ - { day: "M", value: 60 }, - { day: "T", value: 85 }, - { day: "W", value: 70 }, - { day: "T", value: 65 }, - { day: "F", value: 80 }, - { day: "S", value: 90 }, - { day: "S", value: 95 }, -]; - -const focusDistributionData = [ - { name: "Deep Work", value: 45, color: "#6C5BB0" }, - { name: "Shallow Work", value: 30, color: "#54468B" }, - { name: "Meetings", value: 15, color: "#3B3161" }, - { name: "Breaks", value: 10, color: "#221C38" }, -]; - -const upcomingEvents = [ - { time: "10:00 AM", title: "Deep Work Session", category: "FOCUS", color: "border-l-pink-500" }, - { time: "12:30 PM", title: "Team Standup", category: "MEETING", color: "border-l-orange-500" }, - { time: "02:00 PM", title: "Project Review", category: "WORK", color: "border-l-blue-500" }, - { time: "04:00 PM", title: "Workout", category: "HEALTH", color: "border-l-emerald-500" }, - { time: "07:00 PM", title: "Daily Reflection", category: "PERSONAL", color: "border-l-purple-500" }, -]; - -type WidgetId = "sleep" | "water" | "mood" | "distribution"; - -export default function DashboardPage() { - const [isQuickActionOpen, setIsQuickActionOpen] = useState(false); - const [selectedKpi, setSelectedKpi] = useState(null); + // Get current date and date 30 days ago + const today = new Date(); + const thirtyDaysAgo = new Date(); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); - // Widget ordering state - const [widgetOrder, setWidgetOrder] = useState(["sleep", "water", "mood", "distribution"]); + const todayStr = today.toISOString(); + const thirtyDaysAgoStr = thirtyDaysAgo.toISOString(); - return ( -
- - {/* Top Header */} -
-

- Dashboard / Overview -

- -
+ // 1. Fetch tasks + const { data: tasks } = await supabase + .from("tasks") + .select("*") + .or(`due_at.gte.${thirtyDaysAgoStr},created_at.gte.${thirtyDaysAgoStr}`) + .order("due_at", { ascending: true }); -
- - {/* LEFT COLUMN: Focus Trend (Takes 2 columns on XL) */} -
-
-
-
-

Focus Trend

-
87%
-
Average Focus Score
-
-
- - -
-
- -
- - - - - - - - - -
-
-
+ // 2. Fetch projects + const { data: projects } = await supabase + .from("projects") + .select("*") + .order("created_at", { ascending: false }); - {/* RIGHT COLUMN: Quick Add & Upcoming */} -
- - {/* AI Strategy Suggestion (Replacing Quick Add Input) */} -
-
- -
-

- - AI Insight -

-

- "Based on your 100% focus score yesterday, you have high mental momentum. Schedule your hardest task for 10:00 AM today." -

- -
+ // 3. Fetch finances + const { data: finances } = await supabase + .from("finance_transactions") + .select("*") + .gte("transaction_date", thirtyDaysAgoStr) + .order("transaction_date", { ascending: true }); - {/* Upcoming */} -
-
-

Upcoming

- -
-
- {upcomingEvents.map((event, idx) => ( -
-
-
{event.time}
-
{event.title}
-
{event.category}
-
- ))} -
-
+ // 4. Fetch daily logs + const { data: logs } = await supabase + .from("daily_logs") + .select("*") + .gte("log_date", thirtyDaysAgoStr) + .order("log_date", { ascending: true }); -
-
+ // 5. Fetch calendar events (upcoming) + const { data: events } = await supabase + .from("calendar_events") + .select("*") + .gte("starts_at", todayStr) + .order("starts_at", { ascending: true }) + .limit(10); - {/* BOTTOM ROW: Draggable Widgets */} - - {widgetOrder.map((id) => ( - - {id === "sleep" && ( - setSelectedKpi("sleep")} - /> - )} - {id === "water" && ( - setSelectedKpi("water")} - /> - )} - {id === "mood" && ( - setSelectedKpi("mood")} - /> - )} - {id === "distribution" && ( -
-
-
- -
-
-

Focus Distribution

-
-
- - - - {focusDistributionData.map((entry, index) => ( - - ))} - - - -
-
- {focusDistributionData.slice(0, 3).map((item, idx) => ( -
-
-
- {item.name} -
- {item.value}% -
- ))} -
-
-
- )} -
- ))} -
+ const dashboardData = { + tasks: tasks || [], + projects: projects || [], + finances: finances || [], + logs: logs || [], + events: events || [], + }; - {/* Quote Footer */} -
-
-
- -
-
-
SYSTEM REMINDER
-
"Discipline is the bridge between goals and accomplishment."
-
-
-
- Jim Rohn
-
- - {/* Sheet & Modals */} - setIsQuickActionOpen(false)} - /> - - setSelectedKpi(null)}> - - - - {selectedKpi === "sleep" && <> Sleep Analysis} - {selectedKpi === "water" && <> Hydration Trends} - {selectedKpi === "mood" && <> Mood Tracking} - - -
- - - - - - - - - -
-
-
-

AI Observation

-

Your consistency is up 12% from last week. Keep this pace for optimal performance.

-
-
-

Recommendation

-

Try to reach your goal by increasing focus in the evening blocks.

-
-
-
-
- -
- ); -} - -function WidgetCard({ title, icon: Icon, value, unit, goal, data, onExpand }: any) { - return ( -
-
- -
- -
-
-
-

{title}

- -
-
-
- {value} - {unit} -
-
- - - - - - - -
-
Goal: {goal}
-
-
- ); + return ; }