Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions web/sdk/react/components/organization/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Tokens from './tokens';
import Plans from './plans';
import APIKeys from './api-keys';
import ServiceUserPage from './api-keys/service-user';
import { SessionsPage, RevokeSessionConfirm } from './sessions';
import { SessionsPage } from './sessions';
export interface CustomScreen {
name: string;
path: string;
Expand Down Expand Up @@ -241,14 +241,6 @@ const sessionsRoute = createRoute({
component: SessionsPage
});

const revokeSessionRoute = createRoute({
getParentRoute: () => sessionsRoute,
path: '/revoke',
component: RevokeSessionConfirm,
validateSearch: (search: Record<string, unknown>) => ({
sessionId: search.sessionId as string | undefined,
}),
});

interface getRootTreeOptions {
customScreens?: CustomScreen[];
Expand All @@ -258,7 +250,7 @@ export function getRootTree({ customScreens = [] }: getRootTreeOptions) {
return rootRoute.addChildren([
indexRoute,
securityRoute,
sessionsRoute.addChildren([revokeSessionRoute]),
sessionsRoute,
membersRoute,
teamsRoute,
domainsRoute.addChildren([
Expand Down
12 changes: 9 additions & 3 deletions web/sdk/react/components/organization/sessions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export { SessionsPage } from './sessions-page';
export { RevokeSessionConfirm } from './revoke-session-confirm';
export { RevokeSessionFinalConfirm } from './revoke-session-final-confirm';
'use client';

import { useRouteContext } from '@tanstack/react-router';
import { SessionsPage as SessionsPageView } from '~/react/views/sessions';

export const SessionsPage = () => {
const { onLogout } = useRouteContext({ from: '__root__' }) as { onLogout?: () => void };
return <SessionsPageView onLogout={onLogout} />;
};
5 changes: 5 additions & 0 deletions web/sdk/react/views/sessions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as SessionsPage } from './sessions-page';
export type { SessionsPageProps } from './sessions-page';

export { RevokeSessionDialog } from './revoke-session-dialog';
export type { RevokeSessionDialogProps } from './revoke-session-dialog';
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState, useMemo } from 'react';
import { useNavigate, useSearch, useRouteContext } from '@tanstack/react-router';
import {
Button,
Text,
Expand All @@ -8,19 +7,30 @@ import {
List,
Skeleton
} from '@raystack/apsara';
import { useSessions } from '../../../hooks/useSessions';
import { useSessions } from '~/react/hooks/useSessions';
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';

export const RevokeSessionConfirm = () => {
const navigate = useNavigate({ from: '/sessions/revoke' });
const search = useSearch({ from: '/sessions/revoke' }) as { sessionId?: string };
export interface RevokeSessionDialogProps {
open: boolean;
onOpenChange?: (value: boolean) => void;
sessionId: string;
onLogout?: () => void;
}

export const RevokeSessionDialog = ({
open,
onOpenChange,
sessionId,
onLogout
}: RevokeSessionDialogProps) => {
const { sessions, revokeSession, isRevokingSession } = useSessions();
const { onLogout } = useRouteContext({ from: '__root__' }) as { onLogout?: () => void };
const [isFinalConfirmOpen, setIsFinalConfirmOpen] = useState(false);

const handleClose = () => onOpenChange?.(false);

const { mutate: logout } = useMutation(FrontierServiceQueries.authLogout, {
onSuccess: () => {
if (onLogout) {
Expand All @@ -30,49 +40,48 @@ export const RevokeSessionConfirm = () => {
onError: (error) => {
console.error('Failed to logout:', error);
// Fallback to regular session revocation
if (search.sessionId) {
revokeSession(search.sessionId);
navigate({ to: '/sessions' });
if (sessionId) {
revokeSession(sessionId);
handleClose();
}
},
});

// Find the session data based on sessionId from URL params
// Find the session data based on sessionId
const sessionData = useMemo(() => {
if (!search.sessionId || sessions.length === 0) {
if (!sessionId || sessions.length === 0) {
return null;
}
const session = sessions.find(s => s.id === search.sessionId);

const session = sessions.find(s => s.id === sessionId);
if (!session) {
console.error('Not found');
return null;
}

return session;
}, [search.sessionId, sessions]);
}, [sessionId, sessions]);

const handleRevokeClick = () => {
setIsFinalConfirmOpen(true);
};

const handleFinalConfirm = () => {
if (!search.sessionId) return;
if (!sessionId) return;

if (sessionData?.isCurrent) {
logout({});
return;
}

revokeSession(search.sessionId);
navigate({ to: '/sessions' });
};

revokeSession(sessionId);
handleClose();
};

return (
<>
{sessionData ? (
<Dialog open={true} onOpenChange={() => navigate({ to: '/sessions' })}>
<Dialog open={open} onOpenChange={onOpenChange}>
<Dialog.Content
style={{ padding: 0, maxWidth: '400px', width: '100%' }}
>
Expand Down Expand Up @@ -111,7 +120,7 @@ export const RevokeSessionConfirm = () => {
<Button
variant="outline"
color="neutral"
onClick={() => navigate({ to: '/sessions' })}
onClick={handleClose}
data-test-id="frontier-sdk-cancel-revoke-session-dialog"
>
Cancel
Expand All @@ -128,17 +137,17 @@ export const RevokeSessionConfirm = () => {
</Dialog.Footer>
</Dialog.Content>
</Dialog>
) : (
<Dialog open={true} onOpenChange={() => navigate({ to: '/sessions' })}>
) : open ? (
<Dialog open={true} onOpenChange={onOpenChange}>
<Dialog.Content style={{ padding: 0, maxWidth: '400px', width: '100%' }}>
<Skeleton
height="20px"
<Skeleton
height="20px"
containerStyle={{ padding: '2rem' }}
count={4}
/>
</Dialog.Content>
</Dialog>
)}
) : null}

<RevokeSessionFinalConfirm
isOpen={isFinalConfirmOpen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ interface RevokeSessionFinalConfirmProps {
isCurrentSession?: boolean;
}

export const RevokeSessionFinalConfirm = ({
isOpen,
onOpenChange,
onConfirm,
export const RevokeSessionFinalConfirm = ({
isOpen,
onOpenChange,
onConfirm,
isLoading = false,
isCurrentSession = false
}: RevokeSessionFinalConfirmProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ export const SessionSkeleton = ({ count = 3 }: { count?: number }) => {
</>
);
};

Original file line number Diff line number Diff line change
@@ -1,33 +1,49 @@
'use client';

import { useState } from 'react';
import {
Text,
Flex,
Button,
} from '@raystack/apsara';
import { Outlet, useNavigate } from '@tanstack/react-router';
import { useSessions } from '../../../hooks/useSessions';
import { PageHeader } from '../../common/page-header';
import sharedStyles from '../styles.module.css';
import { useSessions } from '~/react/hooks/useSessions';
import { PageHeader } from '~/react/components/common/page-header';
import sharedStyles from '../../components/organization/styles.module.css';
import { SessionSkeleton } from './session-skeleton';
import { RevokeSessionDialog } from './revoke-session-dialog';
import styles from './sessions.module.css';

export const SessionsPage = () => {
const navigate = useNavigate({ from: '/sessions' });
export interface SessionsPageProps {
onLogout?: () => void;
}

export default function SessionsPage({ onLogout }: SessionsPageProps) {
const { sessions, isLoading, error } = useSessions();

const [revokeState, setRevokeState] = useState({
open: false,
sessionId: ''
});

const handleRevokeOpenChange = (value: boolean) => {
if (!value) {
setRevokeState({ open: false, sessionId: '' });
} else {
setRevokeState(prev => ({ ...prev, open: value }));
}
};

const handleRevoke = (sessionId: string) => {
navigate({ to: '/sessions/revoke', search: { sessionId } });
setRevokeState({ open: true, sessionId });
};

if (isLoading) {
return (
<Flex direction="column" width="full">
<Flex direction="column" className={styles.container}>
<Flex direction="row" justify="between" align="center" className={sharedStyles.header}>
<PageHeader
title="Sessions"
<PageHeader
title="Sessions"
description="Devices logged into this account."
/>
</Flex>
Expand All @@ -44,8 +60,8 @@ export const SessionsPage = () => {
<Flex direction="column" width="full">
<Flex direction="column" className={styles.container}>
<Flex direction="row" justify="between" align="center" className={sharedStyles.header}>
<PageHeader
title="Sessions"
<PageHeader
title="Sessions"
description="Devices logged into this account."
/>
</Flex>
Expand All @@ -63,12 +79,12 @@ export const SessionsPage = () => {
<Flex direction="column" width="full">
<Flex direction="column" className={styles.container}>
<Flex direction="row" justify="between" align="center" className={sharedStyles.header}>
<PageHeader
title="Sessions"
<PageHeader
title="Sessions"
description="Devices logged into this account."
/>
</Flex>

<Flex direction="column" className={styles.sessionsList}>
{sessions.length === 0 ? (
<Flex justify="center" align="center" style={{ padding: '2rem' }}>
Expand All @@ -93,10 +109,10 @@ export const SessionsPage = () => {
)}
</Flex>
</Flex>
<Button
variant="text"
color="neutral"
onClick={() => handleRevoke(session.id)}
<Button
variant="text"
color="neutral"
onClick={() => handleRevoke(session.id)}
data-test-id="frontier-sdk-revoke-session-button"
>
{session.isCurrent ? 'Logout' : 'Revoke'}
Expand All @@ -106,7 +122,12 @@ export const SessionsPage = () => {
)}
</Flex>
</Flex>
<Outlet />
<RevokeSessionDialog
open={revokeState.open}
onOpenChange={handleRevokeOpenChange}
sessionId={revokeState.sessionId}
onLogout={onLogout}
/>
</Flex>
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
.listRoot {
gap: 0;
}
/* Revoke Session Confirm END */
/* Revoke Session Confirm END */
Loading