Team group (#2864)
* feat(member-group): Team (#2616) * feat: member-group schema define * feat(fe): create group * feat: add group edit modal * feat(fe): add avatar group component * feat: edit group fix: permission select menu style * feat: bio-mode support for select-member component * fix: avatar group key unique * feat: group manage * feat: divide member into group and clbs * feat: finish team permission * chore: adjust * fix: get clbs * perf: groups code * pref: member group for team (#2706) * chore: fe adjust fix: remove the member from groups when removing from team feat: change the groups avatar when updating the team's avatar * chore: DefaultGroupName as a constant string '' * fix: create default group when create team for root * feat: comment * feat: 4811 init * pref: member group for team (#2732) * chore: default group name * feat: get default group when get by tmbid * feat(fe): adjust * member ui * fix: delete group (#2736) * perf: init4811 * pref: member group (#2818) * fix: update clb per then refetch clb list * fix: calculate group permission * feat(fe): group tag * refactor(fe): team and group manage * feat: manage group member * feat: add group transfer owner modal * feat: group manage member * chore: adjust the file structure * pref: member group * chore: adjust fe style * fix: ts error * chore: fe adjust * chore: fe adjust * chore: adjust * chore: adjust the code * perf: i18n and schema name * pref: member-group (#2862) * feat: group list ordered by updateTime * fix: transfer ownership of group when deleting member * fix: i18n fix * feat: can not set member as admin/owner when user is not active * fix: GroupInfoModal hover input do not change color * fix(fe): searchinput do not scroll * perf: team group ui * doc * remove enum --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
@@ -66,6 +66,7 @@ export function ChangeOwnerModal({
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="modal/changePer"
|
||||
iconColor="primary.600"
|
||||
onClose={onClose}
|
||||
title={t('common:permission.change_owner')}
|
||||
isLoading={loading}
|
||||
@@ -76,7 +77,7 @@ export function ChangeOwnerModal({
|
||||
<Box>{name}</Box>
|
||||
</HStack>
|
||||
<Flex mt={4} justify="start" flexDirection="column">
|
||||
<Box fontSize="14px" fontWeight="500" color="myGray.900">
|
||||
<Box fontSize="14px" fontWeight="500">
|
||||
{t('common:permission.change_owner_to')}
|
||||
</Box>
|
||||
<Flex mt="4" alignItems="center" position={'relative'}>
|
||||
@@ -162,3 +163,5 @@ export function ChangeOwnerModal({
|
||||
</MyModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChangeOwnerModal;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import Tag from '@fastgpt/web/components/common/Tag';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
max: number;
|
||||
names?: string[];
|
||||
};
|
||||
|
||||
function GroupTags({ max, names }: Props) {
|
||||
const length = names?.length || 0;
|
||||
const { isOpen, onToggle, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
<Flex flexWrap="wrap" rowGap={2}>
|
||||
{names?.slice(0, max).map((name, index) => (
|
||||
<Tag key={index} colorSchema={'gray'} ml={2}>
|
||||
{name.length > 10 ? name.slice(0, 10) + '...' : name}
|
||||
</Tag>
|
||||
))}
|
||||
|
||||
<Popover
|
||||
isOpen={isOpen}
|
||||
trigger={'hover'}
|
||||
onOpen={onToggle}
|
||||
onClose={onClose}
|
||||
placement="bottom"
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Box>
|
||||
{length > max && (
|
||||
<Tag colorSchema={'gray'} ml={2} cursor={'pointer'}>
|
||||
{'+' + (length - max)}
|
||||
</Tag>
|
||||
)}
|
||||
</Box>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent w={'fit-content'} bg={'white'} px={4} py={2}>
|
||||
<Flex rowGap={2} flexWrap={'wrap'} columnGap={2}>
|
||||
{names?.slice(max)?.map((name, index) => (
|
||||
<Tag key={index + length} colorSchema={'gray'}>
|
||||
{name}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default GroupTags;
|
||||
@@ -1,15 +1,13 @@
|
||||
import {
|
||||
Flex,
|
||||
Box,
|
||||
Grid,
|
||||
ModalBody,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
Input,
|
||||
Checkbox,
|
||||
ModalFooter,
|
||||
Button,
|
||||
useToast
|
||||
Button
|
||||
} from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -67,7 +65,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
const { mutate: onConfirm, isLoading: isUpdating } = useRequest({
|
||||
mutationFn: () => {
|
||||
return onUpdateCollaborators({
|
||||
tmbIds: selectedMemberIdList,
|
||||
members: selectedMemberIdList,
|
||||
permission: selectedPermission
|
||||
});
|
||||
},
|
||||
@@ -184,7 +182,9 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
||||
_notLast={{ mb: 2 }}
|
||||
>
|
||||
<Avatar src={member.avatar} w="24px" />
|
||||
<Box w="full">{member.memberName}</Box>
|
||||
<Box w="full" ml={2}>
|
||||
{member.memberName}
|
||||
</Box>
|
||||
<MyIcon
|
||||
name="common/closeLight"
|
||||
w="16px"
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
import {
|
||||
ModalBody,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
Td,
|
||||
Box,
|
||||
Flex
|
||||
} from '@chakra-ui/react';
|
||||
import { ModalBody, Table, TableContainer, Tbody, Th, Thead, Tr, Td, Flex } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React from 'react';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
@@ -18,7 +7,7 @@ import PermissionTags from './PermissionTags';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { CollaboratorContext } from './context';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
@@ -38,16 +27,17 @@ function ManageModal({ onClose }: ManageModalProps) {
|
||||
onDelOneCollaborator(tmbId)
|
||||
);
|
||||
|
||||
const { mutate: onUpdate, isLoading: isUpdating } = useRequest({
|
||||
mutationFn: ({ tmbId, per }: { tmbId: string; per: PermissionValueType }) => {
|
||||
return onUpdateCollaborators({
|
||||
tmbIds: [tmbId],
|
||||
const { runAsync: onUpdate, loading: isUpdating } = useRequest2(
|
||||
({ tmbId, per }: { tmbId: string; per: PermissionValueType }) =>
|
||||
onUpdateCollaborators({
|
||||
members: [tmbId],
|
||||
permission: per
|
||||
});
|
||||
},
|
||||
successToast: t('common.Update Success'),
|
||||
errorToast: 'Error'
|
||||
});
|
||||
}),
|
||||
{
|
||||
successToast: t('common.Update Success'),
|
||||
errorToast: 'Error'
|
||||
}
|
||||
);
|
||||
|
||||
const loading = isDeleting || isUpdating;
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import {
|
||||
ButtonProps,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
Box,
|
||||
Radio,
|
||||
useOutsideClick,
|
||||
HStack
|
||||
HStack,
|
||||
MenuButton
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -46,18 +46,17 @@ function PermissionSelect({
|
||||
offset = [0, 5],
|
||||
Button,
|
||||
width = 'auto',
|
||||
onDelete,
|
||||
...props
|
||||
onDelete
|
||||
}: PermissionSelectProps) {
|
||||
const { t } = useTranslation();
|
||||
const { permission, permissionList } = useContextSelector(CollaboratorContext, (v) => v);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const closeTimer = useRef<any>();
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const closeTimer = useRef<NodeJS.Timeout>();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const permissionSelectList = useMemo(() => {
|
||||
const list = Object.entries(permissionList).map(([key, value]) => {
|
||||
const list = Object.entries(permissionList).map(([_, value]) => {
|
||||
return {
|
||||
name: value.name,
|
||||
value: value.value,
|
||||
@@ -85,15 +84,15 @@ function PermissionSelect({
|
||||
|
||||
return permissionList['read'].value;
|
||||
}, [permissionList, value]);
|
||||
const selectedMultipleValues = useMemo(() => {
|
||||
const per = new Permission({ per: value });
|
||||
|
||||
return permissionSelectList.multipleCheckBoxList
|
||||
.filter((item) => {
|
||||
return per.checkPer(item.value);
|
||||
})
|
||||
.map((item) => item.value);
|
||||
}, [permissionSelectList.multipleCheckBoxList, value]);
|
||||
// const selectedMultipleValues = useMemo(() => {
|
||||
// const per = new Permission({ per: value });
|
||||
//
|
||||
// return permissionSelectList.multipleCheckBoxList
|
||||
// .filter((item) => {
|
||||
// return per.checkPer(item.value);
|
||||
// })
|
||||
// .map((item) => item.value);
|
||||
// }, [permissionSelectList.multipleCheckBoxList, value]);
|
||||
|
||||
const onSelectPer = (per: PermissionValueType) => {
|
||||
if (per === value) return;
|
||||
@@ -111,7 +110,7 @@ function PermissionSelect({
|
||||
return (
|
||||
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}>
|
||||
<Box
|
||||
ref={ref}
|
||||
w="fit-content"
|
||||
onMouseEnter={() => {
|
||||
if (trigger === 'hover') {
|
||||
setIsOpen(true);
|
||||
@@ -126,7 +125,8 @@ function PermissionSelect({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
<MenuButton
|
||||
ref={ref}
|
||||
position={'relative'}
|
||||
onClickCapture={() => {
|
||||
if (trigger === 'click') {
|
||||
@@ -134,25 +134,8 @@ function PermissionSelect({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuButton
|
||||
w={'100%'}
|
||||
h={'100%'}
|
||||
position={'absolute'}
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
left={0}
|
||||
/>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
position={'relative'}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{Button}
|
||||
</Flex>
|
||||
</Box>
|
||||
{Button}
|
||||
</MenuButton>
|
||||
<MenuList
|
||||
minW={isOpen ? `${width}px !important` : 0}
|
||||
p="3"
|
||||
|
||||
@@ -22,7 +22,7 @@ export type MemberManagerInputPropsType = {
|
||||
permission: Permission;
|
||||
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
|
||||
permissionList: PermissionListType;
|
||||
onUpdateCollaborators: (props: UpdateClbPermissionProps) => any;
|
||||
onUpdateCollaborators: (props: any) => any; // TODO: type. should be UpdatePermissionBody after app and dataset permission refactored
|
||||
onDelOneCollaborator: (tmbId: string) => any;
|
||||
refreshDeps?: any[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user