mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 05:30:56 +00:00
125 lines
3.8 KiB
TypeScript
125 lines
3.8 KiB
TypeScript
import type { Dispatch, SetStateAction } from 'react';
|
|
import { ActionIcon, Badge, Checkbox, Group, Text, Tooltip } from '@mantine/core';
|
|
import { IconLogout } from '@tabler/icons-react';
|
|
import type { TFunction } from 'i18next';
|
|
import type { AdvancedColumn } from '@ema-platform/ui';
|
|
import type { MySession } from '../../hooks/useSessions';
|
|
|
|
interface Opts {
|
|
t: TFunction;
|
|
sessions: MySession[];
|
|
selected: string[];
|
|
setSelected: Dispatch<SetStateAction<string[]>>;
|
|
/** Undefined when the token carries no session claim — then no row is "this device". */
|
|
currentId?: string;
|
|
showDate: (value: string) => string;
|
|
onRevoke: (session: MySession) => void;
|
|
}
|
|
|
|
export function sessionColumns({
|
|
t,
|
|
sessions,
|
|
selected,
|
|
setSelected,
|
|
currentId,
|
|
showDate,
|
|
onRevoke,
|
|
}: Opts): AdvancedColumn<MySession>[] {
|
|
// The current session is never selectable, so "all" means "all the others".
|
|
const selectable = sessions.filter((s) => s.id !== currentId);
|
|
const allSelected = selectable.length > 0 && selectable.every((s) => selected.includes(s.id));
|
|
|
|
return [
|
|
{
|
|
header: (
|
|
<Checkbox
|
|
aria-label={t('profile.sessions.selectAll')}
|
|
checked={allSelected}
|
|
indeterminate={selected.length > 0 && !allSelected}
|
|
disabled={selectable.length === 0}
|
|
onChange={() => setSelected(allSelected ? [] : selectable.map((s) => s.id))}
|
|
/>
|
|
),
|
|
label: t('profile.sessions.select'),
|
|
size: 40,
|
|
cell: ({ row }) => {
|
|
const isCurrent = row.original.id === currentId;
|
|
return (
|
|
<Checkbox
|
|
aria-label={t('profile.sessions.selectRow', { device: row.original.device })}
|
|
checked={selected.includes(row.original.id)}
|
|
disabled={isCurrent}
|
|
onChange={(e) => {
|
|
const checked = e.currentTarget.checked;
|
|
setSelected((prev) =>
|
|
checked
|
|
? [...prev, row.original.id]
|
|
: prev.filter((id) => id !== row.original.id),
|
|
);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
header: t('profile.sessions.columns.device'),
|
|
cell: ({ row }) => (
|
|
<Group gap="xs" wrap="nowrap">
|
|
<Text size="sm" fw={500}>
|
|
{row.original.device || '—'}
|
|
</Text>
|
|
{row.original.id === currentId && (
|
|
<Badge variant="light" color="emaTeal" size="sm">
|
|
{t('profile.sessions.thisDevice')}
|
|
</Badge>
|
|
)}
|
|
</Group>
|
|
),
|
|
},
|
|
{
|
|
header: t('profile.sessions.columns.signedIn'),
|
|
cell: ({ row }) => <Text size="sm">{showDate(row.original.createdAt)}</Text>,
|
|
},
|
|
{
|
|
header: t('profile.sessions.columns.expires'),
|
|
cell: ({ row }) => <Text size="sm">{showDate(row.original.expiryTime)}</Text>,
|
|
},
|
|
{
|
|
header: t('profile.sessions.columns.status'),
|
|
cell: ({ row }) => (
|
|
<Badge variant="light" size="sm" color={row.original.status === 'ACTIVE' ? 'green' : 'gray'}>
|
|
{row.original.status}
|
|
</Badge>
|
|
),
|
|
},
|
|
{
|
|
header: t('profile.sessions.columns.actions'),
|
|
size: 70,
|
|
align: 'center',
|
|
cell: ({ row }) => {
|
|
const isCurrent = row.original.id === currentId;
|
|
return (
|
|
<Tooltip
|
|
label={
|
|
isCurrent ? t('profile.sessions.cannotRevokeCurrent') : t('profile.sessions.revoke')
|
|
}
|
|
>
|
|
<div>
|
|
<ActionIcon
|
|
variant="subtle"
|
|
color="red"
|
|
size="sm"
|
|
disabled={isCurrent}
|
|
aria-label={t('profile.sessions.revoke')}
|
|
onClick={() => onRevoke(row.original)}
|
|
>
|
|
<IconLogout size={14} />
|
|
</ActionIcon>
|
|
</div>
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
}
|