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:
Archer
2024-10-09 18:32:10 +08:00
committed by GitHub
parent 7afa8f00b8
commit 3a4b4a866b
80 changed files with 2670 additions and 751 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;

View File

@@ -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"

View File

@@ -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[];
};