Skip to content

SignUp Page

SignUp Component

The SignUp component facilitates the registration of new users in a web application built with Next.js and Firebase. It incorporates form handling, custom validations, and user authentication through Firebase. The LoginRight component is responsible for the second half of the sign up page with the image and quote.

Features

  • New user registration with email, password, country, industry, their business description, their website (optional) and their LinkedIn (optional).
  • The registration process has 2 steps with data being saved locally on submission of he first step. Users have the option to go back and make changes to their entered information.
  • Password validation to ensure strength and security.
  • Optional handling of referral codes and free trial activations.
  • Sends a welcome email and verification email upon successful registration.
  • Responsive feedback to users on registration status and errors.

Usage

This component is designed for scenarios where new user sign-ups are required. It uses Firebase for authentication and integrates with an external API for sending a welcome email and handling referral-related functionalities.

Code Structure

  • Imports: The component uses React hooks, Firebase services, react-hook-form for form management, Zod for schema validation, and Axios for HTTP requests.
  • Component Definition: SignUp is a functional React component that handles user registration.
  • State Management: Utilizes the useState hook for managing loading states.
  • Form Handling: Employs react-hook-form with Zod for form validation and error handling.
  • Environment Variables: Uses environment variables for API URLs.

Component Breakdown

Form Schema

Defines the schema for the two step form validation using Zod, ensuring that the password fields match:

const SignUpStep1Schema = z
.object({
contact_person: z.string().nonempty("Please enter a full name"),
email: z.string().email("Invalid email"),
password: z.string().min(8, "Password must be at least 8 characters"),
passwordConfirm: z.string(),
country: z.string().nonempty("Please select your country"),
industry: z.string().nonempty("Please select an industry"),
})
.refine((data) => data.password === data.passwordConfirm, {
message: "Passwords do not match",
path: ["passwordConfirm"],
});
type SignUpStep1Type = z.infer<typeof SignUpStep1Schema>;
const SignUpStep2Schema = z.object({
businessDescription: z.string().optional(),
heardAboutUs: z.string().optional(),
heardAboutUsOther: z.string().optional(),
business_role: z.string().optional(),
isIndividual: z.boolean().default(false),
organization_name: z.string().optional(),
website: z.union([z.literal(""), z.string().url()]).optional(),
linkedIn: z.union([z.literal(""), z.string().url()]).optional(),
});
type SignUpStep2Type = z.infer<typeof SignUpStep2Schema>;

Password Validation Function

Validates the password strength before submission:

function validatePassword(password) {
const errors = [];
if (password.length < 8) {
errors.push("Password must be at least 8 characters long.");
}
if (!/\d/.test(password)) {
errors.push("Password must include at least one number.");
}
return errors;
}

Form Submission Handler

Handles the final step of the signup process, including form validation, user registration, supplier profile creation, sending an email verification, processing referral and subscription details, and navigating to the profile edit page.

const handleFinalSubmit = step2Form.handleSubmit(async (step2Data) => {
try {
const savedStep1 = localStorage.getItem("signupStep1");
if (!savedStep1) {
return toast.error("Something went wrong: missing step1 data.");
}
const step1Data: SignUpStep1Type = JSON.parse(savedStep1);
const {
contact_person,
email,
password,
country,
industry,
} = step1Data;
const passwordErrors = validatePassword(password);
if (passwordErrors.length > 0) {
passwordErrors.forEach((err) => toast.error(err));
return;
}
const finalData = {
contact_person,
email,
password,
country,
industry,
businessDescription: step2Data.businessDescription,
heardAboutUs: step2Data.heardAboutUs,
heardAboutUsOther: step2Data.heardAboutUsOther,
business_role: step2Data.business_role,
isIndividual: step2Data.isIndividual,
organization_name: step2Data.organization_name,
website: step2Data.website,
linkedIn: step2Data.linkedIn,
};
setIsLoading(true);
const user = await registerUser(finalData.email, finalData.password);
if (!user) {
return toast.error("Failed to create user");
}
const responseObj = await createSupplierProfile(user.uid, {
contact_person:finalData.contact_person,
email: finalData.email,
country: finalData.country,
industry: finalData.industry,
businessDescription: finalData.businessDescription,
heardAboutUs: finalData.heardAboutUs,
heardAboutUsOther: finalData.heardAboutUsOther,
business_role: finalData.business_role,
isIndividual: finalData.isIndividual,
organization_name: finalData.organization_name,
website: finalData.website,
linkedIn: finalData.linkedIn,
});
if (responseObj?.status === "error") {
return toast.error(responseObj.message || "Error creating profile");
}
await sendEmailVerification(user);
toast.success("Verification email sent. Please check your inbox.");
if (referral) {
await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/invite/new-signup`, {
referral,
email: finalData.email,
});
}
if (plan && frequency) {
const stripeLink = buildStripeLink(plan, frequency, finalData.email);
window.open(stripeLink, "_blank");
}
localStorage.removeItem("signupStep1");
router.push("/profile/edit");
} catch (err) {
console.error("Error on final submit:", err);
toast.error("Email already in use or other error.");
} finally {
setIsLoading(false);
}
});