mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-06-04 11:14:10 +08:00
fix: add dataset edit permissions (#13223)
This commit is contained in:
parent
186e2d972e
commit
49b4144ffd
@ -23,12 +23,14 @@ type ItemProps = {
|
|||||||
onRemove: (id: string) => void
|
onRemove: (id: string) => void
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
onSave: (newDataset: DataSet) => void
|
onSave: (newDataset: DataSet) => void
|
||||||
|
editable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item: FC<ItemProps> = ({
|
const Item: FC<ItemProps> = ({
|
||||||
config,
|
config,
|
||||||
onSave,
|
onSave,
|
||||||
onRemove,
|
onRemove,
|
||||||
|
editable = true,
|
||||||
}) => {
|
}) => {
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
@ -68,19 +70,21 @@ const Item: FC<ItemProps> = ({
|
|||||||
<div className='flex items-center h-[18px]'>
|
<div className='flex items-center h-[18px]'>
|
||||||
<div className='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
|
<div className='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
|
||||||
{config.provider === 'external'
|
{config.provider === 'external'
|
||||||
? <Badge text={t('dataset.externalTag')}></Badge>
|
? <Badge text={t('dataset.externalTag') as string} />
|
||||||
: <Badge
|
: <Badge
|
||||||
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
|
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px] bg-gradient-to-r from-white/50 to-white to-50%'>
|
<div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px] bg-gradient-to-r from-white/50 to-white to-50%'>
|
||||||
<div
|
{
|
||||||
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
editable && <div
|
||||||
onClick={() => setShowSettingsModal(true)}
|
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
||||||
>
|
onClick={() => setShowSettingsModal(true)}
|
||||||
<RiEditLine className='w-4 h-4 text-gray-500' />
|
>
|
||||||
</div>
|
<RiEditLine className='w-4 h-4 text-gray-500' />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div
|
<div
|
||||||
className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
|
className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
|
||||||
onClick={() => onRemove(config.id)}
|
onClick={() => onRemove(config.id)}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
@ -19,6 +19,8 @@ import {
|
|||||||
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
|
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
|
||||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
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 { 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 = (
|
const Icon = (
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -29,6 +31,7 @@ const Icon = (
|
|||||||
|
|
||||||
const DatasetConfig: FC = () => {
|
const DatasetConfig: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const userProfile = useAppContextSelector(s => s.userProfile)
|
||||||
const {
|
const {
|
||||||
mode,
|
mode,
|
||||||
dataSets: dataSet,
|
dataSets: dataSet,
|
||||||
@ -105,6 +108,20 @@ const DatasetConfig: FC = () => {
|
|||||||
setModelConfig(newModelConfig)
|
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 (
|
return (
|
||||||
<FeaturePanel
|
<FeaturePanel
|
||||||
className='mt-2'
|
className='mt-2'
|
||||||
@ -122,12 +139,13 @@ const DatasetConfig: FC = () => {
|
|||||||
{hasData
|
{hasData
|
||||||
? (
|
? (
|
||||||
<div className='flex flex-wrap mt-1 px-3 pb-3 justify-between'>
|
<div className='flex flex-wrap mt-1 px-3 pb-3 justify-between'>
|
||||||
{dataSet.map(item => (
|
{formattedDataset.map(item => (
|
||||||
<CardItem
|
<CardItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
config={item}
|
config={item}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
|
editable={item.editable}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,7 @@ import Divider from '@/app/components/base/divider'
|
|||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Textarea from '@/app/components/base/textarea'
|
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 { useToastContext } from '@/app/components/base/toast'
|
||||||
import { updateDatasetSetting } from '@/service/datasets'
|
import { updateDatasetSetting } from '@/service/datasets'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
@ -134,7 +134,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any
|
} as any
|
||||||
if (permission === 'partial_members') {
|
if (permission === DatasetPermission.partialMembers) {
|
||||||
requestParams.body.partial_member_list = selectedMemberIDs.map((id) => {
|
requestParams.body.partial_member_list = selectedMemberIDs.map((id) => {
|
||||||
return {
|
return {
|
||||||
user_id: id,
|
user_id: id,
|
||||||
|
@ -17,7 +17,7 @@ import Input from '@/app/components/base/input'
|
|||||||
import Textarea from '@/app/components/base/textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
|
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
import { updateDatasetSetting } from '@/service/datasets'
|
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 DatasetDetailContext from '@/context/dataset-detail'
|
||||||
import { type RetrievalConfig } from '@/types/app'
|
import { type RetrievalConfig } from '@/types/app'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
@ -145,7 +145,7 @@ const Form = () => {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any
|
} as any
|
||||||
if (permission === 'partial_members') {
|
if (permission === DatasetPermission.partialMembers) {
|
||||||
requestParams.body.partial_member_list = selectedMemberIDs.map((id) => {
|
requestParams.body.partial_member_list = selectedMemberIDs.map((id) => {
|
||||||
return {
|
return {
|
||||||
user_id: id,
|
user_id: id,
|
||||||
|
@ -12,7 +12,7 @@ import Avatar from '@/app/components/base/avatar'
|
|||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { Users01, UsersPlus } from '@/app/components/base/icons/src/vender/solid/users'
|
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 { useAppContext } from '@/context/app-context'
|
||||||
import type { Member } from '@/models/common'
|
import type { Member } from '@/models/common'
|
||||||
export type RoleSelectorProps = {
|
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))
|
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])
|
}, [memberList, searchKeywords, userProfile])
|
||||||
|
|
||||||
|
const isOnlyMe = permission === DatasetPermission.onlyMe
|
||||||
|
const isAllTeamMembers = permission === DatasetPermission.allTeamMembers
|
||||||
|
const isPartialMembers = permission === DatasetPermission.partialMembers
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalToFollowElem
|
<PortalToFollowElem
|
||||||
open={open}
|
open={open}
|
||||||
@ -72,14 +76,14 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
onClick={() => !disabled && setOpen(v => !v)}
|
onClick={() => !disabled && setOpen(v => !v)}
|
||||||
className='block'
|
className='block'
|
||||||
>
|
>
|
||||||
{permission === 'only_me' && (
|
{isOnlyMe && (
|
||||||
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200', disabled && 'hover:!bg-gray-100 !cursor-default')}>
|
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200', disabled && 'hover:!bg-gray-100 !cursor-default')}>
|
||||||
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} className='shrink-0 mr-2' size={24} />
|
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} className='shrink-0 mr-2' size={24} />
|
||||||
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsOnlyMe')}</div>
|
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsOnlyMe')}</div>
|
||||||
{!disabled && <RiArrowDownSLine className='shrink-0 w-4 h-4 text-gray-700' />}
|
{!disabled && <RiArrowDownSLine className='shrink-0 w-4 h-4 text-gray-700' />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{permission === 'all_team_members' && (
|
{isAllTeamMembers && (
|
||||||
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
|
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
|
||||||
<div className='mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#EEF4FF]'>
|
<div className='mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#EEF4FF]'>
|
||||||
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
||||||
@ -88,7 +92,7 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
{!disabled && <RiArrowDownSLine className='shrink-0 w-4 h-4 text-gray-700' />}
|
{!disabled && <RiArrowDownSLine className='shrink-0 w-4 h-4 text-gray-700' />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{permission === 'partial_members' && (
|
{isPartialMembers && (
|
||||||
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
|
<div className={cn('flex items-center px-3 py-[6px] rounded-lg bg-gray-100 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
|
||||||
<div className='mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#EEF4FF]'>
|
<div className='mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#EEF4FF]'>
|
||||||
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
||||||
@ -102,17 +106,17 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
<div className='relative w-[480px] rounded-lg border-[0.5px] bg-white shadow-lg'>
|
<div className='relative w-[480px] rounded-lg border-[0.5px] bg-white shadow-lg'>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
||||||
onChange('only_me')
|
onChange(DatasetPermission.onlyMe)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}>
|
}}>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} className='shrink-0 mr-2' size={24} />
|
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} className='shrink-0 mr-2' size={24} />
|
||||||
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsOnlyMe')}</div>
|
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsOnlyMe')}</div>
|
||||||
{permission === 'only_me' && <Check className='w-4 h-4 text-primary-600' />}
|
{isOnlyMe && <Check className='w-4 h-4 text-primary-600' />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
||||||
onChange('all_team_members')
|
onChange(DatasetPermission.allTeamMembers)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}>
|
}}>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
@ -120,23 +124,23 @@ const PermissionSelector = ({ disabled, permission, value, memberList, onChange,
|
|||||||
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
<Users01 className='w-3.5 h-3.5 text-[#444CE7]' />
|
||||||
</div>
|
</div>
|
||||||
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsAllMember')}</div>
|
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsAllMember')}</div>
|
||||||
{permission === 'all_team_members' && <Check className='w-4 h-4 text-primary-600' />}
|
{isAllTeamMembers && <Check className='w-4 h-4 text-primary-600' />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
<div className='pl-3 pr-2 py-1 rounded-lg hover:bg-gray-50 cursor-pointer' onClick={() => {
|
||||||
onChange('partial_members')
|
onChange(DatasetPermission.partialMembers)
|
||||||
onMemberSelect([userProfile.id])
|
onMemberSelect([userProfile.id])
|
||||||
}}>
|
}}>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<div className={cn('mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#FFF6ED]', permission === 'partial_members' && '!bg-[#EEF4FF]')}>
|
<div className={cn('mr-2 flex items-center justify-center w-6 h-6 rounded-lg bg-[#FFF6ED]', isPartialMembers && '!bg-[#EEF4FF]')}>
|
||||||
<UsersPlus className={cn('w-3.5 h-3.5 text-[#FB6514]', permission === 'partial_members' && '!text-[#444CE7]')} />
|
<UsersPlus className={cn('w-3.5 h-3.5 text-[#FB6514]', isPartialMembers && '!text-[#444CE7]')} />
|
||||||
</div>
|
</div>
|
||||||
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsInvitedMembers')}</div>
|
<div className='grow mr-2 text-gray-900 text-sm leading-5'>{t('datasetSettings.form.permissionsInvitedMembers')}</div>
|
||||||
{permission === 'partial_members' && <Check className='w-4 h-4 text-primary-600' />}
|
{isPartialMembers && <Check className='w-4 h-4 text-primary-600' />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{permission === 'partial_members' && (
|
{isPartialMembers && (
|
||||||
<div className='max-h-[360px] border-t-[1px] border-gray-100 p-1 overflow-y-auto'>
|
<div className='max-h-[360px] border-t-[1px] border-gray-100 p-1 overflow-y-auto'>
|
||||||
<div className='sticky left-0 top-0 p-2 pb-1 bg-white'>
|
<div className='sticky left-0 top-0 p-2 pb-1 bg-white'>
|
||||||
<Input
|
<Input
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import s from './index.module.css'
|
|
||||||
import classNames from '@/utils/classnames'
|
|
||||||
import type { DataSet } from '@/models/datasets'
|
|
||||||
|
|
||||||
const itemClass = `
|
|
||||||
flex items-center w-full sm:w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
|
|
||||||
`
|
|
||||||
const radioClass = `
|
|
||||||
w-4 h-4 border-[2px] border-gray-200 rounded-full
|
|
||||||
`
|
|
||||||
type IPermissionsRadioProps = {
|
|
||||||
value?: DataSet['permission']
|
|
||||||
onChange: (v?: DataSet['permission']) => 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 (
|
|
||||||
<div className={classNames(s.wrapper, 'flex justify-between w-full flex-wrap gap-y-2')}>
|
|
||||||
{
|
|
||||||
options.map(option => (
|
|
||||||
<div
|
|
||||||
key={option.key}
|
|
||||||
className={classNames(
|
|
||||||
itemClass,
|
|
||||||
itemClassName,
|
|
||||||
s.item,
|
|
||||||
option.key === value && s['item-active'],
|
|
||||||
disable && s.disable,
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
if (!disable)
|
|
||||||
onChange(option.key as DataSet['permission'])
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className={classNames(s['user-icon'], 'mr-3')} />
|
|
||||||
<div className='grow text-sm text-gray-900'>{option.text}</div>
|
|
||||||
<div className={classNames(radioClass, s.radio)} />
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PermissionsRadio
|
|
@ -23,6 +23,7 @@ type Props = {
|
|||||||
onRemove: () => void
|
onRemove: () => void
|
||||||
onChange: (dataSet: DataSet) => void
|
onChange: (dataSet: DataSet) => void
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
|
editable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const DatasetItem: FC<Props> = ({
|
const DatasetItem: FC<Props> = ({
|
||||||
@ -30,6 +31,7 @@ const DatasetItem: FC<Props> = ({
|
|||||||
onRemove,
|
onRemove,
|
||||||
onChange,
|
onChange,
|
||||||
readonly,
|
readonly,
|
||||||
|
editable = true,
|
||||||
}) => {
|
}) => {
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -75,14 +77,16 @@ const DatasetItem: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
{!readonly && (
|
{!readonly && (
|
||||||
<div className='hidden group-hover/dataset-item:flex shrink-0 ml-2 items-center space-x-1'>
|
<div className='hidden group-hover/dataset-item:flex shrink-0 ml-2 items-center space-x-1'>
|
||||||
<ActionButton
|
{
|
||||||
onClick={(e) => {
|
editable && <ActionButton
|
||||||
e.stopPropagation()
|
onClick={(e) => {
|
||||||
showSettingsModal()
|
e.stopPropagation()
|
||||||
}}
|
showSettingsModal()
|
||||||
>
|
}}
|
||||||
<RiEditLine className='w-4 h-4 flex-shrink-0 text-text-tertiary' />
|
>
|
||||||
</ActionButton>
|
<RiEditLine className='w-4 h-4 flex-shrink-0 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
|
}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
onClick={handleRemove}
|
onClick={handleRemove}
|
||||||
state={ActionButtonState.Destructive}
|
state={ActionButtonState.Destructive}
|
||||||
@ -102,7 +106,7 @@ const DatasetItem: FC<Props> = ({
|
|||||||
{
|
{
|
||||||
payload.provider === 'external' && <Badge
|
payload.provider === 'external' && <Badge
|
||||||
className='group-hover/dataset-item:hidden shrink-0'
|
className='group-hover/dataset-item:hidden shrink-0'
|
||||||
text={t('dataset.externalTag')}
|
text={t('dataset.externalTag') as string}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Item from './dataset-item'
|
import Item from './dataset-item'
|
||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
|
import { useSelector as useAppContextSelector } from '@/context/app-context'
|
||||||
|
import { hasEditPermissionForDataset } from '@/utils/permission'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
list: DataSet[]
|
list: DataSet[]
|
||||||
onChange: (list: DataSet[]) => void
|
onChange: (list: DataSet[]) => void
|
||||||
@ -17,6 +20,7 @@ const DatasetList: FC<Props> = ({
|
|||||||
readonly,
|
readonly,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const userProfile = useAppContextSelector(s => s.userProfile)
|
||||||
|
|
||||||
const handleRemove = useCallback((index: number) => {
|
const handleRemove = useCallback((index: number) => {
|
||||||
return () => {
|
return () => {
|
||||||
@ -35,10 +39,25 @@ const DatasetList: FC<Props> = ({
|
|||||||
onChange(newList)
|
onChange(newList)
|
||||||
}
|
}
|
||||||
}, [list, onChange])
|
}, [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 (
|
return (
|
||||||
<div className='space-y-1'>
|
<div className='space-y-1'>
|
||||||
{list.length
|
{formattedList.length
|
||||||
? list.map((item, index) => {
|
? formattedList.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
key={index}
|
key={index}
|
||||||
@ -46,6 +65,7 @@ const DatasetList: FC<Props> = ({
|
|||||||
onRemove={handleRemove(index)}
|
onRemove={handleRemove(index)}
|
||||||
onChange={handleChange(index)}
|
onChange={handleChange(index)}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
|
editable={item.editable}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,11 @@ export enum DataSourceType {
|
|||||||
WEB = 'website_crawl',
|
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 {
|
export enum ChunkingMode {
|
||||||
'text' = 'text_model', // General text
|
'text' = 'text_model', // General text
|
||||||
@ -40,7 +44,7 @@ export type DataSet = {
|
|||||||
retrieval_model_dict: RetrievalConfig
|
retrieval_model_dict: RetrievalConfig
|
||||||
retrieval_model: RetrievalConfig
|
retrieval_model: RetrievalConfig
|
||||||
tags: Tag[]
|
tags: Tag[]
|
||||||
partial_member_list?: any[]
|
partial_member_list?: string[]
|
||||||
external_knowledge_info: {
|
external_knowledge_info: {
|
||||||
external_knowledge_id: string
|
external_knowledge_id: string
|
||||||
external_knowledge_api_id: string
|
external_knowledge_api_id: string
|
||||||
|
18
web/utils/permission.ts
Normal file
18
web/utils/permission.ts
Normal file
@ -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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user