feature - full font management

This commit is contained in:
James Wyndham 2024-02-22 07:02:48 +08:00
parent 0cd8f39a40
commit 574e072ae5
28 changed files with 100 additions and 1031 deletions

View File

@ -24,7 +24,7 @@ export default async function BlogsPage() {
subtitle={ subtitle={
<> <>
{" "} {" "}
<h2 className="text-base-content font-bold text-2xl lg:text-3xl text-center max-w-5xl mx-auto px-6"> <h2 className="text-base-content text-2xl font-medium text-center max-w-5xl mx-auto px-6">
Join us for the journey as we make great food. Learn and grow Join us for the journey as we make great food. Learn and grow
together together
</h2> </h2>

View File

@ -2,13 +2,24 @@ import Footer from "@/components/Footer";
import Background from "@/components/Utilities/Background"; import Background from "@/components/Utilities/Background";
import Spacer from "@/components/Utilities/Spacer"; import Spacer from "@/components/Utilities/Spacer";
import FormLeftDescriptionRightContactUs from "@/sections/ContactUs/FormLeftDescriptionRightContactUs"; import FormLeftDescriptionRightContactUs from "@/sections/ContactUs/FormLeftDescriptionRightContactUs";
import PageHeader from "@/sections/PageHeader";
import React from "react"; import React from "react";
const page = () => { const page = () => {
return ( return (
<div className="flex flex-col h-full w-full bg-base-100 "> <div className="flex flex-col h-full w-full bg-base-100 ">
<Background> <Background>
<Spacer className="pt-28" /> <PageHeader
title="Contact Us"
subtitle={
<>
{" "}
<h2 className="text-base-content text-2xl font-medium content text-center max-w-6xl mx-auto px-6">
We&apos;d love to talk about how we can help you.
</h2>
</>
}
/>
<FormLeftDescriptionRightContactUs /> <FormLeftDescriptionRightContactUs />
<Spacer className="mt-auto" /> <Spacer className="mt-auto" />
<Footer /> <Footer />

View File

@ -42,7 +42,7 @@ export default function PricingPage() {
subtitle={ subtitle={
<> <>
{" "} {" "}
<h2 className="text-base-content content text-center max-w-6xl mx-auto px-6"> <h2 className="text-base-content text-2xl font-medium content text-center max-w-6xl mx-auto px-6">
Are you ready for fresh South West produce to be delivered to Are you ready for fresh South West produce to be delivered to
your door? your door?
</h2> </h2>
@ -158,8 +158,8 @@ function PriceCard({
</div> </div>
<div className="flex flex-col mx-auto mt-auto"> <div className="flex flex-col mx-auto mt-auto">
{!loading && ( {!loading && (
<div className="flex flex-row mx-auto gap-x-4 justify-center items-center mb-2"> <div className="flex flex-row mx-auto gap-x-2 justify-center items-center mb-2">
<h1 className="text-base-content "> <h1 className="text-base-content text-4xl font-bold">
$ $
{isAnnual {isAnnual
? (product?.yearlyPrice?.unit_amount ?? 0) / 100 ? (product?.yearlyPrice?.unit_amount ?? 0) / 100
@ -177,7 +177,7 @@ function PriceCard({
isAnnual ? product.yearlyPrice : product.monthlyPrice isAnnual ? product.yearlyPrice : product.monthlyPrice
) )
} }
className=" mx-auto flex text-sm font-semibold py-2 px-20 m-2 bg-gradient-to-r from-primary to-secondary rounded-full mb-4 cursor-pointer group-hover:animate-bounce" className="btn btn-primary rounded-full bg-gradient-to-r from-primary to-secondary"
> >
Try it! Try it!
</button> </button>

View File

@ -4,26 +4,6 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
:root { h1, h2, h3, h4, h5, h6 {
--foreground-rgb: 0, 0, 0; font-family: var(--heading-font);
--background-start-rgb: 214, 219, 220; }
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

View File

@ -1,6 +1,6 @@
import "./globals.css"; import "./globals.css";
import type { Metadata } from "next"; import type { Metadata } from "next";
import "@/styles/style.css"; import "../app/globals.css";
import { Arimo, Raleway } from "next/font/google"; import { Arimo, Raleway } from "next/font/google";
import { ToastContainer, toast } from "react-toastify"; import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
@ -11,7 +11,7 @@ import { GTagProvider, PHProvider, ThemeProvider } from "./providers";
import PrelineScript from "@/components/Utilities/PrelineScript"; import PrelineScript from "@/components/Utilities/PrelineScript";
const raleway = Raleway({ const raleway = Raleway({
variable: "--display-font", variable: "--heading-font",
subsets: ["latin"], subsets: ["latin"],
}); });
@ -33,7 +33,11 @@ export default async function RootLayout({
const isUserLoggedIn = await isAuthenticated(cookies()); const isUserLoggedIn = await isAuthenticated(cookies());
return ( return (
<html lang="en" className="h-full" suppressHydrationWarning> <html
lang="en"
className={`h-full ${raleway.variable} ${arimo.variable}`}
suppressHydrationWarning
>
<PHProvider> <PHProvider>
<GTagProvider /> <GTagProvider />
<body className={`${arimo.className} bg-base-100 flex`}> <body className={`${arimo.className} bg-base-100 flex`}>

View File

@ -30,7 +30,9 @@ function BlogCard({
/> />
</div> </div>
<div className="w-full h-2/5 "> <div className="w-full h-2/5 ">
<h1 className="pt-4 pb-2 xl:pt-8 text-base-content ">{title}</h1> <h1 className="pt-4 pb-2 xl:pt-8 text-base-content text-xl font-bold ">
{title}
</h1>
<p className="text-base-content ">{subtitle}</p> <p className="text-base-content ">{subtitle}</p>
</div> </div>
</Link> </Link>

View File

@ -1,61 +0,0 @@
import { ModalStatus } from "@/types";
import React from "react";
interface ButtonProps {
status: ModalStatus;
text: string;
}
export const Button = ({ status, text }: ButtonProps) => {
const className = (() => {
switch (status) {
case ModalStatus.Loading:
return "btn text-primary-base-content bg-base-100 hover:bg-base-100 shadow disabled:bg-base-content disabled:shadow-none";
case ModalStatus.Success:
return "btn text-primary-base-content bg-base-100 hover:bg-base-100 shadow disabled:bg-base-content disabled:shadow-none";
case ModalStatus.Default:
return "btn text-primary-base-content bg-base-100 hover:bg-base-100 shadow";
default:
return "btn text-primary-base-content bg-base-100 hover:bg-base-100 shadow";
}
})();
return (
<button
type="submit"
className={className}
disabled={
status === ModalStatus.Success || status === ModalStatus.Loading
}
>
{status === ModalStatus.Loading ? (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
className="animate-spin -ml-1 mr-3 h-5 w-5 bg-base-content"
viewBox="0 0 24 24"
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
className="opacity-25"
></circle>
<path
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
className="opacity-75"
></path>
</svg>
) : (
<></>
)}
{text}
</button>
);
};
export default Button;

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import React from "react"; import React from "react";
import Icon from "./icon"; import Icon from "./Icon";
function Footer() { function Footer() {
return ( return (

View File

@ -2,8 +2,8 @@
import React from "react"; import React from "react";
import Navigation from "./Navigation"; import Navigation from "./Navigation";
import ModalSignUp from "@/sections/ModalSignUp/ModalSignUp"; import ModalSignUp from "@/components/Modals/ModalSignUp";
import ModalSignIn from "@/sections/ModalSignIn/ModalSignIn"; import ModalSignIn from "@/components/Modals/ModalSignIn";
import { logout } from "@/app/(auth)/actions"; import { logout } from "@/app/(auth)/actions";
import { getAuthCookie } from "@/app/(auth)/actions"; import { getAuthCookie } from "@/app/(auth)/actions";
import { SourceModal } from "@/types"; import { SourceModal } from "@/types";

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import Icon from "./icon"; import Icon from "./Icon";
import Link from "next/link"; import Link from "next/link";
interface LogoProps { interface LogoProps {

View File

@ -1,29 +0,0 @@
import React from "react";
const Modal = () => {
return (
<>
{/* You can open the modal using document.getElementById('ID').showModal() method */}
<button
className="btn"
onClick={() => document.getElementById("my_modal_4").showModal()}
>
open modal
</button>
<dialog id="my_modal_4" className="modal">
<div className="modal-box w-11/12 max-w-5xl">
<h3 className="font-bold text-lg">Hello!</h3>
<p className="py-4">Click the button below to close</p>
<div className="modal-action">
<form method="dialog">
{/* if there is a button, it will close the modal */}
<button className="btn">Close</button>
</form>
</div>
</div>
</dialog>
</>
);
};
export default Modal;

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import Icon from "@/components/icon"; import Icon from "@/components/Icon";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import colors from "@/utils/colors"; import colors from "@/utils/colors";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
@ -7,8 +7,6 @@ import { yupResolver } from "@hookform/resolvers/yup";
import { signInValidationSchema } from "@/utils/form"; import { signInValidationSchema } from "@/utils/form";
function ModalSignIn() { function ModalSignIn() {
const { theme } = useTheme();
const color = colors[theme ?? "light"]["base-content"];
const { const {
register, register,
handleSubmit, handleSubmit,
@ -19,23 +17,27 @@ function ModalSignIn() {
}); });
return ( return (
<> <>
<input type="checkbox" id="sign-in-modal" className="modal-toggle" /> <input
type="checkbox"
id="sign-in-modal"
className="modal-toggle"
onClick={() => {
reset();
}}
/>
<label htmlFor="sign-in-modal" className="modal cursor-pointer"> <label htmlFor="sign-in-modal" className="modal cursor-pointer">
<label className="modal-box relative bg-base-100 max-w-full md:max-w-[550px] py-4 px-3 md:p-6"> <label className="modal-box relative bg-base-100 max-w-full md:max-w-[550px] py-4 px-3 md:p-6">
<div className="flex justify-end pb-2 select-none"> <div className="flex justify-end pb-2 select-none">
<label <label
htmlFor="sign-in-modal" htmlFor="sign-in-modal"
className="cursor-pointer" className="cursor-pointer text-base-content"
onClick={() => {
reset();
}}
> >
<Icon name="Dismiss20Filled" size="medium" color={color} /> <Icon name="Dismiss20Filled" size="medium" />
</label> </label>
</div> </div>
<div> <div>
<div <div
className="w-[100%] bg-gradient-to-r from-primary to-secondary px-6 pt-2 mt-3 pb-6 rounded-lg" className="w-[100%] bg-gradient-to-r from-primary to-secondary px-6 pt-2 mt-3 pb-6 rounded-lg text-primary-content"
data-aos="fade-up" data-aos="fade-up"
> >
<h3 className="pb-1 text-3xl font-bold md:text-3xl pt-6"> <h3 className="pb-1 text-3xl font-bold md:text-3xl pt-6">

View File

@ -1,10 +1,5 @@
"use client"; import React from "react";
// TODO: Needs a complete rework to use the DaisyUI component import Icon from "@/components/Icon";
import React, { useRef, useState } from "react";
import { ModalStatus } from "@/types";
import { FormRefMethods } from "../ModalSignIn/ModalSignInForm";
import Icon from "@/components/icon";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { signUpValidationSchema } from "@/utils/form"; import { signUpValidationSchema } from "@/utils/form";
@ -26,14 +21,11 @@ function ModalSignUp() {
id="sign-up-modal" id="sign-up-modal"
className="modal-toggle" className="modal-toggle"
name="" name=""
/>
<label
htmlFor="sign-up-modal"
className="modal cursor-pointer"
onClick={() => { onClick={() => {
reset(); reset();
}} }}
> />
<label htmlFor="sign-up-modal" className="modal cursor-pointer">
<label className="modal-box relative max-w-full md:max-w-[550px] py-4 px-3 md:p-6"> <label className="modal-box relative max-w-full md:max-w-[550px] py-4 px-3 md:p-6">
<div className="flex justify-end pb-2 select-none"> <div className="flex justify-end pb-2 select-none">
<label <label
@ -48,7 +40,7 @@ function ModalSignUp() {
</div> </div>
<div className="flex flex-grow flex-col h-[30rem] lg:h-full overflow-y-scroll"> <div className="flex flex-grow flex-col h-[30rem] lg:h-full overflow-y-scroll">
<div <div
className="w-[100%] bg-gradient-to-r from-primary to-secondary px-6 mt-3 pb-6 rounded-lg" className="w-[100%] bg-gradient-to-r from-primary to-secondary px-6 mt-3 pb-6 rounded-lg text-primary-content"
data-aos="fade-up" data-aos="fade-up"
> >
<h3 className="pb-1 text-3xl font-bold md:text-3xl pt-6"> <h3 className="pb-1 text-3xl font-bold md:text-3xl pt-6">

View File

@ -31,7 +31,7 @@ function Navigation({ isUserLoggedIn }: { isUserLoggedIn: boolean }) {
</div> </div>
<div className="flex-none hidden lg:block"> <div className="flex-none hidden lg:block">
<ul className="menu menu-horizontal items-center text-base-content"> <ul className="menu menu-horizontal items-center text-base-content font-medium">
{/* Site branding */} {/* Site branding */}
<div className="shrink-0 mr-4"> <div className="shrink-0 mr-4">
{/* Logo */} {/* Logo */}

View File

@ -4,12 +4,11 @@ import {
createManagementSubscriptionSession, createManagementSubscriptionSession,
getSubscriptions, getSubscriptions,
} from "@/app/(auth)/actions"; } from "@/app/(auth)/actions";
import { ModalStatus, Subscription, User } from "@/types"; import { Subscription, User } from "@/types";
import React from "react"; import React from "react";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import Button from "@/components/Button";
import { useQRCode } from "next-qrcode"; import { useQRCode } from "next-qrcode";
interface ManageSubscriptionProps { interface ManageSubscriptionProps {
@ -69,9 +68,7 @@ function AccountContent({ user }: ManageSubscriptionProps) {
}; };
return loading ? ( return loading ? (
<div className="flex flex-col"> <div className="flex flex-col"></div>
<Button status={ModalStatus.Loading} text="Loading..." />
</div>
) : ( ) : (
<div className="flex flex-col"> <div className="flex flex-col">
{/* //TODO: Create Application Component */} {/* //TODO: Create Application Component */}

View File

@ -4,7 +4,7 @@ import Image from "next/image";
function BlogContent({ post }: { post: any }) { function BlogContent({ post }: { post: any }) {
return ( return (
<div className="bg-base-200 mx-4 lg:mx-0 px-8 py-12 rounded-lg"> <div className="bg-base-200 mx-4 lg:mx-0 px-8 py-12 rounded-lg">
<h1 className="prose text-4xl font-bold text-base-content"> <h1 className="font-heading text-4xl font-bold text-base-content">
{post.title} {post.title}
</h1> </h1>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@ -30,14 +30,6 @@ const FormLeftDescriptionRightContactUs = () => {
{/* Contact Us */} {/* Contact Us */}
<div className="max-w-[85rem] px-4 sm:px-6 lg:px-8 mx-auto pb-10"> <div className="max-w-[85rem] px-4 sm:px-6 lg:px-8 mx-auto pb-10">
<div className="max-w-2xl lg:max-w-5xl mx-auto"> <div className="max-w-2xl lg:max-w-5xl mx-auto">
<div className="text-center">
<h1 className="font-bold text-base-content sm:text-4xl ">
Contact us
</h1>
<p className="mt-1 text-base-content ">
We&apos;d love to talk about how we can help you.
</p>
</div>
<div className="mt-12 grid items-start lg:grid-cols-2 gap-6 lg:gap-16"> <div className="mt-12 grid items-start lg:grid-cols-2 gap-6 lg:gap-16">
{/* Card */} {/* Card */}
<div className="flex flex-col rounded-xl p-4 sm:p-6 lg:p-8 bg-base-100 min-h-[20rem]"> <div className="flex flex-col rounded-xl p-4 sm:p-6 lg:p-8 bg-base-100 min-h-[20rem]">

View File

@ -4,7 +4,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import Icon from "@/components/icon"; import Icon from "@/components/Icon";
import colors, { hexToRgb } from "@/utils/colors"; import colors, { hexToRgb } from "@/utils/colors";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
@ -38,7 +38,7 @@ const WaitingListWithImageHero = () => {
> >
<div className="h-screen w-full bg-clip flex items-center justify-center"> <div className="h-screen w-full bg-clip flex items-center justify-center">
<div className="text-center py-8 px-4 sm:px-6 lg:px-8"> <div className="text-center py-8 px-4 sm:px-6 lg:px-8">
<h1 className="text-3xl text-base-content sm:text-4xl whitespace-nowrap flex flex-row justify-center pb-6 gap-x-3"> <h1 className="font-semibold text-3xl sm:text-6xl text-base-content font-heading whitespace-nowrap flex flex-row justify-center pb-6 gap-x-3">
<Icon name="LeafThree24Filled" size="xlarge" /> <Icon name="LeafThree24Filled" size="xlarge" />
Bethel Farms Bethel Farms
</h1> </h1>
@ -186,7 +186,7 @@ const WaitingListWithImageHero = () => {
</form> </form>
</> </>
) : ( ) : (
<h2 className="text-2xl text-base-content sm:text-4xl"> <h2 className="sm:text-3xl font-body text-base-content">
You are on our waiting list. We will be in touch! You are on our waiting list. We will be in touch!
</h2> </h2>
)} )}

View File

@ -1,73 +0,0 @@
import React from "react";
import ModalLearnMore from "./ModalLearnMore/ModalLearnMore";
import { SourceModal } from "@/types";
import { Raleway } from "next/font/google";
const raleway = Raleway({
variable: "--display-font",
subsets: ["latin"],
});
function HeroHome() {
const bookDemoOnClick = () => {
const signUpModal = document.getElementById("sign-up-modal");
if (!signUpModal) return;
signUpModal.setAttribute("name", SourceModal.BookDemo);
signUpModal.removeAttribute("price_id");
signUpModal.click();
};
const learnMoreOnClick = () => {
const learnMoreModal = document.getElementById("learn-more-modal");
if (!learnMoreModal) return;
learnMoreModal.click();
};
return (
<section>
<div className="max-w-6xl mx-auto px-4 sm:px-6 relative">
{/* Hero content */}
<div className="relative pt-[12rem] pb-10 md:pt-40 md:pb-16">
{/* Section header */}
<div className="max-w-3xl mx-auto text-center pb-12 md:pb-10">
<h1
className={`${raleway.className} text-black font-raleway text-3xl leading-10 lg:py-12 tracking-[-1px] select-none w-[fit-content] md:leading-[6rem] m-auto mb-4 lg:mb-0 md:text-[5.5rem]`}
data-aos="fade-up"
>
Your Offline Digital Forms Simplified.
</h1>
<p
className="text-lg font-semibold pb-12 mb-8 md:text-4xl text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary w-[fit-content] mx-auto"
data-aos="fade-up"
data-aos-delay="200"
>
Sign365
</p>
<div className="max-w-xs mx-auto mt-[8rem] sm:max-w-none sm:flex sm:justify-center md:mt-auto">
<div data-aos="fade-up" data-aos-delay="400">
<button
onClick={bookDemoOnClick}
className="btn text-base capitalize rounded bg-base-content bg-secondary hover:bg-pink-700 w-full mb-4 sm:w-auto sm:mb-0"
>
Book Demo
</button>
</div>
<div data-aos="fade-up" data-aos-delay="600">
<button
onClick={learnMoreOnClick}
className="btn text-base capitalize rounded bg-base-content bg-gray-825 hover:bg-gray-650 w-full sm:w-auto sm:ml-4"
>
Learn more
</button>
</div>
</div>
</div>
</div>
</div>
<ModalLearnMore />
</section>
);
}
export default HeroHome;

View File

@ -1,63 +0,0 @@
"use client";
import React, { useState } from "react";
import SuccessModal from "../../sections/SuccessModal";
import LoadingModal from "../../sections/LoadingModal";
import xButton from "@/images/icon-x.svg";
import ModalLearnMoreForm from "../../sections/ModalLearnMore/ModalLearnMoreForm";
import Image from "next/image";
import { ModalStatus } from "@/types";
function ModalLearnMore() {
const [status, setStatus] = useState<ModalStatus>(ModalStatus.Default);
const whenModalOpens = () =>
setTimeout(() => setStatus(ModalStatus.Default), 500);
return (
<>
<input
type="checkbox"
id="learn-more-modal"
className="modal-toggle"
onChange={whenModalOpens}
/>
<label htmlFor="learn-more-modal" className="modal cursor-pointer">
<label className="modal-box relative bg-gray-850 max-w-full md:max-w-[550px] py-4 px-3 md:p-6">
<div className="flex justify-end pb-2 select-none">
<label
onClick={() => setStatus(ModalStatus.Default)}
htmlFor="learn-more-modal"
className="cursor-pointer"
>
<Image
className="rounded-full p-1 hover:bg-gray-500"
src={xButton}
alt="learnmore"
/>
</label>
</div>
<div className={status === "default" ? "block" : "hidden"}>
<div
className="w-[100%] bg-gradient-to-r from-primary to-secondary px-6 pt-2 pb-6"
data-aos="fade-up"
>
<h3 className="bg-base-content pt-4 pb-2 text-lg md:text-3xl pt-6">
Want to learn more? Get the no-nonsense facts straight from the
source
</h3>
<p className="text-xs bg-base-content md:text-base">
Enter your email and we will email you a copy of our Whitepaper
</p>
</div>
<ModalLearnMoreForm setStatus={setStatus} />
</div>
{status === ModalStatus.Success && <SuccessModal />}
{status === ModalStatus.Loading && <LoadingModal />}
</label>
</label>
</>
);
}
export default ModalLearnMore;

View File

@ -1,113 +0,0 @@
"use client";
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { learnMoreValidationSchema } from "@/utils/form";
import { LearnMoreForm, ModalStatus, SourceModal } from "@/types";
import { mailchimp } from "@/app/(auth)/actions";
import { toast } from "react-toastify";
interface ModalLearnMoreFormProps {
setStatus: React.Dispatch<React.SetStateAction<ModalStatus>>;
}
const ModalLearnMoreForm = ({ setStatus }: ModalLearnMoreFormProps) => {
const [email, setEmail] = useState("");
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(learnMoreValidationSchema),
});
const submitForm = async (data: LearnMoreForm) => {
setEmail(data.email);
if (!data.email) {
return;
}
setStatus(ModalStatus.Loading);
try {
await sendMailchimpRequest(data);
setEmail("");
reset();
// await handleSendgridSubmit(data.email);
setStatus(ModalStatus.Success);
} catch (error) {
if (error instanceof Error) {
toast.error(error.message, {
position: "bottom-left",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "colored",
});
}
setStatus(ModalStatus.Default);
}
};
const sendMailchimpRequest = async (data: LearnMoreForm) => {
console.log("sendMailchimpRequest call initiated");
await mailchimp({
email: data.email,
first_name: "",
last_name: "",
phone_number: "",
company_size: "",
source: SourceModal.LearnMore,
});
console.log("sendMailchimpRequest call success");
};
const handleSendgridSubmit = async (email: string) => {
const data = {
subscriberEmail: email,
};
//call to the Netlify Function you created
return fetch("./.netlify/functions/triggerLearnMoreEmail", {
method: "POST",
body: JSON.stringify({
subscriberEmail: data.subscriberEmail,
}),
});
};
return (
<form onSubmit={handleSubmit(submitForm)} className="w-full md:w-1/2">
<div className="relative mt-3 mb-2">
<input
{...register("email")}
id="learnMoreEmail"
type="text"
className="w-full appearance-none bg-transparent border border-white focus:border-white rounded-sm px-4 mr-2 bg-base-content placeholder-white"
placeholder="Your best email…"
aria-label="Your best email…"
autoComplete="on"
/>
<label
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
htmlFor="learnMoreEmail"
>
{errors.email ? "*" + errors.email.message : ""}
</label>
</div>
<button
type="submit"
className="btn capitalize font-bold text-black bg-base-100 hover:bg-white shadow"
>
Submit
</button>
</form>
);
};
export default ModalLearnMoreForm;

View File

@ -1,117 +0,0 @@
"use client";
import React, { useState, forwardRef, useImperativeHandle } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { signInValidationSchema } from "@/utils/form";
import { login } from "@/app/(auth)/actions";
import { ModalStatus, SignInForm, TextOnUse } from "@/types";
import { useRouter } from "next/navigation";
export interface FormRefMethods {
resetForm: () => void;
}
interface ModalSignInFormProps {
textOnUse: TextOnUse;
setStatus: React.Dispatch<React.SetStateAction<ModalStatus>>;
}
const ModalSignInForm = forwardRef<FormRefMethods, ModalSignInFormProps>(
({ textOnUse, setStatus }: ModalSignInFormProps, ref) => {
// const context = useContext(Context);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [organisation, setOrganisation] = useState("");
const router = useRouter();
const {
register,
handleSubmit,
reset,
setError,
clearErrors,
formState: { errors },
} = useForm({
resolver: yupResolver(signInValidationSchema),
});
const submitForm = async (data: SignInForm) => {
console.log("data", data);
clearErrors();
setEmail(data.email);
setPassword(!data.password ? "" : data.password);
setStatus(ModalStatus.Loading);
if (!data.password) return;
let response = undefined;
try {
console.log("before login");
response = await login({ email: data.email, password: data.password });
if (!response.success) {
throw new Error(response.error);
}
const signInModal = document.getElementById("sign-in-modal");
if (!!signInModal) signInModal.click();
router.push("/account");
} catch (error) {
setStatus(ModalStatus.Default);
console.log(error);
setError("email", { type: "custom", message: "Invalid Credentials" });
}
};
useImperativeHandle(ref, () => ({
resetForm: () => {
reset();
setEmail("");
setPassword("");
setOrganisation("");
},
}));
return (
<form onSubmit={handleSubmit(submitForm)} className="w-full md:w-1/2">
<div className="relative mt-3">
<input
{...register("email")}
id="SignInEmail"
type="text"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Your email…"
aria-label="Your email…"
autoComplete="on"
/>
<label
htmlFor="SignInEmail"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.email ? "*" + errors.email.message : ""}
</label>
</div>
<div className="relative mt-1 mb-4">
<input
{...register("password")}
id="SignInPwd"
type="password"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Your password..."
aria-label="Your password..."
/>
<label
htmlFor="SignInPwd"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.password ? "*" + errors.password.message : ""}
</label>
</div>
<button
type="submit"
className="btn capitalize font-bold text-black bg-base-100 hover:bg-base-100 shadow"
>
{textOnUse.buttonText}
</button>
</form>
);
}
);
ModalSignInForm.displayName = "ModalSignInForm";
export default ModalSignInForm;

View File

@ -1,317 +0,0 @@
"use client";
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useState,
} from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { signUpValidationSchema } from "@/utils/form";
import {
login,
signup,
createCheckoutSession,
mailchimp,
} from "@/app/(auth)/actions";
import { CheckoutSession, ModalStatus, SignUpForm, TextOnUse } from "@/types";
import { toast } from "react-toastify";
import { useRouter } from "next/navigation";
import { FormRefMethods } from "../ModalSignIn/ModalSignInForm";
interface ModalSignUpFormProps {
generateCheckoutSession: boolean;
textOnUse: TextOnUse;
companySizeList: string[];
setStatus: React.Dispatch<React.SetStateAction<ModalStatus>>;
getPriceId: () => string | undefined;
}
const ModalSignUpForm = forwardRef<FormRefMethods, ModalSignUpFormProps>(
(
{
generateCheckoutSession,
companySizeList,
textOnUse,
setStatus,
getPriceId,
}: ModalSignUpFormProps,
ref
) => {
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [organisation, setOrganisation] = useState("");
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(signUpValidationSchema),
});
const submitForm = async (data: SignUpForm) => {
setEmail(data.email);
setPassword(data.password!);
setOrganisation(data.organisation);
setStatus(ModalStatus.Loading);
try {
await sendMailchimpRequest(data);
await sendSignUpRequest(data);
await sendSignInRequest(data);
const price_id = getPriceId();
console.log("price_id", price_id);
const checkoutSession = await checkoutSessionRequest(price_id);
setEmail("");
setPassword("");
setOrganisation("");
reset();
setStatus(ModalStatus.Success);
if (!!checkoutSession) {
router.push(checkoutSession.url);
}
} catch (error) {
if (error instanceof Error) {
toast.error(error.message, {
position: "bottom-left",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "colored",
});
}
setStatus(ModalStatus.Default);
}
};
const sendMailchimpRequest = async (data: SignUpForm) => {
console.log("sendMailchimpRequest call initiated");
await mailchimp({
email: data.email,
first_name: data.first_name,
last_name: data.last_name,
phone_number: !data.phone_number ? "" : data.phone_number,
company_size: data.company_size,
source: textOnUse.source,
});
console.log("sendMailchimpRequest call success");
};
const checkoutSessionRequest = async (price_id?: string) => {
if (!price_id) {
return;
}
console.log("createCheckoutSession call initiated");
const checkoutSession = await createCheckoutSession(price_id);
console.log("createCheckoutSession successful");
return checkoutSession;
};
const sendSignUpRequest = async (data: SignUpForm) => {
try {
console.log("Signup call initiated");
await signup(data);
console.log("Signup successful");
} catch (error) {
if (error instanceof Error) {
console.error(
"Error during the signup or email process:",
error.message
);
}
throw error; // This will allow .catch block outside this function to capture the error
}
};
const sendSignInRequest = async (data: SignUpForm) => {
try {
console.log("SignIn call initiated");
await login({ email: data.email, password: data.password! });
console.log("SignIn successful");
} catch (error) {
if (error instanceof Error) {
console.error(
"Error during the SignIn or email process:",
error.message
);
}
throw error; // This will allow .catch block outside this function to capture the error
}
};
useImperativeHandle(ref, () => ({
resetForm: () => {
reset();
setEmail("");
setPassword("");
setOrganisation("");
},
}));
return (
<form onSubmit={handleSubmit(submitForm)} className="w-full md:w-1/2">
<div className="relative mt-3">
<input
{...register("email")}
type="text"
id="SignUpEmail"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Email…"
aria-label="Email…"
autoComplete="on"
/>
<label
htmlFor="SignUpEmail"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.email ? "*" + errors.email.message : ""}
</label>
</div>
<div className="relative mt-1">
<input
{...register("first_name")}
id="SignUpFirstName"
type="text"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black"
placeholder="First Name…"
aria-label="First Name…"
autoComplete="on"
/>
<label
htmlFor="SignUpFirstName"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.first_name ? "*" + errors.first_name.message : ""}
</label>
</div>
<div className="relative mt-1">
<input
{...register("last_name")}
id="SignUpLastName"
type="text"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Last Name…"
aria-label="Last Name…"
/>
<label
htmlFor="SignUpLastName"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.last_name ? "*" + errors.last_name.message : ""}
</label>
</div>
<div className="relative mt-1">
<input
{...register("phone_number")}
id="SignUpPhoneNumber"
type="text"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Phone Number…"
aria-label="Phone Number…"
/>
<label
htmlFor="SignUpPhoneNumber"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.phone_number ? "*" + errors.phone_number.message : ""}
</label>
</div>
<div className="relative mt-1">
<input
{...register("organisation")}
id="SignUpOrganisation"
type="text"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Organisation…"
aria-label="Organisation…"
/>
<label
htmlFor="SignUpOrganisation"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.organisation ? "*" + errors.organisation.message : ""}
</label>
</div>
<div className="relative mt-1">
<select
{...register("company_size")}
id="SignUpCompanySize"
defaultValue={""}
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
>
<option className="bg-gray-850" value={""} disabled>
Company Size
</option>
{companySizeList.map((companySizeOption, i) => {
return (
<option
className="bg-gray-850"
value={companySizeOption}
key={i}
>
{companySizeOption}
</option>
);
})}
</select>
<label
htmlFor="SignUpCompanySize"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.company_size ? "*" + errors.company_size.message : ""}
</label>
</div>
<div className="relative mt-1">
<input
{...register("password")}
id="SignUpPwd"
type="password"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Password..."
aria-label="Password"
/>
<label
htmlFor="SignUpPwd"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.password ? "*" + errors.password.message : ""}
</label>
</div>
<div className="relative mt-1 mb-4">
<input
{...register("passwordConfirmation")}
id="SignUpPwdConfirm"
type="password"
className="w-full appearance-none bg-transparent border rounded-sm px-4 mr-2 text-black placeholder-black border-black dark:placeholder-white dark:border-white"
placeholder="Confirmed Password..."
aria-label="Confirmed Password"
/>
<label
htmlFor="SignUpPwd"
className="md:absolute top-[50%] translate-y-[-50%] left-[105%] w-[220px] text-red-400"
>
{errors.passwordConfirmation
? "*" + errors.passwordConfirmation.message
: ""}
</label>
</div>
<button
type="submit"
className="btn capitalize font-bold text-black bg-base-100 hover:bg-base-100 shadow"
>
{textOnUse.buttonText}
</button>
</form>
);
}
);
ModalSignUpForm.displayName = "ModalSignUpForm";
export default ModalSignUpForm;

View File

@ -1,82 +0,0 @@
import { ModalStatus, SourceModal } from '@/types';
import { useState } from 'react';
function useTextsBasedOnActivityState(setStatus: React.Dispatch<React.SetStateAction<ModalStatus>>){
const signUpTexts = {
title: "We Are Growing Fast, and We Want You To Join The Party!",
subTitle: "Join our super awesome waitlist and be one of the first to know when spots open up!",
buttonText: "Sign Up",
netlifyFunction: "triggerSignUpEmail",
source: SourceModal.SignUp
}
const signUpViaPurchaseTexts = {
title: "Get Ready For Ultimate Productivity",
subTitle: "Excited to see what we've got! Signup and get started!",
buttonText: "Sign Up",
netlifyFunction: "triggerSignUpEmail",
source: SourceModal.SignUpViaPurchase
}
const bookDemoTexts = {
title: "Get Ready For Ultimate Productivity",
subTitle: "Excited to see what we've got! Signup and get started!",
buttonText: "Book Now",
netlifyFunction: "triggerBookNowEmail",
source: SourceModal.BookDemo
}
const TryItTexts = {
title: "Get Ready For Ultimate Productivity",
subTitle: "Excited to see what we've got! Send us your details and our marketing rep will schedule a demo ASAP.",
buttonText: "Try It",
netlifyFunction: "triggerBookNowEmail",
source: SourceModal.TryIt
}
const [textOnUse, setTextOnUse] = useState(signUpTexts)
const companySizeList = [
"1-10 employees",
"10-30 employees",
"30-70 employees",
"70-100 employees",
"100+ employees"
]
const whenModalOpens = () => {
const signUpModal = document.getElementById("sign-up-modal");
if (!signUpModal) return;
const nameAttribute = signUpModal.getAttribute("name");
switch(nameAttribute){
case SourceModal.BookDemo:
setTextOnUse(bookDemoTexts);
break;
case SourceModal.TryIt:
setTextOnUse(TryItTexts);
break;
case SourceModal.SignUp:
setTextOnUse(signUpTexts);
break;
case SourceModal.SignUpViaPurchase:
setTextOnUse(signUpViaPurchaseTexts)
break;
default:
setTextOnUse(bookDemoTexts);
break;
}
setTimeout(() => setStatus(ModalStatus.Default), 500);
}
const getPriceId = () => {
const signUpModal = document.getElementById("sign-up-modal");
if (!signUpModal) return undefined;
const price_id = signUpModal.getAttribute("price_id");
if (!price_id) return undefined;
return price_id;
}
return {textOnUse, companySizeList, whenModalOpens, getPriceId}
}
export default useTextsBasedOnActivityState;

View File

@ -5,7 +5,6 @@ import { NewsLetterForm, SourceModal } from "@/types";
import { mailchimp } from "@/app/(auth)/actions"; import { mailchimp } from "@/app/(auth)/actions";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { ModalStatus } from "@/types"; import { ModalStatus } from "@/types";
import Button from "@/components/Button";
const NewsletterForm = () => { const NewsletterForm = () => {
const [status, setStatus] = useState<ModalStatus>(ModalStatus.Default); const [status, setStatus] = useState<ModalStatus>(ModalStatus.Default);
@ -77,7 +76,7 @@ const NewsletterForm = () => {
return ( return (
<> <>
<div className="mb-6 lg:mr-16 lg:mb-0 text-center lg:text-left lg:w-1/2"> <div className="mb-6 lg:mr-16 lg:mb-0 text-center lg:text-left lg:w-1/2 text-primary-content">
<h3 className=" mb-2 text-3xl font-black"> <h3 className=" mb-2 text-3xl font-black">
{status !== ModalStatus.Success {status !== ModalStatus.Success
? "Stay Ahead of the Curve" ? "Stay Ahead of the Curve"
@ -93,9 +92,17 @@ const NewsletterForm = () => {
<div className="w-full lg:w-1/2"> <div className="w-full lg:w-1/2">
<form <form
onSubmit={(e) => submitForm(e)} onSubmit={(e) => submitForm(e)}
className="flex flex-col sm:flex-row justify-center max-w-xs mx-auto sm:max-w-md lg:max-w-none" className="flex flex-col sm:flex-row justify-center max-w-xs mx-auto sm:max-w-md lg:max-w-none gap-x-2"
> >
<input <input
id="NewsletterEmail"
type="text"
className="py-3 px-4 block w-full text-base-content border-white rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none "
placeholder="Email…"
aria-label="Email…"
// {...register("email")}
/>
{/* <input
required required
name="email" name="email"
type="email" type="email"
@ -106,22 +113,8 @@ const NewsletterForm = () => {
aria-label="Your best email…" aria-label="Your best email…"
disabled={status === ModalStatus.Success} disabled={status === ModalStatus.Success}
autoComplete="on" autoComplete="on"
/> /> */}
<Button <button className="btn btn-primary">Subscribe</button>
status={status}
text={(() => {
switch (status) {
case ModalStatus.Loading:
return "Sending...";
case ModalStatus.Success:
return "Success";
case ModalStatus.Default:
return "Subscribe";
default:
return "default-class";
}
})()}
/>
</form> </form>
</div> </div>
</> </>

View File

@ -7,7 +7,7 @@ interface PageHeaderProps {
function PageHeader({ title, subtitle }: PageHeaderProps) { function PageHeader({ title, subtitle }: PageHeaderProps) {
return ( return (
<div className="pt-28 max-w-screen flex flex-col"> <div className="pt-28 max-w-screen flex flex-col">
<h1 className="text-5xl font-black text-base-content mb-12 mx-auto"> <h1 className="text-5xl font-bold text-base-content mb-6 mx-auto">
{title} {title}
</h1> </h1>
{subtitle} {subtitle}

View File

@ -1,19 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Inter:wght@400;500;600;700;800;900&display=fallback');
@import url('https://fonts.googleapis.com/css2?family=Arimo:wght@400;500;600;700;800;900&display=fallback');
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
/* Additional Tailwind directives: https://tailwindcss.com/docs/functions-and-directives/#responsive */
@layer utilities {
.rtl {
direction: rtl;
}
}
/* See Alpine.js: https://github.com/alpinejs/alpine#x-cloak */
[x-cloak=""] {
display: none;
}

View File

@ -11,57 +11,9 @@ const config: Config = {
], ],
theme: { theme: {
extend: { extend: {
colors: {
gray: {
100: "#EBF1F5",
200: "#D9E3EA",
300: "#C5D2DC",
400: "#9BA9B4",
500: "#707D86",
550: "#52485B",
600: "#55595F",
650: "#4c5058",
700: "#33363A",
800: "#25282C",
825: "#34363A",
850: "#383040",
900: "#151719",
925: "#191D24",
950: "#000000",
},
purple: {
default: "#8000FF",
100: "#F4F4FF",
200: "#E2E1FF",
300: "#CBCCFF",
400: "#ABABFF",
500: "#8D8DFF",
600: "#5D5DFF",
700: "#4B4ACF",
800: "#38379C",
900: "#262668",
},
pink: {
default: "#FF0DCA",
500: "#D18DFF",
550: "#8A4CC8",
600: "#AB4FD7",
700: "#7a2ec5",
900: "#3C3445",
},
red: {
300: "#C84C4C",
600: "#8b2e2e",
},
},
spacing: {
"9/16": "56.25%",
"3/4": "75%",
"1/1": "100%",
},
fontFamily: { fontFamily: {
primary: ["Raleway Variable", ...defaultTheme.fontFamily.sans], heading: 'var(--heading-font)',
secondary: ["Arimo Variable", ...defaultTheme.fontFamily.sans], body: 'var(--heading-font)',
}, },
fontSize: { fontSize: {
xs: "0.75rem", xs: "0.75rem",
@ -75,9 +27,6 @@ const config: Config = {
"5xl": "3.25rem", "5xl": "3.25rem",
"6xl": "4rem", "6xl": "4rem",
}, },
inset: {
full: "100%",
},
letterSpacing: { letterSpacing: {
tighter: "-0.02em", tighter: "-0.02em",
tight: "-0.01em", tight: "-0.01em",
@ -86,12 +35,33 @@ const config: Config = {
wider: "0.02em", wider: "0.02em",
widest: "0.4em", widest: "0.4em",
}, },
minWidth: { typography: {
"10": "2.5rem", DEFAULT: {
}, css: {
scale: { p: {
"98": ".98", fontFamily: 'var(--body-font)'
}, },
h1: {
fontFamily: 'var(--heading-font)'
},
h2: {
fontFamily: 'var(--heading-font)'
},
h3: {
fontFamily: 'var(--heading-font)'
},
h4: {
fontFamily: 'var(--heading-font)'
},
h5: {
fontFamily: 'var(--heading-font)'
},
h6: {
fontFamily: 'var(--heading-font)'
}
}
}
}
}, },
}, },
daisyui: { daisyui: {