diff --git a/app/layout.tsx b/app/layout.tsx
new file mode 100644
index 0000000..2a836e4
--- /dev/null
+++ b/app/layout.tsx
@@ -0,0 +1,19 @@
+import type { Metadata } from "next";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "Mood Tracker MVP",
+ description: "LocalStorage tabanli minimal mood dashboard uygulamasi.",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/app/page.tsx b/app/page.tsx
new file mode 100644
index 0000000..f071c30
--- /dev/null
+++ b/app/page.tsx
@@ -0,0 +1,437 @@
+"use client";
+
+import { motion } from "framer-motion";
+import {
+ Cell,
+ Legend,
+ Line,
+ LineChart,
+ Pie,
+ PieChart,
+ ResponsiveContainer,
+ Tooltip,
+ XAxis,
+ YAxis,
+} from "recharts";
+import { FormEvent, useEffect, useMemo, useState } from "react";
+
+type Mood = "happy" | "neutral" | "sad" | "angry";
+type MoodScore = 4 | 3 | 2 | 1;
+type Energy = 1 | 2 | 3 | 4 | 5;
+
+type MoodEntry = {
+ id: string;
+ date: string;
+ mood: Mood;
+ mood_score: MoodScore;
+ energy: Energy;
+};
+
+const STORAGE_KEY = "mood-tracker-entries";
+
+const moodConfig: Record<
+ Mood,
+ { label: string; icon: string; score: MoodScore; color: string }
+> = {
+ happy: { label: "Mutlu", icon: "😊", score: 4, color: "#2f7d63" },
+ neutral: { label: "Nötr", icon: "😐", score: 3, color: "#4f7fbf" },
+ sad: { label: "Üzgün", icon: "😔", score: 2, color: "#e3aa38" },
+ angry: { label: "Sinirli", icon: "😠", score: 1, color: "#d8644a" },
+};
+
+const moodOptions = Object.entries(moodConfig) as Array<
+ [Mood, (typeof moodConfig)[Mood]]
+>;
+
+const today = () => new Date().toISOString().slice(0, 10);
+
+const createId = () =>
+ typeof crypto !== "undefined" && "randomUUID" in crypto
+ ? crypto.randomUUID()
+ : `${Date.now()}-${Math.random().toString(16).slice(2)}`;
+
+export default function Home() {
+ const [entries, setEntries] = useState([]);
+ const [date, setDate] = useState(today);
+ const [mood, setMood] = useState("happy");
+ const [energy, setEnergy] = useState(3);
+ const [message, setMessage] = useState("");
+ const [isLoaded, setIsLoaded] = useState(false);
+
+ useEffect(() => {
+ const rawEntries = window.localStorage.getItem(STORAGE_KEY);
+
+ if (!rawEntries) {
+ setIsLoaded(true);
+ return;
+ }
+
+ try {
+ const parsedEntries = JSON.parse(rawEntries) as MoodEntry[];
+ if (Array.isArray(parsedEntries)) {
+ setEntries(sortEntries(parsedEntries));
+ }
+ } catch {
+ window.localStorage.removeItem(STORAGE_KEY);
+ } finally {
+ setIsLoaded(true);
+ }
+ }, []);
+
+ useEffect(() => {
+ if (!isLoaded) {
+ return;
+ }
+
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));
+ }, [entries, isLoaded]);
+
+ const lineData = useMemo(
+ () =>
+ entries.map((entry) => ({
+ date: formatShortDate(entry.date),
+ mood: entry.mood_score,
+ energy: entry.energy,
+ })),
+ [entries],
+ );
+
+ const pieData = useMemo(
+ () =>
+ moodOptions
+ .map(([key, config]) => ({
+ name: config.label,
+ value: entries.filter((entry) => entry.mood === key).length,
+ color: config.color,
+ }))
+ .filter((item) => item.value > 0),
+ [entries],
+ );
+
+ const insights = useMemo(() => buildInsights(entries), [entries]);
+
+ function handleSubmit(event: FormEvent) {
+ event.preventDefault();
+
+ if (!date) {
+ setMessage("Lütfen tarih seç.");
+ return;
+ }
+
+ if (!mood) {
+ setMessage("Lütfen ruh halini seç.");
+ return;
+ }
+
+ if (energy < 1 || energy > 5) {
+ setMessage("Enerji seviyesi 1 ile 5 arasında olmalı.");
+ return;
+ }
+
+ const moodScore = moodConfig[mood].score;
+
+ setEntries((currentEntries) => {
+ const existingEntry = currentEntries.find((entry) => entry.date === date);
+ const nextEntry: MoodEntry = {
+ id: existingEntry?.id ?? createId(),
+ date,
+ mood,
+ mood_score: moodScore,
+ energy,
+ };
+
+ const nextEntries = existingEntry
+ ? currentEntries.map((entry) =>
+ entry.date === date ? nextEntry : entry,
+ )
+ : [...currentEntries, nextEntry];
+
+ return sortEntries(nextEntries);
+ });
+
+ setMessage("Kayıt kaydedildi. Dashboard güncellendi.");
+ }
+
+ return (
+
+
+
+
+
+
+
Bugünün kaydı
+
+ Aynı tarih tekrar kaydedilirse eski kayıt güncellenir.
+
+
+
+
+ setDate(event.target.value)}
+ className="mt-2 w-full rounded-md border border-ink/15 bg-mist px-3 py-2 outline-none transition focus:border-leaf focus:ring-2 focus:ring-leaf/20"
+ />
+
+
+
+
+ setEnergy(Number(event.target.value) as Energy)}
+ className="mt-3 w-full accent-leaf"
+ />
+
+ 1
+ 2
+ 3
+ 4
+ 5
+
+
+
+
+ {message ? (
+
+ {message}
+
+ ) : null}
+
+
+
+
+ {entries.length > 0 ? (
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ {pieData.length > 0 ? (
+
+
+
+ {pieData.map((item) => (
+ |
+ ))}
+
+
+
+
+
+ ) : (
+
+ )}
+
+
+
+ {insights.length > 0 ? (
+
+ {insights.map((insight) => (
+ -
+ {insight}
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+function ChartCard({
+ title,
+ children,
+}: {
+ title: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {title}
+ {children}
+
+ );
+}
+
+function EmptyState({ text }: { text: string }) {
+ return (
+
+ {text}
+
+ );
+}
+
+function sortEntries(entries: MoodEntry[]) {
+ return [...entries].sort((a, b) => a.date.localeCompare(b.date));
+}
+
+function formatShortDate(date: string) {
+ return new Intl.DateTimeFormat("tr-TR", {
+ day: "2-digit",
+ month: "short",
+ }).format(new Date(`${date}T00:00:00`));
+}
+
+function buildInsights(entries: MoodEntry[]) {
+ if (entries.length === 0) {
+ return [];
+ }
+
+ const latestSeven = [...entries]
+ .sort((a, b) => b.date.localeCompare(a.date))
+ .slice(0, 7);
+ const moodAverage = average(latestSeven.map((entry) => entry.mood_score));
+ const energyAverage = average(latestSeven.map((entry) => entry.energy));
+ const mostFrequentMood = getMostFrequentMood(entries);
+ const insights = [
+ moodAverage >= 3
+ ? "Son 7 kayıtta genel ruh halin pozitif görünüyor."
+ : "Son 7 kayıtta ruh hali ortalaman düşük görünüyor.",
+ ];
+
+ if (energyAverage < 3) {
+ insights.push("Enerji seviyen son kayıtlarda düşük seyrediyor.");
+ } else {
+ insights.push("Enerji seviyen son kayıtlarda dengeli görünüyor.");
+ }
+
+ if (mostFrequentMood) {
+ insights.push(
+ `En sık görülen ruh halin: ${moodConfig[mostFrequentMood].label}.`,
+ );
+ }
+
+ return insights;
+}
+
+function average(values: number[]) {
+ return values.reduce((total, value) => total + value, 0) / values.length;
+}
+
+function getMostFrequentMood(entries: MoodEntry[]) {
+ const counts = entries.reduce>(
+ (currentCounts, entry) => {
+ currentCounts[entry.mood] += 1;
+ return currentCounts;
+ },
+ { happy: 0, neutral: 0, sad: 0, angry: 0 },
+ );
+
+ return moodOptions.reduce((winner, [moodKey]) => {
+ if (!winner || counts[moodKey] > counts[winner]) {
+ return moodKey;
+ }
+
+ return winner;
+ }, null);
+}