diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..4fb7b2d --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,105 @@ +# MindSpace Proje Yol Haritası (Roadmap) + +Bu doküman, `PRD.md` gereksinimlerinin **Supabase**, **Gemini API** ve **Shadcn UI** kullanılarak uygulanması için detaylı, faz bazlı geliştirme planını içermektedir. + +--- + +## Faz 1: Proje Kurulumu ve Altyapı +**Amacı:** Geliştirme ortamının hazırlanması, UI bileşen sisteminin kurulması ve veritabanı / yetkilendirme (Auth) altyapısının Supabase ile entegre edilmesi. + +1. **Next.js Kurulumu** + - Next.js (App Router), React 19, TypeScript ve Tailwind CSS kullanılarak projenin başlatılması. + - Dizin yapısının oluşturulması (`src/app`, `src/components`, `src/lib`, `src/hooks`). +2. **Shadcn UI ve Stil Entegrasyonu** + - Shadcn UI CLI ile başlatılması. + - Temel bileşenlerin (Button, Card, Input, Label, Dialog, Form, Toast vb.) eklenmesi. + - `next-themes` kullanılarak Dark/Light mod geçişinin ayarlanması (Varsayılan olarak Dark Mode ağırlıklı tasarım). +3. **Supabase Proje Kurulumu** + - Supabase projesinin oluşturulması ve API key'lerin `.env.local` dosyasına eklenmesi. + - `@supabase/ssr` veya `@supabase/supabase-js` ile client ve server bileşenleri için istemcilerin oluşturulması. +4. **Veritabanı Şemasının Oluşturulması (Supabase SQL)** + - `users` tablosu (Supabase Auth ile senkronize çalışacak yapı). + - `journals` tablosu: Kullanıcının günlükleri (id, user_id, date, mood, energy, content, ai_tags, ai_sentiment_score, ai_summary vb.). + - `tasks` tablosu: Görev yönetimi (id, user_id, title, description, status, source_journal_id). + - `chat_sessions` ve `chat_messages` tabloları (Geçmiş sohbetleri tutmak için). +5. **Kimlik Doğrulama (Authentication)** + - Supabase Auth kullanılarak Email/Şifre ile kayıt olma (Register) ve giriş yapma (Login) sayfalarının geliştirilmesi. + - Auth middleware ile yetkisiz kullanıcıların Dashboard'a erişiminin engellenmesi. + +--- + +## Faz 2: Dashboard ve Temel Düzen (Layout) +**Amacı:** Kullanıcının giriş yaptığında karşılaşacağı ana navigasyon ve dashboard iskeletinin modern bir görünümle inşa edilmesi. + +1. **Ana Layout ve Navigasyon** + - Masaüstü için sol veya üst menü bar (Sidebar/Navbar). + - Menü linkleri: Dashboard, Günlük, Görevler, Sohbet, Ayarlar. + - Responsive tasarım ile mobilde hamburger menü yapısı. +2. **Dashboard İskeleti** + - CSS Grid/Flexbox kullanarak kartların yerleşimi. + - Profil bilgisi ve Çıkış Yap butonu içeren bir üst bilgi (header) alanı. +3. **Boş Durum (Empty State) Yönetimi** + - Kullanıcının henüz günlüğü, görevi veya AI analizi yoksa gösterilecek yönlendirici ve estetik bileşenlerin tasarlanması (örn. "Henüz veri yok, ilk günlüğünü yaz"). + +--- + +## Faz 3: Günlük (Journal) ve Görev (Task) Modülleri +**Amacı:** Kullanıcının duygu, enerji ve günlük notlarını yazabilmesi, ayrıca manuel olarak görev ekleyip takip edebilmesi. + +1. **Günlük (Journal) Modülü** + - **Oluşturma Ekranı:** Günlük yazma formu. Shadcn form bileşenleriyle validasyon sağlanması. + - **Mood & Enerji Seçimi:** Buton grupları veya slider kullanılarak duygu durumu ve enerji (1-5) seçimi. + - **Listeleme:** Kaydedilen günlüklerin tarih sırasına göre listelenmesi. + - **Detay Görünümü:** Seçilen günlüğün tam sayfa veya modal içinde okunması, silinmesi ve düzenlenmesi. +2. **Görev (Task) Modülü** + - **Görev Ekleme:** Basit başlık ve detay içeren görev ekleme formu. + - **Görev Durumu:** "Todo", "In Progress", "Completed" durumlarının listelenmesi ve checkbox veya sürükle bırak tarzında güncellenmesi. + - **Günlükle İlişkilendirme:** Görevlerin varsa kaynak günlük ID'sini göstermesi. + +--- + +## Faz 4: Gemini API Entegrasyonu (AI Analizi) +**Amacı:** Kullanıcının girdiği günlük metninin Google Gemini API ile analiz edilip etiketler, duygu skoru, özet ve görev önerileri üretmesi. + +1. **Gemini API Kurulumu** + - `@google/generative-ai` SDK'sının projeye dahil edilmesi. + - API Key'in `.env.local` içinde güvenli yapılandırılması. +2. **Analiz Endpoint'inin Geliştirilmesi** + - `POST /api/analyze-journal` gibi bir route oluşturularak, kullanıcının günlüğünün modele gönderilmesi. + - **Prompt Engineering:** Gemini'ye gönderilecek prompt'un, yanıtın stabil bir JSON formatında (tags, sentiment_score, summary, reflection, suggested_tasks) dönmesini garantileyecek şekilde tasarlanması. +3. **Veritabanı ve UI Entegrasyonu** + - Dönen AI analiz sonuçlarının Supabase'deki `journals` tablosuna (ai_tags, ai_summary vb.) Update edilmesi. + - Günlük detay ekranında AI analiz sonucunun görsel bir kart olarak sunulması. +4. **Görev Önerileri (Suggested Tasks) Akışı** + - AI'ın günlüğe bakarak çıkardığı görev önerilerinin kullanıcıya sunulması ("Bunu görev listene eklemek ister misin?"). + - Onaylanan önerilerin `tasks` tablosuna kaydedilmesi. + +--- + +## Faz 5: Dashboard Veri Görselleştirme +**Amacı:** Toplanan verilerin (mood, enerji, etiketler) grafikler yardımıyla analiz edilmesi ve özetlerin sunulması. + +1. **Recharts Entegrasyonu ve Veri Hazırlama** + - Recharts kütüphanesinin eklenmesi. + - Supabase'den son 7 ve 30 güne ait verilerin çekilip grafiklere uygun formata dönüştürülmesi. +2. **Mood ve Enerji Trend Grafikleri** + - Günlere göre duygu skorları ve enerji (1-5) seviyesini gösteren çizgi veya bar grafikleri. +3. **Etiket Dağılımı Grafiği** + - AI tarafından en çok oluşturulan etiketlerin pasta (Pie) veya yatay çubuk (Bar) grafik ile gösterilmesi. +4. **Dashboard Özet Kartları** + - "Son Günlüklerin", "Aktif Görevlerin" ve "Haftalık AI İçgörüsünün" özet widget'lar halinde Dashboard ekranında sergilenmesi. + +--- + +## Faz 6: AI Chat Modülü (Sohbet) +**Amacı:** Kullanıcının geçmiş günlüklerini bağlam olarak sunup, bir "Dijital Yansıma Asistanı" (Gemini) ile sohbet etmesi. + +1. **Sohbet UI Tasarımı** + - Shadcn UI ile scrollable mesaj listesi, kullanıcı giriş alanı ve mesaj baloncuklarının tasarlanması. +2. **Bağlam (Context) Yönetimi** + - Kullanıcı bir soru sorduğunda, Supabase'den son X günlüğün veya içeriğe uygun günlüklerin çekilerek Gemini API'ye sistem mesajı/context olarak verilmesi. +3. **Sohbet Akışı ve Veri Kaydı** + - Gemini API üzerinden yanıtların tercihen "stream" (akış) yöntemiyle hızlıca UI'da gösterilmesi. + - Tamamlanan konuşmaların `chat_sessions` ve `chat_messages` tablolarına kaydedilerek, sayfa yenilendiğinde eski sohbetin görünmesinin sağlanması. +4. **Güvenlik ve Sınırlar** + - Prompt seviyesinde Gemini'ye "Tıbbi veya klinik tavsiye verme, sadece yansıtıcı ol" komutunun sıkıca verilmesi. diff --git a/app/globals.css b/app/globals.css index dbb3439..e0a7f85 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,35 +1,58 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss/base"; +@import "tailwindcss/components"; +@import "tailwindcss/utilities"; -:root { - color-scheme: light; - background: #f5f7f4; +@layer base { + :root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.985 0 0); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --radius: 0.625rem; + } + .dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --destructive-foreground: oklch(0.985 0 0); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + } } -* { - box-sizing: border-box; -} - -html { - min-height: 100%; -} - -body { - min-height: 100%; - margin: 0; - background: - linear-gradient(135deg, rgba(47, 125, 99, 0.08), transparent 34%), - linear-gradient(315deg, rgba(216, 100, 74, 0.08), transparent 30%), - #f5f7f4; - color: #17211f; - font-family: - Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", - sans-serif; -} - -button, -input, -select { - font: inherit; -} +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/app/journal/page.tsx b/app/journal/page.tsx new file mode 100644 index 0000000..4e2135e --- /dev/null +++ b/app/journal/page.tsx @@ -0,0 +1,216 @@ +"use client"; + +import { useState } from "react"; +import { v4 as uuidv4 } from "uuid"; +import { useLiveQuery } from "dexie-react-hooks"; +import { db } from "@/lib/db"; +import { analyzeJournalWithLocalAI } from "@/lib/ai"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; + +const moods = [ + { id: "happy", label: "Mutlu", emoji: "😊" }, + { id: "neutral", label: "Nötr", emoji: "😐" }, + { id: "sad", label: "Üzgün", emoji: "😔" }, + { id: "angry", label: "Sinirli", emoji: "😠" }, +]; + +export default function JournalPage() { + const [content, setContent] = useState(""); + const [mood, setMood] = useState("happy"); + const [energy, setEnergy] = useState("3"); + const [isSubmitting, setIsSubmitting] = useState(false); + + // Dexie.js üzerinden günlükleri tarih sırasına göre çekiyoruz (en yeni en üstte) + const journals = useLiveQuery( + () => db.journals.orderBy("date").reverse().toArray() + ); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!content.trim()) return; + + setIsSubmitting(true); + const now = new Date().toISOString(); + const entryId = uuidv4(); + const currentContent = content; // Analiz için metni kopyala + + try { + // 1. Veriyi veritabanına hemen ekle (Kullanıcı beklemesin) + await db.journals.add({ + id: entryId, + date: now.split("T")[0], + mood, + energy: parseInt(energy, 10), + content: currentContent, + created_at: now, + updated_at: now, + }); + + // UI'ı Sıfırla + setContent(""); + setEnergy("3"); + setMood("happy"); + + // 2. Arka planda AI Analizi başlat + try { + const aiResult = await analyzeJournalWithLocalAI(currentContent); + if (aiResult) { + await db.journals.update(entryId, { + ai_tags: aiResult.ai_tags, + ai_sentiment_score: aiResult.ai_sentiment_score, + ai_summary: aiResult.ai_summary, + }); + + // Eğer AI görev önerdiyse Görevler tablosuna at (pending / onay bekliyor yapısı eklenebilir ama direkt atalım) + if (aiResult.suggested_tasks && aiResult.suggested_tasks.length > 0) { + for (const taskTitle of aiResult.suggested_tasks) { + await db.tasks.add({ + id: uuidv4(), + journal_id: entryId, + title: `AI Önerisi: ${taskTitle}`, + status: "todo", + ai_generated: true, + date: now.split("T")[0], + created_at: now, + }); + } + } + } + } catch (aiErr) { + console.error("Arka plan AI analizi hatası:", aiErr); + } + + } catch (error) { + console.error("Günlük kaydedilemedi:", error); + } finally { + setIsSubmitting(false); + } + }; + + const handleDelete = async (id: string) => { + if(confirm("Bu günlüğü silmek istediğinden emin misin?")) { + await db.journals.delete(id); + } + }; + + return ( +
+
+

Günlük

+

Zihnini boşalt, hislerini kaydet. Verilerin sadece cihazında kalır.

+
+ + + + Yeni Kayıt + Bugün nasıl hissediyorsun? + + +
+
+
+ +
+ {moods.map((m) => ( + + ))} +
+
+
+ + setEnergy(e.target.value)} + className="w-full" + /> +
+
+ +
+ +