mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 04:28:33 +00:00
Resolve merge conflicts
This commit is contained in:
@@ -2,10 +2,13 @@ import React, { useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { usePopUp } from '@app/hooks/usePopUp';
|
||||
import addUserToOrg from '@app/pages/api/organization/addUserToOrg';
|
||||
import getWorkspaces from '@app/pages/api/workspace/getWorkspaces';
|
||||
|
||||
import Button from '../basic/buttons/Button';
|
||||
import { EmailServiceSetupModal } from '../v2';
|
||||
|
||||
/**
|
||||
* This is the last step of the signup flow. People can optionally invite their teammates here.
|
||||
@@ -14,6 +17,10 @@ export default function TeamInviteStep(): JSX.Element {
|
||||
const [emails, setEmails] = useState('');
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||
'setUpEmail'
|
||||
] as const);
|
||||
|
||||
// Redirect user to the getting started page
|
||||
const redirectToHome = async () => {
|
||||
@@ -62,10 +69,20 @@ export default function TeamInviteStep(): JSX.Element {
|
||||
</div>
|
||||
<Button
|
||||
text={t('signup:step5-send-invites') ?? ''}
|
||||
onButtonPressed={() => inviteUsers({ emails })}
|
||||
onButtonPressed={() => {
|
||||
if(serverDetails?.emailConfigured){
|
||||
inviteUsers({ emails })
|
||||
}else{
|
||||
handlePopUpOpen('setUpEmail');
|
||||
}
|
||||
}}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ const POSTHOG_HOST =
|
||||
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
|
||||
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
|
||||
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
|
||||
const SITE_URL = "https://app.infisical.com";
|
||||
|
||||
export {
|
||||
ENV,
|
||||
POSTHOG_API_KEY,
|
||||
POSTHOG_HOST,
|
||||
SITE_URL,
|
||||
STRIPE_PRODUCT_PRO,
|
||||
STRIPE_PRODUCT_STARTER
|
||||
};
|
||||
@@ -1,7 +1,5 @@
|
||||
import { jsPDF } from 'jspdf';
|
||||
|
||||
import { SITE_URL } from './config';
|
||||
|
||||
interface PDFProps {
|
||||
personalName: string;
|
||||
personalEmail: string;
|
||||
@@ -19,7 +17,7 @@ const yyyy = today.getFullYear();
|
||||
const todayFormatted = `${mm}/${dd}/${yyyy}`;
|
||||
|
||||
|
||||
function createPdfHeader(doc: jsPDF, personalName : string) {
|
||||
function createPdfHeader(doc: jsPDF, personalName: string) {
|
||||
doc.setFillColor(255, 255, 255);
|
||||
doc.rect(0, 0, 600, 900, 'F');
|
||||
doc.setTextColor(23, 23, 23);
|
||||
@@ -30,6 +28,10 @@ function createPdfHeader(doc: jsPDF, personalName : string) {
|
||||
}
|
||||
|
||||
function createPdfContent(doc: jsPDF, personalEmail: string, generatedKey: string) {
|
||||
const { protocol, hostname, port } = window.location;
|
||||
const portSuffix = port && port !== '80' ? `:${port}` : '';
|
||||
const siteURL = `${protocol}//${hostname}${portSuffix}`;
|
||||
|
||||
doc.setFontSize(14);
|
||||
doc.text(
|
||||
'In case you get locked out of you Infisical account, you`ll need these account details',
|
||||
@@ -73,7 +75,7 @@ function createPdfContent(doc: jsPDF, personalEmail: string, generatedKey: strin
|
||||
doc.roundedRect(170, 488, 375, 35, 5, 5, 'F');
|
||||
doc.setTextColor(23, 23, 23);
|
||||
doc.setFontSize(14);
|
||||
doc.text(`${SITE_URL}/login`, 180, 420);
|
||||
doc.text(`${siteURL}/login`, 180, 420);
|
||||
doc.text(personalEmail, 180, 465);
|
||||
doc.text(generatedKey, 180, 510);
|
||||
doc.text('Need help? Contact us at [email protected]', 32, 575);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Button } from '../Button';
|
||||
import { Modal, ModalContent } from '../Modal';
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
};
|
||||
|
||||
export const EmailServiceSetupModal = ({ isOpen, onOpenChange }: Props): JSX.Element => (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent title="Email service not configured">
|
||||
<p className="mb-4 text-bunker-300">
|
||||
The administrators of this Infisical instance have not yet set up an email service provider required to perform this action
|
||||
</p>
|
||||
|
||||
<a href="https://infisical.com/docs/self-hosting/configuration/email">
|
||||
<Button className="mr-4">Learn more</Button>
|
||||
</a>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export { EmailServiceSetupModal } from './EmailServiceSetupModal';
|
||||
@@ -3,6 +3,7 @@ export * from './Card';
|
||||
export * from './Checkbox';
|
||||
export * from './DeleteActionModal';
|
||||
export * from './Dropdown';
|
||||
export * from './EmailServiceSetupModal'
|
||||
export * from './EmptyState';
|
||||
export * from './FormControl';
|
||||
export * from './IconButton';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { useFetchServerStatus } from './queries'
|
||||
@@ -0,0 +1,19 @@
|
||||
import {useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { apiRequest } from '@app/config/request';
|
||||
|
||||
import { ServerStatus } from './types';
|
||||
|
||||
// cache key
|
||||
const serverStatusKeys = {
|
||||
serverStatus: ['serverStatus'] as const
|
||||
};
|
||||
|
||||
const fetchServerStatus = async () => {
|
||||
const {data} = await apiRequest.get<ServerStatus>('/api/status');
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useFetchServerStatus= () => {
|
||||
return useQuery({ queryKey: serverStatusKeys.serverStatus, queryFn: fetchServerStatus });
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export type ServerStatus = {
|
||||
date: string;
|
||||
message: string;
|
||||
emailConfigured: boolean;
|
||||
};
|
||||
@@ -83,8 +83,14 @@ export const useAddUserToWs = () => {
|
||||
|
||||
export const useAddUserToOrg = () => {
|
||||
const queryClient = useQueryClient();
|
||||
type Response = {
|
||||
data: {
|
||||
message: string,
|
||||
completeInviteLink: string | undefined
|
||||
}
|
||||
}
|
||||
|
||||
return useMutation<{}, {}, AddUserToOrgDTO>({
|
||||
return useMutation<Response, {}, AddUserToOrgDTO>({
|
||||
mutationFn: (dto) => apiRequest.post(`/api/v1/invite-org/signup`, dto),
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(userKeys.getOrgUsers(organizationId));
|
||||
|
||||
@@ -13,6 +13,7 @@ import TeamInviteStep from '@app/components/signup/TeamInviteStep';
|
||||
import UserInfoStep from '@app/components/signup/UserInfoStep';
|
||||
import SecurityClient from '@app/components/utilities/SecurityClient';
|
||||
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
|
||||
import checkEmailVerificationCode from './api/auth/CheckEmailVerificationCode';
|
||||
import getWorkspaces from './api/workspace/getWorkspaces';
|
||||
@@ -25,10 +26,12 @@ export default function SignUp() {
|
||||
const [password, setPassword] = useState('');
|
||||
const [firstName, setFirstName] = useState('');
|
||||
const [lastName, setLastName] = useState('');
|
||||
const [code, setCode] = useState('');
|
||||
const [code, setCode] = useState('123456');
|
||||
const [codeError, setCodeError] = useState(false);
|
||||
const [step, setStep] = useState(1);
|
||||
const router = useRouter();
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -68,6 +71,19 @@ export default function SignUp() {
|
||||
}
|
||||
};
|
||||
|
||||
// when email service is not configured, skip step 2 and 5
|
||||
useEffect(() => {
|
||||
if (!serverDetails?.emailConfigured && step === 2){
|
||||
incrementStep()
|
||||
}
|
||||
|
||||
if (!serverDetails?.emailConfigured && step === 5){
|
||||
getWorkspaces().then((userWorkspaces)=>{
|
||||
router.push(`/dashboard/${userWorkspaces[0]._id}`);
|
||||
});
|
||||
}
|
||||
}, [step]);
|
||||
|
||||
return (
|
||||
<div className="bg-bunker-800 h-screen flex flex-col items-center justify-center">
|
||||
<Head>
|
||||
@@ -111,9 +127,7 @@ export default function SignUp() {
|
||||
password={password}
|
||||
name={`${firstName} ${lastName}`}
|
||||
/>
|
||||
) : (
|
||||
<TeamInviteStep />
|
||||
)}
|
||||
) : (serverDetails?.emailConfigured ? <TeamInviteStep /> : "")}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,19 @@ import Link from 'next/link';
|
||||
import Button from '@app/components/basic/buttons/Button';
|
||||
import InputField from '@app/components/basic/InputField';
|
||||
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||
import { EmailServiceSetupModal } from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
|
||||
import SendEmailOnPasswordReset from './api/auth/SendEmailOnPasswordReset';
|
||||
|
||||
export default function VerifyEmail() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [step, setStep] = useState(1);
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||
'setUpEmail'
|
||||
] as const);
|
||||
|
||||
/**
|
||||
* This function sends the verification email and forwards a user to the next step.
|
||||
@@ -63,7 +70,13 @@ export default function VerifyEmail() {
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center w-full md:p-2 max-h-20 max-w-md mt-4 mx-auto text-sm">
|
||||
<div className="text-l mt-6 m-8 px-8 py-3 text-lg">
|
||||
<Button text="Continue" onButtonPressed={sendVerificationEmail} size="lg" />
|
||||
<Button text="Continue" onButtonPressed={()=>{
|
||||
if (serverDetails?.emailConfigured){
|
||||
sendVerificationEmail()
|
||||
} else {
|
||||
handlePopUpOpen('setUpEmail');
|
||||
}
|
||||
}} size="lg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,6 +93,11 @@ export default function VerifyEmail() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { plans } from 'public/data/frequentConstants';
|
||||
|
||||
@@ -55,6 +56,8 @@ export const OrgSettingsPage = () => {
|
||||
const addIncidentContact = useAddIncidentContact();
|
||||
const removeIncidentContact = useDeleteIncidentContact();
|
||||
|
||||
const [completeInviteLink, setcompleteInviteLink] = useState<string|undefined>("")
|
||||
|
||||
const isMoreUsersNotAllowed =
|
||||
(orgUsers || []).length >= 5 &&
|
||||
subscriptionPlan === plans.starter &&
|
||||
@@ -100,11 +103,16 @@ export const OrgSettingsPage = () => {
|
||||
if (!currentOrg?._id) return;
|
||||
|
||||
try {
|
||||
await addUserToOrg.mutateAsync({ organizationId: currentOrg?._id, inviteeEmail: email });
|
||||
createNotification({
|
||||
text: 'Successfully invited user to the organization.',
|
||||
type: 'success'
|
||||
});
|
||||
const {data} = await addUserToOrg.mutateAsync({ organizationId: currentOrg?._id, inviteeEmail: email });
|
||||
setcompleteInviteLink(data?.completeInviteLink)
|
||||
|
||||
// only show this notification when email is configured. A [completeInviteLink] will not be sent if smtp is configured
|
||||
if (!data.completeInviteLink){
|
||||
createNotification({
|
||||
text: 'Successfully invited user to the organization.',
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
@@ -249,6 +257,8 @@ export const OrgSettingsPage = () => {
|
||||
onRemoveMember={onRemoveUserOrgMembership}
|
||||
onRoleChange={onUpdateOrgUserRole}
|
||||
onGrantAccess={onGrantUserAccess}
|
||||
completeInviteLink={completeInviteLink}
|
||||
setCompleteInviteLink={setcompleteInviteLink}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-6 mt-2 w-full rounded-md bg-white/5 p-6">
|
||||
|
||||
+16
-2
@@ -13,6 +13,7 @@ import * as yup from 'yup';
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
EmailServiceSetupModal,
|
||||
EmptyState,
|
||||
FormControl,
|
||||
IconButton,
|
||||
@@ -29,6 +30,7 @@ import {
|
||||
Tr
|
||||
} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { IncidentContact } from '@app/hooks/api/types';
|
||||
|
||||
type Props = {
|
||||
@@ -51,9 +53,11 @@ export const OrgIncidentContactsTable = ({
|
||||
isLoading
|
||||
}: Props) => {
|
||||
const [searchContact, setSearchContact] = useState('');
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addContact',
|
||||
'removeContact'
|
||||
'removeContact',
|
||||
'setUpEmail'
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -93,7 +97,13 @@ export const OrgIncidentContactsTable = ({
|
||||
<div>
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen('addContact')}
|
||||
onClick={() => {
|
||||
if (serverDetails?.emailConfigured){
|
||||
handlePopUpOpen('addContact');
|
||||
} else {
|
||||
handlePopUpOpen('setUpEmail');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Add Contact
|
||||
</Button>
|
||||
@@ -181,6 +191,10 @@ export const OrgIncidentContactsTable = ({
|
||||
onChange={(isOpen) => handlePopUpToggle('removeContact', isOpen)}
|
||||
onDeleteApproved={onRemoveIncidentContact}
|
||||
/>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+53
-16
@@ -1,7 +1,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCheck, faCopy, faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
@@ -9,7 +9,7 @@ import * as yup from 'yup';
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
EmptyState,
|
||||
EmailServiceSetupModal, EmptyState,
|
||||
FormControl,
|
||||
IconButton,
|
||||
Input,
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
Tr,
|
||||
UpgradePlanModal} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { OrgUser, Workspace } from '@app/hooks/api/types';
|
||||
|
||||
type Props = {
|
||||
@@ -42,6 +43,8 @@ type Props = {
|
||||
onGrantAccess: (userId: string, publicKey: string) => Promise<void>;
|
||||
// the current user id to block remove org button
|
||||
userId: string;
|
||||
completeInviteLink: string | undefined,
|
||||
setCompleteInviteLink: Dispatch<SetStateAction<string | undefined>>
|
||||
};
|
||||
|
||||
const addMemberFormSchema = yup.object({
|
||||
@@ -60,14 +63,18 @@ export const OrgMembersTable = ({
|
||||
onGrantAccess,
|
||||
onRoleChange,
|
||||
userId,
|
||||
isLoading
|
||||
isLoading,
|
||||
completeInviteLink,
|
||||
setCompleteInviteLink
|
||||
}: Props) => {
|
||||
const router = useRouter();
|
||||
const [searchMemberFilter, setSearchMemberFilter] = useState('');
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'addMember',
|
||||
'removeMember',
|
||||
'upgradePlan'
|
||||
'upgradePlan',
|
||||
'setUpEmail'
|
||||
] as const);
|
||||
|
||||
const {
|
||||
@@ -79,8 +86,11 @@ export const OrgMembersTable = ({
|
||||
|
||||
const onAddMember = async ({ email }: TAddMemberForm) => {
|
||||
await onInviteMember(email);
|
||||
handlePopUpClose('addMember');
|
||||
reset();
|
||||
if (serverDetails?.emailConfigured){
|
||||
handlePopUpClose('addMember');
|
||||
}
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
const onRemoveOrgMemberApproved = async () => {
|
||||
@@ -106,6 +116,11 @@ export const OrgMembersTable = ({
|
||||
[members, searchMemberFilter]
|
||||
);
|
||||
|
||||
const copyTokenToClipboard = () => {
|
||||
navigator.clipboard.writeText(completeInviteLink as string);
|
||||
// setIsTokenCopied.on();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-4 flex">
|
||||
@@ -171,7 +186,7 @@ export const OrgMembersTable = ({
|
||||
<SelectItem value="member">member</SelectItem>
|
||||
</Select>
|
||||
)}
|
||||
{(status === 'invited' || status === 'verified') && (
|
||||
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && (
|
||||
<Button className='w-40' colorSchema="secondary" onClick={() => onInviteMember(email)}>
|
||||
Resend Invite
|
||||
</Button>
|
||||
@@ -232,20 +247,23 @@ export const OrgMembersTable = ({
|
||||
isOpen={popUp?.addMember?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle('addMember', isOpen);
|
||||
reset();
|
||||
setCompleteInviteLink(undefined)
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title={`Invite others to ${orgName}`}
|
||||
subTitle={
|
||||
<>
|
||||
An invite is specific to an email address and expires after 1 day.
|
||||
<br />
|
||||
For security reasons, you will need to separately add members to projects.
|
||||
</>
|
||||
<div>
|
||||
{!completeInviteLink && <div>
|
||||
An invite is specific to an email address and expires after 1 day.
|
||||
<br />
|
||||
For security reasons, you will need to separately add members to projects.
|
||||
</div>}
|
||||
{completeInviteLink && "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<form onSubmit={handleSubmit(onAddMember)}>
|
||||
{!completeInviteLink && <form onSubmit={handleSubmit(onAddMember)} >
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
@@ -274,7 +292,22 @@ export const OrgMembersTable = ({
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</form>}
|
||||
{
|
||||
completeInviteLink &&
|
||||
<div className="mt-2 mb-3 mr-2 flex items-center justify-end rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
|
||||
<p className="mr-4 break-all">{completeInviteLink}</p>
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
colorSchema="secondary"
|
||||
className="group relative"
|
||||
onClick={copyTokenToClipboard}
|
||||
>
|
||||
<FontAwesomeIcon icon={false ? faCheck : faCopy} />
|
||||
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">click to copy</span>
|
||||
</IconButton>
|
||||
</div>
|
||||
}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<DeleteActionModal
|
||||
@@ -289,6 +322,10 @@ export const OrgMembersTable = ({
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||
/>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+19
-2
@@ -1,7 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { Checkbox } from '@app/components/v2';
|
||||
import { Checkbox, EmailServiceSetupModal } from '@app/components/v2';
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||
import { usePopUp } from '@app/hooks/usePopUp';
|
||||
|
||||
import { useGetUser } from '../../../../hooks/api';
|
||||
import { User } from '../../../../hooks/api/types';
|
||||
@@ -11,6 +13,11 @@ export const SecuritySection = () => {
|
||||
const [isMfaEnabled, setIsMfaEnabled] = useState(false);
|
||||
const { data: user } = useGetUser();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||
'setUpEmail'
|
||||
] as const);
|
||||
|
||||
const {data: serverDetails } = useFetchServerStatus()
|
||||
|
||||
useEffect(() => {
|
||||
if (user && typeof user.isMfaEnabled !== 'undefined') {
|
||||
@@ -42,6 +49,7 @@ export const SecuritySection = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<form>
|
||||
<div className="mb-6 mt-2 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-2">
|
||||
<p className="mb-4 mt-2 text-xl font-semibold">
|
||||
@@ -52,12 +60,21 @@ export const SecuritySection = () => {
|
||||
id="isTwoFAEnabled"
|
||||
isChecked={isMfaEnabled}
|
||||
onCheckedChange={(state) => {
|
||||
toggleMfa(state as boolean);
|
||||
if (serverDetails?.emailConfigured){
|
||||
toggleMfa(state as boolean);
|
||||
} else {
|
||||
handlePopUpOpen('setUpEmail');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Enable 2-factor authentication via your personal email.
|
||||
</Checkbox>
|
||||
</div>
|
||||
</form>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user