mirror of https://github.com/buster-so/buster.git
optimistic updates for assigning permission groups
This commit is contained in:
parent
ae689150b3
commit
1671c2d8fb
|
@ -9,8 +9,9 @@ import {
|
|||
updatePermissionUsers
|
||||
} from './requests';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { QueryClient, useQueryClient } from '@tanstack/react-query';
|
||||
import { getDatasetPermissionsOverview_server } from './serverRequests';
|
||||
import { ListPermissionUsersResponse } from './responseInterfaces';
|
||||
|
||||
export const useGetDatasetPermissionsOverview = (dataset_id: string) => {
|
||||
const queryFn = useMemoizedFn(() => getDatasetPermissionsOverview({ dataset_id }));
|
||||
|
@ -38,7 +39,8 @@ export const useListPermissionGroups = (dataset_id: string) => {
|
|||
|
||||
return useCreateReactQuery({
|
||||
queryKey: ['list_permission_groups', dataset_id],
|
||||
queryFn
|
||||
queryFn,
|
||||
staleTime: 1000 * 5 // 5 seconds
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -61,43 +63,77 @@ export const useListPermissionUsers = (dataset_id: string) => {
|
|||
};
|
||||
|
||||
export const useUpdatePermissionGroups = (dataset_id: string) => {
|
||||
const mutationFn = useMemoizedFn((groups: { id: string; assigned: boolean }[]) =>
|
||||
updatePermissionGroups({ dataset_id, groups })
|
||||
);
|
||||
const onSuccess = useMemoizedFn(() => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['dataset_permissions_overview', dataset_id] });
|
||||
const queryClient = useQueryClient();
|
||||
const mutationFn = useMemoizedFn((groups: { id: string; assigned: boolean }[]) => {
|
||||
const keyedChanges: Record<string, { id: string; assigned: boolean }> = {};
|
||||
groups.forEach(({ id, assigned }) => {
|
||||
keyedChanges[id] = { id, assigned };
|
||||
});
|
||||
queryClient.setQueryData(
|
||||
['list_permission_groups', dataset_id],
|
||||
(oldData: ListPermissionUsersResponse[]) => {
|
||||
return oldData?.map((group) => {
|
||||
const updatedGroup = keyedChanges[group.id];
|
||||
if (updatedGroup) return { ...group, assigned: updatedGroup.assigned };
|
||||
return group;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return updatePermissionGroups({ dataset_id, groups });
|
||||
});
|
||||
|
||||
return useCreateReactMutation({
|
||||
mutationFn,
|
||||
onSuccess
|
||||
mutationFn
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateDatasetGroups = (dataset_id: string) => {
|
||||
const mutationFn = useMemoizedFn((groups: { id: string; assigned: boolean }[]) =>
|
||||
updateDatasetGroups({ dataset_id, groups })
|
||||
);
|
||||
const onSuccess = useMemoizedFn(() => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['dataset_permissions_overview', dataset_id] });
|
||||
const queryClient = useQueryClient();
|
||||
const mutationFn = useMemoizedFn((groups: { id: string; assigned: boolean }[]) => {
|
||||
const keyedChanges: Record<string, { id: string; assigned: boolean }> = {};
|
||||
groups.forEach(({ id, assigned }) => {
|
||||
keyedChanges[id] = { id, assigned };
|
||||
});
|
||||
queryClient.setQueryData(
|
||||
['list_dataset_groups', dataset_id],
|
||||
(oldData: ListPermissionUsersResponse[]) => {
|
||||
return oldData?.map((group) => {
|
||||
const updatedGroup = keyedChanges[group.id];
|
||||
if (updatedGroup) return { ...group, assigned: updatedGroup.assigned };
|
||||
return group;
|
||||
});
|
||||
}
|
||||
);
|
||||
return updateDatasetGroups({ dataset_id, groups });
|
||||
});
|
||||
|
||||
return useCreateReactMutation({
|
||||
mutationFn,
|
||||
onSuccess
|
||||
mutationFn
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdatePermissionUsers = (dataset_id: string) => {
|
||||
const mutationFn = useMemoizedFn((users: { id: string; assigned: boolean }[]) =>
|
||||
updatePermissionUsers({ dataset_id, users })
|
||||
);
|
||||
const onSuccess = useMemoizedFn(() => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['dataset_permissions_overview', dataset_id] });
|
||||
const queryClient = useQueryClient();
|
||||
const mutationFn = useMemoizedFn((users: { id: string; assigned: boolean }[]) => {
|
||||
const keyedChanges: Record<string, { id: string; assigned: boolean }> = {};
|
||||
users.forEach(({ id, assigned }) => {
|
||||
keyedChanges[id] = { id, assigned };
|
||||
});
|
||||
queryClient.setQueryData(
|
||||
['list_permission_users', dataset_id],
|
||||
(oldData: ListPermissionUsersResponse[]) => {
|
||||
return oldData?.map((user) => {
|
||||
const updatedUser = keyedChanges[user.id];
|
||||
if (updatedUser) return { ...user, assigned: updatedUser.assigned };
|
||||
return user;
|
||||
});
|
||||
}
|
||||
);
|
||||
return updatePermissionUsers({ dataset_id, users });
|
||||
});
|
||||
|
||||
return useCreateReactMutation({
|
||||
mutationFn,
|
||||
onSuccess
|
||||
mutationFn
|
||||
});
|
||||
};
|
||||
|
|
|
@ -50,7 +50,12 @@ export const updatePermissionGroups = async ({
|
|||
}: {
|
||||
dataset_id: string;
|
||||
groups: { id: string; assigned: boolean }[];
|
||||
}): Promise<void> => {
|
||||
}): Promise<
|
||||
{
|
||||
id: string;
|
||||
assigned: boolean;
|
||||
}[]
|
||||
> => {
|
||||
return await mainApi.put(`/datasets/${dataset_id}/permission_groups`, groups);
|
||||
};
|
||||
|
||||
|
|
|
@ -7,3 +7,4 @@ export * from './search';
|
|||
export * from './assets';
|
||||
export * from './api_keys';
|
||||
export * from './sql';
|
||||
export * from './datasets';
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import { Title, Text } from '@/components';
|
||||
|
||||
export const HeaderExplanation: React.FC<{
|
||||
className?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}> = React.memo(
|
||||
({
|
||||
className = '',
|
||||
title = 'Access & lineage',
|
||||
description = 'View which users can query this dataset. Lineage is provided to show where each user’s access originates from.'
|
||||
}) => {
|
||||
return (
|
||||
<div className={`flex flex-col space-y-1.5 ${className}`}>
|
||||
<Title level={4}>{title}</Title>
|
||||
<Text type="secondary">{description}</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
HeaderExplanation.displayName = 'HeaderExplanation';
|
|
@ -1,7 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const PermissionPermissionGroup: React.FC<{}> = React.memo(({}) => {
|
||||
return <div>asdf</div>;
|
||||
});
|
||||
|
||||
PermissionPermissionGroup.displayName = 'PermissionPermissionGroup';
|
|
@ -3,20 +3,21 @@ import { Input } from 'antd';
|
|||
import { AppMaterialIcons } from '@/components';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export const PermissionOverviewSearch: React.FC<{
|
||||
export const PermissionSearch: React.FC<{
|
||||
className?: string;
|
||||
searchText: string;
|
||||
setSearchText: (text: string) => void;
|
||||
}> = ({ className = '', searchText, setSearchText }) => {
|
||||
placeholder?: string;
|
||||
}> = ({ className = '', searchText, setSearchText, placeholder = 'Search by name or email' }) => {
|
||||
const onChange = useMemoizedFn((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchText(e.target.value);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col space-y-1.5 ${className}`}>
|
||||
<div className={`flex w-full flex-col space-y-1.5 ${className}`}>
|
||||
<Input
|
||||
className="max-w-[280px]"
|
||||
placeholder="Search by name or email"
|
||||
className="w-[280px] max-w-[280px]"
|
||||
placeholder={placeholder}
|
||||
value={searchText}
|
||||
onChange={onChange}
|
||||
allowClear
|
||||
|
@ -25,4 +26,4 @@ export const PermissionOverviewSearch: React.FC<{
|
|||
</div>
|
||||
);
|
||||
};
|
||||
PermissionOverviewSearch.displayName = 'PermissionOverviewSearch';
|
||||
PermissionSearch.displayName = 'PermissionOverviewSearch';
|
|
@ -6,7 +6,7 @@ import { AnimatePresence, motion } from 'framer-motion';
|
|||
import { PermissionApps } from './config';
|
||||
import { PermissionDatasetGroups } from './PermissionDatasetGroups';
|
||||
import { PermissionOverview } from './_PermissionOverview';
|
||||
import { PermissionPermissionGroup } from './PermissionPermissionGroup';
|
||||
import { PermissionPermissionGroup } from './_PermissionPermissionGroup/PermissionPermissionGroup';
|
||||
import { PermissionUsers } from './PermissionUsers';
|
||||
|
||||
const selectedAppComponent: Record<PermissionApps, React.FC<{ datasetId: string }>> = {
|
||||
|
@ -26,7 +26,8 @@ export const PermissionsAppContainer: React.FC<{ datasetId: string }> = React.me
|
|||
return {
|
||||
initial: { opacity: 0 },
|
||||
animate: { opacity: 1 },
|
||||
exit: { opacity: 0 }
|
||||
exit: { opacity: 0 },
|
||||
transition: { duration: 0.125 }
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Title, Text } from '@/components';
|
||||
|
||||
export const HeaderExplanation: React.FC<{ className?: string }> = React.memo(
|
||||
({ className = '' }) => {
|
||||
return (
|
||||
<div className={`flex flex-col space-y-1.5 ${className}`}>
|
||||
<Title level={4}>Access & lineage</Title>
|
||||
<Text type="secondary">
|
||||
{`View which users can query this dataset. Lineage is provided to show where each user’s
|
||||
access originates from.`}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
HeaderExplanation.displayName = 'HeaderExplanation';
|
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
import { DatasetPermissionOverviewUser } from '@/api/busterv2/datasets';
|
||||
|
||||
export const PermissionListUser: React.FC<{
|
||||
className?: string;
|
||||
user: DatasetPermissionOverviewUser;
|
||||
}> = React.memo(({ className = '', user }) => {
|
||||
return <div className={`flex flex-col space-y-1.5 ${className}`}>swag</div>;
|
||||
});
|
||||
PermissionListUser.displayName = 'PermissionListUser';
|
|
@ -15,26 +15,29 @@ export const PermissionListUserContainer: React.FC<{
|
|||
|
||||
const numberOfUsers = filteredUsers.length;
|
||||
|
||||
const columns: BusterListColumn[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
width: 290,
|
||||
render: (_: string, user: DatasetPermissionOverviewUser) => {
|
||||
return <UserInfoCell user={user} />;
|
||||
const columns: BusterListColumn[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
width: 290,
|
||||
render: (_: string, user: DatasetPermissionOverviewUser) => {
|
||||
return <UserInfoCell user={user} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Lineage',
|
||||
dataIndex: 'lineage',
|
||||
render: (
|
||||
lineage: DatasetPermissionOverviewUser['lineage'],
|
||||
user: DatasetPermissionOverviewUser
|
||||
) => {
|
||||
return <UserLineageCell user={user} />;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Lineage',
|
||||
dataIndex: 'lineage',
|
||||
render: (
|
||||
lineage: DatasetPermissionOverviewUser['lineage'],
|
||||
user: DatasetPermissionOverviewUser
|
||||
) => {
|
||||
return <UserLineageCell user={user} />;
|
||||
}
|
||||
}
|
||||
];
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const { cannotQueryUsers, canQueryUsers } = useMemo(() => {
|
||||
const result: {
|
||||
|
@ -66,28 +69,32 @@ export const PermissionListUserContainer: React.FC<{
|
|||
return result;
|
||||
}, [filteredUsers]);
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 'header-assigned',
|
||||
data: {},
|
||||
hidden: canQueryUsers.length === 0,
|
||||
rowSection: {
|
||||
title: 'Assigned',
|
||||
secondaryTitle: numberOfUsers.toString()
|
||||
}
|
||||
},
|
||||
...canQueryUsers,
|
||||
{
|
||||
id: 'header-not-assigned',
|
||||
data: {},
|
||||
hidden: cannotQueryUsers.length === 0,
|
||||
rowSection: {
|
||||
title: 'Not Assigned',
|
||||
secondaryTitle: cannotQueryUsers.length.toString()
|
||||
}
|
||||
},
|
||||
...cannotQueryUsers
|
||||
].filter((row) => !(row as any).hidden);
|
||||
const rows = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
id: 'header-assigned',
|
||||
data: {},
|
||||
hidden: canQueryUsers.length === 0,
|
||||
rowSection: {
|
||||
title: 'Assigned',
|
||||
secondaryTitle: numberOfUsers.toString()
|
||||
}
|
||||
},
|
||||
...canQueryUsers,
|
||||
{
|
||||
id: 'header-not-assigned',
|
||||
data: {},
|
||||
hidden: cannotQueryUsers.length === 0,
|
||||
rowSection: {
|
||||
title: 'Not Assigned',
|
||||
secondaryTitle: cannotQueryUsers.length.toString()
|
||||
}
|
||||
},
|
||||
...cannotQueryUsers
|
||||
].filter((row) => !(row as any).hidden),
|
||||
[canQueryUsers, cannotQueryUsers, numberOfUsers]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -3,13 +3,10 @@ import {
|
|||
useGetDatasetPermissionsOverview
|
||||
} from '@/api/busterv2/datasets';
|
||||
import React, { useMemo, useState, useTransition, useEffect } from 'react';
|
||||
import { Title, Text } from '@/components/text';
|
||||
import { Input } from 'antd';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { AppMaterialIcons } from '@/components';
|
||||
import { useDebounceFn } from 'ahooks';
|
||||
import { HeaderExplanation } from './HeaderExplanation';
|
||||
import { PermissionOverviewSearch } from './PermissionOverviewSearch';
|
||||
import { HeaderExplanation } from '../HeaderExplanation';
|
||||
import { PermissionSearch } from '../PermissionSearch';
|
||||
import { PermissionListUserContainer } from './PermissionListUserContainer';
|
||||
|
||||
export const PermissionOverview: React.FC<{
|
||||
|
@ -65,7 +62,7 @@ export const PermissionOverview: React.FC<{
|
|||
<>
|
||||
<HeaderExplanation className="mb-5" />
|
||||
<div className="flex h-full flex-col space-y-3">
|
||||
<PermissionOverviewSearch searchText={searchText} setSearchText={handleSearchChange} />
|
||||
<PermissionSearch searchText={searchText} setSearchText={handleSearchChange} />
|
||||
<PermissionListUserContainer filteredUsers={filteredUsers} />
|
||||
{/* You can use filteredUsers here to display the results */}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
import { ListPermissionGroupsResponse, useUpdatePermissionGroups } from '@/api/busterv2/datasets';
|
||||
import { BusterListColumn, BusterListRowItem } from '@/components/list';
|
||||
import { BusterInfiniteList } from '@/components/list/BusterInfiniteList';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Select } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
export const PermissionListPermissionGroupContainer: React.FC<{
|
||||
filteredPermissionGroups: ListPermissionGroupsResponse[];
|
||||
datasetId: string;
|
||||
}> = React.memo(({ filteredPermissionGroups, datasetId }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
const { mutateAsync: updatePermissionGroups } = useUpdatePermissionGroups(datasetId);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
||||
|
||||
const numberOfPermissionGroups = filteredPermissionGroups.length;
|
||||
|
||||
const onSelectAssigned = useMemoizedFn(async (params: { id: string; assigned: boolean }) => {
|
||||
updatePermissionGroups([params]);
|
||||
});
|
||||
|
||||
const columns: BusterListColumn[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
width: 270,
|
||||
render: (name: string) => {
|
||||
return <PermissionGroupInfoCell name={name} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Assigned',
|
||||
dataIndex: 'assigned',
|
||||
render: (assigned: boolean, permissionGroup: ListPermissionGroupsResponse) => {
|
||||
return (
|
||||
<div className="flex justify-end">
|
||||
<PermissionGroupAssignedCell
|
||||
id={permissionGroup.id}
|
||||
assigned={assigned}
|
||||
onSelect={onSelectAssigned}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const { cannotQueryPermissionGroups, canQueryPermissionGroups } = useMemo(() => {
|
||||
const result: {
|
||||
cannotQueryPermissionGroups: BusterListRowItem[];
|
||||
canQueryPermissionGroups: BusterListRowItem[];
|
||||
} = filteredPermissionGroups.reduce<{
|
||||
cannotQueryPermissionGroups: BusterListRowItem[];
|
||||
canQueryPermissionGroups: BusterListRowItem[];
|
||||
}>(
|
||||
(acc, permissionGroup) => {
|
||||
if (permissionGroup.assigned) {
|
||||
acc.canQueryPermissionGroups.push({
|
||||
id: permissionGroup.id,
|
||||
data: permissionGroup
|
||||
});
|
||||
} else {
|
||||
acc.cannotQueryPermissionGroups.push({
|
||||
id: permissionGroup.id,
|
||||
data: permissionGroup
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
cannotQueryPermissionGroups: [] as BusterListRowItem[],
|
||||
canQueryPermissionGroups: [] as BusterListRowItem[]
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}, [filteredPermissionGroups]);
|
||||
|
||||
const rows = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
id: 'header-assigned',
|
||||
data: {},
|
||||
hidden: canQueryPermissionGroups.length === 0,
|
||||
rowSection: {
|
||||
title: 'Assigned',
|
||||
secondaryTitle: canQueryPermissionGroups.length.toString()
|
||||
}
|
||||
},
|
||||
...canQueryPermissionGroups,
|
||||
{
|
||||
id: 'header-not-assigned',
|
||||
data: {},
|
||||
hidden: cannotQueryPermissionGroups.length === 0,
|
||||
rowSection: {
|
||||
title: 'Not Assigned',
|
||||
secondaryTitle: cannotQueryPermissionGroups.length.toString()
|
||||
}
|
||||
},
|
||||
...cannotQueryPermissionGroups
|
||||
].filter((row) => !(row as any).hidden),
|
||||
[canQueryPermissionGroups, cannotQueryPermissionGroups, numberOfPermissionGroups]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cx('', styles.container)}>
|
||||
<BusterInfiniteList
|
||||
columns={columns}
|
||||
rows={rows}
|
||||
showHeader={false}
|
||||
showSelectAll={false}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
onSelectChange={setSelectedRowKeys}
|
||||
onScrollEnd={() => {
|
||||
console.log('scrolled');
|
||||
}}
|
||||
emptyState={<div className="py-12">No teams found</div>}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
PermissionListPermissionGroupContainer.displayName = 'PermissionListTeamContainer';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
container: css`
|
||||
border: 0.5px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
overflow: hidden;
|
||||
`
|
||||
}));
|
||||
|
||||
const PermissionGroupInfoCell = React.memo(({ name }: { name: string }) => {
|
||||
return <div>{name}</div>;
|
||||
});
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: 'Assigned',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: 'Not Assigned',
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
||||
const PermissionGroupAssignedCell = React.memo(
|
||||
({
|
||||
id,
|
||||
assigned,
|
||||
onSelect
|
||||
}: {
|
||||
id: string;
|
||||
assigned: boolean;
|
||||
onSelect: (value: { id: string; assigned: boolean }) => void;
|
||||
}) => {
|
||||
return (
|
||||
<Select
|
||||
options={options}
|
||||
defaultValue={assigned}
|
||||
popupMatchSelectWidth
|
||||
onSelect={(value) => {
|
||||
onSelect({ id, assigned: value });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
|
||||
PermissionGroupAssignedCell.displayName = 'PermissionGroupAssignedCell';
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useEffect, useState, useTransition } from 'react';
|
||||
import { HeaderExplanation } from '../HeaderExplanation';
|
||||
import { PermissionSearch } from '../PermissionSearch';
|
||||
import { useDebounceFn, useMemoizedFn } from 'ahooks';
|
||||
import { Button } from 'antd';
|
||||
import { AppMaterialIcons } from '@/components';
|
||||
import { PermissionListPermissionGroupContainer } from './PermissionListPermissionGroupContainer';
|
||||
import { ListPermissionGroupsResponse, useListPermissionGroups } from '@/api/busterv2/datasets';
|
||||
|
||||
export const PermissionPermissionGroup: React.FC<{
|
||||
datasetId: string;
|
||||
}> = React.memo(({ datasetId }) => {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const { data: permissionGroups } = useListPermissionGroups(datasetId);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [filteredPermissionGroups, setFilteredPermissionGroups] = useState<
|
||||
ListPermissionGroupsResponse[]
|
||||
>([]);
|
||||
|
||||
const filterPermissionGroups = useMemoizedFn((text: string): ListPermissionGroupsResponse[] => {
|
||||
if (!text) return permissionGroups || [];
|
||||
const lowerCaseSearchText = text.toLowerCase();
|
||||
return (permissionGroups || []).filter((p) => {
|
||||
return p.name.toLowerCase().includes(lowerCaseSearchText);
|
||||
});
|
||||
});
|
||||
|
||||
const updateFilteredPermissionGroups = useMemoizedFn((text: string) => {
|
||||
startTransition(() => {
|
||||
setFilteredPermissionGroups(filterPermissionGroups(text));
|
||||
});
|
||||
});
|
||||
|
||||
const { run: debouncedSearch } = useDebounceFn(
|
||||
(text: string) => {
|
||||
updateFilteredPermissionGroups(text);
|
||||
},
|
||||
{ wait: 300 }
|
||||
);
|
||||
|
||||
const handleSearchChange = useMemoizedFn((text: string) => {
|
||||
setSearchText(text);
|
||||
debouncedSearch(text);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredPermissionGroups(permissionGroups || []);
|
||||
}, [permissionGroups]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderExplanation
|
||||
className="mb-5"
|
||||
title="Dataset permissions"
|
||||
description="Manage who can build dashboards & metrics using this dataset"
|
||||
/>
|
||||
<div className="flex h-full flex-col space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<PermissionSearch
|
||||
searchText={searchText}
|
||||
setSearchText={handleSearchChange}
|
||||
placeholder="Search by permission group"
|
||||
/>
|
||||
|
||||
<Button type="default" icon={<AppMaterialIcons icon="add" />}>
|
||||
New permission group
|
||||
</Button>
|
||||
</div>
|
||||
<PermissionListPermissionGroupContainer
|
||||
filteredPermissionGroups={filteredPermissionGroups}
|
||||
datasetId={datasetId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
PermissionPermissionGroup.displayName = 'PermissionPermissionGroup';
|
|
@ -0,0 +1 @@
|
|||
export * from './PermissionPermissionGroup';
|
|
@ -165,8 +165,6 @@ export const ThreadItemsContainer: React.FC<{
|
|||
[]
|
||||
);
|
||||
|
||||
console.log(threadsByDate);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={tableContainerRef}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './BusterInfiniteList';
|
Loading…
Reference in New Issue