Files
ax3Client/src/Components/SignupPage/SignupPage.js
Kenneth Jannette 2cdcd22727 more
2024-02-01 22:43:34 -06:00

662 lines
19 KiB
JavaScript

import React, { useState } from "react";
import Col from "react-bootstrap/Col";
import Form from "react-bootstrap/Form";
import Row from "react-bootstrap/Row";
import Button from "../../pageElements/Button";
import { useParams } from "react-router-dom";
import { db, auth } from "../../firebase";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { useNavigate } from "react-router-dom";
import Toggle from "../../pageElements/Toggle";
import TextInput from "../../pageElements/TextInput";
import { v4 as uuidv4 } from "uuid";
import { collection, setDoc, doc } from "firebase/firestore";
import { splitEvery } from "../../Utils/Array";
import {
getFormDataDefaults,
getValidatedFormData,
handleFormDataChange,
isFormDataHasErrors,
} from "../../Utils/Form";
import { objectMap } from "../../Utils/Object";
import {
signupFields,
stateDataValues,
} from "../../Constants/Fields/SignupFields";
import { paymentfields } from "../../Constants/Fields/PaymentFields";
import { signupRadioFields } from "../../Constants/Fields/RadioFields";
import PaymentModal from "../Modals/PaymentModal";
import Radio from "../../pageElements/Radio";
import "../../styles/signup.scss";
import Stripe from "stripe";
import { stripeApiKey } from "../../secrets";
const SignupPage = () => {
const { isUpgrade, currentPlan } = useParams();
const navigate = useNavigate();
const [notice, setNotice] = useState("");
const fieldsChunkSize = 2;
const [isBusy, setIsBusy] = useState(false);
const [data, setData] = useState(getFormDataDefaults(signupFields));
const [paymentData, setPaymentData] = useState(
getFormDataDefaults(paymentfields)
);
const [showPaymentModal, setShowPaymentModal] = useState(false);
const [showSelectPlan, setShowSelectPlan] = useState(
isUpgrade ? true : false
);
const [activeRadioOption, setActiveRadioOption] = useState("partner");
const [selectedPlan, setSelectedPlan] = useState([signupRadioFields[1]]);
const [isAnnual, setIsAnnual] = useState(1);
const stripe = new Stripe(stripeApiKey);
const apiUrl =
process.env.NODE_ENV === "development"
? process.env.REACT_APP_API_DEV
: process.env.REACT_APP_API_PROD;
const [showAddAccount, setShowAddAccount] = useState(false);
const [numberOfAccountsToAdd, setNumberOfAccountsToAdd] = useState();
const handleChangeInput = (e, name) => {
const newData = handleFormDataChange(e, name, data, signupFields);
if (newData !== null) {
setData(newData);
}
};
const handleChangeAccountSelect = (e, name) => {
const num = e.target.value;
const temp = Number(num);
setNumberOfAccountsToAdd(temp);
};
const handleChangePaymentInput = (e, name) => {
const newPaymentData = handleFormDataChange(
e,
name,
paymentData,
paymentfields
);
if (newPaymentData !== null) {
setPaymentData(newPaymentData);
}
};
const handleChangeRadioInput = (value) => {
const tempPlan = signupRadioFields.filter((plan) => {
if (!isUpgrade) {
if (plan.value === value) {
return plan;
}
} else {
if (plan.value === value && plan.value === currentPlan) {
//
} else if (plan.value === value) {
return plan;
}
}
});
setSelectedPlan(tempPlan);
setActiveRadioOption(tempPlan[0].value);
};
const vals = [
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2],
];
const selectAdditionalAccountValues =
selectedPlan[0].value === "partner" ? vals[1] : vals[0];
const validateUserData = () => {
const newData = getValidatedFormData(data, signupFields);
const hasErrors = isFormDataHasErrors(newData);
setData(newData);
return hasErrors ? null : objectMap(({ value }) => value, newData);
};
const validatePaymentData = () => {
const newPaymentData = getValidatedFormData(paymentData, paymentfields);
const hasErrors = isFormDataHasErrors(newPaymentData);
setPaymentData(newPaymentData);
return hasErrors ? null : objectMap(({ value }) => value, newPaymentData);
};
async function saveLeadData(dataValues) {
const signupId = uuidv4();
const {
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state,
zipCode,
barNumber,
practiceArea,
email,
} = dataValues;
const userData = {
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state,
zipCode,
barNumber,
practiceArea,
email,
signupId,
};
try {
const collecRef = collection(db, "signupLeads");
await setDoc(doc(collecRef, `${signupId}`), userData);
} catch (error) {
console.log(`Error saving new user to db: ${error}`);
}
}
async function saveUserData(authId, dataValues, customerId, subscriptionId) {
const appUserId = uuidv4();
const firmId = uuidv4();
const fbAuthUid = authId;
const plan = selectedPlan;
const docsAllowed =
plan === "associate"
? 1
: plan === "partner"
? 3
: plan === "unlimited"
? 4000
: 0;
const docsGenerated = 0;
const {
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state,
zipCode,
barNumber,
practiceArea,
email,
} = dataValues;
const userState = stateDataValues.filter((value) => {
if (value.name === state) return value;
});
const userData = {
docsAllowed,
docsGenerated,
appUserId,
fbAuthUid,
firmId,
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state: userState,
zipCode,
barNumber,
practiceArea,
email,
customerId: customerId,
subscriptionId: subscriptionId,
subscriptionPlan: plan,
};
try {
const usersRef = collection(db, "users");
await setDoc(doc(usersRef, fbAuthUid), userData);
} catch (error) {
console.log(`Error saving new user to db: ${error}`);
}
}
const handleProceedToPayment = (e) => {
e.preventDefault();
setShowPaymentModal(true);
};
const handleAdd = () => {
//add accounts, close section
setShowAddAccount(!showAddAccount);
setShowPaymentModal(true);
};
const handleOpenSelectPlan = (e) => {
e.preventDefault();
if (isBusy) {
return;
}
const dataValues = validateUserData();
if (dataValues === null) {
return;
}
saveLeadData(dataValues);
setShowSelectPlan(!showSelectPlan);
};
const handleToggle = (e) => {
if (isAnnual === e) {
return;
} else {
setIsAnnual(e);
}
};
const handleAddAccounts = () => {
setShowAddAccount(!showAddAccount);
};
const handleCancelAdd = () => {
setShowAddAccount(!showAddAccount);
};
// ******************** STRIPE PAYMENT API CALL ******************** //
async function handleStripeAuthorization(
paymentDataValues,
customerDataValues
) {
const additionalAccounts = numberOfAccountsToAdd
? numberOfAccountsToAdd
: 0;
let error = false;
let subscriptionId = null;
let customerId = null;
const token = await stripe.tokens.create({
card: {
number: paymentDataValues.cardNumber,
exp_month: paymentDataValues.cardExpirationMonth,
exp_year: paymentDataValues.cardExpirationYear,
cvc: paymentDataValues.cardCvvCode,
name:
paymentDataValues.cardFirstName +
" " +
paymentDataValues.cardLastName,
},
});
const type = selectedPlan[0].value ? selectedPlan[0].value : null;
const tempAnnual = isAnnual === 1 ? true : false;
try {
const response = await fetch(`${apiUrl}/create-subscription`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
planType: type,
additionalAccounts: additionalAccounts,
isAnnual: tempAnnual,
customerData: {
email: customerDataValues.email,
name: `${customerDataValues.firstName} ${customerDataValues.lastName}`,
},
token,
}),
});
const data = await response.json();
//subscriptionId = response.subscriptionId;
//customerId = response.customerId;
return {
data,
};
} catch (error) {
console.log(error);
return error;
}
}
// ******************** END STRIPE PAYMENT API CALL ******************** //
const handleSignup = async (e) => {
e.preventDefault();
const paymentDataValues = validatePaymentData();
if (paymentDataValues === null) {
return;
}
let dataValues = validateUserData();
if (dataValues === null) {
return;
}
//const planType = determinePlan(isAnnual, activeRadioOption);
setIsBusy(true);
if (paymentDataValues && dataValues) {
try {
const data = await handleStripeAuthorization(
paymentDataValues,
dataValues
);
const customerId = data.data.customerId;
const subscriptionId = data.data.subscriptionId;
if (data === undefined) {
setIsBusy(false);
setNotice("Sorry, something went wrong. Please try again.");
return;
} else {
const userCredential = await createUserWithEmailAndPassword(
auth,
dataValues.email,
dataValues.password
);
const user = userCredential.user;
dataValues = { ...dataValues, ...data };
console.log(
"customerId, subscriptionId in handleSignup right before saveUserData",
customerId,
subscriptionId
);
await saveUserData(user.uid, dataValues, customerId, subscriptionId);
navigate("/dashboard");
}
} catch (error) {
setIsBusy(false);
setNotice(
error.message || "Sorry, something went wrong. Please try again."
);
}
}
};
const showNoticeOnPage = "" !== notice && !showPaymentModal;
const addAccountCopy =
selectedPlan[0]?.value === "seniorPartner"
? "Add up to eight"
: "Add up to two";
const showAddAccountBtn =
selectedPlan[0].value === "seniorPartner" ||
selectedPlan[0].value === "partner";
const signupForm = (
<div className="signup-sub-container">
<div className="signup-header">
<h1 className="signup-header-text">Create a Novodraft account</h1>
</div>
<Form className="signup-form">
{splitEvery(
Object.keys(signupFields).slice(0, -2),
fieldsChunkSize
).map((names, j) => (
<Row key={`row${j}`}>
{names.map((name, i) => (
<Col key={`${name}${i + fieldsChunkSize * j}`} className="mb-3">
<TextInput
name={name}
value={data[name].value}
onChange={(e) => handleChangeInput(e, name)}
error={data[name].error}
message={data[name].message}
label={
data[name].value.length === 0
? signupFields[name].label
: ""
}
type={signupFields[name].type}
values={signupFields[name].values}
disabled={isBusy}
/>
</Col>
))}
</Row>
))}
<Row>
<Col className="mb-3">
<TextInput
id="signupPassword"
type="password"
label={
data.password.value.length === 0
? signupFields.password.label
: ""
}
value={data.password.value}
error={data.password.error}
message={data.password.message}
onChange={(e) => handleChangeInput(e, "password")}
disabled={isBusy}
/>
</Col>
</Row>
<Row>
<Col className="mb-3">
<TextInput
id="confirmPassword"
type="password"
label={
data.confirmPassword.value.length === 0
? signupFields.confirmPassword.label
: ""
}
value={data.confirmPassword.value}
error={data.confirmPassword.error}
message={data.confirmPassword.message}
onChange={(e) => handleChangeInput(e, "confirmPassword")}
disabled={isBusy}
/>
</Col>
</Row>
</Form>
<div className="signup-button-box">
<Button
className="primary-button signup-proceed"
type="button"
size="lg"
onClick={handleOpenSelectPlan}
disabled={isBusy}
labelText="Select A Plan"
/>
</div>
{showNoticeOnPage ? (
<>
<br></br>
<div className="alert alert-danger" role="alert">
{notice}
</div>
</>
) : (
<></>
)}
</div>
);
const magicIndex = currentPlan === "associate" ? 1 : 2;
return (
<div className="signup-super-container">
{!isUpgrade ? signupForm : <></>}
{showSelectPlan ? (
<div className="select-plan-container">
<div className="select-sub-left">
<div className="select-plan-header">
<div className="plan-header-right">
<h5 className="signup-subheader-text">
Select a subscription plan
</h5>
</div>
<div className="plan-header-left">
<div className="select-toggle-box">
<p className="select-billing-text">Billed annually</p>
<Toggle onClick={handleToggle} />
<p className="select-billing-text">Billed montly</p>
</div>
</div>
</div>
<div className="radio-group-box">
{signupRadioFields?.map((option, index) => (
<div className="signup-radio-container">
<Radio
onClick={handleChangeRadioInput}
value={option.value}
name={option.name}
description={option.description}
details={option.details}
activeRadioOption={activeRadioOption}
price={option.pricing}
isAnnual={isAnnual}
isUpgrade={index < magicIndex ? true : false}
disabled={index < magicIndex ? true : false}
currentPlan={currentPlan}
/>
</div>
))}
</div>
{showAddAccount ? (
<div className="show-addaccount-wrapper">
<div className="show-addaccount-container">
<div className="plan-header-right-lower">
<h5 className="signup-button-wrapper-text">
Add additional accounts
</h5>
<p className="signup-accounts-info">
User profiles will be setup later
</p>
</div>
<div className="signup-select-wrapper">
<div className="signup-select-box">
<select
id="accountNo"
className={`form-select`}
value={numberOfAccountsToAdd}
name={"addAccountsSelect"}
onChange={(e) =>
handleChangeAccountSelect(e, "addAccountsSelect")
}
>
<option
id="ddlProducts"
className={`select-option`}
value=""
></option>
{selectAdditionalAccountValues.map((item, i) => (
<option
id="ddlProducts"
className={`select-option`}
key={`item${i}`}
>
{item}
</option>
))}
</select>
</div>
</div>
<div className="details-button-box">
<Button
className="secondary-button back-button"
onClick={handleCancelAdd}
labelText="Cancel"
/>
<div className="upload-button-box">
<Button
className="p-2 mr-2 primary-button signup-proceed-btn"
onClick={handleAdd}
labelText="Add And Proceed To Payment"
/>
</div>
</div>
</div>
</div>
) : (
<></>
)}
<div className="signup-button-box">
{!showAddAccount ? (
<>
{showAddAccountBtn ? (
<Button
className="secondary-button cancel-add-account-btn"
type="button"
size="lg"
onClick={handleAddAccounts}
disabled={isBusy}
labelText="Add Account(s)"
style={{ marginTight: "12px;" }}
/>
) : (
<></>
)}
<Button
className="primary-button signup-proceed"
type="button"
size="lg"
onClick={handleProceedToPayment}
disabled={isBusy}
labelText="Proceed to Payment"
/>
</>
) : (
<></>
)}
</div>
</div>
</div>
) : (
<></>
)}
{showPaymentModal ? (
<PaymentModal
key="modal-one-xc"
paymentData={paymentData}
paymentfields={paymentfields}
setShowPaymentModal={setShowPaymentModal}
handleChangePaymentInput={handleChangePaymentInput}
handleSignup={handleSignup}
notice={notice}
setNotice={setNotice}
selectedPlan={selectedPlan}
isAnnual={isAnnual === 1 ? true : false}
numberOfAccountsToAdd={numberOfAccountsToAdd}
fieldsChunkSize={2}
/>
) : (
<></>
)}
</div>
);
};
export default SignupPage;
/*
const userDataValues = {
barNumber: "232323",
city: "broolyn",
email: "j@s.com",
firm: "asdf",
firstName: "ghghg",
lastName: "hhhhh",
password: "123344556",
practiceArea: "dfdfdf",
state: "New York",
streetAddress: "123 Main",
telephone: "(313) 555-5555",
zipCode: "12345",
};
*/