Commit f41775fe by Finley Ge Committed by GitHub

fix: leave team (#5554)

* chore: fe copywrite

* feat: auto accept invitation when login

* perf: only show forbidden filter in sync mode

* chore: auto accept invitation
parent 76a03a83
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
"forbid_hint": "After forbidden, this invitation link will become invalid. This action is irreversible. Are you sure you want to deactivate?", "forbid_hint": "After forbidden, this invitation link will become invalid. This action is irreversible. Are you sure you want to deactivate?",
"forbid_success": "Forbid success", "forbid_success": "Forbid success",
"forbidden": "Forbidden", "forbidden": "Forbidden",
"link_forbidden": "Forbidden",
"group": "group", "group": "group",
"group_name": "Group name", "group_name": "Group name",
"handle_invitation": "Handle Invitation", "handle_invitation": "Handle Invitation",
...@@ -220,7 +221,8 @@ ...@@ -220,7 +221,8 @@
"recover_team_member": "Member Recovery", "recover_team_member": "Member Recovery",
"relocate_department": "Department Mobile", "relocate_department": "Department Mobile",
"remark": "remark", "remark": "remark",
"remove_tip": "Confirm to remove {{username}} from the team?", "remove_tip": "Confirm to remove {{username}} from the team? The member will be marked as 'leave'. Operation data will not be deleted, and resources under the account will be automatically transferred to the team owner.",
"forbidden_tip": "Confirm disabling {{username}}? The member will be marked as 'disabled' and will not be able to log in. Operation data will not be deleted, and resources under the account will be automatically transferred to the team owner.",
"restore_tip": "Confirm to join the team {{username}}? \nOnly the availability and related permissions of this member account are restored, and the resources under the account cannot be restored.", "restore_tip": "Confirm to join the team {{username}}? \nOnly the availability and related permissions of this member account are restored, and the resources under the account cannot be restored.",
"restore_tip_title": "Recovery confirmation", "restore_tip_title": "Recovery confirmation",
"retain_admin_permissions": "Keep administrator rights", "retain_admin_permissions": "Keep administrator rights",
......
...@@ -93,6 +93,7 @@ ...@@ -93,6 +93,7 @@
"forbid_hint": "停用后,该邀请链接将失效。 该操作不可撤销,是否确认停用?", "forbid_hint": "停用后,该邀请链接将失效。 该操作不可撤销,是否确认停用?",
"forbid_success": "停用成功", "forbid_success": "停用成功",
"forbidden": "停用", "forbidden": "停用",
"link_forbidden": "禁用",
"group": "群组", "group": "群组",
"group_name": "群组名称", "group_name": "群组名称",
"handle_invitation": "处理团队邀请", "handle_invitation": "处理团队邀请",
...@@ -225,6 +226,7 @@ ...@@ -225,6 +226,7 @@
"relocate_department": "部门移动", "relocate_department": "部门移动",
"remark": "备注", "remark": "备注",
"remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“离开”,不删除操作数据,账号下资源自动转让给团队所有者。", "remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“离开”,不删除操作数据,账号下资源自动转让给团队所有者。",
"forbidden_tip": "确认将 {{username}} 禁用?成员将被标记为“禁用”并无法登录,不删除操作数据,账号下资源自动转让给团队所有者。",
"restore_tip": "确认将 {{username}} 加入团队吗?仅恢复该成员账号可用性及相关权限,无法恢复账号下资源。", "restore_tip": "确认将 {{username}} 加入团队吗?仅恢复该成员账号可用性及相关权限,无法恢复账号下资源。",
"restore_tip_title": "恢复确认", "restore_tip_title": "恢复确认",
"retain_admin_permissions": "保留管理员权限", "retain_admin_permissions": "保留管理员权限",
......
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
"forbid_hint": "停用後,該邀請連結將失效。該操作不可撤銷,是否確認停用?", "forbid_hint": "停用後,該邀請連結將失效。該操作不可撤銷,是否確認停用?",
"forbid_success": "停用成功", "forbid_success": "停用成功",
"forbidden": "停用", "forbidden": "停用",
"link_forbidden": "禁用",
"group": "群組", "group": "群組",
"group_name": "群組名稱", "group_name": "群組名稱",
"handle_invitation": "處理團隊邀請", "handle_invitation": "處理團隊邀請",
...@@ -220,7 +221,8 @@ ...@@ -220,7 +221,8 @@
"recover_team_member": "成員恢復", "recover_team_member": "成員恢復",
"relocate_department": "部門移動", "relocate_department": "部門移動",
"remark": "備註", "remark": "備註",
"remove_tip": "確認將 {{username}} 移出團隊?", "remove_tip": "確認將 {{username}} 移出團隊?成員將被標記為“離開”,不刪除操作數據,賬號下資源自動轉讓給團隊所有者。",
"forbidden_tip": "確認將 {{username}} 禁用?成員將被標記為“禁用”並無法登錄,不刪除操作數據,賬號下資源自動轉讓給團隊所有者。",
"restore_tip": "確認將 {{username}} 加入團隊嗎?\n僅恢復該成員賬號可用性及相關權限,無法恢復賬號下資源。", "restore_tip": "確認將 {{username}} 加入團隊嗎?\n僅恢復該成員賬號可用性及相關權限,無法恢復賬號下資源。",
"restore_tip_title": "恢復確認", "restore_tip_title": "恢復確認",
"retain_admin_permissions": "保留管理員權限", "retain_admin_permissions": "保留管理員權限",
......
...@@ -201,7 +201,7 @@ const InviteModal = ({ onClose }: { onClose: () => void }) => { ...@@ -201,7 +201,7 @@ const InviteModal = ({ onClose }: { onClose: () => void }) => {
Trigger={ Trigger={
<Button variant="outline" ml="10px" size="sm" color="myGray.900"> <Button variant="outline" ml="10px" size="sm" color="myGray.900">
<Icon name="common/lineStop" w="16px" mr="1" /> <Icon name="common/lineStop" w="16px" mr="1" />
{t('account_team:forbidden')} {t('account_team:link_forbidden')}
</Button> </Button>
} }
closeOnBlur={true} closeOnBlur={true}
......
...@@ -23,7 +23,6 @@ import { ...@@ -23,7 +23,6 @@ import {
postRestoreMember postRestoreMember
} from '@/web/support/user/team/api'; } from '@/web/support/user/team/api';
import Tag from '@fastgpt/web/components/common/Tag'; import Tag from '@fastgpt/web/components/common/Tag';
import Icon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { TeamContext } from './context'; import { TeamContext } from './context';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
...@@ -60,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -60,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { toast } = useToast(); const { toast } = useToast();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const isSyncMember = feConfigs?.register_method?.includes('sync'); const isSyncMode = feConfigs?.register_method?.includes('sync');
const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v); const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v);
...@@ -76,8 +75,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -76,8 +75,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
}, },
{ {
label: t('account_team:leave'), label: t('account_team:leave'),
value: 'inactive' value: 'leave'
},
...(isSyncMode
? [
{
label: t('account_team:forbidden'),
value: 'forbidden'
} }
]
: [])
]; ];
const [status, setStatus] = useState<string>(); const [status, setStatus] = useState<string>();
...@@ -192,7 +199,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -192,7 +199,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:label_sync')} {t('account_team:label_sync')}
</Button> </Button>
)} )}
{userInfo?.team.permission.hasManagePer && isSyncMember && ( {userInfo?.team.permission.hasManagePer && isSyncMode && (
<Button <Button
variant={'primary'} variant={'primary'}
size="md" size="md"
...@@ -206,7 +213,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -206,7 +213,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:sync_immediately')} {t('account_team:sync_immediately')}
</Button> </Button>
)} )}
{userInfo?.team.permission.hasManagePer && !isSyncMember && ( {userInfo?.team.permission.hasManagePer && !isSyncMode && (
<Button <Button
variant={'primary'} variant={'primary'}
size="md" size="md"
...@@ -218,7 +225,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -218,7 +225,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:user_team_invite_member')} {t('account_team:user_team_invite_member')}
</Button> </Button>
)} )}
{userInfo?.team.permission.isOwner && isSyncMember && ( {userInfo?.team.permission.isOwner && isSyncMode && (
<Button <Button
variant={'whitePrimary'} variant={'whitePrimary'}
size="md" size="md"
...@@ -235,7 +242,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -235,7 +242,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:export_members')} {t('account_team:export_members')}
</Button> </Button>
)} )}
{!userInfo?.team.permission.isOwner && ( {!userInfo?.team.permission.isOwner && !isSyncMode && (
<PopoverConfirm <PopoverConfirm
Trigger={ Trigger={
<Button <Button
...@@ -285,7 +292,9 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -285,7 +292,9 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{member.memberName} {member.memberName}
{member.status !== 'active' && ( {member.status !== 'active' && (
<Tag ml="2" colorSchema="gray" bg={'myGray.100'} color={'myGray.700'}> <Tag ml="2" colorSchema="gray" bg={'myGray.100'} color={'myGray.700'}>
{t('account_team:leave')} {member.status === 'forbidden'
? t('account_team:forbidden')
: t('account_team:leave')}
</Tag> </Tag>
)} )}
</Box> </Box>
...@@ -331,14 +340,19 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -331,14 +340,19 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
</Box> </Box>
} }
type="delete" type="delete"
content={t('account_team:remove_tip', { content={
isSyncMode
? t('account_team:forbidden_tip', {
username: member.memberName username: member.memberName
})} })
: t('account_team:remove_tip', {
username: member.memberName
})
}
onConfirm={() => onRemoveMember(member.tmbId)} onConfirm={() => onRemoveMember(member.tmbId)}
/> />
</HStack> </HStack>
) : ( ) : (
member.status === TeamMemberStatusEnum.forbidden && (
<PopoverConfirm <PopoverConfirm
Trigger={ Trigger={
<Box display={'inline-block'}> <Box display={'inline-block'}>
...@@ -355,7 +369,6 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -355,7 +369,6 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
})} })}
onConfirm={() => onRestore(member.tmbId)} onConfirm={() => onRestore(member.tmbId)}
/> />
)
))} ))}
</Td> </Td>
</Tr> </Tr>
......
...@@ -4,9 +4,10 @@ import { LoginContainer } from '@/pageComponents/login'; ...@@ -4,9 +4,10 @@ import { LoginContainer } from '@/pageComponents/login';
import I18nLngSelector from '@/components/Select/I18nLngSelector'; import I18nLngSelector from '@/components/Select/I18nLngSelector';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import type { ResLogin } from '@/global/support/api/userRes';
type LoginModalProps = { type LoginModalProps = {
onSuccess?: () => void; onSuccess?: (res: ResLogin) => void;
}; };
const LoginModal = ({ onSuccess }: LoginModalProps) => { const LoginModal = ({ onSuccess }: LoginModalProps) => {
......
...@@ -21,6 +21,8 @@ import { useTranslation } from 'next-i18next'; ...@@ -21,6 +21,8 @@ import { useTranslation } from 'next-i18next';
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm'; import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
import { GET } from '@/web/common/api/request'; import { GET } from '@/web/common/api/request';
import { getDocPath } from '@/web/common/system/doc'; import { getDocPath } from '@/web/common/system/doc';
import { postAcceptInvitationLink } from '@/web/support/user/team/api';
import { useRouter } from 'next/router';
const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm')); const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm')); const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
...@@ -194,10 +196,12 @@ export const LoginContainer = ({ ...@@ -194,10 +196,12 @@ export const LoginContainer = ({
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}` | null>(null); const [pageType, setPageType] = useState<`${LoginPageTypeEnum}` | null>(null);
const [showCommunityModal, setShowCommunityModal] = useState(false); const [showCommunityModal, setShowCommunityModal] = useState(false);
const router = useRouter();
const { lastRoute = '' } = router.query as { lastRoute: string };
// login success handler // login success handler
const loginSuccess = useCallback( const loginSuccess = useCallback(
(res: ResLogin) => { async (res: ResLogin) => {
setUserInfo(res.user); setUserInfo(res.user);
onSuccess?.(res); onSuccess?.(res);
}, },
......
...@@ -4,21 +4,35 @@ import { serviceSideProps } from '@/web/common/i18n/utils'; ...@@ -4,21 +4,35 @@ import { serviceSideProps } from '@/web/common/i18n/utils';
import { clearToken } from '@/web/support/user/auth'; import { clearToken } from '@/web/support/user/auth';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import LoginModal from '@/pageComponents/login/LoginModal'; import LoginModal from '@/pageComponents/login/LoginModal';
import { postAcceptInvitationLink } from '@/web/support/user/team/api';
import type { ResLogin } from '@/global/support/api/userRes';
const Login = () => { const Login = () => {
const router = useRouter(); const router = useRouter();
const { lastRoute = '' } = router.query as { lastRoute: string }; const { lastRoute = '' } = router.query as { lastRoute: string };
const loginSuccess = useCallback(() => { const loginSuccess = useCallback(
async (res: ResLogin) => {
const decodeLastRoute = decodeURIComponent(lastRoute); const decodeLastRoute = decodeURIComponent(lastRoute);
const navigateTo = await (async () => {
const navigateTo = if (res.user.team.status !== 'active') {
decodeLastRoute && !decodeLastRoute.includes('/login') && decodeLastRoute.startsWith('/') if (decodeLastRoute.includes('/account/team?invitelinkid=')) {
const id = decodeLastRoute.split('invitelinkid=')[1];
await postAcceptInvitationLink(id);
return '/dashboard/apps';
}
}
return decodeLastRoute &&
!decodeLastRoute.includes('/login') &&
decodeLastRoute.startsWith('/')
? lastRoute ? lastRoute
: '/dashboard/apps'; : '/dashboard/apps';
})();
router.push(navigateTo); router.replace(navigateTo);
}, [lastRoute, router]); },
[lastRoute, router]
);
useMount(() => { useMount(() => {
clearToken(); clearToken();
......
...@@ -19,6 +19,7 @@ import { ...@@ -19,6 +19,7 @@ import {
getSourceDomain, getSourceDomain,
removeFastGPTSem removeFastGPTSem
} from '@/web/support/marketing/utils'; } from '@/web/support/marketing/utils';
import { postAcceptInvitationLink } from '@/web/support/user/team/api';
let isOauthLogging = false; let isOauthLogging = false;
...@@ -35,11 +36,31 @@ const provider = () => { ...@@ -35,11 +36,31 @@ const provider = () => {
: '/dashboard/apps'; : '/dashboard/apps';
const errorRedirectPage = lastRoute.startsWith('/chat') ? lastRoute : '/login'; const errorRedirectPage = lastRoute.startsWith('/chat') ? lastRoute : '/login';
// const loginSuccess = useCallback(async () => {
// const decodeLastRoute = decodeURIComponent(lastRoute);
// router.push(navigateTo);
// }, [lastRoute, router]);
const loginSuccess = useCallback( const loginSuccess = useCallback(
(res: ResLogin) => { async (res: ResLogin) => {
const decodeLastRoute = decodeURIComponent(lastRoute);
setUserInfo(res.user); setUserInfo(res.user);
const navigateTo = await (async () => {
router.replace(lastRoute); if (res.user.team.status !== 'active') {
if (decodeLastRoute.includes('/account/team?invitelinkid=')) {
const id = decodeLastRoute.split('invitelinkid=')[1];
await postAcceptInvitationLink(id);
return '/dashboard/apps';
}
}
return decodeLastRoute &&
!decodeLastRoute.includes('/login') &&
decodeLastRoute.startsWith('/')
? lastRoute
: '/dashboard/apps';
})();
router.replace(navigateTo);
}, },
[setUserInfo, router, lastRoute] [setUserInfo, router, lastRoute]
); );
...@@ -94,7 +115,6 @@ const provider = () => { ...@@ -94,7 +115,6 @@ const provider = () => {
return; return;
} }
console.log('SSO', { initd, loginStore, props, state });
if (!props || !initd) return; if (!props || !initd) return;
if (isOauthLogging) return; if (isOauthLogging) return;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment