From f849c330ea28b55b529b18c6f3b130dd0e260030 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 14:35:49 +0200 Subject: [PATCH 01/36] feat: add init code --- .../react/components/organization/routes.tsx | 8 +++++ .../organization/sessions/index.tsx | 31 +++++++++++++++++++ .../organization/sessions/sessions.module.css | 7 +++++ .../organization/sidebar/helpers.ts | 7 ++++- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 sdks/js/packages/core/react/components/organization/sessions/index.tsx create mode 100644 sdks/js/packages/core/react/components/organization/sessions/sessions.module.css diff --git a/sdks/js/packages/core/react/components/organization/routes.tsx b/sdks/js/packages/core/react/components/organization/routes.tsx index 8d77d5349..43ddb5a26 100644 --- a/sdks/js/packages/core/react/components/organization/routes.tsx +++ b/sdks/js/packages/core/react/components/organization/routes.tsx @@ -44,6 +44,7 @@ import ServiceUserPage from './api-keys/service-user'; import { DeleteServiceAccount } from './api-keys/delete'; import { DeleteServiceAccountKey } from './api-keys/service-user/delete'; import ManageServiceUserProjects from './api-keys/service-user/projects'; +import SessionsPage from './sessions'; export interface CustomScreen { name: string; path: string; @@ -334,6 +335,12 @@ const deleteServiceAccountKeyRoute = createRoute({ component: DeleteServiceAccountKey }); +const sessionsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/sessions', + component: SessionsPage +}); + interface getRootTreeOptions { customScreens?: CustomScreen[]; } @@ -342,6 +349,7 @@ export function getRootTree({ customScreens = [] }: getRootTreeOptions) { return rootRoute.addChildren([ indexRoute.addChildren([deleteOrgRoute]), securityRoute, + sessionsRoute, membersRoute.addChildren([inviteMemberRoute, removeMemberRoute]), teamsRoute.addChildren([addTeamRoute]), domainsRoute.addChildren([ diff --git a/sdks/js/packages/core/react/components/organization/sessions/index.tsx b/sdks/js/packages/core/react/components/organization/sessions/index.tsx new file mode 100644 index 000000000..69fca985b --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/index.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { + Text, + Flex, + Headline, +} from '@raystack/apsara/v1'; +import { Outlet } from '@tanstack/react-router'; +import styles from './sessions.module.css'; + +export default function SessionsPage() { + + return ( + + + + + + Session + + + Devices logged into this account. + + + + {/* Sessions list will go here */} + + + + ); +} \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css new file mode 100644 index 000000000..72b77ecc8 --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -0,0 +1,7 @@ +.container { + height: 100%; + padding: var(--rs-space-9) var(--rs-space-11); + overflow: hidden; + } + + \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sidebar/helpers.ts b/sdks/js/packages/core/react/components/organization/sidebar/helpers.ts index d169ffd84..11fafdd5b 100644 --- a/sdks/js/packages/core/react/components/organization/sidebar/helpers.ts +++ b/sdks/js/packages/core/react/components/organization/sidebar/helpers.ts @@ -99,7 +99,12 @@ export const getUserNavItems = (options: getUserNavItemsOptions = {}) => { name: 'Preferences', to: '/preferences', show: options?.showPreferences - } + }, + { + name: 'Sessions', + to: '/sessions', + show: true + }, ]; const customRoutes = getCustomRoutes(options?.customRoutes); return [...routes, ...customRoutes].filter( From cc2d024c492e78a04808f03690d3fb177f1f2e1c Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 15:58:48 +0200 Subject: [PATCH 02/36] feat: add list items --- .../organization/sessions/index.tsx | 51 ++++++++++++++----- .../organization/sessions/sessions.module.css | 15 ++++-- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/index.tsx b/sdks/js/packages/core/react/components/organization/sessions/index.tsx index 69fca985b..aa5610d0b 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/index.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/index.tsx @@ -4,26 +4,53 @@ import { Text, Flex, Headline, + Button, } from '@raystack/apsara/v1'; import { Outlet } from '@tanstack/react-router'; import styles from './sessions.module.css'; export default function SessionsPage() { - return ( - - - - - Session - - - Devices logged into this account. - + + + + + Session + + + Devices logged into this account. + + + + + + + + + Chrome on Mac OS x + + Bangalore + + Current session + + + + + + + + Chrome on Mac OS x + + Bangalore + + Last active 10 minutes ago + + + + + - - {/* Sessions list will go here */} diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css index 72b77ecc8..4659af287 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -1,7 +1,14 @@ +.header { + margin-bottom: var(--rs-space-9); +} + .container { - height: 100%; - padding: var(--rs-space-9) var(--rs-space-11); - overflow: hidden; - } + padding: var(--rs-space-9) var(--rs-space-11); +} + +.sessionItem { + padding: var(--rs-space-7) 0 var(--rs-space-7) 0; + border-bottom: 1px solid var(--rs-color-border-base-primary); +} \ No newline at end of file From 61d081fd2b8d6a670ac45628573900b164ec3cf9 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 17:01:41 +0200 Subject: [PATCH 03/36] style: add list styling --- .../react/components/organization/routes.tsx | 10 +- .../sessions/RevokeSessionConfirm.tsx | 102 ++++++++++++++++++ .../organization/sessions/SessionsPage.tsx | 63 +++++++++++ .../organization/sessions/index.tsx | 60 +---------- .../organization/sessions/sessions.module.css | 32 +++++- 5 files changed, 204 insertions(+), 63 deletions(-) create mode 100644 sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx create mode 100644 sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx diff --git a/sdks/js/packages/core/react/components/organization/routes.tsx b/sdks/js/packages/core/react/components/organization/routes.tsx index bea9016ba..8c4a31074 100644 --- a/sdks/js/packages/core/react/components/organization/routes.tsx +++ b/sdks/js/packages/core/react/components/organization/routes.tsx @@ -45,7 +45,7 @@ import ServiceUserPage from './api-keys/service-user'; import { DeleteServiceAccount } from './api-keys/delete'; import { DeleteServiceAccountKey } from './api-keys/service-user/delete'; import ManageServiceUserProjects from './api-keys/service-user/projects'; -import SessionsPage from './sessions'; +import { SessionsPage, RevokeSessionConfirm } from './sessions'; export interface CustomScreen { name: string; path: string; @@ -348,6 +348,12 @@ const sessionsRoute = createRoute({ component: SessionsPage }); +const revokeSessionRoute = createRoute({ + getParentRoute: () => sessionsRoute, + path: '/revoke', + component: RevokeSessionConfirm +}); + interface getRootTreeOptions { customScreens?: CustomScreen[]; } @@ -356,7 +362,7 @@ export function getRootTree({ customScreens = [] }: getRootTreeOptions) { return rootRoute.addChildren([ indexRoute.addChildren([deleteOrgRoute]), securityRoute, - sessionsRoute, + sessionsRoute.addChildren([revokeSessionRoute]), membersRoute.addChildren([inviteMemberRoute, removeMemberRoute]), teamsRoute.addChildren([addTeamRoute]), domainsRoute.addChildren([ diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx new file mode 100644 index 000000000..d1fafb408 --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -0,0 +1,102 @@ +import { + Button, + toast, + Image, + Text, + Dialog, + Flex, + List +} from '@raystack/apsara/v1'; +import cross from '~/react/assets/cross.svg'; +import { useNavigate } from '@tanstack/react-router'; +import { useFrontier } from '~/react/contexts/FrontierContext'; +import { useState } from 'react'; +import styles from './sessions.module.css'; + +export const RevokeSessionConfirm = () => { + const navigate = useNavigate({ from: '/sessions/revoke' }); + const { client, activeOrganization } = useFrontier(); + const [isLoading, setIsLoading] = useState(false); + + const handleRevoke = async () => { + setIsLoading(true); + try { + // TODO: Add API call to revoke session + navigate({ to: '/sessions' }); + toast.success('Session revoked'); + } catch ({ error }: any) { + toast.error('Something went wrong', { + description: error.message + }); + } finally { + setIsLoading(false); + } + }; + + return ( + navigate({ to: '/sessions' })}> + + + + + Chrome on Mac OS x + + cross (isLoading ? null : navigate({ to: '/sessions' }))} + style={{ cursor: isLoading ? 'not-allowed' : 'pointer' }} + data-test-id="close-revoke-session-dialog" + /> + + + + + + + Device + Chrome on Mac OS x + + + IP Address + 203.0.113.25 + + + Last Location + Bangalore, India + + + Last Active + 10 minutes ago + + + + + + + + + + + + + ); +}; diff --git a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx new file mode 100644 index 000000000..25b6b19f7 --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx @@ -0,0 +1,63 @@ +'use client'; + +import { + Text, + Flex, + Headline, + Button, +} from '@raystack/apsara/v1'; +import { Outlet, useNavigate } from '@tanstack/react-router'; +import styles from './sessions.module.css'; + +export const SessionsPage = () => { + const navigate = useNavigate({ from: '/sessions' }); + + const handleRevoke = () => { + navigate({ to: '/sessions/revoke' }); + }; + + return ( + + + + + + Session + + + Devices logged into this account. + + + + + + + + Chrome on Mac OS x + + Bangalore + + Current session + + + + + + + + Chrome on Mac OS x + + Bangalore + + Last active 10 minutes ago + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sessions/index.tsx b/sdks/js/packages/core/react/components/organization/sessions/index.tsx index aa5610d0b..a9edab379 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/index.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/index.tsx @@ -1,58 +1,2 @@ -'use client'; - -import { - Text, - Flex, - Headline, - Button, -} from '@raystack/apsara/v1'; -import { Outlet } from '@tanstack/react-router'; -import styles from './sessions.module.css'; - -export default function SessionsPage() { - return ( - - - - - - Session - - - Devices logged into this account. - - - - - - - - - Chrome on Mac OS x - - Bangalore - - Current session - - - - - - - - Chrome on Mac OS x - - Bangalore - - Last active 10 minutes ago - - - - - - - - - - ); -} \ No newline at end of file +export { SessionsPage } from './SessionsPage'; +export { RevokeSessionConfirm } from './RevokeSessionConfirm'; \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css index 4659af287..9fe3c10fa 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -1,14 +1,40 @@ +* { + font-family: var(--rs-font-inter); +} + .header { margin-bottom: var(--rs-space-9); } + .container { padding: var(--rs-space-9) var(--rs-space-11); } .sessionItem { padding: var(--rs-space-7) 0 var(--rs-space-7) 0; - border-bottom: 1px solid var(--rs-color-border-base-primary); + border-bottom: 0.9px solid var(--rs-color-border-base-primary); +} + +/* Revoke Session Confirm START */ +.revokeSessionConfirmHeader { + border-bottom: none; +} + +.revokeSessionConfirmBody { + padding: var(--rs-space-5) var(--rs-space-7); + border-bottom: 0.9px solid var(--rs-color-border-base-primary); +} + +.listItem { + border-bottom: 0.9px solid var(--rs-color-border-base-primary); +} + +.listItem:last-child { + border-bottom: none; +} + +.listRoot { + gap: 0; } - - \ No newline at end of file +/* Revoke Session Confirm END */ \ No newline at end of file From 7a01885df969937df69d8a7772d45307a30d714b Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 17:16:06 +0200 Subject: [PATCH 04/36] chore: minor changes --- .../sessions/RevokeSessionConfirm.tsx | 16 ++++++++-------- .../organization/sessions/SessionsPage.tsx | 2 +- .../organization/sessions/sessions.module.css | 3 +++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx index d1fafb408..a725d677e 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -40,7 +40,7 @@ export const RevokeSessionConfirm = () => { > - + Chrome on Mac OS x { src={cross as unknown as string} onClick={() => (isLoading ? null : navigate({ to: '/sessions' }))} style={{ cursor: isLoading ? 'not-allowed' : 'pointer' }} - data-test-id="close-revoke-session-dialog" + data-test-id="frontier-sdk-close-revoke-session-dialog" /> @@ -56,19 +56,19 @@ export const RevokeSessionConfirm = () => { - Device + Device Chrome on Mac OS x - IP Address + IP Address 203.0.113.25 - Last Location + Last Location Bangalore, India - Last Active + Last Active 10 minutes ago @@ -80,7 +80,7 @@ export const RevokeSessionConfirm = () => { variant="outline" color="neutral" onClick={() => navigate({ to: '/sessions' })} - data-test-id="cancel-revoke-session-dialog" + data-test-id="frontier-sdk-cancel-revoke-session-dialog" disabled={isLoading} > Cancel @@ -89,7 +89,7 @@ export const RevokeSessionConfirm = () => { variant="solid" color="danger" onClick={handleRevoke} - data-test-id="confirm-revoke-session-dialog" + data-test-id="frontier-sdk-confirm-revoke-session-dialog" disabled={isLoading} > {isLoading ? 'Revoking...' : 'Revoke'} diff --git a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx index 25b6b19f7..96a39175a 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx @@ -22,7 +22,7 @@ export const SessionsPage = () => { - Session + Sessions Devices logged into this account. diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css index 9fe3c10fa..2b95302b5 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -28,6 +28,8 @@ .listItem { border-bottom: 0.9px solid var(--rs-color-border-base-primary); + padding-top: var(--rs-space-5); + padding-bottom: var(--rs-space-5); } .listItem:last-child { @@ -37,4 +39,5 @@ .listRoot { gap: 0; } + /* Revoke Session Confirm END */ \ No newline at end of file From 25d8e05fec7cbf1e405bc355497bc3b60477897e Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 17:18:39 +0200 Subject: [PATCH 05/36] chore: add data-test-id --- .../components/organization/sessions/RevokeSessionConfirm.tsx | 4 +++- .../react/components/organization/sessions/SessionsPage.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx index a725d677e..d5c717c95 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -91,8 +91,10 @@ export const RevokeSessionConfirm = () => { onClick={handleRevoke} data-test-id="frontier-sdk-confirm-revoke-session-dialog" disabled={isLoading} + loading={isLoading} + loadingText="Revoking..." > - {isLoading ? 'Revoking...' : 'Revoke'} + Revoke diff --git a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx index 96a39175a..225ef8d32 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx @@ -40,7 +40,7 @@ export const SessionsPage = () => { Current session - + @@ -52,7 +52,7 @@ export const SessionsPage = () => { Last active 10 minutes ago - + From adc7f4103afa7d17304f4b199e81d3c80ed7ed23 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 17:28:05 +0200 Subject: [PATCH 06/36] chore: use IconButton instead of Image --- .../sessions/RevokeSessionConfirm.tsx | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx index d5c717c95..acfdc0759 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -1,14 +1,15 @@ import { Button, toast, - Image, Text, Dialog, Flex, - List + List, + IconButton, + Image } from '@raystack/apsara/v1'; -import cross from '~/react/assets/cross.svg'; import { useNavigate } from '@tanstack/react-router'; +import cross from '~/react/assets/cross.svg'; import { useFrontier } from '~/react/contexts/FrontierContext'; import { useState } from 'react'; import styles from './sessions.module.css'; @@ -43,13 +44,18 @@ export const RevokeSessionConfirm = () => { Chrome on Mac OS x - cross (isLoading ? null : navigate({ to: '/sessions' }))} - style={{ cursor: isLoading ? 'not-allowed' : 'pointer' }} + !isLoading && navigate({ to: '/sessions' })} + disabled={isLoading} data-test-id="frontier-sdk-close-revoke-session-dialog" - /> + aria-label="Close dialog" + > + close + From 542ae9cf430509364a7f20b241ea7047068bbc56 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 24 Jul 2025 17:32:09 +0200 Subject: [PATCH 07/36] chore: import sort --- .../organization/sessions/RevokeSessionConfirm.tsx | 6 +++--- .../components/organization/sessions/sessions.module.css | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx index acfdc0759..c85e3a515 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -1,3 +1,5 @@ +import { useState } from 'react'; +import { useNavigate } from '@tanstack/react-router'; import { Button, toast, @@ -8,10 +10,8 @@ import { IconButton, Image } from '@raystack/apsara/v1'; -import { useNavigate } from '@tanstack/react-router'; -import cross from '~/react/assets/cross.svg'; import { useFrontier } from '~/react/contexts/FrontierContext'; -import { useState } from 'react'; +import cross from '~/react/assets/cross.svg'; import styles from './sessions.module.css'; export const RevokeSessionConfirm = () => { diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css index 2b95302b5..5706a9c67 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -6,7 +6,6 @@ margin-bottom: var(--rs-space-9); } - .container { padding: var(--rs-space-9) var(--rs-space-11); } @@ -39,5 +38,4 @@ .listRoot { gap: 0; } - /* Revoke Session Confirm END */ \ No newline at end of file From bd71f02ee4b0b75249262da636caa2a824bc754b Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 25 Jul 2025 13:15:32 +0200 Subject: [PATCH 08/36] chore: props --- .../sessions/RevokeSessionConfirm.tsx | 17 ++--------------- .../organization/sessions/SessionsPage.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx index c85e3a515..997fccf75 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx @@ -6,9 +6,7 @@ import { Text, Dialog, Flex, - List, - IconButton, - Image + List } from '@raystack/apsara/v1'; import { useFrontier } from '~/react/contexts/FrontierContext'; import cross from '~/react/assets/cross.svg'; @@ -44,18 +42,7 @@ export const RevokeSessionConfirm = () => { Chrome on Mac OS x - !isLoading && navigate({ to: '/sessions' })} - disabled={isLoading} - data-test-id="frontier-sdk-close-revoke-session-dialog" - aria-label="Close dialog" - > - close - + diff --git a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx index 225ef8d32..3ef737a33 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx @@ -35,9 +35,9 @@ export const SessionsPage = () => { Chrome on Mac OS x - Bangalore - - Current session + Bangalore + + Current session @@ -47,9 +47,9 @@ export const SessionsPage = () => { Chrome on Mac OS x - Bangalore - - Last active 10 minutes ago + Bangalore + + Last active 10 minutes ago From 1dd998ff10ecd2ca67ba0ed005738cb35a2b2fe8 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 26 Sep 2025 14:53:41 +0530 Subject: [PATCH 09/36] feat: add api --- Makefile | 2 +- sdks/js/packages/core/api-client/V1Beta1.ts | 27 +++- .../core/api-client/data-contracts.ts | 89 ++++++++++++ sdks/js/packages/core/package.json | 2 +- .../react/components/organization/routes.tsx | 5 +- .../sessions/RevokeSessionConfirm.tsx | 97 ------------- .../organization/sessions/SessionsPage.tsx | 63 -------- .../organization/sessions/index.tsx | 5 +- .../sessions/revoke-session-confirm.tsx | 136 ++++++++++++++++++ .../sessions/revoke-session-final-confirm.tsx | 79 ++++++++++ .../organization/sessions/sessions-page.tsx | 116 +++++++++++++++ .../packages/core/react/hooks/useSessions.ts | 77 ++++++++++ sdks/js/pnpm-lock.yaml | 16 +-- 13 files changed, 538 insertions(+), 176 deletions(-) delete mode 100644 sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx delete mode 100644 sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx create mode 100644 sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx create mode 100644 sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx create mode 100644 sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx create mode 100644 sdks/js/packages/core/react/hooks/useSessions.ts diff --git a/Makefile b/Makefile index c0fd9fc36..02cc1975a 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ TAG := $(shell git rev-list --tags --max-count=1) VERSION := $(shell git describe --tags ${TAG}) .PHONY: build check fmt lint test test-race vet test-cover-html help install proto ui compose-up-dev .DEFAULT_GOAL := build -PROTON_COMMIT := "c8a089a76adfdc1312c3ffda43b76fa7203bf6bb" +PROTON_COMMIT := "fba39927b8b974dc1cc1ae0f05f1390580ec6d58" ui: @echo " > generating ui build" diff --git a/sdks/js/packages/core/api-client/V1Beta1.ts b/sdks/js/packages/core/api-client/V1Beta1.ts index 019781cb0..6f5c1e976 100644 --- a/sdks/js/packages/core/api-client/V1Beta1.ts +++ b/sdks/js/packages/core/api-client/V1Beta1.ts @@ -130,6 +130,7 @@ import { V1Beta1GetBillingAccountResponse, V1Beta1GetBillingBalanceResponse, V1Beta1GetCheckoutResponse, + V1Beta1GetCurrentAdminUserResponse, V1Beta1GetCurrentUserResponse, V1Beta1GetFeatureResponse, V1Beta1GetGroupResponse, @@ -1386,6 +1387,23 @@ export class V1Beta1< format: "json", ...params, }); + /** + * @description Returns the current authenticated admin user profile. Returns 403 Forbidden if the user is not an admin. + * + * @tags User + * @name AdminServiceGetCurrentAdminUser + * @summary Get current admin user + * @request GET:/v1beta1/admin/users/self + * @secure + */ + adminServiceGetCurrentAdminUser = (params: RequestParams = {}) => + this.request({ + path: `/v1beta1/admin/users/self`, + method: "GET", + secure: true, + format: "json", + ...params, + }); /** * @description List all webhooks. * @@ -2581,12 +2599,13 @@ export class V1Beta1< ...params, }); /** - * @description Returns a list of audit logs of an organization in Frontier. + * @description Returns a list of audit logs of an organization in Frontier. DEPRECATED: Use admin ListAuditRecords API instead. * * @tags AuditLog * @name FrontierServiceListOrganizationAuditLogs * @summary List audit logs * @request GET:/v1beta1/organizations/{org_id}/auditlogs + * @deprecated * @secure */ frontierServiceListOrganizationAuditLogs = ( @@ -2614,12 +2633,13 @@ export class V1Beta1< ...params, }); /** - * @description Create new audit logs in a batch. + * @description Create new audit logs in a batch. DEPRECATED: Use ListAuditRecords API instead with filters. * * @tags AuditLog * @name FrontierServiceCreateOrganizationAuditLogs * @summary Create audit log * @request POST:/v1beta1/organizations/{org_id}/auditlogs + * @deprecated * @secure */ frontierServiceCreateOrganizationAuditLogs = ( @@ -2639,12 +2659,13 @@ export class V1Beta1< ...params, }); /** - * @description Get an audit log by ID. + * @description Get an audit log by ID. DEPRECATED: Use admin GetAuditRecord API instead. * * @tags AuditLog * @name FrontierServiceGetOrganizationAuditLog * @summary Get audit log * @request GET:/v1beta1/organizations/{org_id}/auditlogs/{id} + * @deprecated * @secure */ frontierServiceGetOrganizationAuditLog = ( diff --git a/sdks/js/packages/core/api-client/data-contracts.ts b/sdks/js/packages/core/api-client/data-contracts.ts index 44fd9ad50..263f91438 100644 --- a/sdks/js/packages/core/api-client/data-contracts.ts +++ b/sdks/js/packages/core/api-client/data-contracts.ts @@ -215,6 +215,13 @@ export interface SearchUserProjectsResponseUserProject { user_id?: string; } +export interface SessionMeta { + operating_system?: string; + browser?: string; + ip_address?: string; + location?: string; +} + export interface SubscriptionPhase { /** @format date-time */ effective_at?: string; @@ -563,6 +570,43 @@ export interface V1Beta1AuditLogTarget { name?: string; } +export interface V1Beta1AuditRecord { + id?: string; + actor?: V1Beta1AuditRecordActor; + event?: string; + resource?: V1Beta1AuditRecordResource; + target?: V1Beta1AuditRecordTarget; + /** @format date-time */ + occurred_at?: string; + org_id?: string; + request_id?: string; + metadata?: object; + /** @format date-time */ + created_at?: string; +} + +export interface V1Beta1AuditRecordActor { + id: string; + /** not mandatory if id is zeroUUID */ + type?: string; + name?: string; + metadata?: object; +} + +export interface V1Beta1AuditRecordResource { + id: string; + type: string; + name?: string; + metadata?: object; +} + +export interface V1Beta1AuditRecordTarget { + id?: string; + type?: string; + name?: string; + metadata?: object; +} + export interface V1Beta1AuthCallbackRequest { /** strategy_name will not be set for oidc but can be utilized for methods like email magic links */ strategy_name?: string; @@ -803,6 +847,10 @@ export interface V1Beta1CheckoutSubscriptionBody { provider_coupon_id?: string; } +export interface V1Beta1CreateAuditRecordResponse { + audit_record?: V1Beta1AuditRecord; +} + export interface V1Beta1CreateBillingAccountResponse { /** Created billing account */ billing_account?: V1Beta1BillingAccount; @@ -1197,6 +1245,11 @@ export interface V1Beta1GetCheckoutResponse { checkout_session?: V1Beta1CheckoutSession; } +export interface V1Beta1GetCurrentAdminUserResponse { + user?: V1Beta1User; + service_user?: V1Beta1ServiceUser; +} + export interface V1Beta1GetCurrentUserResponse { user?: V1Beta1User; serviceuser?: V1Beta1ServiceUser; @@ -1453,6 +1506,12 @@ export interface V1Beta1ListAllUsersResponse { users?: V1Beta1User[]; } +export interface V1Beta1ListAuditRecordsResponse { + audit_records?: V1Beta1AuditRecord[]; + pagination?: V1Beta1RQLQueryPaginationResponse; + group?: V1Beta1RQLQueryGroupResponse; +} + export interface V1Beta1ListAuthStrategiesResponse { strategies?: V1Beta1AuthStrategy[]; } @@ -1724,6 +1783,10 @@ export interface V1Beta1ListServiceUsersResponse { serviceusers?: V1Beta1ServiceUser[]; } +export interface V1Beta1ListSessionsResponse { + sessions?: V1Beta1Session[]; +} + export interface V1Beta1ListSubscriptionsResponse { /** List of subscriptions */ subscriptions?: V1Beta1Subscription[]; @@ -1741,6 +1804,10 @@ export interface V1Beta1ListUserPreferencesResponse { preferences?: V1Beta1Preference[]; } +export interface V1Beta1ListUserSessionsResponse { + sessions?: V1Beta1Session[]; +} + export interface V1Beta1ListUsersResponse { /** @format int32 */ count?: number; @@ -1909,6 +1976,8 @@ export interface V1Beta1PermissionRequestBody { key?: string; } +export type V1Beta1PingUserSessionResponse = object; + export interface V1Beta1Plan { id?: string; name?: string; @@ -2113,6 +2182,12 @@ export interface V1Beta1Prospect { metadata?: object; } +export interface V1Beta1RQLExportRequest { + filters?: V1Beta1RQLFilter[]; + search?: string; + sort?: V1Beta1RQLSort[]; +} + export interface V1Beta1RQLFilter { name?: string; operator?: string; @@ -2263,6 +2338,10 @@ export interface V1Beta1RevertBillingUsageRequest { export type V1Beta1RevertBillingUsageResponse = object; +export type V1Beta1RevokeSessionResponse = object; + +export type V1Beta1RevokeUserSessionResponse = object; + export interface V1Beta1Role { id?: string; name?: string; @@ -2464,6 +2543,16 @@ export interface V1Beta1ServiceUserToken { created_at?: string; } +export interface V1Beta1Session { + id?: string; + metadata?: SessionMeta; + is_current_session?: boolean; + /** @format date-time */ + created_at?: string; + /** @format date-time */ + updated_at?: string; +} + export interface V1Beta1SetOrganizationKycResponse { organization_kyc?: V1Beta1OrganizationKyc; } diff --git a/sdks/js/packages/core/package.json b/sdks/js/packages/core/package.json index 0922d30ea..4e62b954e 100644 --- a/sdks/js/packages/core/package.json +++ b/sdks/js/packages/core/package.json @@ -99,7 +99,7 @@ "@connectrpc/connect-query": "^2.1.1", "@connectrpc/connect-web": "^2.0.2", "@hookform/resolvers": "^3.10.0", - "@raystack/proton": "0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a", + "@raystack/proton": "0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58", "@tanstack/react-query": "^5.83.0", "@tanstack/react-router": "1.58.17", "axios": "^1.9.0", diff --git a/sdks/js/packages/core/react/components/organization/routes.tsx b/sdks/js/packages/core/react/components/organization/routes.tsx index 6d2cfc92f..723bfa959 100644 --- a/sdks/js/packages/core/react/components/organization/routes.tsx +++ b/sdks/js/packages/core/react/components/organization/routes.tsx @@ -359,7 +359,10 @@ const sessionsRoute = createRoute({ const revokeSessionRoute = createRoute({ getParentRoute: () => sessionsRoute, path: '/revoke', - component: RevokeSessionConfirm + component: RevokeSessionConfirm, + validateSearch: (search: Record) => ({ + sessionId: search.sessionId as string | undefined, + }), }); interface getRootTreeOptions { diff --git a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx deleted file mode 100644 index 997fccf75..000000000 --- a/sdks/js/packages/core/react/components/organization/sessions/RevokeSessionConfirm.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { useState } from 'react'; -import { useNavigate } from '@tanstack/react-router'; -import { - Button, - toast, - Text, - Dialog, - Flex, - List -} from '@raystack/apsara/v1'; -import { useFrontier } from '~/react/contexts/FrontierContext'; -import cross from '~/react/assets/cross.svg'; -import styles from './sessions.module.css'; - -export const RevokeSessionConfirm = () => { - const navigate = useNavigate({ from: '/sessions/revoke' }); - const { client, activeOrganization } = useFrontier(); - const [isLoading, setIsLoading] = useState(false); - - const handleRevoke = async () => { - setIsLoading(true); - try { - // TODO: Add API call to revoke session - navigate({ to: '/sessions' }); - toast.success('Session revoked'); - } catch ({ error }: any) { - toast.error('Something went wrong', { - description: error.message - }); - } finally { - setIsLoading(false); - } - }; - - return ( - navigate({ to: '/sessions' })}> - - - - - Chrome on Mac OS x - - - - - - - - - Device - Chrome on Mac OS x - - - IP Address - 203.0.113.25 - - - Last Location - Bangalore, India - - - Last Active - 10 minutes ago - - - - - - - - - - - - - ); -}; diff --git a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx b/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx deleted file mode 100644 index 3ef737a33..000000000 --- a/sdks/js/packages/core/react/components/organization/sessions/SessionsPage.tsx +++ /dev/null @@ -1,63 +0,0 @@ -'use client'; - -import { - Text, - Flex, - Headline, - Button, -} from '@raystack/apsara/v1'; -import { Outlet, useNavigate } from '@tanstack/react-router'; -import styles from './sessions.module.css'; - -export const SessionsPage = () => { - const navigate = useNavigate({ from: '/sessions' }); - - const handleRevoke = () => { - navigate({ to: '/sessions/revoke' }); - }; - - return ( - - - - - - Sessions - - - Devices logged into this account. - - - - - - - - Chrome on Mac OS x - - Bangalore - - Current session - - - - - - - - Chrome on Mac OS x - - Bangalore - - Last active 10 minutes ago - - - - - - - - - - ); -}; \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sessions/index.tsx b/sdks/js/packages/core/react/components/organization/sessions/index.tsx index a9edab379..b0191b81c 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/index.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/index.tsx @@ -1,2 +1,3 @@ -export { SessionsPage } from './SessionsPage'; -export { RevokeSessionConfirm } from './RevokeSessionConfirm'; \ No newline at end of file +export { SessionsPage } from './sessions-page'; +export { RevokeSessionConfirm } from './revoke-session-confirm'; +export { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; \ No newline at end of file diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx new file mode 100644 index 000000000..d0258deca --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -0,0 +1,136 @@ +import { useState, useEffect } from 'react'; +import { useNavigate, useSearch } from '@tanstack/react-router'; +import { + Button, + toast, + Text, + Dialog, + Flex, + List, + Skeleton +} from '@raystack/apsara/v1'; +import { useFrontier } from '~/react/contexts/FrontierContext'; +import { useSessions, SessionData } from '../../../hooks/useSessions'; +import { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; +import styles from './sessions.module.css'; + +export const RevokeSessionConfirm = () => { + const navigate = useNavigate({ from: '/sessions/revoke' }); + const search = useSearch({ from: '/sessions/revoke' }) as { sessionId?: string }; + const { sessions, revokeSession, isRevokingSession } = useSessions(); + const [sessionData, setSessionData] = useState(null); + const [isFinalConfirmOpen, setIsFinalConfirmOpen] = useState(false); + + // Find the session data based on sessionId from URL params + useEffect(() => { + if (search.sessionId && sessions.length > 0) { + const session = sessions.find(s => s.id === search.sessionId); + if (session) { + setSessionData(session); + console.log('Found session for revoke:', session); + } else { + console.log('Session not found for ID:', search.sessionId); + } + } + }, [search.sessionId, sessions]); + + const handleRevokeClick = () => { + setIsFinalConfirmOpen(true); + }; + + const handleFinalConfirm = async () => { + if (!search.sessionId) return; + + try { + await revokeSession(search.sessionId); + navigate({ to: '/sessions' }); + toast.success('Session revoked successfully'); + } catch (error: any) { + toast.error('Failed to revoke session', { + description: error.message || 'Something went wrong' + }); + } + }; + + + return ( + <> + {sessionData ? ( + navigate({ to: '/sessions' })}> + + + + + {sessionData.browser} on {sessionData.operatingSystem} + + + + + + + + + Device + {sessionData.browser} on {sessionData.operatingSystem} + + + IP Address + {sessionData.ipAddress} + + + Last Location + {sessionData.location} + + + Last Active + {sessionData.lastActive} + + + + + + + + + + + + + ) : ( + navigate({ to: '/sessions' })}> + + + + + + + + + + )} + + + + ); +}; diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx new file mode 100644 index 000000000..7781211b6 --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -0,0 +1,79 @@ +import { + Button, + toast, + Dialog, + Flex, + Text +} from '@raystack/apsara/v1'; +import styles from './sessions.module.css'; + +interface RevokeSessionFinalConfirmProps { + isOpen: boolean; + onOpenChange: (isOpen: boolean) => void; + onConfirm: () => void; + isLoading?: boolean; +} + +export const RevokeSessionFinalConfirm = ({ + isOpen, + onOpenChange, + onConfirm, + isLoading = false +}: RevokeSessionFinalConfirmProps) => { + const handleConfirm = async () => { + try { + onConfirm(); + onOpenChange(false); + } catch (error: any) { + toast.error('Failed to revoke session', { + description: error.message || 'Something went wrong' + }); + } + }; + + return ( + + + + Revoke + + + + + + + Are you sure you want to revoke this session? This action cannot be undone. + + + + + + + + + + + + + ); +}; diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx new file mode 100644 index 000000000..b15ead8a3 --- /dev/null +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -0,0 +1,116 @@ +'use client'; + +import { + Text, + Flex, + Headline, + Button, + Skeleton, +} from '@raystack/apsara/v1'; +import { Outlet, useNavigate } from '@tanstack/react-router'; +import { useSessions } from '../../../hooks/useSessions'; +import styles from './sessions.module.css'; + +export const SessionsPage = () => { + const navigate = useNavigate({ from: '/sessions' }); + const { sessions, isLoading, error } = useSessions(); + + // Console.log the sessions data for debugging + console.log('Sessions data:', { sessions, isLoading, error }); + + const handleRevoke = (sessionId: string) => { + navigate({ to: '/sessions/revoke', search: { sessionId } }); + }; + + // Common header JSX + const renderSessionsHeader = () => ( + + Sessions + + Devices logged into this account. + + + ); + + if (isLoading) { + return ( + + + + {renderSessionsHeader()} + + + + + + + + + ); + } + + if (error) { + return ( + + + + {renderSessionsHeader()} + + + + {error} + + + + + ); + } + + return ( + + + + {renderSessionsHeader()} + + + + {sessions.length === 0 ? ( + + + No active sessions found. + + + ) : ( + sessions.map((session) => ( + + + + {session.browser} on {session.operatingSystem} + + + {session.location} + + {session.isCurrent ? ( + Current session + ) : ( + Last active {session.lastActive} + )} + + + + + )) + )} + + + + + ); +}; \ No newline at end of file diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts new file mode 100644 index 000000000..03789fc38 --- /dev/null +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -0,0 +1,77 @@ +import { useFrontier } from '../contexts/FrontierContext'; +import { useQuery, useMutation } from '@connectrpc/connect-query'; +import { FrontierServiceQueries } from '@raystack/proton/frontier'; + +export interface SessionData { + id: string; + browser: string; + operatingSystem: string; + ipAddress: string; + location: string; + lastActive: string; + isCurrent: boolean; +} + +export const useSessions = () => { + const { client } = useFrontier(); + + console.log('Using ListSessions API with FrontierServiceQueries...'); + + const { + data: sessionsData, + isLoading, + error + } = useQuery( + FrontierServiceQueries.listSessions, + {}, + { + enabled: !!client, + } + ); + + console.log('Raw API response:', sessionsData); + console.log('Loading state:', isLoading); + console.log('Error state:', error); + + // Map the API response to our SessionData interface + const sessions: SessionData[] = (sessionsData?.sessions || []).map((session: any) => ({ + id: session.id || '', + browser: session.metadata?.browser || 'Unknown Browser', + operatingSystem: session.metadata?.operatingSystem || 'Unknown OS', + ipAddress: session.metadata?.ipAddress || 'Unknown', + location: session.metadata?.location || 'Unknown location', + lastActive: session.updatedAt ? new Date(session.updatedAt.seconds * 1000).toLocaleString() : 'Unknown', + isCurrent: session.isCurrent || false, + })); + + console.log('Mapped sessions data:', sessions); + + // RevokeSession mutation + const { + mutate: revokeSession, + isPending: isRevokingSession, + } = useMutation(FrontierServiceQueries.revokeSession, { + onSuccess: () => { + console.log('Session revoked successfully'); + // Refetch sessions after successful revocation + // Note: useQuery will automatically refetch when the component re-renders + }, + onError: (error) => { + console.error('Failed to revoke session:', error); + }, + }); + + const handleRevokeSession = (sessionId: string) => { + console.log('Revoking session:', sessionId); + revokeSession({ sessionId }); + }; + + return { + sessions, + isLoading, + error: error?.message || null, + refetch: () => {}, // useQuery handles refetching automatically + revokeSession: handleRevokeSession, + isRevokingSession, + }; +}; diff --git a/sdks/js/pnpm-lock.yaml b/sdks/js/pnpm-lock.yaml index e0168a594..fee151a39 100644 --- a/sdks/js/pnpm-lock.yaml +++ b/sdks/js/pnpm-lock.yaml @@ -46,8 +46,8 @@ importers: specifier: ^3.10.0 version: 3.10.0(react-hook-form@7.57.0(react@18.3.1)) '@raystack/proton': - specifier: 0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a - version: 0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58 + version: 0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-query': specifier: ^5.83.0 version: 5.83.0(react@18.3.1) @@ -2098,8 +2098,8 @@ packages: resolution: {integrity: sha512-UJM1mgqtY7WphnI0uZNDofjCmtpV6OGfjO5JEBfBTUI+aRfJ1YLWFDP7d+S8c2qZxc9wvupyrvqGWRJB+jMxeQ==} engines: {node: '>=18'} - '@raystack/proton@0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a': - resolution: {integrity: sha512-YSDOtLJn28NiqSptZmLIFTLpQ+utzP/8i+2/aARBtXXuK2dpjKLKkRRwZMX2P67YKcgSswgaqpKfgEaPuQhyfg==} + '@raystack/proton@0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58': + resolution: {integrity: sha512-cPJesmAHojNJf9ADrNMMwwHlhE+FaWL4PD8sBG76Nqks5ojJ1hrp+9KxkoP1hCKzZnPrfOE7/DNnM5UMcBz1ig==} peerDependencies: '@tanstack/react-query': ^5.0.0 peerDependenciesMeta: @@ -9351,7 +9351,7 @@ snapshots: - '@types/react-dom' - react-dom - '@raystack/proton@0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@raystack/proton@0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@bufbuild/protobuf': 2.6.3 '@connectrpc/connect': 2.0.3(@bufbuild/protobuf@2.6.3) @@ -11159,7 +11159,7 @@ snapshots: debug: 4.4.1 enhanced-resolve: 5.17.1 eslint: 7.32.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -11172,7 +11172,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -11194,7 +11194,7 @@ snapshots: doctrine: 2.1.0 eslint: 7.32.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 From ecd3cb915ed4b25a912c2b7ce94e5ad6aa4951f0 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 29 Sep 2025 09:00:13 +0530 Subject: [PATCH 10/36] fix: container scrollable --- .../sessions/revoke-session-confirm.tsx | 12 ++++---- .../organization/sessions/sessions-page.tsx | 15 +++++----- .../organization/sessions/sessions.module.css | 7 +++++ .../packages/core/react/hooks/useSessions.ts | 30 +++++++------------ 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index d0258deca..3c97ed95d 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -9,7 +9,6 @@ import { List, Skeleton } from '@raystack/apsara/v1'; -import { useFrontier } from '~/react/contexts/FrontierContext'; import { useSessions, SessionData } from '../../../hooks/useSessions'; import { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; import styles from './sessions.module.css'; @@ -115,12 +114,11 @@ export const RevokeSessionConfirm = () => { ) : ( navigate({ to: '/sessions' })}> - - - - - - + )} diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index b15ead8a3..6dec00e8d 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -15,14 +15,11 @@ export const SessionsPage = () => { const navigate = useNavigate({ from: '/sessions' }); const { sessions, isLoading, error } = useSessions(); - // Console.log the sessions data for debugging - console.log('Sessions data:', { sessions, isLoading, error }); const handleRevoke = (sessionId: string) => { navigate({ to: '/sessions/revoke', search: { sessionId } }); }; - // Common header JSX const renderSessionsHeader = () => ( Sessions @@ -39,10 +36,12 @@ export const SessionsPage = () => { {renderSessionsHeader()} - - - - + + @@ -73,7 +72,7 @@ export const SessionsPage = () => { {renderSessionsHeader()} - + {sessions.length === 0 ? ( diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css index 5706a9c67..4cab85e43 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions.module.css @@ -8,6 +8,13 @@ .container { padding: var(--rs-space-9) var(--rs-space-11); + max-height: 80vh; + overflow-y: auto; +} + +.sessionsList { + max-height: 75vh; + overflow-y: auto; } .sessionItem { diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts index 03789fc38..f4b70bb58 100644 --- a/sdks/js/packages/core/react/hooks/useSessions.ts +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -1,5 +1,6 @@ import { useFrontier } from '../contexts/FrontierContext'; import { useQuery, useMutation } from '@connectrpc/connect-query'; +import { useQueryClient } from '@tanstack/react-query'; import { FrontierServiceQueries } from '@raystack/proton/frontier'; export interface SessionData { @@ -14,8 +15,7 @@ export interface SessionData { export const useSessions = () => { const { client } = useFrontier(); - - console.log('Using ListSessions API with FrontierServiceQueries...'); + const queryClient = useQueryClient(); const { data: sessionsData, @@ -29,32 +29,25 @@ export const useSessions = () => { } ); - console.log('Raw API response:', sessionsData); - console.log('Loading state:', isLoading); - console.log('Error state:', error); - - // Map the API response to our SessionData interface const sessions: SessionData[] = (sessionsData?.sessions || []).map((session: any) => ({ id: session.id || '', - browser: session.metadata?.browser || 'Unknown Browser', - operatingSystem: session.metadata?.operatingSystem || 'Unknown OS', + browser: session.metadata?.browser || 'Unknown', + operatingSystem: session.metadata?.operatingSystem || 'Unknown', ipAddress: session.metadata?.ipAddress || 'Unknown', - location: session.metadata?.location || 'Unknown location', - lastActive: session.updatedAt ? new Date(session.updatedAt.seconds * 1000).toLocaleString() : 'Unknown', + location: session.metadata?.location || 'Unknown', + lastActive: session.updatedAt?.seconds ? new Date(Number(session.updatedAt.seconds) * 1000).toLocaleString() : 'Unknown', isCurrent: session.isCurrent || false, })); - console.log('Mapped sessions data:', sessions); - - // RevokeSession mutation const { mutate: revokeSession, isPending: isRevokingSession, } = useMutation(FrontierServiceQueries.revokeSession, { onSuccess: () => { - console.log('Session revoked successfully'); - // Refetch sessions after successful revocation - // Note: useQuery will automatically refetch when the component re-renders + // Invalidate and refetch the sessions list + queryClient.invalidateQueries({ + queryKey: [FrontierServiceQueries.listSessions], + }); }, onError: (error) => { console.error('Failed to revoke session:', error); @@ -62,7 +55,6 @@ export const useSessions = () => { }); const handleRevokeSession = (sessionId: string) => { - console.log('Revoking session:', sessionId); revokeSession({ sessionId }); }; @@ -70,7 +62,7 @@ export const useSessions = () => { sessions, isLoading, error: error?.message || null, - refetch: () => {}, // useQuery handles refetching automatically + refetch: () => {}, revokeSession: handleRevokeSession, isRevokingSession, }; From 86b7fcb57c0a606449fddbd51fdfd1404698380a Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 29 Sep 2025 11:38:15 +0530 Subject: [PATCH 11/36] chore: revert swagger files --- sdks/js/packages/core/api-client/V1Beta1.ts | 27 +----- .../core/api-client/data-contracts.ts | 89 ------------------- 2 files changed, 3 insertions(+), 113 deletions(-) diff --git a/sdks/js/packages/core/api-client/V1Beta1.ts b/sdks/js/packages/core/api-client/V1Beta1.ts index 6f5c1e976..019781cb0 100644 --- a/sdks/js/packages/core/api-client/V1Beta1.ts +++ b/sdks/js/packages/core/api-client/V1Beta1.ts @@ -130,7 +130,6 @@ import { V1Beta1GetBillingAccountResponse, V1Beta1GetBillingBalanceResponse, V1Beta1GetCheckoutResponse, - V1Beta1GetCurrentAdminUserResponse, V1Beta1GetCurrentUserResponse, V1Beta1GetFeatureResponse, V1Beta1GetGroupResponse, @@ -1387,23 +1386,6 @@ export class V1Beta1< format: "json", ...params, }); - /** - * @description Returns the current authenticated admin user profile. Returns 403 Forbidden if the user is not an admin. - * - * @tags User - * @name AdminServiceGetCurrentAdminUser - * @summary Get current admin user - * @request GET:/v1beta1/admin/users/self - * @secure - */ - adminServiceGetCurrentAdminUser = (params: RequestParams = {}) => - this.request({ - path: `/v1beta1/admin/users/self`, - method: "GET", - secure: true, - format: "json", - ...params, - }); /** * @description List all webhooks. * @@ -2599,13 +2581,12 @@ export class V1Beta1< ...params, }); /** - * @description Returns a list of audit logs of an organization in Frontier. DEPRECATED: Use admin ListAuditRecords API instead. + * @description Returns a list of audit logs of an organization in Frontier. * * @tags AuditLog * @name FrontierServiceListOrganizationAuditLogs * @summary List audit logs * @request GET:/v1beta1/organizations/{org_id}/auditlogs - * @deprecated * @secure */ frontierServiceListOrganizationAuditLogs = ( @@ -2633,13 +2614,12 @@ export class V1Beta1< ...params, }); /** - * @description Create new audit logs in a batch. DEPRECATED: Use ListAuditRecords API instead with filters. + * @description Create new audit logs in a batch. * * @tags AuditLog * @name FrontierServiceCreateOrganizationAuditLogs * @summary Create audit log * @request POST:/v1beta1/organizations/{org_id}/auditlogs - * @deprecated * @secure */ frontierServiceCreateOrganizationAuditLogs = ( @@ -2659,13 +2639,12 @@ export class V1Beta1< ...params, }); /** - * @description Get an audit log by ID. DEPRECATED: Use admin GetAuditRecord API instead. + * @description Get an audit log by ID. * * @tags AuditLog * @name FrontierServiceGetOrganizationAuditLog * @summary Get audit log * @request GET:/v1beta1/organizations/{org_id}/auditlogs/{id} - * @deprecated * @secure */ frontierServiceGetOrganizationAuditLog = ( diff --git a/sdks/js/packages/core/api-client/data-contracts.ts b/sdks/js/packages/core/api-client/data-contracts.ts index 263f91438..44fd9ad50 100644 --- a/sdks/js/packages/core/api-client/data-contracts.ts +++ b/sdks/js/packages/core/api-client/data-contracts.ts @@ -215,13 +215,6 @@ export interface SearchUserProjectsResponseUserProject { user_id?: string; } -export interface SessionMeta { - operating_system?: string; - browser?: string; - ip_address?: string; - location?: string; -} - export interface SubscriptionPhase { /** @format date-time */ effective_at?: string; @@ -570,43 +563,6 @@ export interface V1Beta1AuditLogTarget { name?: string; } -export interface V1Beta1AuditRecord { - id?: string; - actor?: V1Beta1AuditRecordActor; - event?: string; - resource?: V1Beta1AuditRecordResource; - target?: V1Beta1AuditRecordTarget; - /** @format date-time */ - occurred_at?: string; - org_id?: string; - request_id?: string; - metadata?: object; - /** @format date-time */ - created_at?: string; -} - -export interface V1Beta1AuditRecordActor { - id: string; - /** not mandatory if id is zeroUUID */ - type?: string; - name?: string; - metadata?: object; -} - -export interface V1Beta1AuditRecordResource { - id: string; - type: string; - name?: string; - metadata?: object; -} - -export interface V1Beta1AuditRecordTarget { - id?: string; - type?: string; - name?: string; - metadata?: object; -} - export interface V1Beta1AuthCallbackRequest { /** strategy_name will not be set for oidc but can be utilized for methods like email magic links */ strategy_name?: string; @@ -847,10 +803,6 @@ export interface V1Beta1CheckoutSubscriptionBody { provider_coupon_id?: string; } -export interface V1Beta1CreateAuditRecordResponse { - audit_record?: V1Beta1AuditRecord; -} - export interface V1Beta1CreateBillingAccountResponse { /** Created billing account */ billing_account?: V1Beta1BillingAccount; @@ -1245,11 +1197,6 @@ export interface V1Beta1GetCheckoutResponse { checkout_session?: V1Beta1CheckoutSession; } -export interface V1Beta1GetCurrentAdminUserResponse { - user?: V1Beta1User; - service_user?: V1Beta1ServiceUser; -} - export interface V1Beta1GetCurrentUserResponse { user?: V1Beta1User; serviceuser?: V1Beta1ServiceUser; @@ -1506,12 +1453,6 @@ export interface V1Beta1ListAllUsersResponse { users?: V1Beta1User[]; } -export interface V1Beta1ListAuditRecordsResponse { - audit_records?: V1Beta1AuditRecord[]; - pagination?: V1Beta1RQLQueryPaginationResponse; - group?: V1Beta1RQLQueryGroupResponse; -} - export interface V1Beta1ListAuthStrategiesResponse { strategies?: V1Beta1AuthStrategy[]; } @@ -1783,10 +1724,6 @@ export interface V1Beta1ListServiceUsersResponse { serviceusers?: V1Beta1ServiceUser[]; } -export interface V1Beta1ListSessionsResponse { - sessions?: V1Beta1Session[]; -} - export interface V1Beta1ListSubscriptionsResponse { /** List of subscriptions */ subscriptions?: V1Beta1Subscription[]; @@ -1804,10 +1741,6 @@ export interface V1Beta1ListUserPreferencesResponse { preferences?: V1Beta1Preference[]; } -export interface V1Beta1ListUserSessionsResponse { - sessions?: V1Beta1Session[]; -} - export interface V1Beta1ListUsersResponse { /** @format int32 */ count?: number; @@ -1976,8 +1909,6 @@ export interface V1Beta1PermissionRequestBody { key?: string; } -export type V1Beta1PingUserSessionResponse = object; - export interface V1Beta1Plan { id?: string; name?: string; @@ -2182,12 +2113,6 @@ export interface V1Beta1Prospect { metadata?: object; } -export interface V1Beta1RQLExportRequest { - filters?: V1Beta1RQLFilter[]; - search?: string; - sort?: V1Beta1RQLSort[]; -} - export interface V1Beta1RQLFilter { name?: string; operator?: string; @@ -2338,10 +2263,6 @@ export interface V1Beta1RevertBillingUsageRequest { export type V1Beta1RevertBillingUsageResponse = object; -export type V1Beta1RevokeSessionResponse = object; - -export type V1Beta1RevokeUserSessionResponse = object; - export interface V1Beta1Role { id?: string; name?: string; @@ -2543,16 +2464,6 @@ export interface V1Beta1ServiceUserToken { created_at?: string; } -export interface V1Beta1Session { - id?: string; - metadata?: SessionMeta; - is_current_session?: boolean; - /** @format date-time */ - created_at?: string; - /** @format date-time */ - updated_at?: string; -} - export interface V1Beta1SetOrganizationKycResponse { organization_kyc?: V1Beta1OrganizationKyc; } From 81bc445beafae1198cd123a3e08afd6c3f7fda07 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 29 Sep 2025 14:05:33 +0530 Subject: [PATCH 12/36] feat: add ping user session api --- .../core/react/components/window/index.tsx | 4 ++ .../core/react/hooks/useLastActiveTracker.ts | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 sdks/js/packages/core/react/hooks/useLastActiveTracker.ts diff --git a/sdks/js/packages/core/react/components/window/index.tsx b/sdks/js/packages/core/react/components/window/index.tsx index 65a5aa4fa..5a95437da 100644 --- a/sdks/js/packages/core/react/components/window/index.tsx +++ b/sdks/js/packages/core/react/components/window/index.tsx @@ -6,6 +6,7 @@ import closeDefault from '~/react/assets/close-default.svg'; import resizeCollapse from '~/react/assets/resize-collapse.svg'; import resizeDefault from '~/react/assets/resize-default.svg'; import resizeExpand from '~/react/assets/resize-expand.svg'; +import { useLastActiveTracker } from '../../hooks/useLastActiveTracker'; // @ts-ignore import styles from './window.module.css'; @@ -24,6 +25,9 @@ export const Window = ({ const [zoom, setZoom] = useState(false); const [isCloseActive, setCloseActive] = useState(false); const [isZoomActive, setZoomActive] = useState(false); + + // Track user last active + useLastActiveTracker(); return ( { + const { client } = useFrontier(); + + const { + mutate: pingUserSession, + } = useMutation(FrontierServiceQueries.pingUserSession, { + onSuccess: () => { + console.log('Session pinged successfully'); + }, + onError: (error) => { + console.error('Failed to ping session:', error); + }, + }); + + useEffect(() => { + if (!client) return; + + // Increment reference count + trackingCount++; + + // Start tracking if this is the first component + if (trackingCount === 1) { + console.log('Starting activity tracking (first component)'); + pingUserSession({}); + globalIntervalId = setInterval(() => { + pingUserSession({}); + }, 10 * 60 * 1000); + } else { + console.log(`Activity tracking already running (${trackingCount} components)`); + } + + return () => { + // Decrement reference count + trackingCount--; + console.log(`Component unmounted, tracking count: ${trackingCount}`); + + // Stop tracking if this was the last component + if (trackingCount === 0 && globalIntervalId) { + console.log('Stopping activity tracking (last component)'); + clearInterval(globalIntervalId); + globalIntervalId = null; + } + }; + }, [client, pingUserSession]); +}; \ No newline at end of file From 247452811d3a220b303e2ce9ee84481dda0ebd7d Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 8 Oct 2025 09:40:38 +0530 Subject: [PATCH 13/36] feat: handle time and formatting --- .../sessions/revoke-session-confirm.tsx | 15 ++---- .../sessions/revoke-session-final-confirm.tsx | 2 +- .../organization/sessions/sessions-page.tsx | 2 +- .../packages/core/react/hooks/useSessions.ts | 48 ++++++++++++++----- .../users/details/security/sessions/index.tsx | 10 ++-- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 3c97ed95d..6476e3b92 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -26,7 +26,7 @@ export const RevokeSessionConfirm = () => { const session = sessions.find(s => s.id === search.sessionId); if (session) { setSessionData(session); - console.log('Found session for revoke:', session); + console.log('Found session for revoking:', session); } else { console.log('Session not found for ID:', search.sessionId); } @@ -37,18 +37,11 @@ export const RevokeSessionConfirm = () => { setIsFinalConfirmOpen(true); }; - const handleFinalConfirm = async () => { + const handleFinalConfirm = () => { if (!search.sessionId) return; - try { - await revokeSession(search.sessionId); - navigate({ to: '/sessions' }); - toast.success('Session revoked successfully'); - } catch (error: any) { - toast.error('Failed to revoke session', { - description: error.message || 'Something went wrong' - }); - } + revokeSession(search.sessionId); + navigate({ to: '/sessions' }); }; diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx index 7781211b6..c9de59a90 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -67,7 +67,7 @@ export const RevokeSessionFinalConfirm = ({ data-test-id="frontier-sdk-confirm-final-revoke-dialog" disabled={isLoading} loading={isLoading} - loadingText="Revoking..." + loaderText="Revoking..." > Revoke diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index 6dec00e8d..6c98806a0 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -38,7 +38,7 @@ export const SessionsPage = () => { diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts index f4b70bb58..069a657d2 100644 --- a/sdks/js/packages/core/react/hooks/useSessions.ts +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -2,6 +2,19 @@ import { useFrontier } from '../contexts/FrontierContext'; import { useQuery, useMutation } from '@connectrpc/connect-query'; import { useQueryClient } from '@tanstack/react-query'; import { FrontierServiceQueries } from '@raystack/proton/frontier'; +import { toast } from '@raystack/apsara'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; +import { useMemo } from 'react'; + +dayjs.extend(relativeTime); + +// Utility function to format device display +export const formatDeviceDisplay = (browser?: string, operatingSystem?: string): string => { + const browserName = browser || "Unknown"; + const osName = operatingSystem || "Unknown"; + return browserName === "Unknown" && osName === "Unknown" ? "Unknown browser and OS" : `${browserName} on ${osName}`; +}; export interface SessionData { id: string; @@ -29,15 +42,25 @@ export const useSessions = () => { } ); - const sessions: SessionData[] = (sessionsData?.sessions || []).map((session: any) => ({ - id: session.id || '', - browser: session.metadata?.browser || 'Unknown', - operatingSystem: session.metadata?.operatingSystem || 'Unknown', - ipAddress: session.metadata?.ipAddress || 'Unknown', - location: session.metadata?.location || 'Unknown', - lastActive: session.updatedAt?.seconds ? new Date(Number(session.updatedAt.seconds) * 1000).toLocaleString() : 'Unknown', - isCurrent: session.isCurrent || false, - })); + const formatLastActive = (updatedAt?: any) => { + if (!updatedAt) return "Unknown"; + + const seconds = typeof updatedAt.seconds === 'bigint' ? Number(updatedAt.seconds) : updatedAt.seconds; + const date = new Date(seconds * 1000); + return dayjs(date).fromNow(); + }; + + const sessions: SessionData[] = useMemo(() => + (sessionsData?.sessions || []).map((session: any) => ({ + id: session.id || '', + browser: session.metadata?.browser || 'Unknown', + operatingSystem: session.metadata?.operatingSystem || 'Unknown', + ipAddress: session.metadata?.ipAddress || 'Unknown', + location: session.metadata?.location || 'Unknown', + lastActive: formatLastActive(session.updatedAt), + isCurrent: session.isCurrentSession || false, + })), [sessionsData?.sessions] + ); const { mutate: revokeSession, @@ -48,9 +71,12 @@ export const useSessions = () => { queryClient.invalidateQueries({ queryKey: [FrontierServiceQueries.listSessions], }); + toast.success('Session revoked successfully'); }, - onError: (error) => { - console.error('Failed to revoke session:', error); + onError: (error: any) => { + toast.error('Failed to revoke session', { + description: error.message || 'Something went wrong' + }); }, }); diff --git a/ui/src/pages/users/details/security/sessions/index.tsx b/ui/src/pages/users/details/security/sessions/index.tsx index 2365e84db..daccd8b32 100644 --- a/ui/src/pages/users/details/security/sessions/index.tsx +++ b/ui/src/pages/users/details/security/sessions/index.tsx @@ -152,9 +152,13 @@ export const UserSessions = () => { {session.metadata?.location || "Unknown location"} - - Last active {formatLastActive(session.updatedAt)} - + {session.isCurrentSession ? ( + Current session + ) : ( + + Last active {formatLastActive(session.updatedAt)} + + )} )) From 494b277471c3324412e047e545aebb3f5245accc Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 8 Oct 2025 13:57:07 +0530 Subject: [PATCH 18/36] chore: rename copy --- .../organization/sessions/revoke-session-confirm.tsx | 3 ++- .../sessions/revoke-session-final-confirm.tsx | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 6476e3b92..25992d6b3 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -98,7 +98,7 @@ export const RevokeSessionConfirm = () => { onClick={handleRevokeClick} data-test-id="frontier-sdk-confirm-revoke-session-dialog" > - Revoke + {sessionData?.isCurrent ? 'Sign out' : 'Revoke'} @@ -121,6 +121,7 @@ export const RevokeSessionConfirm = () => { onOpenChange={setIsFinalConfirmOpen} onConfirm={handleFinalConfirm} isLoading={isRevokingSession} + isCurrentSession={sessionData?.isCurrent} /> ); diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx index c9de59a90..5e8ee4b25 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -12,13 +12,15 @@ interface RevokeSessionFinalConfirmProps { onOpenChange: (isOpen: boolean) => void; onConfirm: () => void; isLoading?: boolean; + isCurrentSession?: boolean; } export const RevokeSessionFinalConfirm = ({ isOpen, onOpenChange, onConfirm, - isLoading = false + isLoading = false, + isCurrentSession = false }: RevokeSessionFinalConfirmProps) => { const handleConfirm = async () => { try { @@ -67,9 +69,9 @@ export const RevokeSessionFinalConfirm = ({ data-test-id="frontier-sdk-confirm-final-revoke-dialog" disabled={isLoading} loading={isLoading} - loaderText="Revoking..." + loaderText={isCurrentSession ? "Signing out..." : "Revoking..."} > - Revoke + {isCurrentSession ? 'Sign out' : 'Revoke'} From e4286a5edffb1154072a121ec86f545e446bd2a9 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 8 Oct 2025 14:08:55 +0530 Subject: [PATCH 19/36] style: title --- .../security/sessions/revoke-session-confirm.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx b/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx index 9b2c078bc..8791d6034 100644 --- a/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx +++ b/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx @@ -3,7 +3,8 @@ import { Button, Dialog, Flex, - List + List, + Text } from '@raystack/apsara'; import { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; import { formatDeviceDisplay } from './index'; @@ -42,8 +43,12 @@ export const RevokeSessionConfirm = ({ isOpen, onOpenChange, sessionInfo, onRevo style={{ padding: 0, maxWidth: '400px', width: '100%' }} > - {sessionInfo ? formatDeviceDisplay(sessionInfo.browser, sessionInfo.operatingSystem) : "Unknown browser and OS"} - + + + {sessionInfo ? formatDeviceDisplay(sessionInfo.browser, sessionInfo.operatingSystem) : "Unknown browser and OS"} + + + From 17357dddafef35c7a9729e499da7369266a1c786 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 8 Oct 2025 14:14:58 +0530 Subject: [PATCH 20/36] chore: conditional show error message --- .../sessions/revoke-session-final-confirm.tsx | 9 ++++++++- sdks/js/packages/core/react/hooks/useSessions.ts | 10 +++++++++- ui/src/pages/users/details/security/sessions/index.tsx | 9 ++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx index 5e8ee4b25..ce298e0bb 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -7,6 +7,13 @@ import { } from '@raystack/apsara/v1'; import styles from './sessions.module.css'; +const getErrorMessage = (error: any): string => { + if (error?.status === 500) { + return 'Something went wrong'; + } + return error?.message || 'Something went wrong'; +}; + interface RevokeSessionFinalConfirmProps { isOpen: boolean; onOpenChange: (isOpen: boolean) => void; @@ -28,7 +35,7 @@ export const RevokeSessionFinalConfirm = ({ onOpenChange(false); } catch (error: any) { toast.error('Failed to revoke session', { - description: error.message || 'Something went wrong' + description: getErrorMessage(error) }); } }; diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts index de7ddf8d1..f1a95a2ca 100644 --- a/sdks/js/packages/core/react/hooks/useSessions.ts +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -9,6 +9,14 @@ import { useMemo } from 'react'; dayjs.extend(relativeTime); +// Utility function to format error messages based on status code +const getErrorMessage = (error: any): string => { + if (error?.status === 500) { + return 'Something went wrong'; + } + return error?.message || 'Something went wrong'; +}; + export const formatDeviceDisplay = (browser?: string, operatingSystem?: string): string => { const browserName = browser || "Unknown"; const osName = operatingSystem || "Unknown"; @@ -87,7 +95,7 @@ export const useSessions = () => { }, onError: (error: any) => { toast.error('Failed to revoke session', { - description: error.message || 'Something went wrong' + description: getErrorMessage(error) }); }, }); diff --git a/ui/src/pages/users/details/security/sessions/index.tsx b/ui/src/pages/users/details/security/sessions/index.tsx index daccd8b32..87e1670b2 100644 --- a/ui/src/pages/users/details/security/sessions/index.tsx +++ b/ui/src/pages/users/details/security/sessions/index.tsx @@ -12,6 +12,13 @@ import styles from "./sessions.module.css"; dayjs.extend(relativeTime); +const getErrorMessage = (error: any): string => { + if (error?.status === 500) { + return 'Something went wrong'; + } + return error?.message || 'Something went wrong'; +}; + export const formatDeviceDisplay = (browser?: string, operatingSystem?: string): string => { const browserName = browser || "Unknown"; const osName = operatingSystem || "Unknown"; @@ -61,7 +68,7 @@ export const UserSessions = () => { }, onError: (error: any) => { toast.error('Failed to revoke session', { - description: error.message || 'Something went wrong' + description: getErrorMessage(error) }); }, }); From 9929b9385d5a2f86faaa5a1577a890be8a00e40a Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Wed, 8 Oct 2025 14:21:38 +0530 Subject: [PATCH 21/36] chore: remove console --- .../organization/sessions/revoke-session-confirm.tsx | 10 +++++----- .../packages/core/react/hooks/useLastActiveTracker.ts | 8 -------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 25992d6b3..8c21a4904 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -24,12 +24,12 @@ export const RevokeSessionConfirm = () => { useEffect(() => { if (search.sessionId && sessions.length > 0) { const session = sessions.find(s => s.id === search.sessionId); - if (session) { - setSessionData(session); - console.log('Found session for revoking:', session); - } else { - console.log('Session not found for ID:', search.sessionId); + if (!session) { + console.error('Session not found for ID:', search.sessionId); + return; } + + setSessionData(session); } }, [search.sessionId, sessions]); diff --git a/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts b/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts index d72a9296a..b1f69bdba 100644 --- a/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts +++ b/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts @@ -13,9 +13,6 @@ export const useLastActiveTracker = () => { const { mutate: pingUserSession, } = useMutation(FrontierServiceQueries.pingUserSession, { - onSuccess: () => { - console.log('Session pinged successfully'); - }, onError: (error) => { console.error('Failed to ping session:', error); }, @@ -29,23 +26,18 @@ export const useLastActiveTracker = () => { // Start tracking if this is the first component if (trackingCount === 1) { - console.log('Starting activity tracking (first component)'); pingUserSession({}); globalIntervalId = setInterval(() => { pingUserSession({}); }, 10 * 60 * 1000); - } else { - console.log(`Activity tracking already running (${trackingCount} components)`); } return () => { // Decrement reference count trackingCount--; - console.log(`Component unmounted, tracking count: ${trackingCount}`); // Stop tracking if this was the last component if (trackingCount === 0 && globalIntervalId) { - console.log('Stopping activity tracking (last component)'); clearInterval(globalIntervalId); globalIntervalId = null; } From 89cbea04bf70f0c286c2e4aca9f4a5fd7f2b94c9 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 9 Oct 2025 17:18:05 +0530 Subject: [PATCH 22/36] fix: comments on PR --- .../sessions/revoke-session-confirm.tsx | 52 ++++++++++++++----- .../packages/core/react/hooks/useSessions.ts | 30 ++++------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 8c21a4904..a0535ed23 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -1,15 +1,17 @@ -import { useState, useEffect } from 'react'; +import { useState, useMemo } from 'react'; import { useNavigate, useSearch } from '@tanstack/react-router'; import { Button, - toast, Text, Dialog, Flex, List, Skeleton } from '@raystack/apsara/v1'; -import { useSessions, SessionData } from '../../../hooks/useSessions'; +import { useSessions } from '../../../hooks/useSessions'; +import { useFrontier } from '../../../contexts/FrontierContext'; +import { useMutation } from '@connectrpc/connect-query'; +import { FrontierServiceQueries } from '@raystack/proton/frontier'; import { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; import styles from './sessions.module.css'; @@ -17,20 +19,37 @@ export const RevokeSessionConfirm = () => { const navigate = useNavigate({ from: '/sessions/revoke' }); const search = useSearch({ from: '/sessions/revoke' }) as { sessionId?: string }; const { sessions, revokeSession, isRevokingSession } = useSessions(); - const [sessionData, setSessionData] = useState(null); + const { setUser } = useFrontier(); const [isFinalConfirmOpen, setIsFinalConfirmOpen] = useState(false); - // Find the session data based on sessionId from URL params - useEffect(() => { - if (search.sessionId && sessions.length > 0) { - const session = sessions.find(s => s.id === search.sessionId); - if (!session) { - console.error('Session not found for ID:', search.sessionId); - return; + const { mutate: logout } = useMutation(FrontierServiceQueries.authLogout, { + onSuccess: () => { + setUser(undefined); + window.location.href = '/login'; + }, + onError: (error) => { + console.error('Failed to logout:', error); + // Fallback to regular session revocation + if (search.sessionId) { + revokeSession(search.sessionId); + navigate({ to: '/sessions' }); } - - setSessionData(session); + }, + }); + + // Find the session data based on sessionId from URL params + const sessionData = useMemo(() => { + if (!search.sessionId || sessions.length === 0) { + return null; + } + + const session = sessions.find(s => s.id === search.sessionId); + if (!session) { + console.error('Not found'); + return null; } + + return session; }, [search.sessionId, sessions]); const handleRevokeClick = () => { @@ -40,6 +59,13 @@ export const RevokeSessionConfirm = () => { const handleFinalConfirm = () => { if (!search.sessionId) return; + // If revoking current session, logout first + if (sessionData?.isCurrent) { + logout({}); + return; + } + + // For non-current sessions, just revoke the session revokeSession(search.sessionId); navigate({ to: '/sessions' }); }; diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts index f1a95a2ca..4746b2cdb 100644 --- a/sdks/js/packages/core/react/hooks/useSessions.ts +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -1,20 +1,22 @@ -import { useFrontier } from '../contexts/FrontierContext'; +import { useMemo } from 'react'; import { useQuery, useMutation, createConnectQueryKey, useTransport } from '@connectrpc/connect-query'; import { useQueryClient } from '@tanstack/react-query'; import { FrontierServiceQueries } from '@raystack/proton/frontier'; import { toast } from '@raystack/apsara'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; -import { useMemo } from 'react'; dayjs.extend(relativeTime); // Utility function to format error messages based on status code -const getErrorMessage = (error: any): string => { - if (error?.status === 500) { +const getErrorMessage = (error: unknown): string => { + if (error instanceof Error && 'status' in error && error.status === 500) { return 'Something went wrong'; } - return error?.message || 'Something went wrong'; + if (error instanceof Error) { + return error.message; + } + return 'Something went wrong'; }; export const formatDeviceDisplay = (browser?: string, operatingSystem?: string): string => { @@ -23,18 +25,7 @@ export const formatDeviceDisplay = (browser?: string, operatingSystem?: string): return browserName === "Unknown" && osName === "Unknown" ? "Unknown browser and OS" : `${browserName} on ${osName}`; }; -export interface SessionData { - id: string; - browser: string; - operatingSystem: string; - ipAddress: string; - location: string; - lastActive: string; - isCurrent: boolean; -} - export const useSessions = () => { - const { client } = useFrontier(); const queryClient = useQueryClient(); const transport = useTransport(); @@ -44,10 +35,7 @@ export const useSessions = () => { error } = useQuery( FrontierServiceQueries.listSessions, - {}, - { - enabled: !!client, - } + {} ); const formatLastActive = (updatedAt?: any) => { @@ -58,7 +46,7 @@ export const useSessions = () => { return dayjs(date).fromNow(); }; - const sessions: SessionData[] = useMemo(() => + const sessions = useMemo(() => (sessionsData?.sessions || []) .map((session: any) => ({ id: session.id || '', From 08f0bc20566058c9bf56e79d66b4ec3842638da8 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Thu, 9 Oct 2025 17:21:48 +0530 Subject: [PATCH 23/36] fix: remove comment --- .../components/organization/sessions/revoke-session-confirm.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index a0535ed23..e5ea48b97 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -59,13 +59,11 @@ export const RevokeSessionConfirm = () => { const handleFinalConfirm = () => { if (!search.sessionId) return; - // If revoking current session, logout first if (sessionData?.isCurrent) { logout({}); return; } - // For non-current sessions, just revoke the session revokeSession(search.sessionId); navigate({ to: '/sessions' }); }; From 9b1036bb71aa3c355c78f18093c30c488933c541 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Fri, 10 Oct 2025 08:30:42 +0530 Subject: [PATCH 24/36] fix: change useLastActivityTracker invoke strategy --- sdks/js/packages/core/react/components/window/index.tsx | 3 --- sdks/js/packages/core/react/contexts/FrontierContext.tsx | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sdks/js/packages/core/react/components/window/index.tsx b/sdks/js/packages/core/react/components/window/index.tsx index 5a95437da..5c206464d 100644 --- a/sdks/js/packages/core/react/components/window/index.tsx +++ b/sdks/js/packages/core/react/components/window/index.tsx @@ -6,7 +6,6 @@ import closeDefault from '~/react/assets/close-default.svg'; import resizeCollapse from '~/react/assets/resize-collapse.svg'; import resizeDefault from '~/react/assets/resize-default.svg'; import resizeExpand from '~/react/assets/resize-expand.svg'; -import { useLastActiveTracker } from '../../hooks/useLastActiveTracker'; // @ts-ignore import styles from './window.module.css'; @@ -26,8 +25,6 @@ export const Window = ({ const [isCloseActive, setCloseActive] = useState(false); const [isZoomActive, setZoomActive] = useState(false); - // Track user last active - useLastActiveTracker(); return ( Date: Fri, 10 Oct 2025 12:51:02 +0530 Subject: [PATCH 25/36] Apply suggestion from @paanSinghCoder --- .../organization/sessions/revoke-session-final-confirm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx index ce298e0bb..bb88d163b 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -78,7 +78,7 @@ export const RevokeSessionFinalConfirm = ({ loading={isLoading} loaderText={isCurrentSession ? "Signing out..." : "Revoking..."} > - {isCurrentSession ? 'Sign out' : 'Revoke'} + {isCurrentSession ? 'Logout' : 'Revoke'} From 6922fd8098e9b39a45195a443da81f940d173464 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Fri, 10 Oct 2025 12:51:10 +0530 Subject: [PATCH 26/36] Apply suggestion from @paanSinghCoder --- .../react/components/organization/sessions/sessions-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index b89dcd2ee..1692c5dcc 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -102,7 +102,7 @@ export const SessionsPage = () => { onClick={() => handleRevoke(session.id)} data-test-id="frontier-sdk-revoke-session-button" > - {session.isCurrent ? 'Sign out' : 'Revoke'} + {session.isCurrent ? 'Logout' : 'Revoke'} )) From cf79c95100846de9a2e777d107a0c976132708e7 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Fri, 10 Oct 2025 12:51:25 +0530 Subject: [PATCH 27/36] Apply suggestion from @paanSinghCoder --- .../components/organization/sessions/revoke-session-confirm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index e5ea48b97..672a7d5c8 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -122,7 +122,7 @@ export const RevokeSessionConfirm = () => { onClick={handleRevokeClick} data-test-id="frontier-sdk-confirm-revoke-session-dialog" > - {sessionData?.isCurrent ? 'Sign out' : 'Revoke'} + {sessionData?.isCurrent ? 'Logout' : 'Revoke'} From 3e9b5a7b3e5bcdace569cbeaa7220598c53d4ef5 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Mon, 13 Oct 2025 12:36:24 +0530 Subject: [PATCH 28/36] Apply suggestion from @rsbh Co-authored-by: Rishabh Mishra --- .../components/organization/sessions/revoke-session-confirm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 672a7d5c8..49697e44d 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -77,7 +77,7 @@ export const RevokeSessionConfirm = () => { style={{ padding: 0, maxWidth: '400px', width: '100%' }} > - + {sessionData.browser} on {sessionData.operatingSystem} From 8fde07ec29aad50da61da496fac7835aa9d13450 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Mon, 13 Oct 2025 12:37:05 +0530 Subject: [PATCH 29/36] Apply suggestion from @rsbh Co-authored-by: Rishabh Mishra --- .../react/components/organization/sessions/sessions-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index 1692c5dcc..c05c73560 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -31,7 +31,7 @@ export const SessionsPage = () => { if (isLoading) { return ( - + {renderSessionsHeader()} From 197a5b8c6efa31be758759fba663f2ebd91311aa Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Mon, 13 Oct 2025 12:37:15 +0530 Subject: [PATCH 30/36] Apply suggestion from @rsbh Co-authored-by: Rishabh Mishra --- .../react/components/organization/sessions/sessions-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index c05c73560..d5525b7f9 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -50,7 +50,7 @@ export const SessionsPage = () => { if (error) { return ( - + {renderSessionsHeader()} From f4b472e1b36eb69f6f187bd06b15b9439976ee50 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 13 Oct 2025 13:46:17 +0530 Subject: [PATCH 31/36] fix: use unknown as type for error --- .../organization/sessions/revoke-session-final-confirm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx index bb88d163b..35fc4a43d 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-final-confirm.tsx @@ -7,11 +7,11 @@ import { } from '@raystack/apsara/v1'; import styles from './sessions.module.css'; -const getErrorMessage = (error: any): string => { - if (error?.status === 500) { +const getErrorMessage = (error: unknown): string => { + if (error && typeof error === 'object' && 'status' in error && error.status === 500) { return 'Something went wrong'; } - return error?.message || 'Something went wrong'; + return error instanceof Error ? error.message : 'Something went wrong'; }; interface RevokeSessionFinalConfirmProps { From bf73534fb174ee0d0a96403c99e44738bebfc520 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 13 Oct 2025 14:17:22 +0530 Subject: [PATCH 32/36] chore: use useQuery --- .../core/react/hooks/useLastActiveTracker.ts | 49 +++---------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts b/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts index b1f69bdba..5eaf6518b 100644 --- a/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts +++ b/sdks/js/packages/core/react/hooks/useLastActiveTracker.ts @@ -1,46 +1,13 @@ -import { useEffect } from 'react'; -import { useMutation } from '@connectrpc/connect-query'; +import { useQuery } from '@connectrpc/connect-query'; import { FrontierServiceQueries } from '@raystack/proton/frontier'; -import { useFrontier } from '../contexts/FrontierContext'; - -// Global state with reference counting (this prevents multiple instances of the hook from causing multiple intervals to be set) -let globalIntervalId: number | null = null; -let trackingCount = 0; export const useLastActiveTracker = () => { - const { client } = useFrontier(); - - const { - mutate: pingUserSession, - } = useMutation(FrontierServiceQueries.pingUserSession, { - onError: (error) => { - console.error('Failed to ping session:', error); - }, + useQuery(FrontierServiceQueries.pingUserSession, {}, { + // Ping immediately and then every 10 minutes + refetchInterval: 10 * 60 * 1000, + refetchIntervalInBackground: true, + staleTime: Infinity, + gcTime: Infinity, + retry: false, }); - - useEffect(() => { - if (!client) return; - - // Increment reference count - trackingCount++; - - // Start tracking if this is the first component - if (trackingCount === 1) { - pingUserSession({}); - globalIntervalId = setInterval(() => { - pingUserSession({}); - }, 10 * 60 * 1000); - } - - return () => { - // Decrement reference count - trackingCount--; - - // Stop tracking if this was the last component - if (trackingCount === 0 && globalIntervalId) { - clearInterval(globalIntervalId); - globalIntervalId = null; - } - }; - }, [client, pingUserSession]); }; \ No newline at end of file From 1c1a715f6f3d632fa7fa95af50ef182a29d24924 Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 13 Oct 2025 14:21:16 +0530 Subject: [PATCH 33/36] fix: use dayjs --- sdks/js/packages/core/react/hooks/useSessions.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sdks/js/packages/core/react/hooks/useSessions.ts b/sdks/js/packages/core/react/hooks/useSessions.ts index 4746b2cdb..2e57ce626 100644 --- a/sdks/js/packages/core/react/hooks/useSessions.ts +++ b/sdks/js/packages/core/react/hooks/useSessions.ts @@ -5,6 +5,7 @@ import { FrontierServiceQueries } from '@raystack/proton/frontier'; import { toast } from '@raystack/apsara'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import { timestampToDayjs } from '../../utils/timestamp'; dayjs.extend(relativeTime); @@ -39,11 +40,8 @@ export const useSessions = () => { ); const formatLastActive = (updatedAt?: any) => { - if (!updatedAt) return "Unknown"; - - const seconds = typeof updatedAt.seconds === 'bigint' ? Number(updatedAt.seconds) : updatedAt.seconds; - const date = new Date(seconds * 1000); - return dayjs(date).fromNow(); + const d = timestampToDayjs(updatedAt); + return d ? d.fromNow() : "Unknown"; }; const sessions = useMemo(() => From 47e90e42a9d939856084aefe2c6bded6bcefa8eb Mon Sep 17 00:00:00 2001 From: paanSinghCoder Date: Mon, 13 Oct 2025 19:47:06 +0530 Subject: [PATCH 34/36] fix: pass onLogout via props from client --- .../core/react/components/organization/profile.tsx | 6 ++++-- .../core/react/components/organization/routes.tsx | 3 ++- .../sessions/revoke-session-confirm.tsx | 14 +++++++------- .../packages/sdk-demo/src/pages/Organization.tsx | 3 +++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/sdks/js/packages/core/react/components/organization/profile.tsx b/sdks/js/packages/core/react/components/organization/profile.tsx index f9e0ec72d..7342e8a4b 100644 --- a/sdks/js/packages/core/react/components/organization/profile.tsx +++ b/sdks/js/packages/core/react/components/organization/profile.tsx @@ -30,7 +30,8 @@ export const OrganizationProfile = ({ showAPIKeys = false, showPreferences = false, hideToast = false, - customScreens = [] + customScreens = [], + onLogout = () => {} }: OrganizationProfileProps) => { const memoryHistory = createMemoryHistory({ initialEntries: [defaultRoute] @@ -50,7 +51,8 @@ export const OrganizationProfile = ({ showAPIKeys, hideToast, showPreferences, - customRoutes + customRoutes, + onLogout } }); return ; diff --git a/sdks/js/packages/core/react/components/organization/routes.tsx b/sdks/js/packages/core/react/components/organization/routes.tsx index 723bfa959..dd51ae3fe 100644 --- a/sdks/js/packages/core/react/components/organization/routes.tsx +++ b/sdks/js/packages/core/react/components/organization/routes.tsx @@ -64,6 +64,7 @@ export interface OrganizationProfileProps { showPreferences?: boolean; hideToast?: boolean; customScreens?: CustomScreen[]; + onLogout?: () => void; } export interface CustomRoutes { @@ -79,7 +80,7 @@ type RouterContext = Pick< | 'showAPIKeys' | 'hideToast' | 'showPreferences' -> & { customRoutes: CustomRoutes }; +> & { customRoutes: CustomRoutes; onLogout?: () => void }; export function getCustomRoutes(customScreens: CustomScreen[] = []) { return ( diff --git a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx index 49697e44d..0760545ba 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/revoke-session-confirm.tsx @@ -1,5 +1,5 @@ import { useState, useMemo } from 'react'; -import { useNavigate, useSearch } from '@tanstack/react-router'; +import { useNavigate, useSearch, useRouteContext } from '@tanstack/react-router'; import { Button, Text, @@ -9,7 +9,6 @@ import { Skeleton } from '@raystack/apsara/v1'; import { useSessions } from '../../../hooks/useSessions'; -import { useFrontier } from '../../../contexts/FrontierContext'; import { useMutation } from '@connectrpc/connect-query'; import { FrontierServiceQueries } from '@raystack/proton/frontier'; import { RevokeSessionFinalConfirm } from './revoke-session-final-confirm'; @@ -19,13 +18,14 @@ export const RevokeSessionConfirm = () => { const navigate = useNavigate({ from: '/sessions/revoke' }); const search = useSearch({ from: '/sessions/revoke' }) as { sessionId?: string }; const { sessions, revokeSession, isRevokingSession } = useSessions(); - const { setUser } = useFrontier(); + const { onLogout } = useRouteContext({ from: '__root__' }) as { onLogout?: () => void }; const [isFinalConfirmOpen, setIsFinalConfirmOpen] = useState(false); const { mutate: logout } = useMutation(FrontierServiceQueries.authLogout, { onSuccess: () => { - setUser(undefined); - window.location.href = '/login'; + if (onLogout) { + onLogout(); + } }, onError: (error) => { console.error('Failed to logout:', error); @@ -86,7 +86,7 @@ export const RevokeSessionConfirm = () => { - + Device {sessionData.browser} on {sessionData.operatingSystem} @@ -103,7 +103,7 @@ export const RevokeSessionConfirm = () => { Last Active {sessionData.lastActive} - + diff --git a/sdks/js/packages/sdk-demo/src/pages/Organization.tsx b/sdks/js/packages/sdk-demo/src/pages/Organization.tsx index e7f8667cd..1436ef167 100644 --- a/sdks/js/packages/sdk-demo/src/pages/Organization.tsx +++ b/sdks/js/packages/sdk-demo/src/pages/Organization.tsx @@ -12,6 +12,9 @@ export default function Organization() { showTokens={true} showPreferences={true} showAPIKeys={true} + onLogout={() => { + window.location.href = '/login'; + }} /> ) : null; From f809533a272ec32fe255ab524280d8db4d39df6d Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Tue, 14 Oct 2025 11:36:11 +0530 Subject: [PATCH 35/36] Update sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx Co-authored-by: Rishabh Mishra --- .../react/components/organization/sessions/sessions-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx index d5525b7f9..4fe94002a 100644 --- a/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx +++ b/sdks/js/packages/core/react/components/organization/sessions/sessions-page.tsx @@ -66,7 +66,7 @@ export const SessionsPage = () => { } return ( - + {renderSessionsHeader()} From bf965849a33478486cfa7e8086e729345c53be44 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Tue, 14 Oct 2025 11:36:19 +0530 Subject: [PATCH 36/36] Update ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx Co-authored-by: Rishabh Mishra --- .../users/details/security/sessions/revoke-session-confirm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx b/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx index 8791d6034..67c372145 100644 --- a/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx +++ b/ui/src/pages/users/details/security/sessions/revoke-session-confirm.tsx @@ -43,7 +43,7 @@ export const RevokeSessionConfirm = ({ isOpen, onOpenChange, sessionInfo, onRevo style={{ padding: 0, maxWidth: '400px', width: '100%' }} > - + {sessionInfo ? formatDeviceDisplay(sessionInfo.browser, sessionInfo.operatingSystem) : "Unknown browser and OS"}