Added Role column to user directory
This commit is contained in:
@@ -61,6 +61,7 @@ import { useToast } from '../components/ui/Toast';
|
||||
import { ExportButton } from '../components/ui/ExportButton';
|
||||
import { ExportColumn } from '../utils/exportUtils';
|
||||
import { useTableSort, SortableTh } from '../utils/tableSort';
|
||||
import { UserRoleBadge } from '../components/ui/StatusBadge';
|
||||
import { useAuth } from '../auth/AuthProvider';
|
||||
import {
|
||||
EmailStatuses,
|
||||
@@ -84,6 +85,8 @@ import {
|
||||
getUserEmail,
|
||||
registerUserEmails,
|
||||
getActivityStatusBadge,
|
||||
getUserRoleBadge,
|
||||
USER_ROLE_OPTIONS,
|
||||
EMAIL_STATUSES_OPTIONS,
|
||||
NOTIFICATION_STATUS_OPTIONS,
|
||||
NOTIFICATION_PLATFORM_OPTIONS,
|
||||
@@ -101,6 +104,7 @@ export const SettingsPage: React.FC = () => {
|
||||
// ==========================================
|
||||
const [userEmailSearch, setUserEmailSearch] = useState('');
|
||||
const [userStatusFilter, setUserStatusFilter] = useState<ActivityStatus | 'all'>('all');
|
||||
const [userRoleFilter, setUserRoleFilter] = useState<string>('all');
|
||||
const [selectedUserForDetail, setSelectedUserForDetail] = useState<User | null>(null);
|
||||
const [isUserDetailModalOpen, setIsUserDetailModalOpen] = useState(false);
|
||||
|
||||
@@ -125,6 +129,16 @@ export const SettingsPage: React.FC = () => {
|
||||
queryFn: () => getUsers(1, 100, userFilter),
|
||||
});
|
||||
|
||||
const filteredUsers = useMemo(() => {
|
||||
return usersList.filter((u) => {
|
||||
const matchRole =
|
||||
userRoleFilter === 'all' ||
|
||||
(u.role && String(u.role).toLowerCase() === userRoleFilter.toLowerCase()) ||
|
||||
(!u.role && userRoleFilter.toLowerCase() === 'contributor');
|
||||
return matchRole;
|
||||
});
|
||||
}, [usersList, userRoleFilter]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (usersDirectory && usersDirectory.length > 0) {
|
||||
registerUserEmails(usersDirectory);
|
||||
@@ -462,6 +476,7 @@ export const SettingsPage: React.FC = () => {
|
||||
const userExportColumns: ExportColumn<User>[] = [
|
||||
{ header: 'User Email', key: 'email', format: (val, u) => val || getUserEmail(u.id) || '—' },
|
||||
{ header: 'User Identifier', key: 'id' },
|
||||
{ header: 'Role', key: 'role', format: (val) => getUserRoleBadge(val).label },
|
||||
{ header: 'Status', key: 'status', format: (val) => getActivityStatusBadge(val).label },
|
||||
{ header: 'Last Login', key: 'lastLoginAt', format: (val) => (val ? formatDate(val) : 'Never') },
|
||||
];
|
||||
@@ -483,7 +498,9 @@ export const SettingsPage: React.FC = () => {
|
||||
sortColumn: userSortCol,
|
||||
sortDirection: userSortDir,
|
||||
handleSort: handleUserSort,
|
||||
} = useTableSort(usersList, 'email', 'asc');
|
||||
} = useTableSort(filteredUsers, 'email', 'asc', {
|
||||
role: (u: User) => getUserRoleBadge(u.role).label,
|
||||
});
|
||||
|
||||
const {
|
||||
sortedData: sortedInvites,
|
||||
@@ -682,6 +699,21 @@ export const SettingsPage: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-400">
|
||||
<select
|
||||
value={userRoleFilter}
|
||||
onChange={(e) => setUserRoleFilter(e.target.value)}
|
||||
className="text-xs bg-slate-900 border border-slate-800 rounded-xl px-3 py-1.5 text-white focus:outline-none focus:border-blue-500"
|
||||
>
|
||||
<option value="all">All Roles</option>
|
||||
{USER_ROLE_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-400">
|
||||
<select
|
||||
value={userStatusFilter}
|
||||
@@ -695,12 +727,13 @@ export const SettingsPage: React.FC = () => {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{(userEmailSearch.trim() !== '' || userStatusFilter !== 'all') && (
|
||||
{(userEmailSearch.trim() !== '' || userStatusFilter !== 'all' || userRoleFilter !== 'all') && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setUserEmailSearch('');
|
||||
setUserStatusFilter('all');
|
||||
setUserRoleFilter('all');
|
||||
}}
|
||||
className="inline-flex items-center gap-1 text-[11px] font-semibold text-slate-400 hover:text-slate-200 transition cursor-pointer"
|
||||
>
|
||||
@@ -772,6 +805,9 @@ export const SettingsPage: React.FC = () => {
|
||||
<SortableTh column="id" currentColumn={userSortCol} direction={userSortDir} onSort={handleUserSort} className="py-3.5 px-4">
|
||||
User Identifier
|
||||
</SortableTh>
|
||||
<SortableTh column="role" currentColumn={userSortCol} direction={userSortDir} onSort={handleUserSort} className="py-3.5 px-4">
|
||||
Role
|
||||
</SortableTh>
|
||||
<SortableTh column="status" currentColumn={userSortCol} direction={userSortDir} onSort={handleUserSort} className="py-3.5 px-4">
|
||||
Status
|
||||
</SortableTh>
|
||||
@@ -784,13 +820,13 @@ export const SettingsPage: React.FC = () => {
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
{loadingUsers ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="py-8 text-center text-slate-500">
|
||||
<td colSpan={6} className="py-8 text-center text-slate-500">
|
||||
Loading user directory...
|
||||
</td>
|
||||
</tr>
|
||||
) : sortedUsers.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="py-8 text-center text-slate-500">
|
||||
<td colSpan={6} className="py-8 text-center text-slate-500">
|
||||
No users found matching current filters.
|
||||
</td>
|
||||
</tr>
|
||||
@@ -805,6 +841,9 @@ export const SettingsPage: React.FC = () => {
|
||||
<td className="py-3.5 px-4 font-mono text-slate-400 text-[11px]">
|
||||
{u.id}
|
||||
</td>
|
||||
<td className="py-3.5 px-4">
|
||||
<UserRoleBadge role={u.role} />
|
||||
</td>
|
||||
<td className="py-3.5 px-4">
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-[10px] font-semibold border ${badge.className}`}>
|
||||
{badge.label}
|
||||
@@ -2173,6 +2212,10 @@ export const SettingsPage: React.FC = () => {
|
||||
{userFreshDetail?.id || selectedUserForDetail.id}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-slate-400 font-semibold block text-[11px] uppercase mb-1">System Role:</span>
|
||||
<UserRoleBadge role={userFreshDetail?.role ?? selectedUserForDetail.role} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between pt-1">
|
||||
<div>
|
||||
<span className="text-slate-400 font-semibold block text-[11px] uppercase">Status:</span>
|
||||
|
||||
Reference in New Issue
Block a user