diff --git a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.styles.ts b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.styles.ts index 9dd4166c03..026abb9f0d 100644 --- a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.styles.ts +++ b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.styles.ts @@ -16,12 +16,16 @@ export const useStyles = makeStyles()(theme => ({ fontWeight: 'normal', border: 0, '&:first-of-type': { - borderTopLeftRadius: '8px', - borderBottomLeftRadius: '8px', + '&, & > button': { + borderTopLeftRadius: theme.spacing(1), + borderBottomLeftRadius: theme.spacing(1), + }, }, '&:last-of-type': { - borderTopRightRadius: '8px', - borderBottomRightRadius: '8px', + '&, & > button': { + borderTopRightRadius: theme.spacing(1), + borderBottomRightRadius: theme.spacing(1), + }, }, }, }, diff --git a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx index 08246dd354..e0cba8f626 100644 --- a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx +++ b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx @@ -18,6 +18,7 @@ import { ILocationSettings } from 'hooks/useLocationSettings'; import { formatDateYMD } from 'utils/formatDate'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { useStyles } from './UserListItem.styles'; +import TimeAgo from 'react-timeago'; interface IUserListItemProps { user: IUser; @@ -40,6 +41,20 @@ const UserListItem = ({ const navigate = useNavigate(); const { classes: styles } = useStyles(); + const renderTimeAgo = (date: string) => ( + + + + + + ); + return ( @@ -77,6 +92,17 @@ const UserListItem = ({ {renderRole(user.rootRole)} + + renderTimeAgo(user.seenAt!)} + elseShow={ + + Never logged + + } + /> + { > Role + + Last seen + {hasAccess(ADMIN) ? 'Actions' : ''} diff --git a/frontend/src/component/common/Table/TableCellSortable/TableCellSortable.styles.ts b/frontend/src/component/common/Table/TableCellSortable/TableCellSortable.styles.ts index 4971b723b0..6697522f23 100644 --- a/frontend/src/component/common/Table/TableCellSortable/TableCellSortable.styles.ts +++ b/frontend/src/component/common/Table/TableCellSortable/TableCellSortable.styles.ts @@ -3,7 +3,6 @@ import { makeStyles } from 'tss-react/mui'; export const useStyles = makeStyles()(theme => ({ tableCellHeaderSortable: { padding: 0, - position: 'relative', cursor: 'pointer', '& > svg': { fontSize: 18, diff --git a/frontend/src/hooks/useUsersSort.ts b/frontend/src/hooks/useUsersSort.ts index e87110379f..1be5596845 100644 --- a/frontend/src/hooks/useUsersSort.ts +++ b/frontend/src/hooks/useUsersSort.ts @@ -5,7 +5,7 @@ import { createPersistentGlobalStateHook } from './usePersistentGlobalState'; import useUsers from 'hooks/api/getters/useUsers/useUsers'; import IRole from 'interfaces/role'; -export type UsersSortType = 'created' | 'name' | 'role'; +export type UsersSortType = 'created' | 'name' | 'role' | 'last-seen'; export interface IUsersSort { type: UsersSortType; @@ -50,6 +50,7 @@ export const createUsersFilterSortOptions = (): IUsersFilterSortOption[] => { { type: 'created', name: 'Created' }, { type: 'name', name: 'Name' }, { type: 'role', name: 'Role' }, + { type: 'last-seen', name: 'Last seen' }, ]; }; @@ -65,6 +66,8 @@ const sortAscendingUsers = ( return sortByName(users); case 'role': return sortByRole(users, roles); + case 'last-seen': + return sortByLastSeen(users); default: console.error(`Unknown feature sort type: ${sort.type}`); return users; @@ -112,3 +115,11 @@ const getRoleName = (roleId: number, roles: Readonly) => { const role = roles.find((role: IRole) => role.id === roleId); return role ? role.name : ''; }; + +const sortByLastSeen = (users: Readonly): IUser[] => { + return [...users].sort((a, b) => { + const aSeenAt = a.seenAt ?? ''; + const bSeenAt = b.seenAt ?? ''; + return bSeenAt.localeCompare(aSeenAt); + }); +};