import { useState } from "react"; import { useLocation, Link } from "wouter"; import { useListUsers, useCreateUser, useUpdateUser, useDeleteUser, useSetUserPassword, useListAuditLogs, useGetVersion, getListUsersQueryKey, getListAuditLogsQueryKey, getGetVersionQueryKey, } from "@workspace/api-client-react"; import { useQueryClient } from "@tanstack/react-query"; import { useAuth } from "@/hooks/use-auth"; import { Layout } from "@/components/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { PasswordInput } from "@/components/password-input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { useToast } from "@/hooks/use-toast"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Users, ScrollText, ShieldAlert, Plus, Pencil, Trash2, Clock, AlertTriangle, Wrench, Server } from "lucide-react"; import { format } from "date-fns"; import { AdminToolsTab } from "@/components/admin-tools-tab"; import { useTranslation } from "react-i18next"; export default function Admin() { const { t } = useTranslation(); const [, setLocation] = useLocation(); const { user, isAdmin, isLoading: authLoading } = useAuth(); const { toast } = useToast(); const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const [editUser, setEditUser] = useState<{ id: number; username: string; role: string; tier: string; authProvider?: string } | null>(null); const [editPassword, setEditPassword] = useState(""); const [deleteConfirm, setDeleteConfirm] = useState<{ id: number; username: string } | null>(null); const [newUsername, setNewUsername] = useState(""); const [newPassword, setNewPassword] = useState(""); const [newEmail, setNewEmail] = useState(""); const [newRole, setNewRole] = useState<"admin" | "user">("user"); const [newTier, setNewTier] = useState<"free" | "premium" | "enterprise">("free"); const { data: users, isLoading: loadingUsers } = useListUsers({ query: { queryKey: getListUsersQueryKey(), enabled: isAdmin }, }); const { data: auditLogs, isLoading: loadingLogs } = useListAuditLogs( { limit: 100 }, { query: { queryKey: getListAuditLogsQueryKey({ limit: 100 }), enabled: isAdmin } }, ); const { data: versionInfo } = useGetVersion({ query: { queryKey: getGetVersionQueryKey(), staleTime: Infinity, retry: false, enabled: isAdmin }, }); const createUser = useCreateUser(); const updateUser = useUpdateUser(); const deleteUser = useDeleteUser(); const setUserPassword = useSetUserPassword(); if (!authLoading && !isAdmin) { return ( {t("admin.accessRequired")} {t("admin.accessRequiredSub")} setLocation("/")}>{t("admin.goHome")} ); } const handleCreateUser = () => { if (!newUsername || !newPassword) return; createUser.mutate( { data: { username: newUsername, password: newPassword, email: newEmail || undefined, role: newRole, tier: newTier } }, { onSuccess: () => { toast({ title: t("admin.toastUserCreated"), description: t("admin.toastUserCreatedSub", { username: newUsername }) }); queryClient.invalidateQueries({ queryKey: getListUsersQueryKey() }); setCreateOpen(false); setNewUsername(""); setNewPassword(""); setNewEmail(""); setNewRole("user"); }, onError: (err) => { toast({ title: t("admin.toastUserCreateFailed"), description: err.data?.error ?? err.message, variant: "destructive" }); }, }, ); }; const handleUpdateUser = () => { if (!editUser) return; updateUser.mutate( { id: editUser.id, data: { role: editUser.role as "admin" | "user", tier: editUser.tier as "free" | "premium" | "enterprise" } }, { onSuccess: () => { toast({ title: t("admin.toastUserUpdated") }); queryClient.invalidateQueries({ queryKey: getListUsersQueryKey() }); setEditUser(null); }, onError: (err) => { toast({ title: t("admin.toastUserUpdateFailed"), description: err.data?.error ?? err.message, variant: "destructive" }); }, }, ); }; const handleSetUserPassword = () => { if (!editUser || !editPassword) return; if (editPassword.length < 6) { toast({ title: t("admin.toastPwTooShort"), description: t("admin.toastPwTooShortSub"), variant: "destructive" }); return; } setUserPassword.mutate( { id: editUser.id, data: { password: editPassword } }, { onSuccess: () => { toast({ title: t("admin.toastPwUpdated"), description: t("admin.toastPwUpdatedSub", { username: editUser.username }) }); setEditPassword(""); }, onError: (err) => { toast({ title: t("admin.toastPwSetFailed"), description: (err.data as { error?: string } | null)?.error ?? err.message, variant: "destructive" }); }, }, ); }; const handleDeleteUser = () => { if (!deleteConfirm) return; deleteUser.mutate( { id: deleteConfirm.id }, { onSuccess: () => { toast({ title: t("admin.toastUserDeleted"), description: t("admin.toastUserDeletedSub", { username: deleteConfirm.username }) }); queryClient.invalidateQueries({ queryKey: getListUsersQueryKey() }); setDeleteConfirm(null); }, onError: (err) => { toast({ title: t("admin.toastUserDeleteFailed"), description: (err.data as { error?: string } | null)?.error ?? err.message, variant: "destructive" }); }, }, ); }; function actionBadgeVariant(action: string): "default" | "secondary" | "destructive" | "outline" { if (action === "create") return "default"; if (action === "delete") return "destructive"; return "secondary"; } return ( {t("admin.panel")} {t("admin.panelSub")} {t("admin.redundancyDashboard")} {t("admin.tabUsers")} {t("admin.tabTools")} {t("admin.tabAudit")} {t("admin.tabSystem")} {t("admin.localUsers")} {t("admin.localUsersSub")} setCreateOpen(true)}> {t("admin.addUser")} {loadingUsers ? ( {[1, 2, 3].map((i) => )} ) : ( {users?.map((u) => ( {u.username.charAt(0).toUpperCase()} {u.username} {u.email && {u.email}} {u.role} {u.tier ?? "free"} {u.username !== user?.preferredUsername && ( <> setEditUser({ id: u.id, username: u.username, role: u.role, tier: u.tier ?? "free", authProvider: u.authProvider })} > setDeleteConfirm({ id: u.id, username: u.username })} > > )} ))} {(!users || users.length === 0) && ( {t("admin.noUsersYet")} )} )} {t("admin.auditLog")} {t("admin.auditLogSub")} {loadingLogs ? ( {[1, 2, 3, 4, 5].map((i) => )} ) : ( {auditLogs?.map((log) => ( {log.action} {log.entityType} {log.entityId && ( #{log.entityId} )} {format(new Date(log.createdAt), "dd.MM.yyyy HH:mm")} {t("admin.by")} {log.username} {log.changes && ( {log.changes.length > 120 ? log.changes.slice(0, 120) + "…" : log.changes} )} ))} {(!auditLogs || auditLogs.length === 0) && ( {t("admin.noAuditEntries")} )} )} {t("admin.system")} {t("admin.systemSub")} {t("admin.version")} {versionInfo?.version || "dev"} {t("admin.commit")} {versionInfo?.commitSha ? ( {versionInfo.commitSha.slice(0, 7)} ) : ( — )} {t("admin.buildDate")} {versionInfo?.buildDate ? format(new Date(versionInfo.buildDate), "dd.MM.yyyy HH:mm") : "—"} {t("admin.trashRetention")} {(versionInfo?.trashRetentionDays ?? 0) > 0 ? `${versionInfo?.trashRetentionDays} ${t("admin.days")}` : t("admin.keepForever")} {t("admin.createNewUser")} {t("admin.username")} setNewUsername(e.target.value)} placeholder={t("admin.usernamePlaceholder")} /> {t("admin.password")} setNewPassword(e.target.value)} placeholder={t("admin.minPasswordPlaceholder")} /> {t("admin.emailOptional")} setNewEmail(e.target.value)} placeholder={t("admin.emailPlaceholder")} /> {t("admin.role")} setNewRole(v as "admin" | "user")}> {t("admin.roleUser")} {t("admin.roleAdmin")} {t("admin.plan")} setNewTier(v as "free" | "premium" | "enterprise")}> {t("admin.planFree")} {t("admin.planPremium")} {t("admin.planEnterprise")} setCreateOpen(false)}>{t("common.cancel")} {createUser.isPending ? t("admin.creating") : t("admin.createUser")} { if (!open) { setEditUser(null); setEditPassword(""); } }}> {t("admin.editUser", { username: editUser?.username })} {t("admin.role")} setEditUser(editUser ? { ...editUser, role: v } : null)} > {t("admin.roleUser")} {t("admin.roleAdmin")} {t("admin.plan")} setEditUser(editUser ? { ...editUser, tier: v } : null)} > {t("admin.planFree")} {t("admin.planPremium")} {t("admin.planEnterprise")} {editUser?.authProvider !== "oidc" ? ( {t("admin.setPassword")} setEditPassword(e.target.value)} placeholder={t("admin.minPasswordPlaceholder")} data-testid="input-set-password" /> {t("admin.resetsPassword")} ) : ( {t("admin.idpManaged")} )} { setEditUser(null); setEditPassword(""); }}>{t("common.cancel")} {t("common.save")} {editUser?.authProvider !== "oidc" && ( {setUserPassword.isPending ? t("admin.setting") : t("admin.setPassword")} )} !open && setDeleteConfirm(null)}> {t("admin.deleteUser")} {t("admin.deleteUserConfirm", { username: deleteConfirm?.username })} setDeleteConfirm(null)}>{t("common.cancel")} {deleteUser.isPending ? t("detail.deleting") : t("common.delete")} ); }
{t("admin.accessRequiredSub")}
{t("admin.panelSub")}
{u.username}
{u.email}
{t("admin.noUsersYet")}
{t("admin.noAuditEntries")}
{t("admin.resetsPassword")}
{t("admin.deleteUserConfirm", { username: deleteConfirm?.username })}