style: format code for improved readability and consistency across multiple files
This commit is contained in:
+44
-14
@@ -5,7 +5,14 @@ import { v4 as uuidv4 } from "uuid";
|
|||||||
import { useLiveQuery } from "dexie-react-hooks";
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
import { analyzeJournalWithLocalAI } from "@/lib/ai";
|
import { analyzeJournalWithLocalAI } from "@/lib/ai";
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -25,8 +32,8 @@ export default function JournalPage() {
|
|||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
// Dexie.js üzerinden günlükleri tarih sırasına göre çekiyoruz (en yeni en üstte)
|
// Dexie.js üzerinden günlükleri tarih sırasına göre çekiyoruz (en yeni en üstte)
|
||||||
const journals = useLiveQuery(
|
const journals = useLiveQuery(() =>
|
||||||
() => db.journals.orderBy("date").reverse().toArray()
|
db.journals.orderBy("date").reverse().toArray(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
@@ -83,7 +90,6 @@ export default function JournalPage() {
|
|||||||
} catch (aiErr) {
|
} catch (aiErr) {
|
||||||
console.error("Arka plan AI analizi hatası:", aiErr);
|
console.error("Arka plan AI analizi hatası:", aiErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Günlük kaydedilemedi:", error);
|
console.error("Günlük kaydedilemedi:", error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -92,7 +98,7 @@ export default function JournalPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string) => {
|
||||||
if(confirm("Bu günlüğü silmek istediğinden emin misin?")) {
|
if (confirm("Bu günlüğü silmek istediğinden emin misin?")) {
|
||||||
await db.journals.delete(id);
|
await db.journals.delete(id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -101,7 +107,9 @@ export default function JournalPage() {
|
|||||||
<div className="space-y-6 max-w-4xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<div className="space-y-6 max-w-4xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Günlük</h1>
|
<h1 className="text-3xl font-bold tracking-tight">Günlük</h1>
|
||||||
<p className="text-muted-foreground">Zihnini boşalt, hislerini kaydet. Verilerin sadece cihazında kalır.</p>
|
<p className="text-muted-foreground">
|
||||||
|
Zihnini boşalt, hislerini kaydet. Verilerin sadece cihazında kalır.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@@ -121,7 +129,9 @@ export default function JournalPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => setMood(m.id)}
|
onClick={() => setMood(m.id)}
|
||||||
className={`flex-1 py-2 px-1 rounded-md border text-xl flex items-center justify-center transition-colors ${
|
className={`flex-1 py-2 px-1 rounded-md border text-xl flex items-center justify-center transition-colors ${
|
||||||
mood === m.id ? "bg-primary/20 border-primary" : "bg-card border-border hover:bg-muted"
|
mood === m.id
|
||||||
|
? "bg-primary/20 border-primary"
|
||||||
|
: "bg-card border-border hover:bg-muted"
|
||||||
}`}
|
}`}
|
||||||
title={m.label}
|
title={m.label}
|
||||||
>
|
>
|
||||||
@@ -155,7 +165,11 @@ export default function JournalPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type="submit" disabled={isSubmitting} className="w-full md:w-auto">
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="w-full md:w-auto"
|
||||||
|
>
|
||||||
{isSubmitting ? "Kaydediliyor..." : "Kaydet"}
|
{isSubmitting ? "Kaydediliyor..." : "Kaydet"}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
@@ -167,7 +181,9 @@ export default function JournalPage() {
|
|||||||
{!journals ? (
|
{!journals ? (
|
||||||
<p className="text-sm text-muted-foreground">Yükleniyor...</p>
|
<p className="text-sm text-muted-foreground">Yükleniyor...</p>
|
||||||
) : journals.length === 0 ? (
|
) : journals.length === 0 ? (
|
||||||
<p className="text-sm text-muted-foreground">Henüz kayıt bulunmuyor.</p>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Henüz kayıt bulunmuyor.
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{journals.map((journal) => (
|
{journals.map((journal) => (
|
||||||
@@ -175,7 +191,9 @@ export default function JournalPage() {
|
|||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<CardTitle className="text-base flex items-center gap-2">
|
<CardTitle className="text-base flex items-center gap-2">
|
||||||
<span>{moods.find(m => m.id === journal.mood)?.emoji}</span>
|
<span>
|
||||||
|
{moods.find((m) => m.id === journal.mood)?.emoji}
|
||||||
|
</span>
|
||||||
<span>{journal.date}</span>
|
<span>{journal.date}</span>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<span className="text-xs px-2 py-1 bg-muted rounded-md font-medium text-muted-foreground">
|
<span className="text-xs px-2 py-1 bg-muted rounded-md font-medium text-muted-foreground">
|
||||||
@@ -184,13 +202,18 @@ export default function JournalPage() {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-2">
|
<CardContent className="pt-2">
|
||||||
<p className="whitespace-pre-wrap text-sm leading-relaxed">{journal.content}</p>
|
<p className="whitespace-pre-wrap text-sm leading-relaxed">
|
||||||
|
{journal.content}
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* AI Sonuçlarını Göster */}
|
{/* AI Sonuçlarını Göster */}
|
||||||
{journal.ai_tags && (
|
{journal.ai_tags && (
|
||||||
<div className="mt-4 flex flex-wrap gap-2">
|
<div className="mt-4 flex flex-wrap gap-2">
|
||||||
{journal.ai_tags.map(tag => (
|
{journal.ai_tags.map((tag) => (
|
||||||
<span key={tag} className="text-xs px-2 py-1 bg-primary/10 text-primary rounded-md">
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="text-xs px-2 py-1 bg-primary/10 text-primary rounded-md"
|
||||||
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
@@ -203,7 +226,14 @@ export default function JournalPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex justify-end mt-4">
|
<div className="flex justify-end mt-4">
|
||||||
<Button variant="ghost" size="sm" className="text-red-500 hover:text-red-600 hover:bg-red-500/10" onClick={() => handleDelete(journal.id)}>Sil</Button>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="text-red-500 hover:text-red-600 hover:bg-red-500/10"
|
||||||
|
onClick={() => handleDelete(journal.id)}
|
||||||
|
>
|
||||||
|
Sil
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
+6
-2
@@ -6,7 +6,7 @@ import { ThemeProvider } from "@/components/theme-provider";
|
|||||||
import { Sidebar } from "@/components/layout/sidebar";
|
import { Sidebar } from "@/components/layout/sidebar";
|
||||||
import { Header } from "@/components/layout/header";
|
import { Header } from "@/components/layout/header";
|
||||||
|
|
||||||
const geist = Geist({subsets:['latin'],variable:'--font-sans'});
|
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "MindSpace",
|
title: "MindSpace",
|
||||||
@@ -19,7 +19,11 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="tr" className={cn("font-sans", geist.variable)} suppressHydrationWarning>
|
<html
|
||||||
|
lang="tr"
|
||||||
|
className={cn("font-sans", geist.variable)}
|
||||||
|
suppressHydrationWarning
|
||||||
|
>
|
||||||
<body>
|
<body>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
|
|||||||
+133
-39
@@ -15,27 +15,36 @@ import {
|
|||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
BarChart,
|
BarChart,
|
||||||
Bar,
|
Bar,
|
||||||
Legend
|
Legend,
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
|
|
||||||
const moodScores: Record<string, number> = { happy: 4, neutral: 3, sad: 2, angry: 1 };
|
const moodScores: Record<string, number> = {
|
||||||
|
happy: 4,
|
||||||
|
neutral: 3,
|
||||||
|
sad: 2,
|
||||||
|
angry: 1,
|
||||||
|
};
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
const journals = useLiveQuery(() => db.journals.orderBy("date").toArray()) || [];
|
const journals =
|
||||||
|
useLiveQuery(() => db.journals.orderBy("date").toArray()) || [];
|
||||||
const tasks = useLiveQuery(() => db.tasks.toArray()) || [];
|
const tasks = useLiveQuery(() => db.tasks.toArray()) || [];
|
||||||
|
|
||||||
const pendingTasksCount = tasks.filter(t => t.status === "todo").length;
|
const pendingTasksCount = tasks.filter((t) => t.status === "todo").length;
|
||||||
|
|
||||||
// Bugüne ait enerji seviyesi
|
// Bugüne ait enerji seviyesi
|
||||||
const today = new Date().toISOString().split("T")[0];
|
const today = new Date().toISOString().split("T")[0];
|
||||||
const todaysJournal = journals.find(j => j.date === today);
|
const todaysJournal = journals.find((j) => j.date === today);
|
||||||
const todaysEnergy = todaysJournal ? todaysJournal.energy : "--";
|
const todaysEnergy = todaysJournal ? todaysJournal.energy : "--";
|
||||||
|
|
||||||
// 1. Grafik: Son 7 günün Ruh Hali ve Enerji trendi
|
// 1. Grafik: Son 7 günün Ruh Hali ve Enerji trendi
|
||||||
const trendData = useMemo(() => {
|
const trendData = useMemo(() => {
|
||||||
const dataMap: Record<string, { date: string, moodSum: number, energySum: number, count: number }> = {};
|
const dataMap: Record<
|
||||||
|
string,
|
||||||
|
{ date: string; moodSum: number; energySum: number; count: number }
|
||||||
|
> = {};
|
||||||
|
|
||||||
journals.forEach(j => {
|
journals.forEach((j) => {
|
||||||
if (!dataMap[j.date]) {
|
if (!dataMap[j.date]) {
|
||||||
dataMap[j.date] = { date: j.date, moodSum: 0, energySum: 0, count: 0 };
|
dataMap[j.date] = { date: j.date, moodSum: 0, energySum: 0, count: 0 };
|
||||||
}
|
}
|
||||||
@@ -45,7 +54,7 @@ export default function DashboardPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return Object.values(dataMap)
|
return Object.values(dataMap)
|
||||||
.map(d => ({
|
.map((d) => ({
|
||||||
date: d.date.slice(5), // Sadece MM-DD formatı alalım
|
date: d.date.slice(5), // Sadece MM-DD formatı alalım
|
||||||
Mood: Number((d.moodSum / d.count).toFixed(1)),
|
Mood: Number((d.moodSum / d.count).toFixed(1)),
|
||||||
Enerji: Number((d.energySum / d.count).toFixed(1)),
|
Enerji: Number((d.energySum / d.count).toFixed(1)),
|
||||||
@@ -56,9 +65,9 @@ export default function DashboardPage() {
|
|||||||
// 2. Grafik: En çok kullanılan AI etiketleri
|
// 2. Grafik: En çok kullanılan AI etiketleri
|
||||||
const tagData = useMemo(() => {
|
const tagData = useMemo(() => {
|
||||||
const counts: Record<string, number> = {};
|
const counts: Record<string, number> = {};
|
||||||
journals.forEach(j => {
|
journals.forEach((j) => {
|
||||||
if (j.ai_tags) {
|
if (j.ai_tags) {
|
||||||
j.ai_tags.forEach(tag => {
|
j.ai_tags.forEach((tag) => {
|
||||||
counts[tag] = (counts[tag] || 0) + 1;
|
counts[tag] = (counts[tag] || 0) + 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,45 +79,55 @@ export default function DashboardPage() {
|
|||||||
}, [journals]);
|
}, [journals]);
|
||||||
|
|
||||||
// Son günlüğe ait AI Summary
|
// Son günlüğe ait AI Summary
|
||||||
const lastInsight = [...journals].reverse().find(j => j.ai_summary)?.ai_summary;
|
const lastInsight = [...journals]
|
||||||
|
.reverse()
|
||||||
|
.find((j) => j.ai_summary)?.ai_summary;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 max-w-6xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<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">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Bugün Nasılsın?</h1>
|
<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>
|
<p className="text-muted-foreground">
|
||||||
|
Kişisel özetin ve yapay zeka analizlerin burada görünecek.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
<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>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
Günlük Kayıtları
|
||||||
|
</CardTitle>
|
||||||
<PenTool className="h-4 w-4 text-muted-foreground" />
|
<PenTool className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{journals.length}</div>
|
<div className="text-2xl font-bold">{journals.length}</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{journals.length === 0 ? "Henüz kayıt girilmedi" : "Toplam kayıt eklendi"}
|
{journals.length === 0
|
||||||
|
? "Henüz kayıt girilmedi"
|
||||||
|
: "Toplam kayıt eklendi"}
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
||||||
<CardTitle className="text-sm font-medium">Aktif Görevler</CardTitle>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
Aktif Görevler
|
||||||
|
</CardTitle>
|
||||||
<CheckCircle className="h-4 w-4 text-muted-foreground" />
|
<CheckCircle className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{pendingTasksCount}</div>
|
<div className="text-2xl font-bold">{pendingTasksCount}</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">Bekleyen görev</p>
|
||||||
Bekleyen görev
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
||||||
<CardTitle className="text-sm font-medium">Bugünkü Enerji</CardTitle>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
Bugünkü Enerji
|
||||||
|
</CardTitle>
|
||||||
<Activity className="h-4 w-4 text-muted-foreground" />
|
<Activity className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -121,7 +140,9 @@ export default function DashboardPage() {
|
|||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
<CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
|
||||||
<CardTitle className="text-sm font-medium">Local AI Durumu</CardTitle>
|
<CardTitle className="text-sm font-medium">
|
||||||
|
Local AI Durumu
|
||||||
|
</CardTitle>
|
||||||
<Brain className="h-4 w-4 text-muted-foreground" />
|
<Brain className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -137,28 +158,70 @@ export default function DashboardPage() {
|
|||||||
{/* Line Chart */}
|
{/* Line Chart */}
|
||||||
<Card className="lg:col-span-4 p-6 flex flex-col justify-between">
|
<Card className="lg:col-span-4 p-6 flex flex-col justify-between">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-lg font-medium mb-1">Ruh Hali & Enerji Trendi</h3>
|
<h3 className="text-lg font-medium mb-1">
|
||||||
<p className="text-sm text-muted-foreground">Son 7 günlük ortalamalar (1-5 Arası Puanlama)</p>
|
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>
|
||||||
<div className="h-[250px] w-full">
|
<div className="h-[250px] w-full">
|
||||||
{trendData.length > 0 ? (
|
{trendData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={trendData}>
|
<LineChart data={trendData}>
|
||||||
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#88888833" />
|
<CartesianGrid
|
||||||
<XAxis dataKey="date" axisLine={false} tickLine={false} tick={{ fontSize: 12 }} />
|
strokeDasharray="3 3"
|
||||||
<YAxis domain={[1, 5]} axisLine={false} tickLine={false} tick={{ fontSize: 12 }} width={30} />
|
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
|
<Tooltip
|
||||||
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }}
|
contentStyle={{
|
||||||
|
borderRadius: "8px",
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
color: "#000",
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
itemStyle={{ fontWeight: "500" }}
|
itemStyle={{ fontWeight: "500" }}
|
||||||
/>
|
/>
|
||||||
<Legend wrapperStyle={{ paddingTop: "10px", fontSize: "14px" }} />
|
<Legend
|
||||||
<Line type="monotone" dataKey="Mood" stroke="#3b82f6" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
|
wrapperStyle={{ paddingTop: "10px", fontSize: "14px" }}
|
||||||
<Line type="monotone" dataKey="Enerji" stroke="#10b981" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
|
/>
|
||||||
|
<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>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full flex items-center justify-center border-2 border-dashed rounded-md bg-muted/20">
|
<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>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Veri bekleniyor...
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -169,25 +232,54 @@ export default function DashboardPage() {
|
|||||||
<Card className="p-6 flex-1 flex flex-col">
|
<Card className="p-6 flex-1 flex flex-col">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-lg font-medium mb-1">AI Konu Dağılımı</h3>
|
<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>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Günlüklerinden çıkarılan en sık 5 etiket
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-[150px] w-full mt-auto">
|
<div className="h-[150px] w-full mt-auto">
|
||||||
{tagData.length > 0 ? (
|
{tagData.length > 0 ? (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={tagData} layout="vertical" margin={{ left: -20 }}>
|
<BarChart
|
||||||
<CartesianGrid strokeDasharray="3 3" horizontal={false} stroke="#88888833" />
|
data={tagData}
|
||||||
|
layout="vertical"
|
||||||
|
margin={{ left: -20 }}
|
||||||
|
>
|
||||||
|
<CartesianGrid
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
horizontal={false}
|
||||||
|
stroke="#88888833"
|
||||||
|
/>
|
||||||
<XAxis type="number" hide />
|
<XAxis type="number" hide />
|
||||||
<YAxis dataKey="name" type="category" axisLine={false} tickLine={false} tick={{ fontSize: 13 }} width={90} />
|
<YAxis
|
||||||
|
dataKey="name"
|
||||||
|
type="category"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fontSize: 13 }}
|
||||||
|
width={90}
|
||||||
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
cursor={{fill: 'transparent'}}
|
cursor={{ fill: "transparent" }}
|
||||||
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }}
|
contentStyle={{
|
||||||
|
borderRadius: "8px",
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
color: "#000",
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Bar
|
||||||
|
dataKey="Değer"
|
||||||
|
fill="#8b5cf6"
|
||||||
|
radius={[0, 4, 4, 0]}
|
||||||
|
barSize={20}
|
||||||
/>
|
/>
|
||||||
<Bar dataKey="Değer" fill="#8b5cf6" radius={[0, 4, 4, 0]} barSize={20} />
|
|
||||||
</BarChart>
|
</BarChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full flex items-center justify-center border-2 border-dashed rounded-md bg-muted/20">
|
<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>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Yeterli etiket yok.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -198,7 +290,9 @@ export default function DashboardPage() {
|
|||||||
<Brain className="w-5 h-5 text-primary" /> Son AI İçgörüsü
|
<Brain className="w-5 h-5 text-primary" /> Son AI İçgörüsü
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-foreground/80 leading-relaxed italic">
|
<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."}
|
{lastInsight
|
||||||
|
? `"${lastInsight}"`
|
||||||
|
: "Henüz bir içgörü oluşmadı. Biraz günlük yaz, AI analiz yapsın."}
|
||||||
</p>
|
</p>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+60
-20
@@ -4,7 +4,13 @@ import { useState } from "react";
|
|||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
@@ -14,7 +20,7 @@ export default function TasksPage() {
|
|||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
|
|
||||||
const tasks = useLiveQuery(
|
const tasks = useLiveQuery(
|
||||||
() => db.tasks.toArray() // Şimdilik hepsini çekiyoruz, sıralama yapılabilir
|
() => db.tasks.toArray(), // Şimdilik hepsini çekiyoruz, sıralama yapılabilir
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAddTask = async (e: React.FormEvent) => {
|
const handleAddTask = async (e: React.FormEvent) => {
|
||||||
@@ -48,14 +54,18 @@ export default function TasksPage() {
|
|||||||
await db.tasks.delete(id);
|
await db.tasks.delete(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const pendingTasks = tasks?.filter(t => t.status === "todo") || [];
|
const pendingTasks = tasks?.filter((t) => t.status === "todo") || [];
|
||||||
const completedTasks = tasks?.filter(t => t.status === "completed") || [];
|
const completedTasks = tasks?.filter((t) => t.status === "completed") || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 max-w-4xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<div className="space-y-6 max-w-4xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Görevler & Planlar</h1>
|
<h1 className="text-3xl font-bold tracking-tight">
|
||||||
<p className="text-muted-foreground">Kişisel hedeflerin ve YZ tarafından önerilen aksiyonlar.</p>
|
Görevler & Planlar
|
||||||
|
</h1>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Kişisel hedeflerin ve YZ tarafından önerilen aksiyonlar.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@@ -80,21 +90,36 @@ export default function TasksPage() {
|
|||||||
{/* Yapılacaklar */}
|
{/* Yapılacaklar */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||||
Yapılacaklar <span className="text-sm px-2 py-0.5 rounded-full bg-primary/20 text-primary">{pendingTasks.length}</span>
|
Yapılacaklar{" "}
|
||||||
|
<span className="text-sm px-2 py-0.5 rounded-full bg-primary/20 text-primary">
|
||||||
|
{pendingTasks.length}
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{pendingTasks.length === 0 ? (
|
{pendingTasks.length === 0 ? (
|
||||||
<p className="text-sm text-muted-foreground">Bekleyen görev yok.</p>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Bekleyen görev yok.
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
pendingTasks.map(task => (
|
pendingTasks.map((task) => (
|
||||||
<div key={task.id} className="flex items-center gap-3 p-3 rounded-lg border bg-card shadow-sm group transition-all hover:border-primary/50">
|
<div
|
||||||
|
key={task.id}
|
||||||
|
className="flex items-center gap-3 p-3 rounded-lg border bg-card shadow-sm group transition-all hover:border-primary/50"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={false}
|
checked={false}
|
||||||
onCheckedChange={() => toggleTaskStatus(task.id, task.status)}
|
onCheckedChange={() =>
|
||||||
|
toggleTaskStatus(task.id, task.status)
|
||||||
|
}
|
||||||
className="w-5 h-5"
|
className="w-5 h-5"
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 text-sm font-medium">{task.title}</span>
|
<span className="flex-1 text-sm font-medium">
|
||||||
<button onClick={() => deleteTask(task.id)} className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10">
|
{task.title}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => deleteTask(task.id)}
|
||||||
|
className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10"
|
||||||
|
>
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,21 +131,36 @@ export default function TasksPage() {
|
|||||||
{/* Tamamlananlar */}
|
{/* Tamamlananlar */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xl font-semibold opacity-70 flex items-center gap-2">
|
<h2 className="text-xl font-semibold opacity-70 flex items-center gap-2">
|
||||||
Tamamlananlar <span className="text-sm px-2 py-0.5 rounded-full bg-muted text-muted-foreground">{completedTasks.length}</span>
|
Tamamlananlar{" "}
|
||||||
|
<span className="text-sm px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
|
||||||
|
{completedTasks.length}
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{completedTasks.length === 0 ? (
|
{completedTasks.length === 0 ? (
|
||||||
<p className="text-sm text-muted-foreground">Hiç görev tamamlanmadı.</p>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Hiç görev tamamlanmadı.
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
completedTasks.map(task => (
|
completedTasks.map((task) => (
|
||||||
<div key={task.id} className="flex items-center gap-3 p-3 rounded-lg border bg-muted/40 shadow-sm group">
|
<div
|
||||||
|
key={task.id}
|
||||||
|
className="flex items-center gap-3 p-3 rounded-lg border bg-muted/40 shadow-sm group"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={true}
|
checked={true}
|
||||||
onCheckedChange={() => toggleTaskStatus(task.id, task.status)}
|
onCheckedChange={() =>
|
||||||
|
toggleTaskStatus(task.id, task.status)
|
||||||
|
}
|
||||||
className="w-5 h-5 data-[state=checked]:bg-muted-foreground data-[state=checked]:border-muted-foreground"
|
className="w-5 h-5 data-[state=checked]:bg-muted-foreground data-[state=checked]:border-muted-foreground"
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 text-sm line-through text-muted-foreground">{task.title}</span>
|
<span className="flex-1 text-sm line-through text-muted-foreground">
|
||||||
<button onClick={() => deleteTask(task.id)} className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10">
|
{task.title}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => deleteTask(task.id)}
|
||||||
|
className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10"
|
||||||
|
>
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,14 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { LayoutDashboard, Book, CheckSquare, MessageCircle, Settings, BrainCircuit } from "lucide-react";
|
import {
|
||||||
|
LayoutDashboard,
|
||||||
|
Book,
|
||||||
|
CheckSquare,
|
||||||
|
MessageCircle,
|
||||||
|
Settings,
|
||||||
|
BrainCircuit,
|
||||||
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@@ -24,9 +31,11 @@ export function Sidebar() {
|
|||||||
</div>
|
</div>
|
||||||
<nav className="flex-1 p-4 space-y-1">
|
<nav className="flex-1 p-4 space-y-1">
|
||||||
{routes.map((route) => {
|
{routes.map((route) => {
|
||||||
const isActive = pathname === route.path || pathname?.startsWith(route.path + "/");
|
const isActive =
|
||||||
|
pathname === route.path || pathname?.startsWith(route.path + "/");
|
||||||
// Home route exact match check
|
// Home route exact match check
|
||||||
const isExactActive = route.path === "/" ? pathname === "/" : isActive;
|
const isExactActive =
|
||||||
|
route.path === "/" ? pathname === "/" : isActive;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
@@ -36,7 +45,7 @@ export function Sidebar() {
|
|||||||
"flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors text-sm font-medium",
|
"flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors text-sm font-medium",
|
||||||
isExactActive
|
isExactActive
|
||||||
? "bg-primary text-primary-foreground"
|
? "bg-primary text-primary-foreground"
|
||||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<route.icon className="h-5 w-5" />
|
<route.icon className="h-5 w-5" />
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ export interface AIAnalysisResult {
|
|||||||
suggested_tasks: string[];
|
suggested_tasks: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function analyzeJournalWithLocalAI(content: string, model: string = "llama3"): Promise<AIAnalysisResult | null> {
|
export async function analyzeJournalWithLocalAI(
|
||||||
|
content: string,
|
||||||
|
model: string = "llama3",
|
||||||
|
): Promise<AIAnalysisResult | null> {
|
||||||
const prompt = `
|
const prompt = `
|
||||||
Sen şefkatli bir yapay zeka asistanı ve kişisel yansıma (reflection) yardımcısısın.
|
Sen şefkatli bir yapay zeka asistanı ve kişisel yansıma (reflection) yardımcısısın.
|
||||||
Aşağıdaki günlük girdisini analiz edip tam olarak belirtilen JSON formatında yanıt vermelisin. Düz metin kullanma, SADECE geçerli bir JSON objesi döndür.
|
Aşağıdaki günlük girdisini analiz edip tam olarak belirtilen JSON formatında yanıt vermelisin. Düz metin kullanma, SADECE geçerli bir JSON objesi döndür.
|
||||||
@@ -29,8 +32,8 @@ export async function analyzeJournalWithLocalAI(content: string, model: string =
|
|||||||
model: model, // Gerekirse "gemini-3-flash-preview:latest" gibi kendi modeliniz de olabilir
|
model: model, // Gerekirse "gemini-3-flash-preview:latest" gibi kendi modeliniz de olabilir
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
stream: false,
|
stream: false,
|
||||||
format: "json"
|
format: "json",
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -40,9 +43,11 @@ export async function analyzeJournalWithLocalAI(content: string, model: string =
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const parsedResult = JSON.parse(data.response);
|
const parsedResult = JSON.parse(data.response);
|
||||||
return parsedResult as AIAnalysisResult;
|
return parsedResult as AIAnalysisResult;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("AI Analizi başarısız oldu (Ollama açık olmayabilir):", error);
|
console.error(
|
||||||
|
"AI Analizi başarısız oldu (Ollama açık olmayabilir):",
|
||||||
|
error,
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export class MindSpaceDB extends Dexie {
|
|||||||
// IndexedDB'de sadece indekslenecek (üzerinde arama/sıralama yapılacak) alanları belirtiriz.
|
// IndexedDB'de sadece indekslenecek (üzerinde arama/sıralama yapılacak) alanları belirtiriz.
|
||||||
this.version(1).stores({
|
this.version(1).stores({
|
||||||
journals: "id, date, mood",
|
journals: "id, date, mood",
|
||||||
tasks: "id, status, date, journal_id"
|
tasks: "id, status, date, journal_id",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -2,7 +2,10 @@ import type { Config } from "tailwindcss";
|
|||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
content: ["./app/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}"],
|
content: [
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|||||||
Reference in New Issue
Block a user