diff --git a/sdks/js/packages/core/react/components/organization/api-keys/add.tsx b/sdks/js/packages/core/react/components/organization/api-keys/add.tsx
index 26975208b..c7a40d6e9 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/add.tsx
+++ b/sdks/js/packages/core/react/components/organization/api-keys/add.tsx
@@ -70,7 +70,7 @@ export const AddServiceAccount = () => {
}
});
}
- } catch ({ error }: any) {
+ } catch (error: any) {
toast.error('Something went wrong', {
description: error.message
});
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/columns.tsx b/sdks/js/packages/core/react/components/organization/api-keys/columns.tsx
index 716fa34ab..cdf398240 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/columns.tsx
+++ b/sdks/js/packages/core/react/components/organization/api-keys/columns.tsx
@@ -1,7 +1,7 @@
import { TrashIcon } from '@radix-ui/react-icons';
import { ApsaraColumnDef } from '@raystack/apsara';
import { Button, Flex, Text } from '@raystack/apsara/v1';
-import { Link } from '@tanstack/react-router';
+import { Link, useNavigate } from '@tanstack/react-router';
import dayjs from 'dayjs';
import { V1Beta1ServiceUser } from '~/api-client';
@@ -53,16 +53,26 @@ export const getColumns = ({
}
},
cell: ({ row, getValue }) => {
- return (
-
- );
+ return ;
}
}
];
};
+
+function ServiceAccountDeleteAction({ id }: { id: string }) {
+ const navigate = useNavigate({ from: '/api-keys' });
+
+ function onDeleteClick() {
+ return navigate({ to: '/api-keys/$id/delete', params: { id: id } });
+ }
+ return (
+
+ );
+}
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/delete.tsx b/sdks/js/packages/core/react/components/organization/api-keys/delete.tsx
new file mode 100644
index 000000000..7da9e0ede
--- /dev/null
+++ b/sdks/js/packages/core/react/components/organization/api-keys/delete.tsx
@@ -0,0 +1,104 @@
+import { Dialog, Separator, Image } from '@raystack/apsara';
+import styles from './styles.module.css';
+import { Button, Flex, Text, toast } from '@raystack/apsara/v1';
+import cross from '~/react/assets/cross.svg';
+import { useNavigate, useParams } from '@tanstack/react-router';
+import { useFrontier } from '~/react/contexts/FrontierContext';
+import { useState } from 'react';
+
+export const DeleteServiceAccount = () => {
+ const { id } = useParams({ from: '/api-keys/$id/delete' });
+ const navigate = useNavigate({ from: '/api-keys/$id/delete' });
+ const { client, activeOrganization: organization } = useFrontier();
+ const [isLoading, setIsLoading] = useState(false);
+
+ const orgId = organization?.id;
+
+ async function onDeleteClick() {
+ try {
+ setIsLoading(true);
+ await client?.frontierServiceDeleteServiceUser(id, { org_id: orgId });
+ navigate({
+ to: '/api-keys',
+ state: {
+ refetch: true
+ }
+ });
+ toast.success('Service account deleted');
+ } catch (err: any) {
+ toast.error('Unable to delete service account', {
+ description: err?.message
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ function onCancel() {
+ navigate({ to: '/api-keys' });
+ }
+
+ return (
+
+ );
+};
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/index.tsx b/sdks/js/packages/core/react/components/organization/api-keys/index.tsx
index 147c71bfe..d4128d7fb 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/index.tsx
+++ b/sdks/js/packages/core/react/components/organization/api-keys/index.tsx
@@ -15,7 +15,7 @@ import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import Skeleton from 'react-loading-skeleton';
import { getColumns } from './columns';
import { V1Beta1ServiceUser } from '~/api-client/dist';
-import { Outlet, useNavigate } from '@tanstack/react-router';
+import { Outlet, useLocation, useNavigate } from '@tanstack/react-router';
const NoServiceAccounts = ({
config
@@ -165,6 +165,8 @@ const ServiceAccountsTable = ({
export default function ApiKeys() {
const [serviceUsers, setServiceUsers] = useState([]);
const [isServiceUsersLoading, setIsServiceUsersLoading] = useState(false);
+ const location = useLocation();
+ const refetch = location?.state?.refetch;
const {
activeOrganization: organization,
@@ -196,7 +198,7 @@ export default function ApiKeys() {
if (organization?.id && canUpdateWorkspace) {
getServiceAccounts(organization?.id);
}
- }, [organization?.id, client, canUpdateWorkspace]);
+ }, [organization?.id, client, canUpdateWorkspace, refetch]);
const isLoading =
isActiveOrganizationLoading ||
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/service-user/delete.tsx b/sdks/js/packages/core/react/components/organization/api-keys/service-user/delete.tsx
new file mode 100644
index 000000000..25dd4a8a4
--- /dev/null
+++ b/sdks/js/packages/core/react/components/organization/api-keys/service-user/delete.tsx
@@ -0,0 +1,114 @@
+import { Dialog, Separator, Image } from '@raystack/apsara';
+import styles from './styles.module.css';
+import { Button, Flex, Text, toast } from '@raystack/apsara/v1';
+import cross from '~/react/assets/cross.svg';
+import { useNavigate, useParams } from '@tanstack/react-router';
+import { useFrontier } from '~/react/contexts/FrontierContext';
+import { useState } from 'react';
+import { DEFAULT_API_PLATFORM_APP_NAME } from '~/react/utils/constants';
+
+export const DeleteServiceAccountKey = () => {
+ const { id, tokenId } = useParams({
+ from: '/api-keys/$id/key/$tokenId/delete'
+ });
+ const navigate = useNavigate({ from: '/api-keys/$id/key/$tokenId/delete' });
+ const { client, config } = useFrontier();
+ const [isLoading, setIsLoading] = useState(false);
+
+ async function onDeleteClick() {
+ try {
+ setIsLoading(true);
+ await client?.frontierServiceDeleteServiceUserToken(id, tokenId);
+ navigate({
+ to: '/api-keys/$id',
+ params: {
+ id: id
+ },
+ state: {
+ refetch: true
+ }
+ });
+ toast.success('Service account key revoked');
+ } catch (err: any) {
+ toast.error('Unable to revoke service account key', {
+ description: err?.message
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ function onCancel() {
+ navigate({
+ to: '/api-keys/$id',
+ params: {
+ id: id
+ }
+ });
+ }
+ const appName = config?.apiPlatform?.appName || DEFAULT_API_PLATFORM_APP_NAME;
+
+ return (
+
+ );
+};
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/service-user/index.tsx b/sdks/js/packages/core/react/components/organization/api-keys/service-user/index.tsx
index bf2e464ee..33a61e79a 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/service-user/index.tsx
+++ b/sdks/js/packages/core/react/components/organization/api-keys/service-user/index.tsx
@@ -48,13 +48,25 @@ const Headings = ({
const ServiceUserTokenItem = ({
token,
- isLoading
+ isLoading,
+ serviceUserId
}: {
token: V1Beta1ServiceUserToken;
isLoading: boolean;
+ serviceUserId: string;
}) => {
const { copy } = useCopyToClipboard();
+ const navigate = useNavigate({ from: '/api-keys/$id' });
+ function onRevokeClick() {
+ navigate({
+ to: '/api-keys/$id/key/$tokenId/delete',
+ params: {
+ tokenId: token?.id || '',
+ id: serviceUserId
+ }
+ });
+ }
return (
@@ -72,6 +84,7 @@ const ServiceUserTokenItem = ({
variant="secondary"
size={'small'}
data-test-id={`frontier-sdk-service-account-token-revoke-btn`}
+ onClick={onRevokeClick}
>
Revoke
@@ -102,10 +115,12 @@ const ServiceUserTokenItem = ({
const SerivceUserTokenList = ({
isLoading,
- tokens
+ tokens,
+ serviceUserId
}: {
isLoading: boolean;
tokens: V1Beta1ServiceUserToken[];
+ serviceUserId: string;
}) => {
const tokenList = isLoading
? [
@@ -122,6 +137,7 @@ const SerivceUserTokenList = ({
token={token}
key={token?.id}
isLoading={isLoading}
+ serviceUserId={serviceUserId}
/>
))}
@@ -133,9 +149,6 @@ export default function ServiceUserPage() {
const { client, config } = useFrontier();
const navigate = useNavigate({ from: '/api-keys/$id' });
- const location = useLocation();
- const existingToken = location?.state?.token;
-
const [serviceUser, setServiceUser] = useState();
const [isServiceUserLoadning, setIsServiceUserLoading] = useState(false);
@@ -146,6 +159,10 @@ export default function ServiceUserPage() {
const [isServiceUserTokensLoading, setIsServiceUserTokensLoading] =
useState(false);
+ const location = useLocation();
+ const existingToken = location?.state?.token;
+ const refetch = location?.state?.refetch;
+
const getServiceUser = useCallback(
async (serviceUserId: string) => {
try {
@@ -187,7 +204,7 @@ export default function ServiceUserPage() {
getServiceUserTokens(id);
}
}
- }, [id, getServiceUser, getServiceUserTokens, existingToken?.id]);
+ }, [id, getServiceUser, getServiceUserTokens, existingToken?.id, refetch]);
const tokenList = existingToken
? [existingToken, ...serviceUserTokens]
@@ -220,7 +237,11 @@ export default function ServiceUserPage() {
config={config?.apiPlatform}
/>
-
+
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/service-user/styles.module.css b/sdks/js/packages/core/react/components/organization/api-keys/service-user/styles.module.css
index c3539452f..86efe0229 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/service-user/styles.module.css
+++ b/sdks/js/packages/core/react/components/organization/api-keys/service-user/styles.module.css
@@ -9,7 +9,7 @@
.content {
width: 100%;
- padding: var(--space-9) var(--space-11);
+ padding: var(--rs-space-9) var(--rs-space-11);
}
.serviceKeyList {
@@ -60,3 +60,27 @@
border: 1px solid var(--rs-color-border-base-primary);
background: var(--rs-color-background-base-primary-hover);
}
+
+.addDialogContent {
+ padding: 0;
+ max-width: 400px;
+ width: 100%;
+ z-index: 60;
+}
+
+.overlay {
+ z-index: 55;
+ background-color: rgba(104, 112, 118, 0.5);
+}
+
+.addDialogForm {
+ padding: var(--rs-space-5) var(--rs-space-7);
+}
+
+.addDialogFormContent {
+ padding: var(--rs-space-9) var(--rs-space-7);
+}
+
+.addDialogFormBtnWrapper {
+ padding: var(--rs-space-5);
+}
diff --git a/sdks/js/packages/core/react/components/organization/api-keys/styles.module.css b/sdks/js/packages/core/react/components/organization/api-keys/styles.module.css
index e7a6b75d3..51f30967c 100644
--- a/sdks/js/packages/core/react/components/organization/api-keys/styles.module.css
+++ b/sdks/js/packages/core/react/components/organization/api-keys/styles.module.css
@@ -5,11 +5,11 @@
.content {
width: 100%;
- padding: var(--space-9) var(--space-11);
+ padding: var(--rs-space-9) var(--rs-space-11);
}
.stateContent {
- padding: var(--space-15) var(--space-11);
+ padding: var(--rs-space-15) var(--rs-space-11);
}
.flex1 {
@@ -43,7 +43,7 @@
}
.addDialogFormContent {
- padding: var(--space-9) var(--space-7);
+ padding: var(--rs-space-9) var(--rs-space-7);
}
.addDialogFormBtnWrapper {
diff --git a/sdks/js/packages/core/react/components/organization/profile.tsx b/sdks/js/packages/core/react/components/organization/profile.tsx
index 6201117e2..f9e0ec72d 100644
--- a/sdks/js/packages/core/react/components/organization/profile.tsx
+++ b/sdks/js/packages/core/react/components/organization/profile.tsx
@@ -63,5 +63,6 @@ declare module '@tanstack/react-router' {
interface HistoryState {
token?: V1Beta1ServiceUserToken;
+ refetch?: boolean;
}
}
diff --git a/sdks/js/packages/core/react/components/organization/routes.tsx b/sdks/js/packages/core/react/components/organization/routes.tsx
index 8b10257cb..701ede8d0 100644
--- a/sdks/js/packages/core/react/components/organization/routes.tsx
+++ b/sdks/js/packages/core/react/components/organization/routes.tsx
@@ -44,6 +44,8 @@ import MemberRemoveConfirm from './members/MemberRemoveConfirm';
import APIKeys from './api-keys';
import { AddServiceAccount } from './api-keys/add';
import ServiceUserPage from './api-keys/service-user';
+import { DeleteServiceAccount } from './api-keys/delete';
+import { DeleteServiceAccountKey } from './api-keys/service-user/delete';
export interface CustomScreen {
name: string;
@@ -308,12 +310,24 @@ const addServiceAccountRoute = createRoute({
component: AddServiceAccount
});
+const deleteServiceAccountRoute = createRoute({
+ getParentRoute: () => apiKeysRoute,
+ path: '/$id/delete',
+ component: DeleteServiceAccount
+});
+
const serviceAccountRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/api-keys/$id',
component: ServiceUserPage
});
+const deleteServiceAccountKeyRoute = createRoute({
+ getParentRoute: () => serviceAccountRoute,
+ path: '/key/$tokenId/delete',
+ component: DeleteServiceAccountKey
+});
+
interface getRootTreeOptions {
customScreens?: CustomScreen[];
}
@@ -337,8 +351,11 @@ export function getRootTree({ customScreens = [] }: getRootTreeOptions) {
billingRoute.addChildren([switchBillingCycleModalRoute]),
plansRoute.addChildren([planDowngradeRoute]),
tokensRoute,
- apiKeysRoute.addChildren([addServiceAccountRoute]),
- serviceAccountRoute,
+ apiKeysRoute.addChildren([
+ addServiceAccountRoute,
+ deleteServiceAccountRoute
+ ]),
+ serviceAccountRoute.addChildren([deleteServiceAccountKeyRoute]),
...customScreens.map(cc =>
createRoute({
path: cc.path,