diff --git a/app/login/actions.ts b/app/login/actions.ts
index 78adebe..e9b8cc0 100644
--- a/app/login/actions.ts
+++ b/app/login/actions.ts
@@ -99,5 +99,5 @@ export async function signOut() {
await callAuthAction<{ success: boolean }>('/sign-out')
revalidatePath('/', 'layout')
- redirect('/login')
+ return { redirectTo: '/login' } as const
}
diff --git a/components/layout/app-shell.tsx b/components/layout/app-shell.tsx
index 9a68dca..2e5cd23 100644
--- a/components/layout/app-shell.tsx
+++ b/components/layout/app-shell.tsx
@@ -20,6 +20,7 @@ import {
TooltipContent,
TooltipProvider,
TooltipTrigger,
+ toast,
} from "poyraz-ui/molecules";
import {
SidebarContent,
@@ -39,7 +40,8 @@ import { ChevronUp, LogOut, Settings } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
-import { usePathname } from "next/navigation";
+import { usePathname, useRouter } from "next/navigation";
+import { useTransition } from "react";
export type AppShellNavItem = {
title: string;
@@ -306,6 +308,21 @@ function ProgressSummary({ progress }: { progress: number }) {
}
function AccountMenu({ user, settingsHref }: { user: ShellUser; settingsHref: string }) {
+ const router = useRouter();
+ const [isSigningOut, startSignOutTransition] = useTransition();
+
+ function handleSignOut() {
+ startSignOutTransition(async () => {
+ try {
+ const result = await signOut();
+ router.replace(result.redirectTo);
+ router.refresh();
+ } catch {
+ toast.error("Çıkış yapılamadı. Lütfen tekrar deneyin.");
+ }
+ });
+ }
+
return (
@@ -352,17 +369,25 @@ function AccountMenu({ user, settingsHref }: { user: ShellUser; settingsHref: st
-
+
+ {isSigningOut ? "Çıkış yapılıyor" : "Çıkış yap"}
+
+
);
diff --git a/docs/self-hosted-redesign/phase-4-poyraz-ui-foundation.md b/docs/self-hosted-redesign/phase-4-poyraz-ui-foundation.md
index 27c1168..7d0445a 100644
--- a/docs/self-hosted-redesign/phase-4-poyraz-ui-foundation.md
+++ b/docs/self-hosted-redesign/phase-4-poyraz-ui-foundation.md
@@ -102,11 +102,10 @@ Renk tek başına anlam taşımaz; badge her zaman Türkçe durum metni içerir.
Branding servisi eski `--primary` benzeri uygulama tokenları yerine Poyraz v3 sözleşmesini server-side üretir:
- `--poyraz-primary` ve foreground/hover/active/scale rolleri
-- `--poyraz-accent` ve foreground/hover rolleri
- `--poyraz-ring` ve `--poyraz-focus-ring`
- `--poyraz-radius-xs` … `--poyraz-radius-xl`
-Değerler root `` içine SSR edilir; bu nedenle ilk render'da varsayılan renkten instance rengine geçiş parlaması oluşmaz. `system` renk modu ilk paint öncesi media query ile `.dark` sınıfına çevrilir ve işletim sistemi değişikliği dinlenir. Poyraz preset kalan surface, status, elevation, density ve motion rollerinin tek kaynağıdır.
+Değerler root `` içine SSR edilir; bu nedenle ilk render'da varsayılan renkten instance rengine geçiş parlaması oluşmaz. `system` renk modu ilk paint öncesi media query ile `.dark` sınıfına çevrilir ve işletim sistemi değişikliği dinlenir. `--poyraz-accent`, secondary, surface, status, elevation, density ve motion gibi semantik etkileşim rolleri inline branding katmanında ezilmez; light/dark karşılıklarının tek kaynağı Poyraz presetidir. Branding API'sindeki türetilmiş `accentColor` alanı mobil ve harici istemciler için korunur, web tema köprüsüne enjekte edilmez.
Recharts içindeki eski `hsl(var(--...))` ifadeleri geçerli Poyraz CSS renk tokenlarına taşındı.
diff --git a/scripts/phase4-ui-boundary.mjs b/scripts/phase4-ui-boundary.mjs
index 01e5a9b..b476bd3 100644
--- a/scripts/phase4-ui-boundary.mjs
+++ b/scripts/phase4-ui-boundary.mjs
@@ -7,7 +7,12 @@ const repoRoot = process.cwd();
const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
const globalsCss = fs.readFileSync(path.join(repoRoot, "app/globals.css"), "utf8");
const appShell = fs.readFileSync(path.join(repoRoot, "components/layout/app-shell.tsx"), "utf8");
+const loginActions = fs.readFileSync(path.join(repoRoot, "app/login/actions.ts"), "utf8");
const rootLayout = fs.readFileSync(path.join(repoRoot, "app/layout.tsx"), "utf8");
+const brandingService = fs.readFileSync(
+ path.join(repoRoot, "server/branding/service.ts"),
+ "utf8",
+);
const settingsPage = fs.readFileSync(
path.join(repoRoot, "app/(dashboard)/settings/page.tsx"),
"utf8",
@@ -63,6 +68,26 @@ assert.doesNotMatch(
/]*\binteractiveMotion=/s,
"Poyraz UI 3.0.2 leaks DropdownMenuItem interactiveMotion to the DOM",
);
+assert.doesNotMatch(
+ appShell,
+ /