Finish preliminary email validation, merge user flow w saml/ldap

This commit is contained in:
Tuan Dang
2024-04-26 20:19:43 -07:00
parent d0cb06d875
commit 858a35812a
28 changed files with 225 additions and 104 deletions
@@ -6,11 +6,11 @@ import ReactCodeInput from "react-code-input";
import Error from "@app/components/basic/Error";
import { createNotification } from "@app/components/notifications";
import { Button } from "@app/components/v2";
import { useUser } from "@app/context";
import {
fetchUsersWithMyEmail,
useSendEmailVerificationCode,
useVerifyEmailVerificationCode} from "@app/hooks/api";
useVerifyEmailVerificationCode
} from "@app/hooks/api";
type Props = {
email: string;
@@ -56,7 +56,6 @@ const propsPhone = {
} as const;
export const EmailConfirmationStep = ({ email, setStep }: Props) => {
const { user } = useUser();
const [code, setCode] = useState("");
const [codeError, setCodeError] = useState(false);
const [isResendingVerificationEmail] = useState(false);
@@ -106,9 +105,6 @@ export const EmailConfirmationStep = ({ email, setStep }: Props) => {
<p className="text-md flex justify-center text-bunker-200">
We&apos;ve sent a verification code to {email}
</p>
<p className="text-md my-1 flex justify-center font-semibold text-bunker-200">
{user?.email}
</p>
<div className="mx-auto hidden w-max min-w-[20rem] md:block">
<ReactCodeInput
name=""
@@ -1,3 +1,4 @@
import { useState } from "react";
import { useRouter } from "next/router";
import { faUsers } from "@fortawesome/free-solid-svg-icons";
@@ -5,6 +6,8 @@ import { createNotification } from "@app/components/notifications";
import {
Button,
EmptyState,
Modal,
ModalContent,
Table,
TableContainer,
TableSkeleton,
@@ -15,28 +18,49 @@ import {
Tr
} from "@app/components/v2";
import { useListUsersWithMyEmail, useMergeUsers } from "@app/hooks/api";
import { UserAliasType } from "@app/hooks/api/users/types";
type Props = {
username: string;
authType?: UserAliasType;
organizationSlug: string;
};
export const MergeUsersStep = ({ username }: Props) => {
export const MergeUsersStep = ({ username, authType, organizationSlug }: Props) => {
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
const [targetUsername, setTargetUsername] = useState("");
const { data: users, isLoading: isLoadingUsers } = useListUsersWithMyEmail();
const { mutateAsync: mergeUser, isLoading: isLoadingMerge } = useMergeUsers();
const handleMergeUser = async (targetUsername: string) => {
const handleMergeUser = async (mergeWithUsername: string) => {
try {
console.log("merge A");
await mergeUser({ username: targetUsername });
// TODO: logout, make user re-login
console.log("merge B");
if (!mergeWithUsername) return;
await mergeUser({ username: mergeWithUsername });
createNotification({
text: "Successfully merged user",
type: "success"
});
router.push("/login");
setIsOpen(false);
switch (authType) {
case UserAliasType.SAML: {
window.open(`/api/v1/sso/redirect/saml2/organizations/${organizationSlug}`);
window.close();
break;
}
case UserAliasType.LDAP: {
router.push(`/login/ldap?organizationSlug=${organizationSlug}`);
break;
}
default: {
router.push("/login");
break;
}
}
setTargetUsername("");
} catch (err) {
console.error(err);
createNotification({
@@ -72,14 +96,16 @@ export const MergeUsersStep = ({ username }: Props) => {
return (
<Tr className="h-10 items-center" key={`same-email-user-${user.id}`}>
<Td>{`${user.firstName ?? ""} ${user.lastName ?? ""}`}</Td>
<Td>{username}</Td>
<Td>{user.username}</Td>
<Td>
<Button
isLoading={isLoadingMerge}
colorSchema="primary"
variant="outline_bg"
type="submit"
onClick={() => handleMergeUser(user.username)}
onClick={() => {
setIsOpen(true);
setTargetUsername(user.username);
}}
>
Merge
</Button>
@@ -97,6 +123,35 @@ export const MergeUsersStep = ({ username }: Props) => {
</TBody>
</Table>
</TableContainer>
<Modal isOpen={isOpen} onOpenChange={setIsOpen}>
<ModalContent title="Merge User Confirmation">
<p className="mb-4 text-bunker-300">
The merge operation will transfer / consolidate your existing organization membership to
the target user you&apos;re merging with.
</p>
<p className="mb-4 text-bunker-300">
If the target user is not yet part of the same organization, then they will be added to
it under your current organization membership. Conversely, if the target user is already
part of the organization, then their existing organization membership will remain.
</p>
<p className="text-bunker-300">
Once the merge operation is complete, you&apos;ll be prompted to re-login.
</p>
<div className="mt-8 flex items-center">
<Button
isLoading={isLoadingMerge}
colorSchema="primary"
onClick={async () => handleMergeUser(targetUsername)}
className="mr-4"
>
Confirm
</Button>
<Button colorSchema="secondary" variant="plain" onClick={() => setIsOpen(false)}>
Cancel
</Button>
</div>
</ModalContent>
</Modal>
</div>
);
};
@@ -17,6 +17,7 @@ import SecurityClient from "@app/components/utilities/SecurityClient";
import { Button, Input } from "@app/components/v2";
import { completeAccountSignup, useSelectOrganization } from "@app/hooks/api/auth/queries";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import { sendEmailVerificationCode } from "@app/hooks/api/users/mutation";
import ProjectService from "@app/services/ProjectService";
// eslint-disable-next-line new-cap
@@ -205,10 +206,11 @@ export const UserInfoSSOStep = ({
if (email) {
// move to verify email
await sendEmailVerificationCode();
setStep(1);
} else {
// move to backup PDF step
setStep(2);
setStep(3);
}
} catch (error) {
setIsLoading(false);