Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lead Form - Página de vendas #203

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions app/lib/models/lead.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,32 @@ export async function registerChallengeLead(
};
}
}

export async function registerLead(request: Request, email: string) {
return axios
.post(
`${environment().API_HOST}/leads`,
{
email,
tags: ["lead-assine"],
},
{},
)
.then((res) => {
return { success: "Cadastro realizado com sucesse!" };
})
.catch((error) => {
let errorMsg =
"Não foi possível realizar o cadastro. Por favor, tente novamente.";

if (error.response.status === 409) {
errorMsg = "Esse email já está cadastrado em nossa lista.";
} else if (error.response.status === 422) {
errorMsg = "Por favor, insira um email válido.";
}

return {
error: errorMsg,
};
});
}
42 changes: 42 additions & 0 deletions app/routes/_api/leads/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { abort404 } from "~/lib/utils/responses.server";
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
import NotFound from "~/components/features/error-handling/not-found";
import { Error500 } from "~/components/features/error-handling/500";
import { registerChallengeLead, registerLead } from "~/lib/models/lead.server";

export async function action({ request }: { request: Request }) {
const formData = await request.formData();
const intent = formData.get("intent") as string;

const email = formData.get("email") as string;
// const tag = formData.get("tag") as string | undefined;

switch (intent) {
case "register-lead-assine":
return registerLead(request, email);
case "register-lead-challenge":
return registerChallengeLead(request, email);
}
}

export async function loader() {
return abort404();
}

export function ErrorBoundary() {
const error = useRouteError();

if (isRouteErrorResponse(error)) {
return (
<div>
<NotFound />
</div>
);
}

return <Error500 error={error} />;
}

export default function Leads() {
return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ import {
SelectValue,
} from "~/components/ui/select";

import MobileSignupForm from "~/routes/_layout-app/_mini-projetos/mini-projetos_.$slug_/components/mobile-signup-form";
import { registerChallengeLead } from "~/lib/models/lead.server";
import MobileSignupForm from "./components/mobile-signup-form";

export const meta = ({ data, params }: any) => {
// para não quebrar se não houver challenge ainda.
Expand Down Expand Up @@ -123,10 +122,6 @@ export async function action({ request }: ActionFunctionArgs) {
case "requestCertificate":
const certifiableId = formData.get("certifiable_id") as string;
return requestCertificate(request, "ChallengeUser", certifiableId);
case "register-lead":
const email = formData.get("email") as string;

return registerChallengeLead(request, email);
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { useLocation } from "@remix-run/react";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import ResponsiveEmailSignup from "~/components/features/auth/responsive-email-signup";
import ResponsiveEmailSignup from "./responsive-email-signup";
import { useMediaQuery } from "~/lib/hooks/use-media-query";
import type { User } from "~/lib/models/user.server";

function MobileSignupForm({ user }: { user: User | null }) {
const isMobile = useMediaQuery("(max-width: 768px)");
const location = useLocation();

const slug = location.pathname.split("mini-projetos/")[1];

const [hideOnMobile, setHideOnMobile] = useState(false);
useEffect(() => {
Expand All @@ -29,7 +25,7 @@ function MobileSignupForm({ user }: { user: User | null }) {
}}
className="w-full bottom-0 h-20 z-20 bg-gradient-to-tr animate-bg from-background-50 to-background-100 border-background-100 dark:from-background-700 dark:to-background-900 fixed shadow-lg border-t dark:border-background-700 flex items-center justify-center"
>
<ResponsiveEmailSignup slug={slug} />
<ResponsiveEmailSignup />
</motion.section>
)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import {
Form,
useActionData,
useNavigation,
useSubmit,
} from "@remix-run/react";
import { useActionData, useFetcher } from "@remix-run/react";

import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { ResponsiveDialog } from "~/components/ui/responsive-dialog";
import LoadingButton from "~/components/features/form/loading-button";
import { useEffect } from "react";
import { useToasterWithSound } from "~/lib/hooks/useToasterWithSound";

export default function ResponsiveEmailSignup({ slug }: { slug: string }) {
const submit = useSubmit();
interface FetcherData {
error?: string;
success?: string;
}

export default function ResponsiveEmailSignup() {
const errors = useActionData();
const transition = useNavigation();
const fetcher = useFetcher();
const { showSuccessToast, showErrorToast } = useToasterWithSound();

const status = transition.state;
const status = fetcher.state;
let isSuccessfulSubmission = status === "idle" && errors === null;

const fetcherData = fetcher.data as FetcherData;
const errorMsg = fetcherData && fetcherData.error ? fetcherData.error : null;
const successMsg =
fetcherData && fetcherData.success ? fetcherData.success : null;

useEffect(() => {
if (successMsg) showSuccessToast(successMsg);
if (errorMsg) showErrorToast(errorMsg);
}, [successMsg, errorMsg, showErrorToast, showSuccessToast]);

async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
submit(event.currentTarget, {
fetcher.submit(event.currentTarget.form, {
method: "post",
action: `/mini-projetos/${slug}`,
});
}

Expand All @@ -33,7 +44,12 @@ export default function ResponsiveEmailSignup({ slug }: { slug: string }) {
triggerButtonSize="lg"
>
<>
<Form method="POST" className="flex flex-col" onSubmit={handleSubmit}>
<fetcher.Form
method="post"
className="flex flex-col"
onSubmit={handleSubmit}
action="/leads?index"
>
<Label htmlFor="email">Seu email</Label>
<Input name="email" id="email" type="email" className="mb-4" />

Expand All @@ -51,12 +67,12 @@ export default function ResponsiveEmailSignup({ slug }: { slug: string }) {
status={status}
isSuccessfulSubmission={isSuccessfulSubmission}
name="intent"
value="register-lead"
value="register-lead-challenge"
>
Receber instruções
</LoadingButton>
</div>
</Form>
</fetcher.Form>
</>
</ResponsiveDialog>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as React from "react";
import { Button } from "~/components/ui/button";

import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "~/components/ui/drawer";
import { useMediaQuery } from "~/lib/hooks/use-media-query";

interface ResponsiveDialogProps {
children: React.ReactNode;
triggerLabel?: string;
title: string;
description: string;
drawerCancelLabel?: string;
open?: boolean;
triggerClassName?: string;
onOpenChange?: (open: boolean) => void;
}

export function ResponsiveDialog({
children,
title,
description,
drawerCancelLabel = "Cancelar",
open,
onOpenChange,
}: ResponsiveDialogProps) {
const isDesktop = useMediaQuery("(min-width: 768px)");

const scroll = () => {
const section = document.querySelector("#price-card");
section?.scrollIntoView({ behavior: "smooth", block: "start" });
};
if (isDesktop) {
return (
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!isOpen) {
scroll();
}
if (onOpenChange) {
onOpenChange(isOpen);
}
}}
>
<DialogTrigger asChild>
<button className="relative inline-flex items-center justify-center text-lg lg:text-2xl px-10 py-4 overflow-hidden font-medium text-gray-100 bg-brand-500 rounded-lg group w-full lg:w-7/12">
<div className="absolute w-0 h-0 transition-all duration-500 ease-out bg-background-700 rounded-full group-hover:w-[105%] group-hover:h-56"></div>
<div className="absolute inset-0 w-full h-full -mt-1 rounded-lg opacity-30 bg-gradient-to-b from-transparent via-transparent to-gray-700"></div>
<span className="relative">
Quero ter acesso ao <span className="font-bold">Codante</span>{" "}
<span className="text-white font-semibold dark:text-gray-900 px-[3px] py-[2px] rounded bg-amber-500">
PRO
</span>
</span>
</button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
{children}
</DialogContent>
</Dialog>
);
}

return (
<Drawer
open={open}
onOpenChange={(isOpen) => {
if (!isOpen) {
scroll();
}
if (onOpenChange) {
onOpenChange(isOpen);
}
}}
>
<DrawerTrigger asChild>
<button
// onClick={scroll}
className="relative inline-flex items-center justify-center text-lg lg:text-2xl px-10 py-4 overflow-hidden font-medium text-gray-100 bg-brand-500 rounded-lg group w-full lg:w-7/12"
>
<div className="absolute w-0 h-0 transition-all duration-500 ease-out bg-background-700 rounded-full group-hover:w-[105%] group-hover:h-56"></div>
<div className="absolute inset-0 w-full h-full -mt-1 rounded-lg opacity-30 bg-gradient-to-b from-transparent via-transparent to-gray-700"></div>
<span className="relative">
Quero ter acesso ao <span className="font-bold">Codante</span>{" "}
<span className="text-white font-semibold dark:text-gray-900 px-[3px] py-[2px] rounded bg-amber-500">
PRO
</span>
</span>
</button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader className="text-left">
<DrawerTitle>{title}</DrawerTitle>
<DrawerDescription>{description}</DrawerDescription>
</DrawerHeader>
<div className="px-4">{children}</div>
<DrawerFooter className="pt-2">
<DrawerClose asChild>
<Button variant="secondary">{drawerCancelLabel}</Button>
</DrawerClose>{" "}
</DrawerFooter>
</DrawerContent>
</Drawer>
);
}
Loading