useAdmin
Administrative operations for user management, tenant configuration, and system monitoring.
Admin Features: User lifecycle, role management, tenant settings, and analytics
Import
import { useAdmin } from '@heliorim/sdk-react';Type Signature
interface AdminState {
isAdmin: boolean;
permissions: Permission[];
users: User[];
tenantInfo: TenantInfo | null;
analytics: Analytics | null;
isLoading: boolean;
error: Error | null;
}
interface AdminActions {
// User Management
listUsers: (filters?: UserFilters) => Promise<User[]>;
getUser: (userId: string) => Promise<User>;
createUser: (userData: CreateUserData) => Promise<User>;
updateUser: (userId: string, updates: Partial<User>) => Promise<User>;
deleteUser: (userId: string) => Promise<void>;
suspendUser: (userId: string, reason: string) => Promise<void>;
reactivateUser: (userId: string) => Promise<void>;
// Role Management
assignRole: (userId: string, role: Role) => Promise<void>;
removeRole: (userId: string, role: Role) => Promise<void>;
createCustomRole: (role: CustomRole) => Promise<Role>;
// Tenant Management
updateTenantSettings: (settings: TenantSettings) => Promise<void>;
getTenantUsage: () => Promise<TenantUsage>;
manageBilling: (action: BillingAction) => Promise<void>;
// Analytics
getAnalytics: (period: Period) => Promise<Analytics>;
exportReport: (type: ReportType) => Promise<Blob>;
}
interface User {
id: string;
email: string;
name: string;
roles: Role[];
status: 'active' | 'suspended' | 'pending' | 'deleted';
createdAt: number;
lastLoginAt?: number;
passkeys: PasskeyInfo[];
metadata?: Record<string, any>;
}
interface TenantInfo {
id: string;
name: string;
plan: 'starter' | 'pro' | 'enterprise';
userLimit: number;
currentUsers: number;
settings: TenantSettings;
createdAt: number;
}
interface Analytics {
period: Period;
totalUsers: number;
activeUsers: number;
newUsers: number;
authentications: number;
failedAuthentications: number;
averageSessionDuration: number;
topEvents: Event[];
}
type UseAdminReturn = AdminState & AdminActions;Basic Usage
function AdminDashboard() {
const {
isAdmin,
permissions,
users,
tenantInfo,
listUsers,
suspendUser,
getAnalytics
} = useAdmin();
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [analytics, setAnalytics] = useState<Analytics | null>(null);
useEffect(() => {
if (isAdmin) {
// Load initial data
listUsers();
getAnalytics('week').then(setAnalytics);
}
}, [isAdmin]);
const handleSuspendUser = async (userId: string) => {
const reason = prompt('Reason for suspension:');
if (reason) {
try {
await suspendUser(userId, reason);
alert('User suspended successfully');
await listUsers(); // Refresh list
} catch (err) {
console.error('Failed to suspend user:', err);
}
}
};
if (!isAdmin) {
return <div>Access denied: Admin privileges required</div>;
}
return (
<div className="admin-dashboard">
<h1>Admin Dashboard</h1>
<div className="tenant-info">
<h2>{tenantInfo?.name}</h2>
<p>Plan: {tenantInfo?.plan}</p>
<p>Users: {tenantInfo?.currentUsers} / {tenantInfo?.userLimit}</p>
</div>
{analytics && (
<div className="analytics-summary">
<h3>Analytics (Last Week)</h3>
<div className="stats-grid">
<div>Total Users: {analytics.totalUsers}</div>
<div>Active Users: {analytics.activeUsers}</div>
<div>New Users: {analytics.newUsers}</div>
<div>Auth Success Rate: {
((analytics.authentications /
(analytics.authentications + analytics.failedAuthentications)) * 100
).toFixed(1)}%
</div>
</div>
</div>
)}
<div className="users-table">
<h3>User Management</h3>
<table>
<thead>
<tr>
<th>Email</th>
<th>Name</th>
<th>Status</th>
<th>Roles</th>
<th>Last Login</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.email}</td>
<td>{user.name}</td>
<td>{user.status}</td>
<td>{user.roles.join(', ')}</td>
<td>{user.lastLoginAt ?
new Date(user.lastLoginAt).toLocaleDateString() :
'Never'
}</td>
<td>
<button onClick={() => setSelectedUser(user)}>Edit</button>
{user.status === 'active' && (
<button onClick={() => handleSuspendUser(user.id)}>
Suspend
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}User Management
User Creation & Invitation
function UserInvitation() {
const { createUser, assignRole } = useAdmin();
const inviteUser = async (formData: FormData) => {
try {
// Create user with pending status
const user = await createUser({
email: formData.get('email') as string,
name: formData.get('name') as string,
status: 'pending',
sendInvite: true,
inviteMessage: 'Welcome to our platform!'
});
// Assign initial role
const role = formData.get('role') as string;
if (role) {
await assignRole(user.id, role as Role);
}
console.log('User invited:', user);
return user;
} catch (err) {
console.error('Failed to invite user:', err);
throw err;
}
};
const bulkInvite = async (emails: string[]) => {
const results = {
success: [] as User[],
failed: [] as { email: string; error: string }[]
};
for (const email of emails) {
try {
const user = await createUser({
email,
status: 'pending',
sendInvite: true
});
results.success.push(user);
} catch (err: any) {
results.failed.push({ email, error: err.message });
}
}
return results;
};
return (
<div className="user-invitation">
<h3>Invite Users</h3>
<form onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
await inviteUser(formData);
(e.target as HTMLFormElement).reset();
}}>
<input name="email" type="email" placeholder="Email" required />
<input name="name" placeholder="Name" required />
<select name="role">
<option value="">Select Role</option>
<option value="user">User</option>
<option value="moderator">Moderator</option>
<option value="admin">Admin</option>
</select>
<button type="submit">Send Invitation</button>
</form>
<div className="bulk-invite">
<h4>Bulk Invite</h4>
<textarea
placeholder="Enter emails (one per line)"
onBlur={async (e) => {
const emails = e.target.value.split('\n').filter(e => e.trim());
if (emails.length > 0) {
const results = await bulkInvite(emails);
console.log('Bulk invite results:', results);
}
}}
/>
</div>
</div>
);
}User Profile Management
function UserProfileManager() {
const { getUser, updateUser, deleteUser } = useAdmin();
const [editingUser, setEditingUser] = useState<User | null>(null);
const loadUserProfile = async (userId: string) => {
const user = await getUser(userId);
setEditingUser(user);
};
const updateUserProfile = async (updates: Partial<User>) => {
if (!editingUser) return;
try {
const updated = await updateUser(editingUser.id, updates);
setEditingUser(updated);
console.log('User updated:', updated);
} catch (err) {
console.error('Update failed:', err);
}
};
const handleDeleteUser = async () => {
if (!editingUser) return;
if (confirm(`Delete user ${editingUser.email}? This action cannot be undone.`)) {
try {
await deleteUser(editingUser.id);
setEditingUser(null);
console.log('User deleted');
} catch (err) {
console.error('Delete failed:', err);
}
}
};
const resetUserPasskeys = async () => {
if (!editingUser) return;
await updateUser(editingUser.id, {
passkeys: [],
metadata: {
...editingUser.metadata,
passkeysResetAt: Date.now(),
passkeysResetBy: getCurrentAdmin()
}
});
};
return (
<div className="user-profile-manager">
{editingUser && (
<div>
<h3>Edit User: {editingUser.email}</h3>
<form onSubmit={(e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
updateUserProfile({
name: formData.get('name') as string,
metadata: {
department: formData.get('department'),
title: formData.get('title')
}
});
}}>
<input name="name" defaultValue={editingUser.name} />
<input name="department" placeholder="Department" />
<input name="title" placeholder="Job Title" />
<button type="submit">Update Profile</button>
</form>
<div className="passkey-management">
<h4>Passkeys ({editingUser.passkeys.length})</h4>
{editingUser.passkeys.map(pk => (
<div key={pk.id}>
{pk.name} - Created: {new Date(pk.createdAt).toLocaleDateString()}
</div>
))}
<button onClick={resetUserPasskeys}>Reset All Passkeys</button>
</div>
<div className="danger-zone">
<h4>Danger Zone</h4>
<button className="danger" onClick={handleDeleteUser}>
Delete User
</button>
</div>
</div>
)}
</div>
);
}Role & Permission Management
Role Assignment
function RoleManager() {
const { users, assignRole, removeRole, createCustomRole } = useAdmin();
const predefinedRoles = [
{ name: 'admin', permissions: ['all'] },
{ name: 'moderator', permissions: ['read', 'write', 'moderate'] },
{ name: 'user', permissions: ['read', 'write'] },
{ name: 'viewer', permissions: ['read'] }
];
const createAndAssignCustomRole = async () => {
// Create custom role
const customRole = await createCustomRole({
name: 'content_manager',
permissions: ['read', 'write', 'publish', 'moderate_content'],
description: 'Can manage and publish content',
maxUsers: 10
});
// Assign to specific users
const contentTeam = users.filter(u =>
u.metadata?.department === 'content'
);
for (const user of contentTeam) {
await assignRole(user.id, customRole);
}
};
const bulkRoleUpdate = async (userIds: string[], role: Role, action: 'assign' | 'remove') => {
const results = [];
for (const userId of userIds) {
try {
if (action === 'assign') {
await assignRole(userId, role);
} else {
await removeRole(userId, role);
}
results.push({ userId, success: true });
} catch (err) {
results.push({ userId, success: false, error: err });
}
}
return results;
};
return (
<div className="role-manager">
<h3>Role Management</h3>
<div className="role-grid">
{predefinedRoles.map(role => (
<div key={role.name} className="role-card">
<h4>{role.name}</h4>
<p>Permissions: {role.permissions.join(', ')}</p>
<p>Users: {
users.filter(u => u.roles.includes(role.name as Role)).length
}</p>
</div>
))}
</div>
<button onClick={createAndAssignCustomRole}>
Create Content Manager Role
</button>
<div className="bulk-operations">
<h4>Bulk Role Assignment</h4>
<button onClick={() => {
const selectedUsers = getSelectedUserIds();
bulkRoleUpdate(selectedUsers, 'moderator', 'assign');
}}>
Assign Moderator to Selected
</button>
</div>
</div>
);
}Tenant Management
Tenant Configuration
function TenantSettings() {
const { tenantInfo, updateTenantSettings, getTenantUsage } = useAdmin();
const [usage, setUsage] = useState<TenantUsage | null>(null);
useEffect(() => {
getTenantUsage().then(setUsage);
}, []);
const updateSettings = async (settings: Partial<TenantSettings>) => {
try {
await updateTenantSettings({
...tenantInfo!.settings,
...settings
});
console.log('Settings updated');
} catch (err) {
console.error('Failed to update settings:', err);
}
};
return (
<div className="tenant-settings">
<h3>Tenant Configuration</h3>
<div className="settings-form">
<h4>Authentication Settings</h4>
<label>
<input
type="checkbox"
defaultChecked={tenantInfo?.settings.requireUserVerification}
onChange={(e) => updateSettings({
requireUserVerification: e.target.checked
})}
/>
Always require user verification
</label>
<label>
<input
type="checkbox"
defaultChecked={tenantInfo?.settings.allowCrossPlatform}
onChange={(e) => updateSettings({
allowCrossPlatform: e.target.checked
})}
/>
Allow cross-platform authenticators
</label>
<label>
Session timeout (minutes):
<input
type="number"
defaultValue={tenantInfo?.settings.sessionTimeout}
onChange={(e) => updateSettings({
sessionTimeout: parseInt(e.target.value)
})}
/>
</label>
</div>
<div className="branding">
<h4>Branding</h4>
<input
placeholder="Company Name"
defaultValue={tenantInfo?.settings.companyName}
onBlur={(e) => updateSettings({ companyName: e.target.value })}
/>
<input
placeholder="Logo URL"
defaultValue={tenantInfo?.settings.logoUrl}
onBlur={(e) => updateSettings({ logoUrl: e.target.value })}
/>
<input
type="color"
defaultValue={tenantInfo?.settings.primaryColor}
onChange={(e) => updateSettings({ primaryColor: e.target.value })}
/>
</div>
{usage && (
<div className="usage-stats">
<h4>Current Usage</h4>
<div>API Calls: {usage.apiCalls} / {usage.apiCallLimit}</div>
<div>Storage: {usage.storageUsed}MB / {usage.storageLimit}MB</div>
<div>Active Sessions: {usage.activeSessions}</div>
<div>Bandwidth: {usage.bandwidthUsed}GB / {usage.bandwidthLimit}GB</div>
</div>
)}
</div>
);
}Analytics & Reporting
Dashboard Analytics
function AdminAnalytics() {
const { getAnalytics, exportReport } = useAdmin();
const [period, setPeriod] = useState<Period>('week');
const [analytics, setAnalytics] = useState<Analytics | null>(null);
useEffect(() => {
getAnalytics(period).then(setAnalytics);
}, [period]);
const downloadReport = async (type: ReportType) => {
const blob = await exportReport(type);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `report-${type}-${Date.now()}.csv`;
a.click();
};
return (
<div className="admin-analytics">
<h3>Analytics Dashboard</h3>
<div className="period-selector">
<button onClick={() => setPeriod('day')}>24 Hours</button>
<button onClick={() => setPeriod('week')}>7 Days</button>
<button onClick={() => setPeriod('month')}>30 Days</button>
<button onClick={() => setPeriod('year')}>12 Months</button>
</div>
{analytics && (
<>
<div className="metrics-grid">
<div className="metric">
<h4>Total Users</h4>
<p className="value">{analytics.totalUsers}</p>
<p className="change">+{analytics.newUsers} new</p>
</div>
<div className="metric">
<h4>Active Users</h4>
<p className="value">{analytics.activeUsers}</p>
<p className="percent">
{((analytics.activeUsers / analytics.totalUsers) * 100).toFixed(1)}%
</p>
</div>
<div className="metric">
<h4>Authentications</h4>
<p className="value">{analytics.authentications}</p>
<p className="failed">Failed: {analytics.failedAuthentications}</p>
</div>
<div className="metric">
<h4>Avg Session</h4>
<p className="value">
{Math.round(analytics.averageSessionDuration / 60)} min
</p>
</div>
</div>
<div className="top-events">
<h4>Top Events</h4>
<table>
<thead>
<tr>
<th>Event</th>
<th>Count</th>
<th>Users</th>
</tr>
</thead>
<tbody>
{analytics.topEvents.map(event => (
<tr key={event.name}>
<td>{event.name}</td>
<td>{event.count}</td>
<td>{event.uniqueUsers}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
<div className="export-options">
<h4>Export Reports</h4>
<button onClick={() => downloadReport('users')}>
Export Users Report
</button>
<button onClick={() => downloadReport('authentication')}>
Export Auth Report
</button>
<button onClick={() => downloadReport('compliance')}>
Export Compliance Report
</button>
</div>
</div>
);
}Security & Compliance
Admin Access Control:
- Requires admin role authentication
- All actions are audit logged
- Sensitive operations require re-authentication
- IP restrictions can be enforced
- Session monitoring for admin accounts
Best Practices
✓ Recommended
- Implement role-based access control
- Log all administrative actions
- Require 2FA for admin accounts
- Regular audit of user permissions
- Automated alerts for suspicious activity
- Implement rate limiting on admin APIs
⚠️ Avoid
- Sharing admin credentials
- Granting unnecessary admin privileges
- Skipping audit logs
- Long-lived admin sessions
- Bulk operations without confirmation