From 49b4144ffddac5ae702321032f79aef034e9706e Mon Sep 17 00:00:00 2001 From: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:26:16 +0800 Subject: [PATCH] fix: add dataset edit permissions (#13223) --- .../dataset-config/card-item/item.tsx | 18 +++-- .../configuration/dataset-config/index.tsx | 22 ++++++- .../dataset-config/settings-modal/index.tsx | 4 +- .../datasets/settings/form/index.tsx | 4 +- .../settings/permission-selector/index.tsx | 30 +++++---- .../settings/permissions-radio/index.tsx | 66 ------------------- .../components/dataset-item.tsx | 22 ++++--- .../components/dataset-list.tsx | 26 +++++++- web/models/datasets.ts | 8 ++- web/utils/permission.ts | 18 +++++ 10 files changed, 112 insertions(+), 106 deletions(-) delete mode 100644 web/app/components/datasets/settings/permissions-radio/index.tsx create mode 100644 web/utils/permission.ts diff --git a/web/app/components/app/configuration/dataset-config/card-item/item.tsx b/web/app/components/app/configuration/dataset-config/card-item/item.tsx index 9c5e6fa785ee4b..7036ae8c8a782e 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/item.tsx +++ b/web/app/components/app/configuration/dataset-config/card-item/item.tsx @@ -23,12 +23,14 @@ type ItemProps = { onRemove: (id: string) => void readonly?: boolean onSave: (newDataset: DataSet) => void + editable?: boolean } const Item: FC = ({ config, onSave, onRemove, + editable = true, }) => { const media = useBreakpoints() const isMobile = media === MediaType.mobile @@ -68,19 +70,21 @@ const Item: FC = ({
{config.name}
{config.provider === 'external' - ? + ? : }
-
setShowSettingsModal(true)} - > - -
+ { + editable &&
setShowSettingsModal(true)} + > + +
+ }
onRemove(config.id)} diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index 78b49f81d00824..ecbc52c5fd62cb 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import produce from 'immer' @@ -19,6 +19,8 @@ import { } from '@/app/components/workflow/nodes/knowledge-retrieval/utils' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useSelector as useAppContextSelector } from '@/context/app-context' +import { hasEditPermissionForDataset } from '@/utils/permission' const Icon = ( @@ -29,6 +31,7 @@ const Icon = ( const DatasetConfig: FC = () => { const { t } = useTranslation() + const userProfile = useAppContextSelector(s => s.userProfile) const { mode, dataSets: dataSet, @@ -105,6 +108,20 @@ const DatasetConfig: FC = () => { setModelConfig(newModelConfig) } + const formattedDataset = useMemo(() => { + return dataSet.map((item) => { + const datasetConfig = { + createdBy: item.created_by, + partialMemberList: item.partial_member_list || [], + permission: item.permission, + } + return { + ...item, + editable: hasEditPermissionForDataset(userProfile?.id || '', datasetConfig), + } + }) + }, [dataSet, userProfile?.id]) + return ( { {hasData ? (
- {dataSet.map(item => ( + {formattedDataset.map(item => ( ))}
diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 506406cfe08c2a..e00eb40341dea4 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -12,7 +12,7 @@ import Divider from '@/app/components/base/divider' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' -import { type DataSet } from '@/models/datasets' +import { type DataSet, DatasetPermission } from '@/models/datasets' import { useToastContext } from '@/app/components/base/toast' import { updateDatasetSetting } from '@/service/datasets' import { useAppContext } from '@/context/app-context' @@ -134,7 +134,7 @@ const SettingsModal: FC = ({ }), }, } as any - if (permission === 'partial_members') { + if (permission === DatasetPermission.partialMembers) { requestParams.body.partial_member_list = selectedMemberIDs.map((id) => { return { user_id: id, diff --git a/web/app/components/datasets/settings/form/index.tsx b/web/app/components/datasets/settings/form/index.tsx index 42ea7d637b37c3..c25c9827a76eac 100644 --- a/web/app/components/datasets/settings/form/index.tsx +++ b/web/app/components/datasets/settings/form/index.tsx @@ -17,7 +17,7 @@ import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development' import { updateDatasetSetting } from '@/service/datasets' -import { type DataSetListResponse } from '@/models/datasets' +import { type DataSetListResponse, DatasetPermission } from '@/models/datasets' import DatasetDetailContext from '@/context/dataset-detail' import { type RetrievalConfig } from '@/types/app' import { useAppContext } from '@/context/app-context' @@ -145,7 +145,7 @@ const Form = () => { }), }, } as any - if (permission === 'partial_members') { + if (permission === DatasetPermission.partialMembers) { requestParams.body.partial_member_list = selectedMemberIDs.map((id) => { return { user_id: id, diff --git a/web/app/components/datasets/settings/permission-selector/index.tsx b/web/app/components/datasets/settings/permission-selector/index.tsx index 16684217721dc8..62ec947f12ee44 100644 --- a/web/app/components/datasets/settings/permission-selector/index.tsx +++ b/web/app/components/datasets/settings/permission-selector/index.tsx @@ -12,7 +12,7 @@ import Avatar from '@/app/components/base/avatar' import Input from '@/app/components/base/input' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { Users01, UsersPlus } from '@/app/components/base/icons/src/vender/solid/users' -import type { DatasetPermission } from '@/models/datasets' +import { DatasetPermission } from '@/models/datasets' import { useAppContext } from '@/context/app-context' import type { Member } from '@/models/common' export type RoleSelectorProps = { @@ -60,6 +60,10 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange, return memberList.filter(member => (member.name.includes(searchKeywords) || member.email.includes(searchKeywords)) && member.id !== userProfile.id && ['owner', 'admin', 'editor', 'dataset_operator'].includes(member.role)) }, [memberList, searchKeywords, userProfile]) + const isOnlyMe = permission === DatasetPermission.onlyMe + const isAllTeamMembers = permission === DatasetPermission.allTeamMembers + const isPartialMembers = permission === DatasetPermission.partialMembers + return ( !disabled && setOpen(v => !v)} className='block' > - {permission === 'only_me' && ( + {isOnlyMe && (
{t('datasetSettings.form.permissionsOnlyMe')}
{!disabled && }
)} - {permission === 'all_team_members' && ( + {isAllTeamMembers && (
@@ -88,7 +92,7 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange, {!disabled && }
)} - {permission === 'partial_members' && ( + {isPartialMembers && (
@@ -102,17 +106,17 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
{ - onChange('only_me') + onChange(DatasetPermission.onlyMe) setOpen(false) }}>
{t('datasetSettings.form.permissionsOnlyMe')}
- {permission === 'only_me' && } + {isOnlyMe && }
{ - onChange('all_team_members') + onChange(DatasetPermission.allTeamMembers) setOpen(false) }}>
@@ -120,23 +124,23 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
{t('datasetSettings.form.permissionsAllMember')}
- {permission === 'all_team_members' && } + {isAllTeamMembers && }
{ - onChange('partial_members') + onChange(DatasetPermission.partialMembers) onMemberSelect([userProfile.id]) }}>
-
- +
+
{t('datasetSettings.form.permissionsInvitedMembers')}
- {permission === 'partial_members' && } + {isPartialMembers && }
- {permission === 'partial_members' && ( + {isPartialMembers && (
void - itemClassName?: string - disable?: boolean -} - -const PermissionsRadio = ({ - value, - onChange, - itemClassName, - disable, -}: IPermissionsRadioProps) => { - const { t } = useTranslation() - const options = [ - { - key: 'only_me', - text: t('datasetSettings.form.permissionsOnlyMe'), - }, - { - key: 'all_team_members', - text: t('datasetSettings.form.permissionsAllMember'), - }, - ] - - return ( -
- { - options.map(option => ( -
{ - if (!disable) - onChange(option.key as DataSet['permission']) - }} - > -
-
{option.text}
-
-
- )) - } -
- ) -} - -export default PermissionsRadio diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx index 3e9be6485b5e34..e65495d370f36a 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx @@ -23,6 +23,7 @@ type Props = { onRemove: () => void onChange: (dataSet: DataSet) => void readonly?: boolean + editable?: boolean } const DatasetItem: FC = ({ @@ -30,6 +31,7 @@ const DatasetItem: FC = ({ onRemove, onChange, readonly, + editable = true, }) => { const media = useBreakpoints() const { t } = useTranslation() @@ -75,14 +77,16 @@ const DatasetItem: FC = ({
{!readonly && (
- { - e.stopPropagation() - showSettingsModal() - }} - > - - + { + editable && { + e.stopPropagation() + showSettingsModal() + }} + > + + + } = ({ { payload.provider === 'external' && } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx index 3a31cddbce3335..a30de8b104bb5c 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx @@ -1,10 +1,13 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import produce from 'immer' import { useTranslation } from 'react-i18next' import Item from './dataset-item' import type { DataSet } from '@/models/datasets' +import { useSelector as useAppContextSelector } from '@/context/app-context' +import { hasEditPermissionForDataset } from '@/utils/permission' + type Props = { list: DataSet[] onChange: (list: DataSet[]) => void @@ -17,6 +20,7 @@ const DatasetList: FC = ({ readonly, }) => { const { t } = useTranslation() + const userProfile = useAppContextSelector(s => s.userProfile) const handleRemove = useCallback((index: number) => { return () => { @@ -35,10 +39,25 @@ const DatasetList: FC = ({ onChange(newList) } }, [list, onChange]) + + const formattedList = useMemo(() => { + return list.map((item) => { + const datasetConfig = { + createdBy: item.created_by, + partialMemberList: item.partial_member_list || [], + permission: item.permission, + } + return { + ...item, + editable: hasEditPermissionForDataset(userProfile?.id || '', datasetConfig), + } + }) + }, [list, userProfile?.id]) + return (
- {list.length - ? list.map((item, index) => { + {formattedList.length + ? formattedList.map((item, index) => { return ( = ({ onRemove={handleRemove(index)} onChange={handleChange(index)} readonly={readonly} + editable={item.editable} /> ) }) diff --git a/web/models/datasets.ts b/web/models/datasets.ts index 673fb5fb1530eb..170fe1911f62aa 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -9,7 +9,11 @@ export enum DataSourceType { WEB = 'website_crawl', } -export type DatasetPermission = 'only_me' | 'all_team_members' | 'partial_members' +export enum DatasetPermission { + 'onlyMe' = 'only_me', + 'allTeamMembers' = 'all_team_members', + 'partialMembers' = 'partial_members', +} export enum ChunkingMode { 'text' = 'text_model', // General text @@ -40,7 +44,7 @@ export type DataSet = { retrieval_model_dict: RetrievalConfig retrieval_model: RetrievalConfig tags: Tag[] - partial_member_list?: any[] + partial_member_list?: string[] external_knowledge_info: { external_knowledge_id: string external_knowledge_api_id: string diff --git a/web/utils/permission.ts b/web/utils/permission.ts new file mode 100644 index 00000000000000..8a7b9f5fa1a9be --- /dev/null +++ b/web/utils/permission.ts @@ -0,0 +1,18 @@ +import { DatasetPermission } from '@/models/datasets' + +type DatasetConfig = { + createdBy: string + partialMemberList: string[] + permission: DatasetPermission +} + +export const hasEditPermissionForDataset = (userId: string, datasetConfig: DatasetConfig) => { + const { createdBy, partialMemberList, permission } = datasetConfig + if (permission === DatasetPermission.onlyMe) + return userId === createdBy + if (permission === DatasetPermission.allTeamMembers) + return true + if (permission === DatasetPermission.partialMembers) + return partialMemberList.includes(userId) + return false +}