feat(admin): Server Action'larla kullanıcı yönetimi ve gerçek oturumlu sayfalar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { UsersTable } from "@/features/admin-users/components/users-table";
|
||||
import { MOCK_ADMIN_USERS } from "@/features/admin-users/mock";
|
||||
import { MOCK_SESSION_USER } from "@/features/auth/mock";
|
||||
import { listAdminUsers } from "@/features/admin-users/queries";
|
||||
import { tr } from "@/i18n/tr";
|
||||
import { requireRole } from "@/lib/dal";
|
||||
|
||||
export const metadata: Metadata = { title: tr.adminUsers.title };
|
||||
|
||||
export default function AdminUsersPage() {
|
||||
// FAZ 3: const users = await listUsers() (Better Auth admin API)
|
||||
export default async function AdminUsersPage() {
|
||||
const [admin, users] = await Promise.all([requireRole("admin"), listAdminUsers()]);
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={tr.adminUsers.title} description={tr.adminUsers.description} />
|
||||
<UsersTable initialUsers={MOCK_ADMIN_USERS} currentUserId={MOCK_SESSION_USER.id} />
|
||||
<UsersTable users={users} currentUserId={admin.id} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { MOCK_SESSION_USER } from "@/features/auth/mock";
|
||||
import { requireRole } from "@/lib/dal";
|
||||
|
||||
/**
|
||||
* Yalnızca sistem admini. Admin olmayana 404 (sayfanın varlığını bile göstermez).
|
||||
* FAZ 3: `await requireRole("admin")` (lib/dal.ts) — sunucu tarafı gerçek kontrol.
|
||||
*/
|
||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||
if (MOCK_SESSION_USER.role !== "admin") notFound();
|
||||
/** Yalnızca sistem admini; diğerlerine 404. Sayfalar ayrıca kendi kontrolünü yapar. */
|
||||
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||
await requireRole("admin");
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { Metadata } from "next";
|
||||
import { Suspense } from "react";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { MOCK_SESSION_USER } from "@/features/auth/mock";
|
||||
import { SummaryCardsSkeleton } from "@/features/dashboard/components/summary-cards-skeleton";
|
||||
import { tr } from "@/i18n/tr";
|
||||
import { requireSession } from "@/lib/dal";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatMoney } from "@/shared/format";
|
||||
|
||||
@@ -51,8 +51,8 @@ async function SummaryCards() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const user = MOCK_SESSION_USER;
|
||||
export default async function DashboardPage() {
|
||||
const user = await requireSession();
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
import { Suspense } from "react";
|
||||
import { AppHeader } from "@/components/layout/app-header";
|
||||
import { AppShellFallback } from "@/components/layout/app-shell-fallback";
|
||||
import { AppSidebar } from "@/components/layout/app-sidebar";
|
||||
import { BottomNav } from "@/components/layout/bottom-nav";
|
||||
import { QuickAddProvider } from "@/components/layout/quick-add";
|
||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { MOCK_SESSION_USER } from "@/features/auth/mock";
|
||||
import { MOCK_HOUSEHOLDS } from "@/features/households/mock";
|
||||
import { requireSession } from "@/lib/dal";
|
||||
import { APP_VERSION } from "@/lib/version";
|
||||
|
||||
/**
|
||||
* Oturum gerektiren alan: yan menü (masaüstü), üst bar, alt menü (mobil), hızlı ekle.
|
||||
* FAZ 3: `const session = await requireSession()` — oturum yoksa /login.
|
||||
* Oturum gerektiren alan. Oturum okuması istek anında yapılır (Cache Components), bu yüzden
|
||||
* kullanıcıya bağlı kabuk Suspense içinde render edilir. Her sayfa ayrıca kendi yetkisini
|
||||
* DAL üzerinden kontrol eder (layout istemci gezinmesinde yeniden çalışmaz).
|
||||
*/
|
||||
export default function AppLayout({ children }: { children: React.ReactNode }) {
|
||||
const user = MOCK_SESSION_USER;
|
||||
return (
|
||||
<Suspense fallback={<AppShellFallback />}>
|
||||
<AuthenticatedShell>{children}</AuthenticatedShell>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
async function AuthenticatedShell({ children }: { children: React.ReactNode }) {
|
||||
const user = await requireSession();
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<QuickAddProvider>
|
||||
<AppSidebar isAdmin={user.role === "admin"} version={APP_VERSION} />
|
||||
<SidebarInset>
|
||||
{/* FAZ 5: kullanıcının üye olduğu haneler */}
|
||||
<AppHeader user={user} households={MOCK_HOUSEHOLDS} version={APP_VERSION} />
|
||||
{/* Mobilde alt menü yüksekliği kadar boşluk */}
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 pb-24 md:p-6 md:pb-6">{children}</div>
|
||||
|
||||
Reference in New Issue
Block a user