added pages for permission groups

This commit is contained in:
Nate Kelley 2025-01-21 20:26:09 -07:00
parent f072b2c461
commit 30cb2a0ff5
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
13 changed files with 155 additions and 40 deletions

View File

@ -5,7 +5,7 @@ export interface GetPermissionGroupResponse {
name: string; name: string;
organization_id: string; organization_id: string;
updated_at: string; updated_at: string;
updated_by: string; //THIS IS THE USER ID updated_by: string;
} }
export interface CreatePermissionGroupResponse extends GetPermissionGroupResponse {} export interface CreatePermissionGroupResponse extends GetPermissionGroupResponse {}

View File

@ -1,2 +1,3 @@
export * from './StatusBadgeIndicator'; export * from './StatusBadgeIndicator';
export * from './FavoriteStar'; export * from './FavoriteStar';
export * from './ListEmptyState';

View File

@ -13,7 +13,6 @@ export const PermissionOverview: React.FC<{
datasetId: string; datasetId: string;
}> = React.memo(({ datasetId }) => { }> = React.memo(({ datasetId }) => {
const { data: datasetPermissionsOverview } = useGetDatasetPermissionsOverview(datasetId); const { data: datasetPermissionsOverview } = useGetDatasetPermissionsOverview(datasetId);
console.log(datasetPermissionsOverview);
const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({ const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({
items: datasetPermissionsOverview?.users || [], items: datasetPermissionsOverview?.users || [],

View File

@ -12,8 +12,7 @@ import { ClientRedirect } from './_components/ClientRedirect';
import { AppLayoutClient } from './layoutClient'; import { AppLayoutClient } from './layoutClient';
export default async function Layout({ export default async function Layout({
children, children
...rest
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {

View File

@ -0,0 +1,68 @@
import {
BusterInfiniteList,
BusterListColumn,
BusterListRowItem,
EmptyStateList,
InfiniteListContainer
} from '@/components/list';
import { Card } from 'antd';
import React, { useMemo, useState } from 'react';
import { Text } from '@/components/text';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { ListUserItem } from '../../_components/ListContent';
import { GetPermissionGroupResponse } from '@/api/buster-rest';
import { ListEmptyState } from '../../_components/Lists/ListEmptyState';
export const ListPermissionGroupsComponent: React.FC<{
permissionGroups: GetPermissionGroupResponse[];
isFetched: boolean;
}> = React.memo(({ permissionGroups, isFetched }) => {
const columns: BusterListColumn[] = useMemo(
() => [
{
title: 'Name',
dataIndex: 'name'
}
],
[]
);
const permissionGroupsRows: BusterListRowItem[] = useMemo(() => {
return permissionGroups.reduce<BusterListRowItem[]>((acc, permissionGroup) => {
const rowItem: BusterListRowItem = {
id: permissionGroup.id,
data: permissionGroup,
link: createBusterRoute({
route: BusterRoutes.SETTINGS_PERMISSION_GROUPS_ID_USERS,
permissionGroupId: permissionGroup.id
})
};
acc.push(rowItem);
return acc;
}, []);
}, [permissionGroups]);
return (
<InfiniteListContainer
showContainerBorder={false}
// popupNode={
// <UserListPopupContainer
// selectedRowKeys={selectedRowKeys}
// onSelectChange={setSelectedRowKeys}
// />
// }
>
<BusterInfiniteList
columns={columns}
rows={permissionGroupsRows}
showHeader={true}
showSelectAll={false}
rowClassName="!pl-[30px]"
columnRowVariant="default"
emptyState={<EmptyStateList text="No permission groups found" />}
/>
</InfiniteListContainer>
);
});
ListPermissionGroupsComponent.displayName = 'ListPermissionGroupsComponent';

View File

@ -0,0 +1,3 @@
export default function Page() {
return <div>Dataset Groups</div>;
}

View File

@ -0,0 +1,3 @@
export default function Page() {
return <div>Datasets</div>;
}

View File

@ -0,0 +1,3 @@
export default function Page() {
return <div>Users</div>;
}

View File

@ -1,14 +1,62 @@
import { SettingsEmptyState } from '../_SettingsEmptyState'; 'use client';
import React, { useState } from 'react';
import { SettingsPageHeader } from '../_SettingsPageHeader'; import { SettingsPageHeader } from '../_SettingsPageHeader';
import { PermissionSearch, NewPermissionGroupModal } from '@appComponents/PermissionComponents';
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
import { useListAllPermissionGroups } from '@/api/buster-rest';
import { ListPermissionGroupsComponent } from './ListPermissionGroupsComponent';
import { useMemoizedFn } from 'ahooks';
import { Button } from 'antd';
import { AppMaterialIcons } from '@/components';
export default function Page() { export default function Page() {
const { data: permissionGroups, isFetched, refetch } = useListAllPermissionGroups();
const [isNewPermissionGroupModalOpen, setIsNewPermissionGroupModalOpen] = useState(false);
const { filteredItems, handleSearchChange, searchText } = useDebounceSearch({
items: permissionGroups || [],
searchPredicate: (item, searchText) =>
item.name.toLowerCase().includes(searchText.toLowerCase())
});
const onCloseNewPermissionGroupModal = useMemoizedFn(() => {
setIsNewPermissionGroupModalOpen(false);
refetch();
});
const onOpenNewPermissionGroupModal = useMemoizedFn(() => {
setIsNewPermissionGroupModalOpen(true);
});
return ( return (
<div> <>
<SettingsPageHeader <div className="flex h-full flex-col space-y-4 overflow-y-auto">
title="Permission Groups" <div className="px-[30px] pt-[46px]">
description="Manage security & how members authenticate" <SettingsPageHeader
title="Permission Groups"
description="Manage your permission groups and set explicit permissions."
type="alternate"
/>
<div className="flex justify-between space-x-3">
<PermissionSearch searchText={searchText} setSearchText={handleSearchChange} />
<Button
onClick={onOpenNewPermissionGroupModal}
type="default"
icon={<AppMaterialIcons icon="add" />}>
New Permission Group
</Button>
</div>
</div>
<div className="">
<ListPermissionGroupsComponent permissionGroups={filteredItems} isFetched={isFetched} />
</div>
</div>
<NewPermissionGroupModal
isOpen={isNewPermissionGroupModalOpen}
onClose={onCloseNewPermissionGroupModal}
/> />
<SettingsEmptyState /> </>
</div>
); );
} }

View File

@ -1,18 +0,0 @@
import React, { useState, useTransition } from 'react';
import { SearchInput } from '@/components/inputs';
export const SearchUsers: React.FC<{
onChange: (value: string) => void;
}> = React.memo(({ onChange }) => {
return (
<div className="flex max-w-[400px] items-center space-x-2">
<SearchInput
className="max-w-[280px]"
placeholder="Search users name or email..."
onChange={onChange}
/>
</div>
);
});
SearchUsers.displayName = 'SearchUsers';

View File

@ -1,5 +0,0 @@
import React from 'react';
export default function Layout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}

View File

@ -2,18 +2,18 @@
import React from 'react'; import React from 'react';
import { SettingsPageHeader } from '../_SettingsPageHeader'; import { SettingsPageHeader } from '../_SettingsPageHeader';
import { SearchUsers } from './SearchUsers';
import { useDebounceSearch } from '@/hooks/useDebounceSearch'; import { useDebounceSearch } from '@/hooks/useDebounceSearch';
import { useGetOrganizationUsers } from '@/api/buster-rest'; import { useGetOrganizationUsers } from '@/api/buster-rest';
import { useUserConfigContextSelector } from '@/context/Users'; import { useUserConfigContextSelector } from '@/context/Users';
import { ListUsersComponent } from './ListUsersComponent'; import { ListUsersComponent } from './ListUsersComponent';
import { PermissionSearch } from '../../_components/PermissionComponents';
export default function Page() { export default function Page() {
const userOrganization = useUserConfigContextSelector((x) => x.userOrganizations); const userOrganization = useUserConfigContextSelector((x) => x.userOrganizations);
const firstOrganizationId = userOrganization?.id! || ''; const firstOrganizationId = userOrganization?.id! || '';
const { data: users, isFetched } = useGetOrganizationUsers(firstOrganizationId); const { data: users, isFetched } = useGetOrganizationUsers(firstOrganizationId);
const { filteredItems, handleSearchChange } = useDebounceSearch({ const { filteredItems, handleSearchChange, searchText } = useDebounceSearch({
items: users || [], items: users || [],
searchPredicate: (item, searchText) => searchPredicate: (item, searchText) =>
item.email.includes(searchText) || item.name.includes(searchText) item.email.includes(searchText) || item.name.includes(searchText)
@ -27,7 +27,11 @@ export default function Page() {
description="Manage your organization's users and their permissions" description="Manage your organization's users and their permissions"
type="alternate" type="alternate"
/> />
<SearchUsers onChange={handleSearchChange} /> <PermissionSearch
placeholder="Search users name or email..."
searchText={searchText}
setSearchText={handleSearchChange}
/>
</div> </div>
<ListUsersComponent users={filteredItems} isFetched={isFetched} /> <ListUsersComponent users={filteredItems} isFetched={isFetched} />

View File

@ -29,7 +29,9 @@ export enum BusterAppRoutes {
SETTINGS_DATASOURCES_ADD = '/app/settings/datasources/add', SETTINGS_DATASOURCES_ADD = '/app/settings/datasources/add',
SETTINGS_INTEGRATIONS = '/app/settings/integrations', SETTINGS_INTEGRATIONS = '/app/settings/integrations',
SETTINGS_PERMISSION_GROUPS = '/app/settings/permission-groups', SETTINGS_PERMISSION_GROUPS = '/app/settings/permission-groups',
SETTINGS_PERMISSION_GROUPS_ID = '/app/settings/permission-groups/:permissionGroupId', SETTINGS_PERMISSION_GROUPS_ID_USERS = '/app/settings/permission-groups/:permissionGroupId/users',
SETTINGS_PERMISSION_GROUPS_ID_DATASET_GROUPS = '/app/settings/permission-groups/:permissionGroupId/dataset-groups',
SETTINGS_PERMISSION_GROUPS_ID_DATASETS = '/app/settings/permission-groups/:permissionGroupId/datasets',
SETTINGS_API_KEYS = '/app/settings/api-keys', SETTINGS_API_KEYS = '/app/settings/api-keys',
SETTINGS_EMBEDS = '/app/settings/embeds', SETTINGS_EMBEDS = '/app/settings/embeds',
SETTINGS_BILLING = '/app/settings/billing', SETTINGS_BILLING = '/app/settings/billing',
@ -86,8 +88,16 @@ export type BusterAppRoutesWithArgs = {
[BusterAppRoutes.SETTINGS_PERMISSION_GROUPS]: { [BusterAppRoutes.SETTINGS_PERMISSION_GROUPS]: {
route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS; route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS;
}; };
[BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID]: { [BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_USERS]: {
route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID; route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_USERS;
permissionGroupId: string;
};
[BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_DATASET_GROUPS]: {
route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_DATASET_GROUPS;
permissionGroupId: string;
};
[BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_DATASETS]: {
route: BusterAppRoutes.SETTINGS_PERMISSION_GROUPS_ID_DATASETS;
permissionGroupId: string; permissionGroupId: string;
}; };
[BusterAppRoutes.SETTINGS_API_KEYS]: { route: BusterAppRoutes.SETTINGS_API_KEYS }; [BusterAppRoutes.SETTINGS_API_KEYS]: { route: BusterAppRoutes.SETTINGS_API_KEYS };