import type { Dispatch, SetStateAction } from "react"; import { useTranslation } from "react-i18next"; import { deleteRole, deleteUser, revokeAllUserSessions, revokeSession, } from "@/main-axios"; import type { Role } from "@/main-axios"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { Activity, KeyRound, Pencil, Plus, RefreshCw, Share2, Trash2, User, } from "lucide-react"; import { toast } from "sonner"; import { AccordionSection } from "./AdminSettingsShared"; export type AdminUser = { id: string; username: string; isAdmin: boolean; isOidc: boolean; passwordHash?: string; }; export type AdminSession = { id: string; userId: string; username?: string; deviceType: string; deviceInfo: string; createdAt: string; expiresAt: string; lastActiveAt: string; isRevoked?: boolean; isCurrentSession?: boolean; }; type ApiErrorLike = { response?: { data?: { error?: string; }; }; }; function apiErrorMessage(error: unknown, fallback: string) { return (error as ApiErrorLike).response?.data?.error || fallback; } type UsersSectionProps = { open: boolean; onToggle: () => void; users: AdminUser[]; setUsers: Dispatch>; loadUsers: () => void; setCreateUserOpen: Dispatch>; setEditUserTarget: Dispatch>; setEditUserOpen: Dispatch>; setLinkAccountTarget: Dispatch< SetStateAction<{ id: string; username: string } | null> >; setLinkAccountOpen: Dispatch>; }; export function AdminUsersSection({ open, onToggle, users, setUsers, loadUsers, setCreateUserOpen, setEditUserTarget, setEditUserOpen, setLinkAccountTarget, setLinkAccountOpen, }: UsersSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("admin.usersCount", { count: users.length })}
{users.map((user) => { const authLabel = user.isOidc && user.passwordHash ? t("admin.authTypeDual") : user.isOidc ? t("admin.authTypeOidc") : t("admin.authTypeLocal"); return (
{user.username[0].toUpperCase()}
{user.username}
{user.isAdmin && ( {t("admin.adminBadge")} )} {authLabel}
{user.isOidc && ( )}
); })}
); } type SessionsSectionProps = { open: boolean; onToggle: () => void; sessions: AdminSession[]; setSessions: Dispatch>; loadSessions: () => void; }; export function AdminSessionsSection({ open, onToggle, sessions, setSessions, loadSessions, }: SessionsSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("admin.sessionsActive", { count: sessions.length })}
{sessions.map((session) => (
{session.username} {session.isCurrentSession && ( {t("admin.youBadge")} )}
{session.deviceInfo} {t("admin.sessionActive", { time: session.lastActiveAt })} {t("admin.sessionExpires", { time: session.expiresAt })}
))}
); } type RolesSectionProps = { open: boolean; onToggle: () => void; roles: Role[]; setRoles: Dispatch>; showCreateRole: boolean; setShowCreateRole: Dispatch>; newRoleName: string; setNewRoleName: Dispatch>; newRoleDisplayName: string; setNewRoleDisplayName: Dispatch>; newRoleDescription: string; setNewRoleDescription: Dispatch>; handleCreateRole: () => void; createRoleLoading: boolean; }; export function AdminRolesSection({ open, onToggle, roles, setRoles, showCreateRole, setShowCreateRole, newRoleName, setNewRoleName, newRoleDisplayName, setNewRoleDisplayName, newRoleDescription, setNewRoleDescription, handleCreateRole, createRoleLoading, }: RolesSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("admin.rolesCount", { count: roles.length })}
{showCreateRole && (
{t("admin.newRole")}
setNewRoleName(e.target.value)} className="text-xs" />
setNewRoleDisplayName(e.target.value)} className="text-xs" />