Files
ax3Client/src/Components/SignupPage/SignupPage.js

365 lines
10 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 { db, auth } from "../../firebase";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";
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 } from "../../Constants/Fields/SignupFields";
import { paymentfields } from "../../Constants/Fields/PaymentFields";
import PaymentModal from "../Modals/PaymentModal";
import "../../styles/signup.scss";
import Stripe from 'stripe';
const SignupPage = () => {
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 modalText = "Description of monthly and annual subscriptions";
// this could also go in App.js if needed in the future
const stripe = new Stripe('pk_test_51NNoasBi8p7FeGFrI5SfpM6HuNMoxwImx6NRKyKbgbt6OPxMxQDiZ9I1GqvDa9qUwB3D3jlJOng6MyjPppWofxzP00Exvr8dBy')
const apiUrl =
process.env.NODE_ENV === "development"
? process.env.REACT_APP_API_DEV
: process.env.REACT_APP_API_PROD;
const handleChangeInput = (e, name) => {
const newData = handleFormDataChange(e, name, data, signupFields);
if (newData !== null) {
setData(newData);
}
};
const handleChangePaymentInput = (e, name) => {
const newPaymentData = handleFormDataChange(
e,
name,
paymentData,
paymentfields
);
if (newPaymentData !== null) {
setPaymentData(newPaymentData);
}
};
const validateData = () => {
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 saveUserData(authId, dataValues) {
const appUserId = uuidv4();
const firmId = uuidv4();
const fbAuthUid = authId;
const {
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state,
zipCode,
barNumber,
practiceArea,
email,
} = dataValues;
const subscriptionId = dataValues?.subscriptionId;
const customerId = dataValues?.customerId;
const userData = {
appUserId,
fbAuthUid,
firmId,
firstName,
lastName,
firm,
telephone,
streetAddress,
city,
state,
zipCode,
barNumber,
practiceArea,
email,
subscriptionId,
customerId,
};
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();
if (isBusy) {
return;
}
const dataValues = validateData();
if (dataValues === null) {
return;
}
setShowPaymentModal(true);
};
async function handleStripeAuthorization(user, paymentDataValues, customerDataValues) {
// Do: calls to Stripe API
// if success, return some truthy value or
// handle failure in handleSignup
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,
}
})
try {
let response = await fetch(`${apiUrl}/create-subscription`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'monthly', // TODO: replace this with the user-chosen value later when we have the UI for it
customerData: {
email: customerDataValues.email,
name: customerDataValues.firstName + ' ' + customerDataValues.lastName,
},
token,
}),
})
response = await response.json()
subscriptionId = response.subscriptionId;
customerId = response.customerId;
console.log('response', response);
return {
subscriptionId,
customerId,
}
}
catch (error) {
console.log(error)
error = true;
}
if(error) {
return false
}
return {
subscriptionId,
customerId,
}
return !error;
}
const handleSignup = async (e) => {
e.preventDefault();
const paymentDataValues = validatePaymentData();
if (paymentDataValues === null) {
return;
}
let dataValues = validateData();
if (dataValues === null) {
return;
}
setIsBusy(true);
try {
const userCredential = await createUserWithEmailAndPassword(
auth,
dataValues.email,
dataValues.password
);
const user = userCredential.user;
const paymentResponse = await handleStripeAuthorization(user, paymentDataValues, dataValues);
if(paymentResponse === false) {
// handle payment failure
setIsBusy(false);
setNotice(
"Sorry, something went wrong. Please try again."
);
return;
}
else {
dataValues = {...dataValues, ...paymentResponse};
await saveUserData(user.uid, dataValues);
navigate("/");
}
} catch (error) {
setIsBusy(false);
setNotice(
error.message || "Sorry, something went wrong. Please try again."
);
}
};
return (
<div className="signup-super-container">
<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">
{console.log("data, name", data, name)}
<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={handleProceedToPayment}
disabled={isBusy}
labelText="Proceed to Payment"
/>
<div className="mt-3">
<span>
<Link to="/login">Go to login</Link>
</span>
</div>
</div>
{"" !== notice && (
<>
<br></br>
<div className="alert alert-danger" role="alert">
{notice}
</div>
</>
)}
</div>
{showPaymentModal ? (
<PaymentModal
key="modal-one-xc"
paymentData={paymentData}
paymentfields={paymentfields}
setShowPaymentModal={setShowPaymentModal}
handleChangePaymentInput={handleChangePaymentInput}
handleSignup={handleSignup}
modalText={modalText}
/>
) : (
<></>
)}
</div>
);
};
export default SignupPage;