Files
neta/app/page.tsx
T
Poyraz AvseverandCopilot c73bf0e499 feat: add form components and UI elements for enhanced user interaction
- Introduced Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage components for structured form handling.
- Added Input, Textarea, and Select components for user input.
- Implemented Label and Separator components for better UI organization.
- Created Skeleton component for loading states.
- Developed Toast and Toaster components for user notifications.
- Integrated useToast hook for managing toast notifications.
- Established AI analysis functionality with analyzeJournalWithLocalAI.
- Set up Dexie for local database management with Journal and Task models.
- Configured Supabase client for server-side and browser-side interactions.
- Defined database schema for journals, tasks, chat sessions, and messages with Row Level Security policies.

Co-authored-by: Copilot <copilot@github.com>
2026-05-06 16:20:14 +03:00

209 lines
8.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useMemo } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Activity, Brain, PenTool, CheckCircle } from "lucide-react";
import { useLiveQuery } from "dexie-react-hooks";
import { db } from "@/lib/db";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
BarChart,
Bar,
Legend
} from "recharts";
const moodScores: Record<string, number> = { happy: 4, neutral: 3, sad: 2, angry: 1 };
export default function DashboardPage() {
const journals = useLiveQuery(() => db.journals.orderBy("date").toArray()) || [];
const tasks = useLiveQuery(() => db.tasks.toArray()) || [];
const pendingTasksCount = tasks.filter(t => t.status === "todo").length;
// Bugüne ait enerji seviyesi
const today = new Date().toISOString().split("T")[0];
const todaysJournal = journals.find(j => j.date === today);
const todaysEnergy = todaysJournal ? todaysJournal.energy : "--";
// 1. Grafik: Son 7 günün Ruh Hali ve Enerji trendi
const trendData = useMemo(() => {
const dataMap: Record<string, { date: string, moodSum: number, energySum: number, count: number }> = {};
journals.forEach(j => {
if (!dataMap[j.date]) {
dataMap[j.date] = { date: j.date, moodSum: 0, energySum: 0, count: 0 };
}
dataMap[j.date].moodSum += moodScores[j.mood] || 3;
dataMap[j.date].energySum += j.energy;
dataMap[j.date].count += 1;
});
return Object.values(dataMap)
.map(d => ({
date: d.date.slice(5), // Sadece MM-DD formatı alalım
Mood: Number((d.moodSum / d.count).toFixed(1)),
Enerji: Number((d.energySum / d.count).toFixed(1)),
}))
.slice(-7); // Sadece son 7 günü göster
}, [journals]);
// 2. Grafik: En çok kullanılan AI etiketleri
const tagData = useMemo(() => {
const counts: Record<string, number> = {};
journals.forEach(j => {
if (j.ai_tags) {
j.ai_tags.forEach(tag => {
counts[tag] = (counts[tag] || 0) + 1;
});
}
});
return Object.entries(counts)
.map(([name, value]) => ({ name, Değer: value }))
.sort((a, b) => b.Değer - a.Değer)
.slice(0, 5); // En çok geçen 5 etiket
}, [journals]);
// Son günlüğe ait AI Summary
const lastInsight = [...journals].reverse().find(j => j.ai_summary)?.ai_summary;
return (
<div className="space-y-6 max-w-6xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex flex-col gap-2">
<h1 className="text-3xl font-bold tracking-tight">Bugün Nasılsın?</h1>
<p className="text-muted-foreground">Kişisel özetin ve yapay zeka analizlerin burada görünecek.</p>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardTitle className="text-sm font-medium">Günlük Kayıtları</CardTitle>
<PenTool className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{journals.length}</div>
<p className="text-xs text-muted-foreground mt-1">
{journals.length === 0 ? "Henüz kayıt girilmedi" : "Toplam kayıt eklendi"}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardTitle className="text-sm font-medium">Aktif Görevler</CardTitle>
<CheckCircle className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{pendingTasksCount}</div>
<p className="text-xs text-muted-foreground mt-1">
Bekleyen görev
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardTitle className="text-sm font-medium">Bugünkü Enerji</CardTitle>
<Activity className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{todaysEnergy}</div>
<p className="text-xs text-muted-foreground mt-1">
{todaysJournal ? "/ 5 Seviyesinde" : "Kayıt bekleniyor"}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardTitle className="text-sm font-medium">Local AI Durumu</CardTitle>
<Brain className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-green-500">Hazır</div>
<p className="text-xs text-muted-foreground mt-1">
Ollama / Veri bekliyor
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7 ">
{/* Line Chart */}
<Card className="lg:col-span-4 p-6 flex flex-col justify-between">
<div className="mb-4">
<h3 className="text-lg font-medium mb-1">Ruh Hali & Enerji Trendi</h3>
<p className="text-sm text-muted-foreground">Son 7 günlük ortalamalar (1-5 Arası Puanlama)</p>
</div>
<div className="h-[250px] w-full">
{trendData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<LineChart data={trendData}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#88888833" />
<XAxis dataKey="date" axisLine={false} tickLine={false} tick={{ fontSize: 12 }} />
<YAxis domain={[1, 5]} axisLine={false} tickLine={false} tick={{ fontSize: 12 }} width={30} />
<Tooltip
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }}
itemStyle={{ fontWeight: "500" }}
/>
<Legend wrapperStyle={{ paddingTop: "10px", fontSize: "14px" }} />
<Line type="monotone" dataKey="Mood" stroke="#3b82f6" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
<Line type="monotone" dataKey="Enerji" stroke="#10b981" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
</LineChart>
</ResponsiveContainer>
) : (
<div className="h-full flex items-center justify-center border-2 border-dashed rounded-md bg-muted/20">
<p className="text-sm text-muted-foreground">Veri bekleniyor...</p>
</div>
)}
</div>
</Card>
{/* Bar Chart & Insight */}
<div className="lg:col-span-3 space-y-4 flex flex-col">
<Card className="p-6 flex-1 flex flex-col">
<div className="mb-4">
<h3 className="text-lg font-medium mb-1">AI Konu Dağılımı</h3>
<p className="text-sm text-muted-foreground">Günlüklerinden çıkarılan en sık 5 etiket</p>
</div>
<div className="h-[150px] w-full mt-auto">
{tagData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<BarChart data={tagData} layout="vertical" margin={{ left: -20 }}>
<CartesianGrid strokeDasharray="3 3" horizontal={false} stroke="#88888833" />
<XAxis type="number" hide />
<YAxis dataKey="name" type="category" axisLine={false} tickLine={false} tick={{ fontSize: 13 }} width={90} />
<Tooltip
cursor={{fill: 'transparent'}}
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }}
/>
<Bar dataKey="Değer" fill="#8b5cf6" radius={[0, 4, 4, 0]} barSize={20} />
</BarChart>
</ResponsiveContainer>
) : (
<div className="h-full flex items-center justify-center border-2 border-dashed rounded-md bg-muted/20">
<p className="text-sm text-muted-foreground">Yeterli etiket yok.</p>
</div>
)}
</div>
</Card>
<Card className="p-6 bg-primary/5 border-primary/20 flex-1">
<h3 className="text-lg font-medium mb-3 flex items-center gap-2">
<Brain className="w-5 h-5 text-primary" /> Son AI İçgörüsü
</h3>
<p className="text-sm text-foreground/80 leading-relaxed italic">
{lastInsight ? `"${lastInsight}"` : "Henüz bir içgörü oluşmadı. Biraz günlük yaz, AI analiz yapsın."}
</p>
</Card>
</div>
</div>
</div>
);
}