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
135 changes: 5 additions & 130 deletions web/sdk/react/components/organization/general/index.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +1,10 @@
'use client';

import { useMemo } from 'react';
import {
Button,
Tooltip,
Separator,
Skeleton,
Text,
Flex
} from '@raystack/apsara';
import { Outlet, useNavigate } from '@tanstack/react-router';
import { useFrontier } from '~/react/contexts/FrontierContext';
import { usePermissions } from '~/react/hooks/usePermissions';
import { PERMISSIONS, shouldShowComponent } from '~/utils';
import { GeneralOrganization } from './general.workspace';
import { AuthTooltipMessage } from '~/react/utils';
import { useTerminology } from '~/react/hooks/useTerminology';
import { PageHeader } from '~/react/components/common/page-header';
import sharedStyles from '../styles.module.css';
import { GeneralPage } from '~/react/views/general';

export default function GeneralSetting() {
const t = useTerminology();
const { activeOrganization: organization, isActiveOrganizationLoading } =
useFrontier();

const resource = `app/organization:${organization?.id}`;

const listOfPermissionsToCheck = useMemo(() => {
return [
{
permission: PERMISSIONS.UpdatePermission,
resource: resource
},
{
permission: PERMISSIONS.DeletePermission,
resource: resource
}
];
}, [resource]);

const { permissions, isFetching: isPermissionsFetching } = usePermissions(
listOfPermissionsToCheck,
!!organization?.id
);

const { canUpdateWorkspace, canDeleteWorkspace } = useMemo(() => {
return {
canUpdateWorkspace: shouldShowComponent(
permissions,
`${PERMISSIONS.UpdatePermission}::${resource}`
),
canDeleteWorkspace: shouldShowComponent(
permissions,
`${PERMISSIONS.DeletePermission}::${resource}`
)
};
}, [permissions, resource]);

const isLoading = isActiveOrganizationLoading || isPermissionsFetching;

return (
<Flex direction="column" style={{ width: '100%' }}>
<Flex direction="column" className={sharedStyles.container}>
<Flex
direction="row"
justify="between"
align="center"
className={sharedStyles.header}
>
<PageHeader
title="General"
description={`Basic configuration for the ${t.organization({
case: 'lower'
})}.`}
/>
</Flex>
<Flex direction="column" gap={9}>
<GeneralOrganization
organization={organization}
canUpdateWorkspace={canUpdateWorkspace}
isLoading={isLoading}
/>
<Separator />
<GeneralDeleteOrganization
isLoading={isLoading}
canDelete={canDeleteWorkspace}
/>
</Flex>
</Flex>
</Flex>
);
return <GeneralPage onDeleteSuccess={() => {
// @ts-ignore
window.location = window.location.origin;
}}/>;
}

export const GeneralDeleteOrganization = ({
isLoading,
canDelete
}: {
isLoading?: boolean;
canDelete: boolean;
}) => {
const t = useTerminology();
const navigate = useNavigate({ from: '/' });
return (
<>
<Flex direction="column" gap={5}>
{isLoading ? (
<Skeleton height={'16px'} width={'50%'} />
) : (
<Text size={3} variant="secondary">
If you want to permanently delete this{' '}
{t.organization({ case: 'lower' })} and all of its data.
</Text>
)}
{isLoading ? (
<Skeleton height={'32px'} width={'64px'} />
) : (
<Tooltip disabled={canDelete} message={AuthTooltipMessage}>
<Button
variant="solid"
color="danger"
type="submit"
onClick={() => navigate({ to: '/delete' })}
disabled={!canDelete}
data-test-id="frontier-sdk-delete-organization-btn"
>
Delete {t.organization({ case: 'lower' })}
</Button>
</Tooltip>
)}
<Outlet />
</Flex>
</>
);
};
193 changes: 2 additions & 191 deletions web/sdk/react/components/organization/members/index.tsx
Original file line number Diff line number Diff line change
@@ -1,196 +1,7 @@
'use client';

import { useEffect, useMemo } from 'react';

import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import {
Button,
Tooltip,
Skeleton,
EmptyState,
Flex,
DataTable
} from '@raystack/apsara';
import { Outlet, useNavigate, useRouterState } from '@tanstack/react-router';
import { useFrontier } from '~/react/contexts/FrontierContext';
import { useOrganizationMembers } from '~/react/hooks/useOrganizationMembers';
import { usePermissions } from '~/react/hooks/usePermissions';
import { AuthTooltipMessage } from '~/react/utils';
import { PERMISSIONS, shouldShowComponent } from '~/utils';
import { getColumns } from './member.columns';
import type { MembersTableType } from './member.types';
import { PageHeader } from '~/react/components/common/page-header';
import sharedStyles from '../styles.module.css';
import styles from './members.module.css';
import { MembersPage } from '~/react/views/members';

export default function WorkspaceMembers() {
const { activeOrganization: organization } = useFrontier();

const routerState = useRouterState();

const isListRoute = useMemo(() => {
return routerState.location.pathname === '/members';
}, [routerState.location.pathname]);

const resource = `app/organization:${organization?.id}`;
const listOfPermissionsToCheck = useMemo(
() => [
{
permission: PERMISSIONS.InvitationCreatePermission,
resource
},
{
permission: PERMISSIONS.UpdatePermission,
resource
}
],
[resource]
);

const { permissions, isFetching: isPermissionsFetching } = usePermissions(
listOfPermissionsToCheck,
!!organization?.id
);

const { canCreateInvite, canDeleteUser } = useMemo(() => {
return {
canCreateInvite: shouldShowComponent(
permissions,
`${PERMISSIONS.InvitationCreatePermission}::${resource}`
),
canDeleteUser: shouldShowComponent(
permissions,
`${PERMISSIONS.UpdatePermission}::${resource}`
)
};
}, [permissions, resource]);

const {
roles,
members,
memberRoles,
refetch,
isFetching: isOrgMembersLoading
} = useOrganizationMembers({
showInvitations: canCreateInvite
});

const isLoading = isOrgMembersLoading || isPermissionsFetching;

useEffect(() => {
if (isListRoute) {
refetch();
}
}, [isListRoute, refetch, routerState.location.state.key]);

return (
<Flex direction="column" className={sharedStyles.pageWrapper}>
<Flex direction="column" className={`${sharedStyles.container} ${sharedStyles.containerFlex}`}>
<Flex direction="row" justify="between" align="center" className={sharedStyles.header}>
<PageHeader
title="Members"
description="Manage members in this domain."
/>
</Flex>
<Flex direction="column" gap={9} className={sharedStyles.contentWrapper}>
{organization?.id ? (
<MembersTable
roles={roles}
users={members}
organizationId={organization?.id}
isLoading={isLoading}
canCreateInvite={canCreateInvite}
canDeleteUser={canDeleteUser}
memberRoles={memberRoles}
refetch={refetch}
/>
) : null}
</Flex>
</Flex>
<Outlet />
</Flex>
);
return <MembersPage />;
}

const MembersTable = ({
isLoading,
users,
canCreateInvite,
canDeleteUser,
organizationId,
memberRoles,
roles,
refetch
}: MembersTableType) => {
const navigate = useNavigate({ from: '/members' });

const columns = useMemo(
() =>
getColumns(organizationId, memberRoles, roles, canDeleteUser, refetch),
[organizationId, memberRoles, canDeleteUser, roles, refetch]
);

return (
<DataTable
data={users}
isLoading={isLoading}
defaultSort={{ name: 'name', order: 'asc' }}
columns={columns}
mode="client"
>
<Flex direction="column" gap={7} className={styles.tableWrapper}>
<Flex justify="between" gap={3}>
<Flex gap={3} justify="start" className={styles.tableSearchWrapper}>
{isLoading ? (
<Skeleton height="34px" width="500px" />
) : (
<DataTable.Search
placeholder="Search by name or email"
size="medium"
/>
)}
</Flex>
{isLoading ? (
<Skeleton height="34px" width="64px" />
) : (
<Tooltip
message={AuthTooltipMessage}
side="left"
disabled={canCreateInvite}
>
<Button
size="small"
style={{ width: 'fit-content', height: '100%' }}
onClick={() =>
navigate({
to: '/members/modal',
state: { from: '/members' }
})
}
disabled={!canCreateInvite}
data-test-id="frontier-sdk-remove-member-link"
>
Invite people
</Button>
</Tooltip>
)}
</Flex>
<DataTable.Content
emptyState={noDataChildren}
classNames={{
root: styles.tableRoot,
header: styles.tableHeader
}}
/>
</Flex>
</DataTable>
);
};

const noDataChildren = (
<EmptyState
icon={<ExclamationTriangleIcon />}
heading="No members found"
subHeading="Get started by adding your first member"
/>
);
Loading
Loading