Skip to content

Commit

Permalink
Email password user creation api integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chakravarthy7102 committed Dec 20, 2023
1 parent 840cb20 commit 89df94e
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 177 deletions.
4 changes: 3 additions & 1 deletion src/api/tenants/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@

import { getApiUrl, useFetchData } from "../../utils";

export type PasswordlessContactMethod = "PHONE" | "EMAIL" | "EMAIL_OR_PHONE";

export type Tenant = {
tenantId: string;
emailPassword: {
enabled: boolean;
};
passwordless: {
enabled: boolean;
contactMethod?: "PHONE" | "EMAIL" | "EMAIL_OR_PHONE";
contactMethod?: PasswordlessContactMethod;
};
thirdParty: {
enabled: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/api/user/create/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ const useCreateUserService = (): ICreateUserService => {
const fetchData = useFetchData();

const createEmailPasswordUser = async (
tenantId: string | undefined,
email: string,
password: string,
tenantId: string | undefined
password: string
): Promise<CreateEmailPasswordUserResponse> => {
const response = await fetchData({
url: getApiUrl("/api/user/create/emailpassword", tenantId),
Expand Down
53 changes: 44 additions & 9 deletions src/ui/components/createUser/CreateEmailPasswordUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,63 @@
* under the License.
*/

import { useState } from "react";
import { useContext, useState } from "react";
import useCreateUserService from "../../../api/user/create";
import { getApiUrl, getImageUrl } from "../../../utils";
import { PopupContentContext } from "../../contexts/PopupContentContext";
import Button from "../button";
import { Dialog, DialogContent, DialogFooter } from "../dialog";
import InputField from "../inputField/InputField";
import { CreateUserDialogStepType } from "./CreateUserDialog";

type CreateEmailPasswordUserProps = {
tenantId: string;
onCloseDialog: () => void;
setCurrentStep: (step: CreateUserDialogStepType) => void;
};

export default function CreateEmailPasswordUser({ onCloseDialog, setCurrentStep }: CreateEmailPasswordUserProps) {
const [email, setEmail] = useState<string | undefined>("");
const [password, setPassword] = useState<string | undefined>("");

export default function CreateEmailPasswordUser({
tenantId,
onCloseDialog,
setCurrentStep,
}: CreateEmailPasswordUserProps) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isCreatingUser, setIsCreatingUser] = useState(false);

function createUser() {
const { createEmailPasswordUser } = useCreateUserService();
const { showToast } = useContext(PopupContentContext);

async function createUser() {
setIsCreatingUser(true);
setTimeout(() => {

try {
const response = await createEmailPasswordUser(tenantId, email, password);
if (response.status === "EMAIL_ALREADY_EXISTS_ERROR") {
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>User with this email already exists in {tenantId} tenant.</>,
});
return;
}
if (response.status === "OK") {
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "success",
children: <>User created successfully!</>,
});
window.open(getApiUrl(`?userid=${response.user.id}`), "_blank");
}
} catch (_) {
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>Something went wrong, please try again!</>,
});
} finally {
setIsCreatingUser(false);
}, 3000);
}
}

return (
Expand All @@ -43,7 +78,7 @@ export default function CreateEmailPasswordUser({ onCloseDialog, setCurrentStep
title="User Info"
onCloseDialog={onCloseDialog}>
<DialogContent className="text-small text-semi-bold">
<div className="email-password-dialog-content-container">
<div className="dialog-form-content-container">
<InputField
label="Email"
hideColon
Expand Down
17 changes: 16 additions & 1 deletion src/ui/components/createUser/CreatePasswordlessUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,38 @@
* under the License.
*/

import { useState } from "react";
import { PasswordlessContactMethod } from "../../../api/tenants/list";
import Button from "../button";
import { Dialog, DialogContent, DialogFooter } from "../dialog";
import InputField from "../inputField/InputField";
import { CreateUserDialogStepType } from "./CreateUserDialog";

type CreatePasswordlessUserProps = {
authMethod: PasswordlessContactMethod | undefined;
onCloseDialog: () => void;
setCurrentStep: (step: CreateUserDialogStepType) => void;
};

export default function CreatePasswordlessUser({ onCloseDialog, setCurrentStep }: CreatePasswordlessUserProps) {
const [email, setEmail] = useState("");

return (
<Dialog
className="max-width-410"
title="User Info"
onCloseDialog={onCloseDialog}>
<DialogContent className="text-small text-semi-bold">
Create passwordless user..
<div className="dialog-form-content-container">
<InputField
label="Email Or Phone"
hideColon
value={email}
handleChange={(e) => setEmail(e.currentTarget.value)}
name="email"
type="email"
/>
</div>
<DialogFooter border="border-top">
<Button
color="gray-outline"
Expand Down
153 changes: 118 additions & 35 deletions src/ui/components/createUser/CreateUserDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ import "./createUserDialog.scss";

import { useState } from "react";
import { Tenant } from "../../../api/tenants/list";
import Alert from "../alert";
import Button from "../button";
import { Dialog, DialogContent, DialogFooter } from "../dialog";
import Select from "../select";
import CreateEmailPasswordUser from "./CreateEmailPasswordUser";
import CreatePasswordlessUser from "./CreatePasswordlessUser";
import SelectTenantAndAuthMethod from "./SelectTenantAndAuthMethod";

export type AuthMethod = "emailpassword" | "passwordless";

export type CreateUserDialogStepType =
| "select-auth-method-and-tenant"
Expand All @@ -36,40 +41,118 @@ export default function CreateUserDialog({
currentSelectedTenantId: string;
}) {
const [currentStep, setCurrentStep] = useState<CreateUserDialogStepType>("create-email-password-user");
const [selectedTenantId, setSelectedTenantId] = useState(currentSelectedTenantId);
const [selectedAuthMethod, setSelectedAuthMethod] = useState<AuthMethod | undefined>(undefined);

const selectedTenantObject = tenantsList.find((tenant) => tenant.tenantId === selectedTenantId)!;

function getSelectableAuthMethods() {
const selectableAuthMethods: { name: string; value: string }[] = [];

if (selectedTenantObject.emailPassword.enabled === true) {
selectableAuthMethods.push({
name: "emailpassword",
value: "emailpassword",
});
}

if (selectedTenantObject.passwordless.enabled === true) {
selectableAuthMethods.push({
name: "passwordless",
value: "passwordless",
});
}

switch (currentStep) {
case "select-auth-method-and-tenant":
return (
// TODO: add no auth methods initlized error on frontend...
<SelectTenantAndAuthMethod
currentSelectedTenantId={currentSelectedTenantId}
onCloseDialog={onCloseDialog}
tenantsList={tenantsList}
setCurrentStep={setCurrentStep}
/>
);
case "create-email-password-user":
return (
<CreateEmailPasswordUser
setCurrentStep={setCurrentStep}
onCloseDialog={onCloseDialog}
/>
);
case "create-passwordless-user":
return (
<CreatePasswordlessUser
setCurrentStep={setCurrentStep}
onCloseDialog={onCloseDialog}
/>
);
default:
return (
<SelectTenantAndAuthMethod
currentSelectedTenantId={currentSelectedTenantId}
onCloseDialog={onCloseDialog}
tenantsList={tenantsList}
setCurrentStep={setCurrentStep}
/>
);
return selectableAuthMethods;
}

if (currentStep === "create-passwordless-user") {
return (
<CreatePasswordlessUser
authMethod={selectedTenantObject.passwordless.contactMethod}
setCurrentStep={setCurrentStep}
onCloseDialog={onCloseDialog}
/>
);
}

if (currentStep === "create-email-password-user") {
return (
<CreateEmailPasswordUser
tenantId={selectedTenantId}
setCurrentStep={setCurrentStep}
onCloseDialog={onCloseDialog}
/>
);
}

return (
<Dialog
className="max-width-410"
title="Create New Use"
onCloseDialog={onCloseDialog}>
<DialogContent className="text-small text-semi-bold">
<div className="create-user-modal-content">
<Alert
padding="sm"
title="info"
type="secondary">
Custom overrides for the sign up recipe function on the backend will be run when a user is
created, however, the sign up API override will not run, for more info regarding this{" "}
<a
rel="noreferrer"
href="https://suppertokens.com/docs/"
target="_blank">
click here.
</a>
</Alert>
<div className="select-container mb-12">
<p className="text-label">
Selected Tenant:{" "}
{tenantsList.length === 1 ? <span className="text-black ">Public</span> : null}
</p>{" "}
{tenantsList.length > 1 ? (
<Select
onOptionSelect={(value) => {
setSelectedTenantId(value);
setSelectedAuthMethod(undefined);
}}
options={tenantsList.map((tenant) => {
return {
name: tenant.tenantId,
value: tenant.tenantId,
};
})}
selectedOption={selectedTenantId}
/>
) : null}
</div>
<div className="select-container mb-28">
<span className="text-label">Select Auth Method:</span>
<Select
onOptionSelect={(value) => setSelectedAuthMethod(value as AuthMethod)}
options={getSelectableAuthMethods()}
selectedOption={selectedAuthMethod}
/>
</div>
</div>
<DialogFooter border="border-top">
<Button
disabled={selectedAuthMethod === undefined}
color={selectedAuthMethod === undefined ? "gray" : "primary"}
onClick={() => {
if (selectedAuthMethod === "emailpassword") {
setCurrentStep("create-email-password-user");
} else if (selectedAuthMethod === "passwordless") {
setCurrentStep("create-passwordless-user");
} else {
alert("Please select a auth method to continue");
}
}}>
Next
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Loading

0 comments on commit 89df94e

Please sign in to comment.