* feat: add org schema * feat: org manage UI * feat: OrgInfoModal * feat: org tree view * feat: org management * fix: init root org * feat: org permission for app * feat: org support for dataset * fix: disable org role control * styles: opt type signatures * fix: remove unused permission * feat: delete org collaborator
116 lines
3.0 KiB
TypeScript
116 lines
3.0 KiB
TypeScript
import { Box, HStack, Text, VStack } from '@chakra-ui/react';
|
|
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
|
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
|
import { useToggle } from 'ahooks';
|
|
import { useMemo, useState } from 'react';
|
|
import IconButton from './IconButton';
|
|
|
|
function OrgTreeNode({
|
|
org,
|
|
list,
|
|
selectedOrg,
|
|
selectOrg,
|
|
indent = 0
|
|
}: {
|
|
org: OrgType;
|
|
list: OrgType[];
|
|
selectedOrg?: OrgType;
|
|
selectOrg?: (org?: OrgType) => void;
|
|
indent?: number;
|
|
}) {
|
|
const children = useMemo(
|
|
() => list.filter((item) => item.path === `${org.path}/${org._id}`),
|
|
[org, list]
|
|
);
|
|
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
|
|
|
return (
|
|
<VStack alignItems={'start'} w="full" gap={'8px'}>
|
|
<HStack
|
|
w="full"
|
|
_hover={{ bgColor: selectedOrg === org ? 'blue.200' : 'gray.100' }}
|
|
borderRadius="4px"
|
|
boxSizing="border-box"
|
|
py="4px"
|
|
pl={`calc(${indent}rem + 4px)`}
|
|
transition={'background 0.1s'}
|
|
{...(selectedOrg === org ? { bgColor: 'blue.100' } : {})}
|
|
>
|
|
{children.length > 0 ? (
|
|
<IconButton
|
|
name={isExpanded ? 'common/downArrowFill' : 'common/rightArrowFill'}
|
|
onClick={() => toggleIsExpanded.toggle()}
|
|
/>
|
|
) : (
|
|
<Box w={'1rem'} h={'1rem'} m="1" />
|
|
)}
|
|
<HStack onClick={() => selectOrg?.(org)} cursor="pointer">
|
|
<Avatar src={org.avatar} w="20px" h="20px" rounded={'50%'} />
|
|
<Text>{org.name}</Text>
|
|
</HStack>
|
|
</HStack>
|
|
{isExpanded &&
|
|
children.length > 0 &&
|
|
children.map((child) => (
|
|
<OrgTreeNode
|
|
key={child._id}
|
|
org={child}
|
|
indent={indent + 1}
|
|
list={list}
|
|
selectedOrg={selectedOrg}
|
|
selectOrg={selectOrg}
|
|
/>
|
|
))}
|
|
</VStack>
|
|
);
|
|
}
|
|
|
|
function OrgTree({
|
|
orgs,
|
|
teamName,
|
|
teamAvatar,
|
|
selectedOrg,
|
|
selectOrg
|
|
}: {
|
|
orgs: OrgType[];
|
|
teamAvatar: string;
|
|
teamName: string;
|
|
selectedOrg?: OrgType;
|
|
selectOrg?: (org?: OrgType) => void;
|
|
}) {
|
|
const root = orgs[0];
|
|
if (!root) return null;
|
|
const children = useMemo(
|
|
() => orgs.filter((item) => item.path === `${root.path}/${root._id}`),
|
|
[root, orgs]
|
|
);
|
|
return (
|
|
<VStack alignItems={'start'} gap={'8px'}>
|
|
<HStack
|
|
w="full"
|
|
onClick={() => selectOrg?.(root)}
|
|
cursor="pointer"
|
|
_hover={{ bgColor: selectedOrg === root ? 'blue.200' : 'gray.100' }}
|
|
borderRadius="4px"
|
|
p="4px"
|
|
transition={'background 0.1s'}
|
|
{...(selectedOrg === root ? { bgColor: 'blue.100' } : {})}
|
|
>
|
|
<Avatar src={teamAvatar} w="20px" h="20px" rounded={'50%'} />
|
|
<Text>{teamName}</Text>
|
|
</HStack>
|
|
{children.map((child) => (
|
|
<OrgTreeNode
|
|
key={child._id}
|
|
org={child}
|
|
list={orgs}
|
|
selectOrg={selectOrg}
|
|
selectedOrg={selectedOrg}
|
|
/>
|
|
))}
|
|
</VStack>
|
|
);
|
|
}
|
|
|
|
export default OrgTree;
|