import React from "react"; import {useSidebar} from "@/components/ui/sidebar"; import {Separator} from "@/components/ui/separator.tsx"; import {Button} from "@/components/ui/button.tsx"; import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx"; import {Checkbox} from "@/components/ui/checkbox.tsx"; import {Input} from "@/components/ui/input.tsx"; import {Label} from "@/components/ui/label.tsx"; import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs.tsx"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table.tsx"; import {Shield, Trash2, Users} from "lucide-react"; import {toast} from "sonner"; import {useTranslation} from "react-i18next"; import { getOIDCConfig, getRegistrationAllowed, getUserList, updateRegistrationAllowed, updateOIDCConfig, makeUserAdmin, removeAdminStatus, deleteUser } from "@/ui/main-axios.ts"; function getCookie(name: string) { return document.cookie.split('; ').reduce((r, v) => { const parts = v.split('='); return parts[0] === name ? decodeURIComponent(parts[1]) : r; }, ""); } interface AdminSettingsProps { isTopbarOpen?: boolean; } export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React.ReactElement { const {t} = useTranslation(); const {state: sidebarState} = useSidebar(); const [allowRegistration, setAllowRegistration] = React.useState(true); const [regLoading, setRegLoading] = React.useState(false); const [oidcConfig, setOidcConfig] = React.useState({ client_id: '', client_secret: '', issuer_url: '', authorization_url: '', token_url: '', identifier_path: 'sub', name_path: 'name', scopes: 'openid email profile', userinfo_url: '' }); const [oidcLoading, setOidcLoading] = React.useState(false); const [oidcError, setOidcError] = React.useState(null); const [users, setUsers] = React.useState>([]); const [usersLoading, setUsersLoading] = React.useState(false); const [newAdminUsername, setNewAdminUsername] = React.useState(""); const [makeAdminLoading, setMakeAdminLoading] = React.useState(false); const [makeAdminError, setMakeAdminError] = React.useState(null); React.useEffect(() => { const jwt = getCookie("jwt"); if (!jwt) return; getOIDCConfig() .then(res => { if (res) setOidcConfig(res); }) .catch(() => { }); fetchUsers(); }, []); React.useEffect(() => { getRegistrationAllowed() .then(res => { if (typeof res?.allowed === 'boolean') { setAllowRegistration(res.allowed); } }) .catch(() => { }); }, []); const fetchUsers = async () => { const jwt = getCookie("jwt"); if (!jwt) return; setUsersLoading(true); try { const response = await getUserList(); setUsers(response.users); } finally { setUsersLoading(false); } }; const handleToggleRegistration = async (checked: boolean) => { setRegLoading(true); const jwt = getCookie("jwt"); try { await updateRegistrationAllowed(checked); setAllowRegistration(checked); } finally { setRegLoading(false); } }; const handleOIDCConfigSubmit = async (e: React.FormEvent) => { e.preventDefault(); setOidcLoading(true); setOidcError(null); const required = ['client_id', 'client_secret', 'issuer_url', 'authorization_url', 'token_url']; const missing = required.filter(f => !oidcConfig[f as keyof typeof oidcConfig]); if (missing.length > 0) { setOidcError(t('admin.missingRequiredFields', { fields: missing.join(', ') })); setOidcLoading(false); return; } const jwt = getCookie("jwt"); try { await updateOIDCConfig(oidcConfig); toast.success(t('admin.oidcConfigurationUpdated')); } catch (err: any) { setOidcError(err?.response?.data?.error || t('admin.failedToUpdateOidcConfig')); } finally { setOidcLoading(false); } }; const handleOIDCConfigChange = (field: string, value: string) => { setOidcConfig(prev => ({...prev, [field]: value})); }; const handleMakeUserAdmin = async (e: React.FormEvent) => { e.preventDefault(); if (!newAdminUsername.trim()) return; setMakeAdminLoading(true); setMakeAdminError(null); const jwt = getCookie("jwt"); try { await makeUserAdmin(newAdminUsername.trim()); toast.success(t('admin.userIsNowAdmin', { username: newAdminUsername })); setNewAdminUsername(""); fetchUsers(); } catch (err: any) { setMakeAdminError(err?.response?.data?.error || t('admin.failedToMakeUserAdmin')); } finally { setMakeAdminLoading(false); } }; const handleRemoveAdminStatus = async (username: string) => { if (!confirm(t('admin.removeAdminStatus', { username }))) return; const jwt = getCookie("jwt"); try { await removeAdminStatus(username); toast.success(t('admin.adminStatusRemoved', { username })); fetchUsers(); } catch (err: any) { console.error('Failed to remove admin status:', err); toast.error(t('admin.failedToRemoveAdminStatus')); } }; const handleDeleteUser = async (username: string) => { if (!confirm(t('admin.deleteUser', { username }))) return; const jwt = getCookie("jwt"); try { await deleteUser(username); toast.success(t('admin.userDeletedSuccessfully', { username })); fetchUsers(); } catch (err: any) { console.error('Failed to delete user:', err); toast.error(t('admin.failedToDeleteUser')); } }; const topMarginPx = isTopbarOpen ? 74 : 26; const leftMarginPx = sidebarState === 'collapsed' ? 26 : 8; const bottomMarginPx = 8; const wrapperStyle: React.CSSProperties = { marginLeft: leftMarginPx, marginRight: 17, marginTop: topMarginPx, marginBottom: bottomMarginPx, height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)` }; return (

{t('admin.title')}

{t('admin.general')} OIDC {t('admin.users')} {t('admin.adminManagement')}

{t('admin.userRegistration')}

{t('admin.externalAuthentication')}

{t('admin.configureExternalProvider')}

{oidcError && ( {t('common.error')} {oidcError} )}
handleOIDCConfigChange('client_id', e.target.value)} placeholder={t('placeholders.clientId')} required/>
handleOIDCConfigChange('client_secret', e.target.value)} placeholder={t('placeholders.clientSecret')} required/>
handleOIDCConfigChange('authorization_url', e.target.value)} placeholder={t('placeholders.authUrl')} required/>
handleOIDCConfigChange('issuer_url', e.target.value)} placeholder={t('placeholders.redirectUrl')} required/>
handleOIDCConfigChange('token_url', e.target.value)} placeholder={t('placeholders.tokenUrl')} required/>
handleOIDCConfigChange('identifier_path', e.target.value)} placeholder={t('placeholders.userIdField')} required/>
handleOIDCConfigChange('name_path', e.target.value)} placeholder={t('placeholders.usernameField')} required/>
handleOIDCConfigChange('scopes', e.target.value)} placeholder={t('placeholders.scopes')} required/>
handleOIDCConfigChange('userinfo_url', e.target.value)} placeholder="https://your-provider.com/application/o/userinfo/"/>

{t('admin.userManagement')}

{usersLoading ? (
{t('admin.loadingUsers')}
) : (
{t('admin.username')} {t('admin.type')} {t('admin.actions')} {users.map((user) => ( {user.username} {user.is_admin && ( {t('admin.adminBadge')} )} {user.is_oidc ? t('admin.external') : t('admin.local')} ))}
)}

{t('admin.adminManagement')}

{t('admin.makeUserAdmin')}

setNewAdminUsername(e.target.value)} placeholder={t('admin.enterUsernameToMakeAdmin')} required/>
{makeAdminError && ( {t('common.error')} {makeAdminError} )}

{t('admin.currentAdmins')}

{t('admin.username')} {t('admin.type')} {t('admin.actions')} {users.filter(u => u.is_admin).map((admin) => ( {admin.username} {t('admin.adminBadge')} {admin.is_oidc ? t('admin.external') : t('admin.local')} ))}
); } export default AdminSettings;