Skip to content

File tree

8 files changed

+209
-70
lines changed

8 files changed

+209
-70
lines changed
 

‎client/packages/lowcoder/src/api/inviteApi.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type InviteInfo = {
2020
class InviteApi extends Api {
2121
static getInviteURL = "/invitation";
2222
static acceptInviteURL = (invitationId: string) => `/invitation/${invitationId}/invite`;
23+
static sendInvitationURL = `${this.getInviteURL}/email/invite`;
2324

2425
// generate invitation
2526
static getInvite(request: GetInviteRequest): AxiosPromise<GenericApiResponse<InviteInfo>> {
@@ -36,6 +37,11 @@ class InviteApi extends Api {
3637
// the same api as getInviteInfo, method is by post
3738
return Api.get(InviteApi.acceptInviteURL(request.invitationId));
3839
}
40+
41+
// send invitations
42+
static sendInvitations(request: {emails: string[], orgId: string}): AxiosPromise<GenericApiResponse<any>> {
43+
return Api.post(InviteApi.sendInvitationURL, request);
44+
}
3945
}
4046

4147
export default InviteApi;

‎client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2484,6 +2484,12 @@ export const en = {
24842484
"inviteUserLabel": "Invitation Link:",
24852485
"inviteCopyLink": "Copy Link",
24862486
"inviteText": "{userName} Invites You to Join the Workspace \"{organization}\", Click on the Link to Join: {inviteLink}",
2487+
"inviteByEmailHelp": "You can enter one or more email addresses to send invitation links",
2488+
"inviteByEmailLabel": "Enter emails:",
2489+
"inviteByEmailButton": "Send Invitations",
2490+
"inviteByEmailSuccess": "Invitations sent successfully!",
2491+
"inviteByEmailError": "Something went wrong while sending invitations. Please try again.",
2492+
"noValidEmails": "No valid emails found",
24872493
"groupName": "Group Name",
24882494
"createTime": "Create Time",
24892495
"manageBtn": "Manage",
@@ -3079,6 +3085,7 @@ export const en = {
30793085
"memberOfOrgs": "Workspaces Membership",
30803086
"apiKeys": "API Keys",
30813087
"createApiKey": "Create API Key",
3088+
"apiKeyInfo": "Make sure to copy your new API key now. You won't be able to see it again.",
30823089
"apiKeyName": "Name",
30833090
"apiKeyDescription": "Description",
30843091
"apiKeyCopy": "Click the Api Key to get the value in your clipboard",

‎client/packages/lowcoder/src/pages/ApplicationV2/components/CreateApiKeyModal.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { validateResponse } from "api/apiUtils";
1313
import _ from "lodash";
1414
import { styled } from "styled-components";
1515
import UserApi, { ApiKeyPayload } from "api/userApi";
16+
import { ApiKeyType } from "./UserApiKeysCard";
1617

1718
const CustomModalStyled = styled(CustomModal)`
1819
button {
@@ -90,7 +91,7 @@ const FormStyled = styled(Form)`
9091
type CreateApiKeyModalProps = {
9192
modalVisible: boolean;
9293
closeModal: () => void;
93-
onConfigCreate: () => void;
94+
onConfigCreate: (apiKey?: ApiKeyType) => void;
9495
};
9596

9697
function CreateApiKeyModal(props: CreateApiKeyModalProps) {
@@ -101,6 +102,7 @@ function CreateApiKeyModal(props: CreateApiKeyModalProps) {
101102
} = props;
102103
const [form] = Form.useForm();
103104
const [saveLoading, setSaveLoading] = useState(false);
105+
const [apiKey, setApiKey] = useState<{id: string, token: string}>();
104106

105107
const handleOk = () => {
106108
form.validateFields().then(values => {
@@ -115,12 +117,15 @@ function CreateApiKeyModal(props: CreateApiKeyModalProps) {
115117
.then((resp) => {
116118
if (validateResponse(resp)) {
117119
messageInstance.success(trans("idSource.saveSuccess"));
120+
onConfigCreate(resp.data.data);
118121
}
119122
})
120-
.catch((e) => messageInstance.error(e.message))
123+
.catch((e) => {
124+
messageInstance.error(e.message);
125+
onConfigCreate();
126+
})
121127
.finally(() => {
122128
setSaveLoading(false);
123-
onConfigCreate();
124129
});
125130
}
126131

‎client/packages/lowcoder/src/pages/ApplicationV2/components/UserApiKeysCard.tsx

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import CreateApiKeyModal from "./CreateApiKeyModal";
1414
import { fetchApiKeysAction } from "redux/reduxActions/userActions";
1515
import UserApi from "@lowcoder-ee/api/userApi";
1616
import { validateResponse } from "@lowcoder-ee/api/apiUtils";
17+
import Alert from "antd/es/alert";
18+
import { CopyOutlined } from "@ant-design/icons";
1719

1820
const TableStyled = styled(Table)`
1921
.ant-table-tbody > tr > td {
@@ -37,10 +39,16 @@ const CreateButton = styled(TacoButton)`
3739
box-shadow: none;
3840
`;
3941

42+
export type ApiKeyType = {
43+
id: string;
44+
token: string;
45+
}
46+
4047
export default function UserApiKeysCard() {
4148
const dispatch = useDispatch();
4249
const apiKeys = useSelector(getApiKeys);
4350
const [modalVisible, setModalVisible] = useState(false);
51+
const [newApiKey, setNewApiKey] = useState<ApiKeyType>();
4452

4553
const handleCopy = (value: string) => {
4654
navigator.clipboard.writeText(value).then(() => {
@@ -66,13 +74,11 @@ export default function UserApiKeysCard() {
6674
{trans("profile.createApiKey")}
6775
</CreateButton>
6876
</Flex>
77+
{Boolean(newApiKey) && <Alert message={trans("profile.apiKeyInfo")} type="info" style={{marginBottom: '16px'}}/>}
6978
<TableStyled
7079
tableLayout={"auto"}
7180
scroll={{ x: "100%" }}
7281
pagination={false}
73-
onRow={(record) => ({
74-
75-
})}
7682
columns={[
7783
{
7884
title: trans("profile.apiKeyName"),
@@ -95,16 +101,19 @@ export default function UserApiKeysCard() {
95101
title: trans("profile.apiKey"),
96102
dataIndex: "token",
97103
ellipsis: true,
98-
render: (value: string) => {
99-
const startToken = value.substring(0, 6);
100-
const endToken = value.substring(value.length - 6);
101-
return (
102-
<Tooltip placement="topLeft" title={ trans("profile.apiKeyCopy")}>
103-
<div onClick={() => handleCopy(value)} style={{ cursor: 'pointer' }}>
104-
{`${startToken}********************${endToken}`}
105-
</div>
106-
</Tooltip>
107-
)
104+
render: (value: string, record: any) => {
105+
if (newApiKey?.id === record.id) {
106+
return (
107+
<Tooltip placement="topLeft" title={ trans("profile.apiKeyCopy")}>
108+
<div onClick={() => handleCopy(newApiKey?.token!)} style={{ cursor: 'pointer' }}>
109+
{value}
110+
&nbsp;
111+
<CopyOutlined />
112+
</div>
113+
</Tooltip>
114+
)
115+
}
116+
return <div>{value}</div>
108117
}
109118
},
110119
{ title: " ", dataIndex: "operation", width: "208px" },
@@ -145,8 +154,9 @@ export default function UserApiKeysCard() {
145154
<CreateApiKeyModal
146155
modalVisible={modalVisible}
147156
closeModal={() => setModalVisible(false)}
148-
onConfigCreate={() => {
157+
onConfigCreate={(apiKey?: ApiKeyType) => {
149158
setModalVisible(false);
159+
setNewApiKey(apiKey);
150160
dispatch(fetchApiKeysAction());
151161
}}
152162
/>

‎client/packages/lowcoder/src/pages/common/inviteDialog.tsx

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { HelpText } from "components/HelpText";
1212
import copyToClipboard from "copy-to-clipboard";
1313
import { trans } from "i18n";
1414
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
15+
import Divider from "antd/es/divider";
16+
import Flex from "antd/es/flex";
17+
import Select from "antd/es/select";
1518

1619
const InviteButton = styled(TacoButton)`
1720
width: 76px;
@@ -23,14 +26,36 @@ const StyledLoading = styled(WhiteLoading)`
2326
height: 170px;
2427
`;
2528

26-
function InviteContent(props: { inviteInfo: InviteInfo }) {
27-
const { inviteInfo } = props;
29+
function InviteContent(props: { inviteInfo: InviteInfo, onClose?: () => void }) {
30+
const { inviteInfo, onClose } = props;
2831
const inviteLink = genInviteLink(inviteInfo?.inviteCode);
2932
const inviteText = trans("memberSettings.inviteText", {
3033
userName: inviteInfo.createUserName,
3134
organization: inviteInfo.invitedOrganizationName,
3235
inviteLink,
3336
});
37+
const [emails, setEmails] = useState<string[]>([]);
38+
39+
const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
40+
41+
const sendInvitations = async () => {
42+
const filteredEmails = emails.filter(isValidEmail);
43+
if (!filteredEmails.length) {
44+
return messageInstance.error(trans("memberSettings.noValidEmails"));
45+
}
46+
try {
47+
const resp = await InviteApi.sendInvitations({emails: filteredEmails, orgId: inviteInfo.invitedOrganizationId})
48+
if (validateResponse(resp) && resp.data.success) {
49+
messageInstance.success(trans('membersSettings.inviteByEmailSuccess'));
50+
onClose?.();
51+
return;
52+
}
53+
throw new Error(trans('membersSettings.inviteByEmailError'));
54+
} catch(e: any) {
55+
messageInstance.error(e.message);
56+
}
57+
}
58+
3459
return (
3560
<>
3661
<HelpText style={{ marginBottom: 16 }}>{trans("memberSettings.inviteUserHelp")}</HelpText>
@@ -48,6 +73,34 @@ function InviteContent(props: { inviteInfo: InviteInfo }) {
4873
{trans("memberSettings.inviteCopyLink")}
4974
</InviteButton>
5075
</div>
76+
<Divider style={{marginTop: '60px'}}/>
77+
<HelpText style={{ marginBottom: 16 }}>{trans("memberSettings.inviteByEmailHelp")}</HelpText>
78+
<CommonTextLabel>{trans("memberSettings.inviteByEmailLabel")}</CommonTextLabel>
79+
<Select
80+
mode="tags"
81+
allowClear
82+
open={false}
83+
style={{ width: '100%', marginTop: '8px', marginBottom: '8px' }}
84+
placeholder="Enter emails"
85+
defaultValue={[]}
86+
onChange={(value) => {
87+
setEmails(value);
88+
}}
89+
options={[]}
90+
showSearch={false}
91+
suffixIcon={''}
92+
/>
93+
<Flex justify="end">
94+
<TacoButton
95+
buttonType="primary"
96+
onClick={() => {
97+
sendInvitations();
98+
}}
99+
disabled={!Boolean(emails?.length)}
100+
>
101+
{trans("memberSettings.inviteByEmailButton")}
102+
</TacoButton>
103+
</Flex>
51104
</>
52105
);
53106
}
@@ -101,7 +154,7 @@ function InviteDialog(props: {
101154
showCancelButton={false}
102155
width="440px"
103156
>
104-
{!inviteInfo ? <StyledLoading size={20} /> : <InviteContent inviteInfo={inviteInfo} />}
157+
{!inviteInfo ? <StyledLoading size={20} /> : <InviteContent inviteInfo={inviteInfo} onClose={() => setInviteDialogVisible(false)} />}
105158
</CustomModal>
106159
</>
107160
);

‎client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default function FormLoginSteps(props: FormLoginProps) {
127127
const isEmailLoginEnabled = useMemo(() => {
128128
return isFormLoginEnabled && signinEnabled;
129129
}, [isFormLoginEnabled, signinEnabled]);
130-
130+
131131
const isEnterpriseMode = useMemo(() => {
132132
return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE";
133133
}, [serverSettings]);
@@ -204,7 +204,7 @@ export default function FormLoginSteps(props: FormLoginProps) {
204204
}
205205
if (resp.data.length === 1) {
206206
// in Enterprise mode, we will get org data in different format
207-
const selectedOrgId = isEnterpriseMode ? resp.data[0].id : resp.data[0].orgId;
207+
const selectedOrgId = resp.data[0]?.id || resp.data[0]?.orgId;
208208
setOrganizationId(selectedOrgId);
209209
dispatch(fetchConfigAction(selectedOrgId));
210210
setCurrentStep(CurrentStepEnum.AUTH_PROVIDERS);
@@ -239,7 +239,7 @@ export default function FormLoginSteps(props: FormLoginProps) {
239239
invitedOrganizationId={organizationId}
240240
authGoal="login"
241241
/>
242-
{signupEnabled && (
242+
{(isEmailLoginEnabled && signupEnabled) && (
243243
<>
244244
<Divider/>
245245
<AuthBottomView>

‎client/packages/lowcoder/src/pages/userAuth/register.tsx

Lines changed: 95 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import { validateResponse } from "@lowcoder-ee/api/apiUtils";
2626
import history from "util/history";
2727
import LoadingOutlined from "@ant-design/icons/LoadingOutlined";
2828
import Spin from "antd/es/spin";
29-
import { useSelector } from "react-redux";
29+
import { useDispatch, useSelector } from "react-redux";
3030
import { getServerSettings } from "@lowcoder-ee/redux/selectors/applicationSelector";
31+
import { fetchConfigAction } from "@lowcoder-ee/redux/reduxActions/configActions";
32+
import { fetchOrgPaginationByEmail } from "@lowcoder-ee/util/pagination/axios";
3133

3234
const StyledFormInput = styled(FormInput)`
3335
margin-bottom: 16px;
@@ -45,6 +47,7 @@ const RegisterContent = styled(FormWrapperMobile)`
4547

4648
function UserRegister() {
4749
const location = useLocation();
50+
const dispatch = useDispatch();
4851
const [submitBtnDisable, setSubmitBtnDisable] = useState(false);
4952
const [account, setAccount] = useState(() => {
5053
const { email } = (location.state || {}) as any;
@@ -53,26 +56,67 @@ function UserRegister() {
5356
const [password, setPassword] = useState("");
5457
const [orgLoading, setOrgLoading] = useState(false);
5558
const [lastEmailChecked, setLastEmailChecked] = useState("");
59+
const [signupEnabled, setSignupEnabled] = useState<boolean>(true);
60+
const [signinEnabled, setSigninEnabled] = useState<boolean>(true);
61+
const [defaultOrgId, setDefaultOrgId] = useState<string|undefined>();
5662
const redirectUrl = useRedirectUrl();
63+
const serverSettings = useSelector(getServerSettings);
5764
const { systemConfig, inviteInfo, fetchUserAfterAuthSuccess } = useContext(AuthContext);
5865
const invitationId = inviteInfo?.invitationId;
59-
66+
const isFormLoginEnabled = systemConfig ? systemConfig?.form.enableLogin : true;
67+
const authId = systemConfig?.form.id;
6068
const orgId = useParams<any>().orgId;
69+
6170
const organizationId = useMemo(() => {
6271
if(inviteInfo?.invitedOrganizationId) {
6372
return inviteInfo?.invitedOrganizationId;
6473
}
65-
return orgId;
66-
}, [ inviteInfo, orgId ]);
67-
68-
const authId = systemConfig?.form.id;
74+
if (orgId) {
75+
return orgId;
76+
}
77+
return defaultOrgId;
78+
}, [ inviteInfo, orgId, defaultOrgId ]);
6979

70-
const serverSettings = useSelector(getServerSettings);
80+
const isEmailLoginEnabled = useMemo(() => {
81+
return isFormLoginEnabled && signinEnabled;
82+
}, [isFormLoginEnabled, signinEnabled]);
7183

7284
const isEnterpriseMode = useMemo(() => {
7385
return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE";
7486
}, [serverSettings]);
7587

88+
useEffect(() => {
89+
const {
90+
LOWCODER_EMAIL_SIGNUP_ENABLED,
91+
LOWCODER_EMAIL_AUTH_ENABLED,
92+
} = serverSettings;
93+
94+
setSignupEnabled(LOWCODER_EMAIL_SIGNUP_ENABLED === 'true');
95+
setSigninEnabled(LOWCODER_EMAIL_AUTH_ENABLED === 'true');
96+
}, [serverSettings]);
97+
98+
useEffect(() => {
99+
if (isEnterpriseMode) {
100+
// dispatch(fetchConfigAction());
101+
fetchOrgPaginationByEmail({
102+
email: ' ',
103+
pageNum: 1,
104+
pageSize: 10,
105+
})
106+
.then((resp) => {
107+
if (resp.success) {
108+
const orgList = resp.data || [];
109+
if (orgList.length) {
110+
// in Enterprise mode, we will get org data in different format
111+
const selectedOrgId = orgList[0]?.id || orgList[0]?.orgId;
112+
setDefaultOrgId(selectedOrgId);
113+
dispatch(fetchConfigAction(selectedOrgId));
114+
}
115+
}
116+
})
117+
}
118+
}, [isEnterpriseMode]);
119+
76120
useEffect(() => {
77121
const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings;
78122
if(
@@ -143,34 +187,38 @@ function UserRegister() {
143187
type="large"
144188
>
145189
<RegisterContent>
146-
<StyledFormInput
147-
className="form-input"
148-
label={trans("userAuth.email")}
149-
defaultValue={account}
150-
onChange={(value, valid) => setAccount(valid ? value : "")}
151-
onBlur={checkEmailExist}
152-
placeholder={trans("userAuth.inputEmail")}
153-
checkRule={{
154-
check: checkEmailValid,
155-
errorMsg: trans("userAuth.inputValidEmail"),
156-
}}
157-
/>
158-
<StyledPasswordInput
159-
className="form-input"
160-
passInputConf={{label:trans("password.label"), placeholder: trans("password.placeholder")}}
161-
confirmPassConf={{label:trans("password.conformLabel"), placeholder: trans("password.conformPlaceholder")}}
162-
valueCheck={checkPassWithMsg}
163-
onChange={(value, valid) => setPassword(valid ? value : "")}
164-
doubleCheck
165-
/>
166-
<ConfirmButton
167-
disabled={!account || !password || submitBtnDisable}
168-
onClick={onSubmit}
169-
loading={loading}
170-
>
171-
{trans("userAuth.register")}
172-
</ConfirmButton>
173-
<TermsAndPrivacyInfo onCheckChange={(e) => setSubmitBtnDisable(!e.target.checked)} />
190+
{ isEmailLoginEnabled && (
191+
<>
192+
<StyledFormInput
193+
className="form-input"
194+
label={trans("userAuth.email")}
195+
defaultValue={account}
196+
onChange={(value, valid) => setAccount(valid ? value : "")}
197+
onBlur={checkEmailExist}
198+
placeholder={trans("userAuth.inputEmail")}
199+
checkRule={{
200+
check: checkEmailValid,
201+
errorMsg: trans("userAuth.inputValidEmail"),
202+
}}
203+
/>
204+
<StyledPasswordInput
205+
className="form-input"
206+
passInputConf={{label:trans("password.label"), placeholder: trans("password.placeholder")}}
207+
confirmPassConf={{label:trans("password.conformLabel"), placeholder: trans("password.conformPlaceholder")}}
208+
valueCheck={checkPassWithMsg}
209+
onChange={(value, valid) => setPassword(valid ? value : "")}
210+
doubleCheck
211+
/>
212+
<ConfirmButton
213+
disabled={!account || !password || submitBtnDisable}
214+
onClick={onSubmit}
215+
loading={loading}
216+
>
217+
{trans("userAuth.register")}
218+
</ConfirmButton>
219+
<TermsAndPrivacyInfo onCheckChange={(e) => setSubmitBtnDisable(!e.target.checked)} />
220+
</>
221+
)}
174222
{(organizationId || isEnterpriseMode) && (
175223
<ThirdPartyAuth
176224
invitationId={invitationId}
@@ -179,14 +227,18 @@ function UserRegister() {
179227
/>
180228
)}
181229
</RegisterContent>
182-
<Divider/>
183-
<StyledRouteLinkLogin to={{
184-
pathname: orgId
185-
? ORG_AUTH_LOGIN_URL.replace(':orgId', orgId)
186-
: AUTH_LOGIN_URL,
187-
state: location.state
188-
}}>{trans("userAuth.userLogin")}
189-
</StyledRouteLinkLogin>
230+
{isEmailLoginEnabled && (
231+
<>
232+
<Divider/>
233+
<StyledRouteLinkLogin to={{
234+
pathname: orgId
235+
? ORG_AUTH_LOGIN_URL.replace(':orgId', orgId)
236+
: AUTH_LOGIN_URL,
237+
state: location.state
238+
}}>{trans("userAuth.userLogin")}
239+
</StyledRouteLinkLogin>
240+
</>
241+
)}
190242
</AuthContainer>
191243
</Spin>
192244
);

‎client/packages/lowcoder/src/pages/userAuth/thirdParty/thirdPartyAuth.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
} from "constants/authConstants";
66
import { WhiteLoading } from "lowcoder-design";
77
import history from "util/history";
8-
import { LoginLogoStyle, LoginLabelStyle, StyledLoginButton } from "pages/userAuth/authComponents";
8+
import { LoginLogoStyle, LoginLabelStyle, StyledLoginButton, TermsAndPrivacyInfo } from "pages/userAuth/authComponents";
99
import { useSelector } from "react-redux";
1010
import { getSystemConfigFetching, selectSystemConfig } from "redux/selectors/configSelectors";
11-
import React, { useMemo } from "react";
11+
import React, { useMemo, useState } from "react";
1212
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
1313
import styled from "styled-components";
1414
import { trans } from "i18n";
@@ -40,6 +40,7 @@ function ThirdPartyLoginButton(props: {
4040
autoJump?: boolean;
4141
authGoal: ThirdPartyAuthGoal;
4242
label: string;
43+
disabled?: boolean;
4344
}) {
4445
const { config, label } = props;
4546
const loginRedirectUrl = useRedirectUrl();
@@ -93,7 +94,7 @@ function ThirdPartyLoginButton(props: {
9394
: `Sign in with ${label}`;
9495

9596
return (
96-
<StyledLoginButton buttonType="normal" onClick={onLoginClick}>
97+
<StyledLoginButton buttonType="normal" onClick={onLoginClick} disabled={props.disabled}>
9798
{config.icon && <MultiIconDisplay identifier={config.icon} width="20px" height="20px" style={{ marginRight: "20px", flexShrink: 0, color: "#000" }} />}
9899
{!config.icon && <LoginLogoStyle alt={config.name} src={config.logo} title={config.name} />}
99100
<LoginLabelStyle className="auth-label">
@@ -114,6 +115,7 @@ export function ThirdPartyAuth(props: {
114115
const systemConfig = useSelector(selectSystemConfig);
115116
const serverSettings = useSelector(getServerSettings);
116117
const isFormLoginEnabled = systemConfig?.form.enableLogin;
118+
const [disableButtons, setDisableButtons] = useState(false);
117119

118120
const isEmailLoginEnabled = useMemo(() => {
119121
return isFormLoginEnabled && serverSettings.LOWCODER_EMAIL_AUTH_ENABLED === 'true';
@@ -145,20 +147,24 @@ export function ThirdPartyAuth(props: {
145147
invitationId={props.invitationId}
146148
invitedOrganizationId={props.invitedOrganizationId}
147149
label={props.labelFormatter ? props.labelFormatter(config.name) : config.name}
150+
disabled={disableButtons}
148151
/>
149152
);
150153
});
151154
return (
152155
<ThirdPartyLoginButtonWrapper>
153156
{ (
154157
(isEmailLoginEnabled && props.authGoal === 'login')
155-
|| (isEmailSignupEnabled && props.authGoal === 'register')
158+
|| (isEmailLoginEnabled && isEmailSignupEnabled && props.authGoal === 'register')
156159
) && Boolean(socialLoginButtons.length) && (
157160
<Divider plain>
158161
<Text type="secondary">or</Text>
159162
</Divider>
160163
)}
161164
{socialLoginButtons}
165+
{!isFormLoginEnabled && (
166+
<TermsAndPrivacyInfo onCheckChange={(e) => setDisableButtons(!e.target.checked)} />
167+
)}
162168
</ThirdPartyLoginButtonWrapper>
163169
);
164170
}

0 commit comments

Comments
 (0)
Please sign in to comment.