feat(admin): kullanıcı yönetimi tablosu ve diyalogları (mock)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 01:35:10 +03:00
parent 1cf9fd9d8c
commit 3b94bceaf0
9 changed files with 1282 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
import type { SystemRole } from "@/shared/schemas/common";
export type AdminAction =
| { type: "delete" }
| { type: "ban" }
| { type: "setRole"; role: SystemRole }
| { type: "edit" }
| { type: "setPassword" };
export type GuardContext = {
actorId: string;
target: { id: string; role: SystemRole };
/** Sistemdeki toplam admin sayısı (hedef dahil). */
adminCount: number;
};
export type GuardViolation = "self" | "lastAdmin";
/**
* Admin işlem kuralları (arayüz ve sunucu aynı fonksiyonu kullanır):
* - Admin kendini silemez, askıya alamaz ve kendi admin rolünü kaldıramaz.
* - Sistemdeki son admin silinemez, askıya alınamaz veya rolü düşürülemez.
*/
export function checkAdminAction(action: AdminAction, ctx: GuardContext): GuardViolation | null {
const isSelf = ctx.actorId === ctx.target.id;
const removesAdmin =
ctx.target.role === "admin" &&
(action.type === "delete" ||
action.type === "ban" ||
(action.type === "setRole" && action.role !== "admin"));
if (isSelf && (action.type === "delete" || action.type === "ban" || removesAdmin)) {
return "self";
}
if (removesAdmin && ctx.adminCount <= 1) return "lastAdmin";
return null;
}
/** Kullanıcı adı / e-posta benzersizlik kontrolü (normalize edilmiş değerlerle). */
export function findUniqueConflicts(
users: ReadonlyArray<{ id: string; username: string; email?: string }>,
values: { username: string; email?: string },
excludeId?: string,
): { username?: "taken"; email?: "taken" } {
const others = users.filter((u) => u.id !== excludeId);
return {
...(others.some((u) => u.username === values.username) ? { username: "taken" as const } : {}),
...(values.email && others.some((u) => u.email === values.email)
? { email: "taken" as const }
: {}),
};
}