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 (