From c6d4c3046130b3badc0112b1c8f381e4e3da4e90 Mon Sep 17 00:00:00 2001
From: suphicakir
Date: Thu, 12 Nov 2026 00:46:17 +0300
Subject: [PATCH] =?UTF-8?q?feat(aray=C3=BCz):=20sayfa=20y=C3=B6nlendirme,?=
=?UTF-8?q?=20korumal=C4=B1=20sayfalar=20ve=20uygulama=20iskeleti?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- react-router: Türkçe adresler (/defter, /plan, /birikim, ...), 404 sayfası
- Korumalı sayfalar: oturum yoksa /giris?donus=..., girişten sonra geri dönüş
- Açık yönlendirme koruması (safeReturnPath, 10 test)
- İskelet: mobilde 5 öğeli alt menü, masaüstünde sol kenar çubuğu
- Diğer sayfası (değerlendirme, raporlar, ayarlar, tema, çıkış)
- Arayüz metinleri lib/strings.ts'de toplanmaya başlandı
- Tasarım önizlemesi /dev/tasarim (yalnızca geliştirmede)
- Düzeltme: çıkışta önbellek temizliği ekranı güncellemiyordu
Co-Authored-By: Claude Opus 5.5
---
CLAUDE.md | 3 +
docs/MEMORY.md | 8 +-
docs/PHASES.md | 4 +-
frontend/package-lock.json | 43 ++++++
frontend/package.json | 1 +
frontend/src/components/layout/app-layout.tsx | 142 ++++++++++++++++++
frontend/src/components/layout/nav-items.ts | 51 +++++++
.../src/components/layout/require-auth.tsx | 42 ++++++
frontend/src/components/page.tsx | 36 +++++
frontend/src/components/theme-toggle.tsx | 11 +-
frontend/src/features/.gitkeep | 0
frontend/src/features/auth/login-page.tsx | 67 +++++++++
frontend/src/features/dashboard/page.tsx | 11 ++
.../dev/design-preview.tsx} | 67 +--------
.../src/features/errors/not-found-page.tsx | 23 +++
frontend/src/features/ledger/page.tsx | 11 ++
frontend/src/features/more/page.tsx | 40 +++++
frontend/src/features/planning/page.tsx | 11 ++
frontend/src/features/reports/page.tsx | 11 ++
frontend/src/features/review/page.tsx | 11 ++
frontend/src/features/savings/page.tsx | 11 ++
frontend/src/features/settings/page.tsx | 11 ++
frontend/src/lib/auth.ts | 6 +-
frontend/src/lib/return-path.test.ts | 15 ++
frontend/src/lib/return-path.ts | 10 ++
frontend/src/lib/strings.ts | 51 +++++++
frontend/src/lib/use-document-title.ts | 9 ++
frontend/src/main.tsx | 5 +-
frontend/src/router.tsx | 53 +++++++
29 files changed, 690 insertions(+), 74 deletions(-)
create mode 100644 frontend/src/components/layout/app-layout.tsx
create mode 100644 frontend/src/components/layout/nav-items.ts
create mode 100644 frontend/src/components/layout/require-auth.tsx
create mode 100644 frontend/src/components/page.tsx
delete mode 100644 frontend/src/features/.gitkeep
create mode 100644 frontend/src/features/auth/login-page.tsx
create mode 100644 frontend/src/features/dashboard/page.tsx
rename frontend/src/{App.tsx => features/dev/design-preview.tsx} (60%)
create mode 100644 frontend/src/features/errors/not-found-page.tsx
create mode 100644 frontend/src/features/ledger/page.tsx
create mode 100644 frontend/src/features/more/page.tsx
create mode 100644 frontend/src/features/planning/page.tsx
create mode 100644 frontend/src/features/reports/page.tsx
create mode 100644 frontend/src/features/review/page.tsx
create mode 100644 frontend/src/features/savings/page.tsx
create mode 100644 frontend/src/features/settings/page.tsx
create mode 100644 frontend/src/lib/return-path.test.ts
create mode 100644 frontend/src/lib/return-path.ts
create mode 100644 frontend/src/lib/strings.ts
create mode 100644 frontend/src/lib/use-document-title.ts
create mode 100644 frontend/src/router.tsx
diff --git a/CLAUDE.md b/CLAUDE.md
index 8008696..ca7b001 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -66,6 +66,9 @@ npm run icons # frontend/brand/*.svg → favicon, PWA, Apple, Tauri ikonl
- Frontend: Tailwind v4 (`src/index.css` token'ları), shadcn/ui (style `radix-nova`, `cn` paketi), `@/` → `src/`. Renkler: `bg-needs`/`text-needs-fg`/`bg-needs-soft` (wants, culture, unexpected aynı), `text-income`/`text-expense`/`text-warning`. Tutarlarda `money` sınıfı. Butonlar ≥44 px (shadcn boyutları büyütüldü).
- API: `lib/api.ts` (`api.get/post/...`, hata → `ApiError` Türkçe `message`), `lib/query.ts` (QueryClient + `queryKeys`), `lib/auth.ts` (`useMe` → undefined/null/MeResponse, `useLogin`/`useRegister`/`useLogout`).
- Tipler: `frontend/src/types/` **elle düzenlenmez**; backend'de DTO'ya `#[derive(TS)] #[ts(export)]` ekle → `npm run types`. İstek yapılarında `optional_fields`.
+- Sayfalar: `src/router.tsx`; adresler `components/layout/nav-items.ts` (`paths`); sayfa bileşenleri `features//page.tsx`; iskelet `components/layout/app-layout.tsx`. Sayfa başlığı `PageHeader` (sekme başlığını da ayarlar). Metinler `lib/strings.ts` (`t`).
+- Yönlendirme: `RequireAuth` (oturum yok → `/giris?donus=`), `RequireGuest` (oturum var → `donus` ya da panel; formlar ayrıca navigate etmez). `donus` daima `safeReturnPath` ile.
+- Geliştirmede tasarım önizlemesi: `/dev/tasarim`.
- Grup tanımları: `lib/groups.ts`; tema: `lib/theme.tsx` (+ `index.html` içindeki ilk-boya betiği); biçimlendirme: `lib/format.ts`.
- API örnekleri: `backend/api.http`
- Backend = lib (`src/lib.rs`) + bin (`src/main.rs`). Entegrasyon testleri `backend/tests/` (her test geçici `kakeibo_test_` DB, `tests/common` yardımcıları).
diff --git a/docs/MEMORY.md b/docs/MEMORY.md
index e30f16a..fdeed37 100644
--- a/docs/MEMORY.md
+++ b/docs/MEMORY.md
@@ -1,7 +1,7 @@
# 🧠 Proje Hafızası
## ▶️ Sıradaki Adım
-**Faz 2 – Adım 4:** react-router kurulumu, korumalı sayfalar (useMe null → /giris), layout: mobilde alt menü (📊 Panel, ✍️ Defter, 🗓️ Plan, 🐷 Birikim, ☰ Diğer), masaüstünde sol kenar çubuğu, boş sayfa iskeletleri, App.tsx önizlemesi kaldırılır (tasarım önizlemesi isterse /dev sayfası).
+**Faz 2 – Adım 5:** Giriş ve kayıt ekranları: react-hook-form + zod (+ @hookform/resolvers), shadcn input/label/checkbox/card, "Beni hatırla", kayıtta hane adı ve davet kodu (`?davet=KOD` ile önceden dolu), sonner toast, satır içi doğrulama mesajları. `features/auth/login-page.tsx` içindeki GEÇİCİ formlar değiştirilir. Sonra Faz 2 kapanır.
## 📌 Alınan Kararlar
- Sürüm kontrolü: Gitea (`https://gitea.ta1sph.info/suphicakir/kakeibo.git`), dal `main`.
@@ -25,7 +25,10 @@
- Logo: Taslak A (indigo zemin, spiral krem defter, ¥) seçildi. Kaynak `frontend/brand/logo.svg` + `logo-fullbleed.svg` (maskable/Apple, içerik %82). Uygulama içi `components/logo.tsx` aynı çizim (elle senkron tutulmalı). Manifest Faz 10'da (vite-plugin-pwa).
- ts-rs 12: `backend/.cargo/config.toml` → `TS_RS_EXPORT_DIR=../frontend/src/types`. TS adları: User, HouseholdSummary, Household, Member, Invite, CatalogItem, Catalog{Create,Update,Order}Request, RegisterRequest, LoginRequest, MeResponse, HealthResponse, ApiErrorBody, Group, Role.
- QueryClient: 4xx tekrar denenmez; herhangi bir sorgu 401 alırsa `me` = null. Çıkışta `qc.clear()`.
-- Sürümler: Rust 1.99.1 (edition 2024), Axum 0.8, React 19, Vite 8, TypeScript 7, Tailwind 4.3, shadcn 4.23, Vitest 5.
+- Adresler Türkçe: / (Panel), /defter, /plan, /birikim, /diger (mobil), /degerlendirme, /raporlar, /ayarlar, /giris, /kayit. Mobil alt menü: Panel, Defter, Plan, Birikim, Diğer.
+- Çıkış: `qc.clear()` KULLANMA (açık ekranlar silinen sorguya bağlı kalıyor) → önce `me=null`, sonra `removeQueries` (me hariç).
+- Canlıda SPA yönlendirmesi (Axum: bilinmeyen yol → index.html) Faz 3'te yapılmalı.
+- Sürümler: Rust 1.99.1 (edition 2024), Axum 0.8, React 19, Vite 8, TypeScript 7, Tailwind 4.3, shadcn 4.23, Vitest 5, react-router 8, TanStack Query 5.
## ❓ Açık Konular
- Üye çıkarma / rol değiştirme / sahipliği devretme henüz yok (Ayarlar ekranıyla birlikte, Faz 2+).
@@ -54,3 +57,4 @@
- **Adım 1:** Tailwind v4 (@tailwindcss/vite), shadcn init (radix, preset nova; `cn` paketi = shadcn'in clsx+tailwind-merge yerine geçeni, incelendi), Inter (@fontsource-variable, çevrimdışı). `index.css`: grup + durum token'ları, odak halkası, `money` utility, reduced-motion. `lib/format.ts` (+22 vitest), `lib/theme.tsx`, `lib/groups.ts`, `components/theme-toggle.tsx`. Butonlar 44/48 px'e büyütüldü. Geçici önizleme ekranı (App.tsx), 375 px headless ekran görüntüsüyle iki temada kontrol edildi.
- **Adım 2:** 3 logo taslağı (A indigo defter, B açık defter + para, C hanko) 512/64/32/16 px önizlemeyle sunuldu; kullanıcı A'yı seçti. `scripts/icons.sh` (`npm run icons`): favicon.svg/.ico, icon-192/512, icon-maskable-512, apple-touch-icon (180), brand/icon-1024 (Tauri). Maskable güvenli alan görsel olarak doğrulandı. `index.html`: ikon linkleri, theme-color, description.
- **Adım 3:** ts-rs eklendi (17 tip), health ve hata gövdesi struct'a çevrildi (`ApiErrorBody`). Frontend: @tanstack/react-query, `lib/api.ts` (+7 vitest), `lib/query.ts`, `lib/auth.ts`, main.tsx'te QueryClientProvider. Geçici oturum paneli. Tarayıcı e2e (CDP betiği): yanlış şifre mesajı, giriş, yenilemede kalıcılık, httpOnly (document.cookie'de yok), çıkış — hepsi geçti.
+- **Adım 4:** react-router 8 (createBrowserRouter), `RequireAuth`/`RequireGuest`/`Splash`, `AppLayout` (masaüstü kenar çubuğu + mobil üst çubuk + 5'li alt menü, safe-area), Diğer sayfası, 7 yer tutucu sayfa, 404, `lib/strings.ts`, `useDocumentTitle`, `lib/return-path.ts` (+10 test, açık yönlendirme koruması), ThemeToggle `compact`. Tasarım önizlemesi → `/dev/tasarim` (yalnızca DEV). CDP e2e (10 kontrol) iki hata buldu, düzeltildi: giriş sonrası `donus` yarışı, çıkışta `qc.clear()`. Masaüstü içerik boşluğu düzeltildi.
diff --git a/docs/PHASES.md b/docs/PHASES.md
index b510a9d..6e11d41 100644
--- a/docs/PHASES.md
+++ b/docs/PHASES.md
@@ -35,5 +35,5 @@
- ✅ 1. Tailwind v4 + shadcn/ui (radix, nova), tasarım token'ları (4 grup + durum renkleri, AA doğrulandı), Inter, tema (aydınlık/karanlık/sistem), `lib/format.ts` + Vitest
- ✅ 2. Logo ve uygulama ikonu (SVG), favicon, PWA/Apple ikonları
- ✅ 3. `lib/api.ts`, TanStack Query, ts-rs tip üretimi, oturum durumu
-- 🔄 4. Router (react-router), korumalı sayfalar, layout: mobil alt menü / masaüstü sol menü
-- ⏳ 5. Giriş / kayıt ekranları (react-hook-form + zod, beni hatırla, davet kodu), arayüz metinleri tek dosyada
+- ✅ 4. Router (react-router), korumalı sayfalar, layout: mobil alt menü / masaüstü sol menü
+- 🔄 5. Giriş / kayıt ekranları (react-hook-form + zod, beni hatırla, davet kodu), arayüz metinleri tek dosyada
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 887c09e..672ec49 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -17,6 +17,7 @@
"radix-ui": "^1.8.1",
"react": "^19.3.1",
"react-dom": "^19.3.1",
+ "react-router": "^8.5.1",
"shadcn": "^4.23.0",
"tailwindcss": "^4.3.5",
"tw-animate-css": "^1.4.0"
@@ -2682,6 +2683,12 @@
"integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==",
"license": "MIT"
},
+ "node_modules/@remix-run/route-pattern": {
+ "version": "0.22.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/route-pattern/-/route-pattern-0.22.1.tgz",
+ "integrity": "sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==",
+ "license": "MIT"
+ },
"node_modules/@rolldown/binding-android-arm-eabi": {
"version": "1.2.19",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.19.tgz",
@@ -4225,6 +4232,12 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
+ "node_modules/cookie-es": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz",
+ "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==",
+ "license": "MIT"
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -6018,6 +6031,36 @@
}
}
},
+ "node_modules/react-router": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-8.5.1.tgz",
+ "integrity": "sha512-5u1eQ46QjuXjNfEnLwzrnou1Bm5dquO3xjVYkkMYCudtkicD2AQHtx+fvXzPATtPJx8TW6DR1x1TDRqsfHyZBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/route-pattern": "^0.22.1",
+ "cookie-es": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=22.22.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=19.2.0",
+ "@types/react-dom": ">=19.2.0",
+ "react": ">=19.2.7",
+ "react-dom": ">=19.2.7"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 4a17ff5..10714ce 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -21,6 +21,7 @@
"radix-ui": "^1.8.1",
"react": "^19.3.1",
"react-dom": "^19.3.1",
+ "react-router": "^8.5.1",
"shadcn": "^4.23.0",
"tailwindcss": "^4.3.5",
"tw-animate-css": "^1.4.0"
diff --git a/frontend/src/components/layout/app-layout.tsx b/frontend/src/components/layout/app-layout.tsx
new file mode 100644
index 0000000..bd9b191
--- /dev/null
+++ b/frontend/src/components/layout/app-layout.tsx
@@ -0,0 +1,142 @@
+import { LogOut } from 'lucide-react'
+import { NavLink, Outlet, ScrollRestoration, useLocation } from 'react-router'
+import { Logo } from '@/components/logo'
+import { ThemeToggle } from '@/components/theme-toggle'
+import { Button } from '@/components/ui/button'
+import { useLogout, useMe } from '@/lib/auth'
+import { t } from '@/lib/strings'
+import { cn } from '@/lib/utils'
+import { bottomNav, moreNav, paths, sideNav } from './nav-items'
+
+/**
+ * Oturum açılmış sayfaların iskeleti.
+ * - Mobil (< md): üstte logo, altta 5 öğeli sabit menü
+ * - Masaüstü (≥ md): solda kenar çubuğu
+ */
+export function AppLayout() {
+ return (
+
+
+
+ {/* Mobil üst çubuk */}
+
+
+
+
+ {/* md:pl-64 → kenar çubuğu kadar boşluk; içerik bu alanda ortalanır.
+ pb-28 → mobilde alt menünün altında içerik kalmasın. */}
+
+ )
+}
+
+export function LogoutButton({ className }: { className?: string }) {
+ const logout = useLogout()
+ return (
+
+ )
+}
diff --git a/frontend/src/components/layout/nav-items.ts b/frontend/src/components/layout/nav-items.ts
new file mode 100644
index 0000000..6086806
--- /dev/null
+++ b/frontend/src/components/layout/nav-items.ts
@@ -0,0 +1,51 @@
+import {
+ CalendarDays,
+ ChartLine,
+ ClipboardCheck,
+ LayoutDashboard,
+ Menu,
+ NotebookPen,
+ PiggyBank,
+ Settings,
+ type LucideIcon,
+} from 'lucide-react'
+import { t } from '@/lib/strings'
+
+export type NavItem = { to: string; label: string; icon: LucideIcon }
+
+/** Sayfa adresleri tek yerde */
+export const paths = {
+ dashboard: '/',
+ ledger: '/defter',
+ planning: '/plan',
+ savings: '/birikim',
+ more: '/diger',
+ review: '/degerlendirme',
+ reports: '/raporlar',
+ settings: '/ayarlar',
+ login: '/giris',
+ register: '/kayit',
+} as const
+
+const dashboard: NavItem = { to: paths.dashboard, label: t.nav.dashboard, icon: LayoutDashboard }
+const ledger: NavItem = { to: paths.ledger, label: t.nav.ledger, icon: NotebookPen }
+const planning: NavItem = { to: paths.planning, label: t.nav.planning, icon: CalendarDays }
+const savings: NavItem = { to: paths.savings, label: t.nav.savings, icon: PiggyBank }
+const review: NavItem = { to: paths.review, label: t.nav.review, icon: ClipboardCheck }
+const reports: NavItem = { to: paths.reports, label: t.nav.reports, icon: ChartLine }
+const settings: NavItem = { to: paths.settings, label: t.nav.settings, icon: Settings }
+
+/** Mobil alt menü: 5 öğe (KAKEIBO_PROMPT §9) */
+export const bottomNav: NavItem[] = [
+ dashboard,
+ ledger,
+ planning,
+ savings,
+ { to: paths.more, label: t.nav.more, icon: Menu },
+]
+
+/** "Diğer" altında toplananlar (mobil) */
+export const moreNav: NavItem[] = [review, reports, settings]
+
+/** Masaüstü sol menü: hepsi doğrudan */
+export const sideNav: NavItem[] = [dashboard, ledger, planning, review, savings, reports, settings]
diff --git a/frontend/src/components/layout/require-auth.tsx b/frontend/src/components/layout/require-auth.tsx
new file mode 100644
index 0000000..8f3191f
--- /dev/null
+++ b/frontend/src/components/layout/require-auth.tsx
@@ -0,0 +1,42 @@
+import { Navigate, Outlet, useLocation, useSearchParams } from 'react-router'
+import { Logo } from '@/components/logo'
+import { useMe } from '@/lib/auth'
+import { safeReturnPath } from '@/lib/return-path'
+import { t } from '@/lib/strings'
+import { paths } from './nav-items'
+
+/** Oturum kontrol edilirken gösterilen tam ekran açılış. */
+export function Splash() {
+ return (
+
+
+
+ )
+}
+
+/** Oturum gerektiren sayfalar. Oturum yoksa → /giris?donus=<şimdiki adres> */
+export function RequireAuth() {
+ const me = useMe()
+ const location = useLocation()
+
+ if (me.isPending) return
+ if (!me.data) {
+ const back = location.pathname + location.search
+ const to = back === '/' ? paths.login : `${paths.login}?donus=${encodeURIComponent(back)}`
+ return
+ }
+ return
+}
+
+/**
+ * Yalnızca oturumsuzken: giriş/kayıt.
+ * Oturum açılınca (giriş/kayıt başarılı olduğunda da) `?donus=` adresine, yoksa panele gönderir.
+ * Yönlendirme yalnızca burada yapılır — formlar ayrıca `navigate` çağırmaz (yarış durumu olmasın).
+ */
+export function RequireGuest() {
+ const me = useMe()
+ const [params] = useSearchParams()
+ if (me.isPending) return
+ if (me.data) return
+ return
+}
diff --git a/frontend/src/components/page.tsx b/frontend/src/components/page.tsx
new file mode 100644
index 0000000..72b64ae
--- /dev/null
+++ b/frontend/src/components/page.tsx
@@ -0,0 +1,36 @@
+import type { ReactNode } from 'react'
+import { Construction } from 'lucide-react'
+import { t } from '@/lib/strings'
+import { useDocumentTitle } from '@/lib/use-document-title'
+
+/** Sayfa başlığı + açıklama; sekme başlığını da ayarlar. */
+export function PageHeader({
+ title,
+ description,
+ actions,
+}: {
+ title: string
+ description?: string
+ actions?: ReactNode
+}) {
+ useDocumentTitle(title)
+ return (
+
+
+
{title}
+ {description &&
{description}
}
+
+ {actions}
+
+ )
+}
+
+/** Henüz geliştirilmemiş ekranlar için yer tutucu. `when`: "Faz 4'te" gibi (Türkçe ek uyumu için tam ifade). */
+export function ComingSoon({ when }: { when: string }) {
+ return (
+
+
+
{t.comingSoon(when)}
+
+ )
+}
diff --git a/frontend/src/components/theme-toggle.tsx b/frontend/src/components/theme-toggle.tsx
index 6f396fd..d0d46ed 100644
--- a/frontend/src/components/theme-toggle.tsx
+++ b/frontend/src/components/theme-toggle.tsx
@@ -8,8 +8,11 @@ const OPTIONS: { value: Theme; label: string; icon: typeof Sun }[] = [
{ value: 'system', label: 'Sistem', icon: Monitor },
]
-/** Aydınlık / Karanlık / Sistem seçici (radyo grubu gibi davranır, klavyeyle gezilebilir). */
-export function ThemeToggle({ className }: { className?: string }) {
+/**
+ * Aydınlık / Karanlık / Sistem seçici (radyo grubu gibi davranır, klavyeyle gezilebilir).
+ * `compact`: yalnızca ikonlar (dar alanlar için; etiketler ekran okuyucuda kalır).
+ */
+export function ThemeToggle({ className, compact = false }: { className?: string; compact?: boolean }) {
const { theme, setTheme } = useTheme()
return (