feat: implement ActiveSessions component and JWT session tracking to manage user device access

This commit is contained in:
estifanos
2026-08-19 11:00:20 +00:00
parent ff89b9af5e
commit ec28f6456d
12 changed files with 518 additions and 11 deletions

View File

@@ -44,7 +44,7 @@ import { z } from 'zod';
import { useTranslation } from 'react-i18next';
import { notify, PageHeader, useErrorHandler, passwordSchema as strongPasswordSchema, PasswordRequirements } from '@ema-platform/ui';
import { useApiMutation } from '@ema-platform/api';
import { setUser } from '@ema-platform/auth';
import { ActiveSessions, setUser } from '@ema-platform/auth';
import type { AuthUser } from '@ema-platform/auth';
import { SUPPORTED_LANGUAGES, type AppLanguage } from '../../../i18n/config';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
@@ -404,8 +404,9 @@ export function ProfilePage() {
{/* ---- Security ---- */}
<Tabs.Panel value="security" pt="md">
<Paper p="xl" shadow="sm" radius="lg" withBorder>
<form onSubmit={handlePasswordSubmit(onChangePassword)}>
<Stack gap="lg">
<Paper p="xl" shadow="sm" radius="lg" withBorder>
<form onSubmit={handlePasswordSubmit(onChangePassword)}>
<Stack gap="xl">
<div>
<Title order={5}>{t('profile.security')}</Title>
@@ -502,8 +503,11 @@ export function ProfilePage() {
</Button>
</Group>
</Stack>
</form>
</Paper>
</form>
</Paper>
<ActiveSessions />
</Stack>
</Tabs.Panel>
{/* ---- Preferences ---- */}

View File

@@ -454,6 +454,37 @@ export const am: Translations = {
dark: "ሌሊት",
system: "ሲስተም",
},
sessions: {
title: 'ንቁ የመግቢያ ክፍለ ጊዜዎች',
hint: 'በአሁኑ ሰዓት ወደ መለያዎ የገቡ መሣሪያዎች። የማያውቁትን ይሰርዙ።',
columns: {
device: 'የአይ ፒ አድራሻ',
signedIn: 'የገባበት ጊዜ',
expires: 'የሚያበቃበት',
status: 'ሁኔታ',
actions: 'እርምጃዎች',
},
select: 'ይምረጡ',
selectAll: 'ሁሉንም ክፍለ ጊዜዎች ይምረጡ',
selectRow: 'ከ {{device}} የመጣውን ክፍለ ጊዜ ይምረጡ',
thisDevice: 'ይህ መሣሪያ',
revoke: 'ሰርዝ',
cannotRevokeCurrent: 'ይህ አሁን እየተጠቀሙበት ያለው ክፍለ ጊዜ ነው።',
revokeSelected_one: 'የተመረጠውን {{count}} ሰርዝ',
revokeSelected_other: 'የተመረጡትን {{count}} ሰርዝ',
signOutOthers: 'ከሌሎች ቦታዎች ሁሉ ውጣ',
empty: 'ንቁ ክፍለ ጊዜ የለም።',
confirm: {
title: 'ክፍለ ጊዜ ሰርዝ',
one: 'ከ {{device}} የመጣው ክፍለ ጊዜ ወዲያውኑ ይወጣል።',
selected_one: '{{count}} ክፍለ ጊዜ ወዲያውኑ ይወጣል።',
selected_other: '{{count}} ክፍለ ጊዜዎች ወዲያውኑ ይወጣሉ።',
others: 'ሌሎቹ ክፍለ ጊዜዎች በሙሉ ወዲያውኑ ይወጣሉ።',
unknownDevice: 'ይህ አሁን እየተጠቀሙበት ያለውን መሣሪያ ሊያካትት ይችላል።',
},
revoked_one: '{{count}} ክፍለ ጊዜ ተሰርዟል',
revoked_other: '{{count}} ክፍለ ጊዜዎች ተሰርዘዋል',
},
twoStep: {
title: "ባለሁለት ደረጃ ማረጋገጫ",
desc: "በየጊዜው ሲገቡ ከስልክዎ የአንድ ጊዜ ኮድ ያስፈልጋል።",

View File

@@ -452,6 +452,37 @@ export const en = {
dark: 'Dark',
system: 'System',
},
sessions: {
title: 'Active sessions',
hint: 'Devices currently signed in to your account. Revoke any you do not recognise.',
columns: {
device: 'IP address',
signedIn: 'Signed in',
expires: 'Expires',
status: 'Status',
actions: 'Actions',
},
select: 'Select',
selectAll: 'Select all sessions',
selectRow: 'Select session from {{device}}',
thisDevice: 'This device',
revoke: 'Revoke',
cannotRevokeCurrent: 'This is the session you are using now.',
revokeSelected_one: 'Revoke {{count}} selected',
revokeSelected_other: 'Revoke {{count}} selected',
signOutOthers: 'Sign out everywhere else',
empty: 'No active sessions.',
confirm: {
title: 'Revoke session',
one: 'The session from {{device}} will be signed out immediately.',
selected_one: '{{count}} session will be signed out immediately.',
selected_other: '{{count}} sessions will be signed out immediately.',
others: 'Every other session will be signed out immediately.',
unknownDevice: 'This may include the device you are using now.',
},
revoked_one: '{{count}} session revoked',
revoked_other: '{{count}} sessions revoked',
},
twoStep: {
title: 'Two-step verification',
desc: 'Require a one-time code from your phone each time you sign in.',