style: format code for improved readability and consistency across multiple files

This commit is contained in:
Poyraz Avsever
2026-05-06 16:20:27 +03:00
parent c73bf0e499
commit 983c8fd6aa
9 changed files with 295 additions and 110 deletions
+1 -1
View File
@@ -55,4 +55,4 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }
+45 -15
View File
@@ -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>
+10 -6
View File
@@ -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,13 +19,17 @@ 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"
defaultTheme="dark" defaultTheme="dark"
enableSystem enableSystem
disableTransitionOnChange disableTransitionOnChange
> >
<div className="flex min-h-screen bg-background text-foreground"> <div className="flex min-h-screen bg-background text-foreground">
<Sidebar /> <Sidebar />
+141 -47
View File
@@ -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,
journals.forEach(j => { { date: string; moodSum: number; energySum: number; count: number }
> = {};
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,57 +158,128 @@ 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}
<Tooltip stroke="#88888833"
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }} />
<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" }} 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>
</Card> </Card>
{/* Bar Chart & Insight */} {/* Bar Chart & Insight */}
<div className="lg:col-span-3 space-y-4 flex flex-col"> <div className="lg:col-span-3 space-y-4 flex flex-col">
<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
<Tooltip dataKey="name"
cursor={{fill: 'transparent'}} type="category"
contentStyle={{ borderRadius: "8px", backgroundColor: "#fff", color: "#000", border: "none" }} 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}
/> />
<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>
+66 -26
View File
@@ -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>
@@ -65,8 +75,8 @@ export default function TasksPage() {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleAddTask} className="flex gap-2"> <form onSubmit={handleAddTask} className="flex gap-2">
<Input <Input
placeholder="Örn: 15 dakika yürüyüş yap..." placeholder="Örn: 15 dakika yürüyüş yap..."
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
className="flex-1" className="flex-1"
@@ -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
<Checkbox key={task.id}
checked={false} className="flex items-center gap-3 p-3 rounded-lg border bg-card shadow-sm group transition-all hover:border-primary/50"
onCheckedChange={() => toggleTaskStatus(task.id, task.status)} >
<Checkbox
checked={false}
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
<Checkbox key={task.id}
checked={true} className="flex items-center gap-3 p-3 rounded-lg border bg-muted/40 shadow-sm group"
onCheckedChange={() => toggleTaskStatus(task.id, task.status)} >
<Checkbox
checked={true}
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>
+14 -5
View File
@@ -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,10 +31,12 @@ 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
key={route.path} key={route.path}
@@ -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" />
+10 -5
View File
@@ -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;
} }
} }
+4 -4
View File
@@ -30,14 +30,14 @@ export class MindSpaceDB extends Dexie {
constructor() { constructor() {
super("MindSpaceDatabase"); super("MindSpaceDatabase");
// Schema tanımlamaları. // Schema tanımlamaları.
// 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",
}); });
} }
} }
export const db = new MindSpaceDB(); export const db = new MindSpaceDB();
+4 -1
View File
@@ -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: {