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
This commit is contained in:
Finley Ge
2025-09-01 20:33:29 +08:00
committed by GitHub
parent 76a03a8363
commit f41775fe56
9 changed files with 107 additions and 49 deletions

View File

@@ -201,7 +201,7 @@ const InviteModal = ({ onClose }: { onClose: () => void }) => {
Trigger={
<Button variant="outline" ml="10px" size="sm" color="myGray.900">
<Icon name="common/lineStop" w="16px" mr="1" />
{t('account_team:forbidden')}
{t('account_team:link_forbidden')}
</Button>
}
closeOnBlur={true}

View File

@@ -23,7 +23,6 @@ import {
postRestoreMember
} from '@/web/support/user/team/api';
import Tag from '@fastgpt/web/components/common/Tag';
import Icon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector';
import { TeamContext } from './context';
import { useSystemStore } from '@/web/common/system/useSystemStore';
@@ -60,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { toast } = useToast();
const { userInfo } = useUserStore();
const { feConfigs } = useSystemStore();
const isSyncMember = feConfigs?.register_method?.includes('sync');
const isSyncMode = feConfigs?.register_method?.includes('sync');
const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v);
@@ -76,8 +75,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
},
{
label: t('account_team:leave'),
value: 'inactive'
}
value: 'leave'
},
...(isSyncMode
? [
{
label: t('account_team:forbidden'),
value: 'forbidden'
}
]
: [])
];
const [status, setStatus] = useState<string>();
@@ -192,7 +199,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:label_sync')}
</Button>
)}
{userInfo?.team.permission.hasManagePer && isSyncMember && (
{userInfo?.team.permission.hasManagePer && isSyncMode && (
<Button
variant={'primary'}
size="md"
@@ -206,7 +213,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:sync_immediately')}
</Button>
)}
{userInfo?.team.permission.hasManagePer && !isSyncMember && (
{userInfo?.team.permission.hasManagePer && !isSyncMode && (
<Button
variant={'primary'}
size="md"
@@ -218,7 +225,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:user_team_invite_member')}
</Button>
)}
{userInfo?.team.permission.isOwner && isSyncMember && (
{userInfo?.team.permission.isOwner && isSyncMode && (
<Button
variant={'whitePrimary'}
size="md"
@@ -235,7 +242,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:export_members')}
</Button>
)}
{!userInfo?.team.permission.isOwner && (
{!userInfo?.team.permission.isOwner && !isSyncMode && (
<PopoverConfirm
Trigger={
<Button
@@ -285,7 +292,9 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{member.memberName}
{member.status !== 'active' && (
<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>
)}
</Box>
@@ -331,31 +340,35 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
</Box>
}
type="delete"
content={t('account_team:remove_tip', {
username: member.memberName
})}
content={
isSyncMode
? t('account_team:forbidden_tip', {
username: member.memberName
})
: t('account_team:remove_tip', {
username: member.memberName
})
}
onConfirm={() => onRemoveMember(member.tmbId)}
/>
</HStack>
) : (
member.status === TeamMemberStatusEnum.forbidden && (
<PopoverConfirm
Trigger={
<Box display={'inline-block'}>
<MyIconButton
icon={'common/confirm/restoreTip'}
size={'1rem'}
hoverColor={'primary.500'}
/>
</Box>
}
type="info"
content={t('account_team:restore_tip', {
username: member.memberName
})}
onConfirm={() => onRestore(member.tmbId)}
/>
)
<PopoverConfirm
Trigger={
<Box display={'inline-block'}>
<MyIconButton
icon={'common/confirm/restoreTip'}
size={'1rem'}
hoverColor={'primary.500'}
/>
</Box>
}
type="info"
content={t('account_team:restore_tip', {
username: member.memberName
})}
onConfirm={() => onRestore(member.tmbId)}
/>
))}
</Td>
</Tr>

View File

@@ -4,9 +4,10 @@ import { LoginContainer } from '@/pageComponents/login';
import I18nLngSelector from '@/components/Select/I18nLngSelector';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import type { ResLogin } from '@/global/support/api/userRes';
type LoginModalProps = {
onSuccess?: () => void;
onSuccess?: (res: ResLogin) => void;
};
const LoginModal = ({ onSuccess }: LoginModalProps) => {

View File

@@ -21,6 +21,8 @@ import { useTranslation } from 'next-i18next';
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
import { GET } from '@/web/common/api/request';
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 ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
@@ -194,10 +196,12 @@ export const LoginContainer = ({
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}` | null>(null);
const [showCommunityModal, setShowCommunityModal] = useState(false);
const router = useRouter();
const { lastRoute = '' } = router.query as { lastRoute: string };
// login success handler
const loginSuccess = useCallback(
(res: ResLogin) => {
async (res: ResLogin) => {
setUserInfo(res.user);
onSuccess?.(res);
},

View File

@@ -4,21 +4,35 @@ import { serviceSideProps } from '@/web/common/i18n/utils';
import { clearToken } from '@/web/support/user/auth';
import { useMount } from 'ahooks';
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 router = useRouter();
const { lastRoute = '' } = router.query as { lastRoute: string };
const loginSuccess = useCallback(() => {
const decodeLastRoute = decodeURIComponent(lastRoute);
const loginSuccess = useCallback(
async (res: ResLogin) => {
const decodeLastRoute = decodeURIComponent(lastRoute);
const navigateTo = await (async () => {
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';
})();
const navigateTo =
decodeLastRoute && !decodeLastRoute.includes('/login') && decodeLastRoute.startsWith('/')
? lastRoute
: '/dashboard/apps';
router.push(navigateTo);
}, [lastRoute, router]);
router.replace(navigateTo);
},
[lastRoute, router]
);
useMount(() => {
clearToken();

View File

@@ -19,6 +19,7 @@ import {
getSourceDomain,
removeFastGPTSem
} from '@/web/support/marketing/utils';
import { postAcceptInvitationLink } from '@/web/support/user/team/api';
let isOauthLogging = false;
@@ -35,11 +36,31 @@ const provider = () => {
: '/dashboard/apps';
const errorRedirectPage = lastRoute.startsWith('/chat') ? lastRoute : '/login';
const loginSuccess = useCallback(
(res: ResLogin) => {
setUserInfo(res.user);
// const loginSuccess = useCallback(async () => {
// const decodeLastRoute = decodeURIComponent(lastRoute);
router.replace(lastRoute);
// router.push(navigateTo);
// }, [lastRoute, router]);
const loginSuccess = useCallback(
async (res: ResLogin) => {
const decodeLastRoute = decodeURIComponent(lastRoute);
setUserInfo(res.user);
const navigateTo = await (async () => {
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]
);
@@ -94,7 +115,6 @@ const provider = () => {
return;
}
console.log('SSO', { initd, loginStore, props, state });
if (!props || !initd) return;
if (isOauthLogging) return;