feat(arayüz): sayfa yönlendirme, korumalı sayfalar ve uygulama iskeleti

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 00:46:17 +03:00
parent 906f05f62f
commit c6d4c30461
29 changed files with 690 additions and 74 deletions
+8 -3
View File
@@ -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 (
<div
@@ -23,14 +26,16 @@ export function ThemeToggle({ className }: { className?: string }) {
type="button"
role="radio"
aria-checked={theme === value}
title={label}
onClick={() => setTheme(value)}
className={cn(
'inline-flex min-h-11 min-w-11 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium text-muted-foreground transition-colors',
compact && 'flex-1 px-0',
theme === value && 'bg-background text-foreground shadow-sm',
)}
>
<Icon className="size-4" aria-hidden />
<span>{label}</span>
<span className={compact ? 'sr-only' : undefined}>{label}</span>
</button>
))}
</div>