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
+67
View File
@@ -0,0 +1,67 @@
import { useState, type FormEvent } from 'react'
import { LogIn } from 'lucide-react'
import { Link } from 'react-router'
import { paths } from '@/components/layout/nav-items'
import { Logo } from '@/components/logo'
import { Button } from '@/components/ui/button'
import { useLogin } from '@/lib/auth'
import { useDocumentTitle } from '@/lib/use-document-title'
// GEÇİCİ: Faz 2 – Adım 5'te react-hook-form + zod ile gerçek giriş ekranı gelecek.
export function LoginPage() {
useDocumentTitle('Giriş')
const login = useLogin()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submit = (e: FormEvent) => {
e.preventDefault()
// Başarılı olunca RequireGuest, ?donus= adresine yönlendirir
login.mutate({ email, password, remember_me: true })
}
return (
<main className="mx-auto flex min-h-dvh max-w-sm flex-col justify-center gap-6 px-4">
<Logo size={56} withText />
<form onSubmit={submit} className="flex flex-col gap-3">
<input
className="h-11 rounded-md border bg-background px-3"
type="email"
placeholder="E-posta"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="h-11 rounded-md border bg-background px-3"
type="password"
placeholder="Şifre"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{login.isError && <p className="text-sm text-expense">{login.error.message}</p>}
<Button type="submit" size="lg" disabled={login.isPending}>
<LogIn /> Giriş
</Button>
</form>
<Link to={paths.register} className="text-center text-sm text-muted-foreground underline">
Hesabınız yok mu? Kayıt olun
</Link>
</main>
)
}
// GEÇİCİ: Adım 5'te kayıt formu (davet kodu dahil).
export function RegisterPage() {
useDocumentTitle('Kayıt')
return (
<main className="mx-auto flex min-h-dvh max-w-sm flex-col justify-center gap-6 px-4 text-center">
<Logo size={56} withText className="justify-center" />
<p className="text-muted-foreground">Kayıt ekranı Faz 2 – Adım 5'te geliştirilecek.</p>
<Link to={paths.login} className="text-sm underline">
Girişe dön
</Link>
</main>
)
}