Merge branch 'development' of ssh://gitea.ecommercetools.com.br:22022/andreamatsunaga/desafio-react-e-typescript-andrea-matsunaga into feature/footer

This commit is contained in:
Andrea Matsunaga 2023-01-17 15:54:24 -03:00
commit b33c70b7be
32 changed files with 483 additions and 260 deletions

View File

@ -6,15 +6,19 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/date-fns": "^2.6.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.11",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/react-input-mask": "^3.0.2",
"apollo-client": "^2.6.10",
"date-fns": "^2.29.3",
"formik": "^2.2.9",
"react": "^18.2.0",
"react-apollo": "^3.1.5",
"react-dom": "^18.2.0",
"react-input-mask": "3.0.0-alpha.2",
"react-router-dom": "^6.6.1",
"react-scripts": "5.0.1",
"sass": "^1.57.1",

View File

@ -1,45 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
import styles from "./AsideMenu.module.scss";
const AsideMenu = () => {
const AsideMenuItems = [
{
name: "Sobre",
value: "sobre",
},
{
name: "Forma de Pagamento",
value: "forma-de-pagamento",
},
{
name: "Entrega",
value: "entrega",
},
{
name: "Troca e Devolução",
value: "troca-e-devolucao",
},
{
name: "Segurança e Privacidade",
value: "seguranca-e-privacidade",
},
{
name: "Contato",
value: "contato",
},
];
return (
<ul className={styles["menu-list"]}>
{AsideMenuItems.map((item, index) => (
<li key={index}>
<Link to={item.value}>{item.name}</Link>
</li>
))}
</ul>
);
};
export { AsideMenu };

View File

@ -100,7 +100,6 @@
}
span {
// font-family: "Proxima Nova"; Adobe Fonts
color: #ff0000;
}
@ -163,20 +162,22 @@
}
}
.success-message {
font-family: "Roboto";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 14px;
color: #008000;
position: absolute;
bottom: 0;
left: 0;
.success-wrapper {
.success-message {
font-family: "Roboto";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 14px;
color: #008000;
position: absolute;
bottom: 0;
left: 0;
@media screen and (width >= 2500px) {
font-size: 24px;
line-height: 28px;
@media screen and (width >= 2500px) {
font-size: 24px;
line-height: 28px;
}
}
}
}

View File

@ -1,26 +1,19 @@
import React, { useCallback, useState } from "react";
import {
Formik,
Form,
Field,
ErrorMessage,
// FormikHelpers,
// FormikBag,
// FormikState,
} from "formik";
import React from "react";
import { Formik, Form, Field, ErrorMessage, FormikHelpers } from "formik";
import FormSchema from "../../schema/FormSchema";
import "./ContactForm.module.scss";
import { CustomInput } from "../CustomInput/CustomInput";
import styles from "./ContactForm.module.scss";
interface IFormikValues {
interface FormValues {
name: string;
email: string;
cpf: string;
dateOfBirth: string;
phoneNumber: string;
instagram: string;
acceptTerms: boolean;
acceptedTerms: boolean;
}
const initialValues = {
@ -30,31 +23,22 @@ const initialValues = {
dateOfBirth: "",
phoneNumber: "",
instagram: "",
acceptTerms: false,
acceptedTerms: false,
};
let clientsList: Array<any> = [];
const ContactForm = () => {
const [hasSubmitBegun, setHasSubmitBegun] = useState(false);
const [hasSubmitCompleted, setHasSubmitCompleted] = useState(false);
const doSubmit = async (values: IFormikValues) =>
console.log("submitted", values);
const handleSubmit = useCallback(
async (values: IFormikValues, { resetForm }: any) => {
setHasSubmitBegun(true);
await doSubmit(values);
setHasSubmitCompleted(true);
resetForm({ ...initialValues });
},
[doSubmit]
);
// const handleSubmit = (values: IFormikValues) => {
// console.log(values);
// };
const handleSubmit = (
values: FormValues,
{ resetForm, setSubmitting }: FormikHelpers<FormValues>
) => {
console.log(values);
clientsList.push(values);
console.log(clientsList);
resetForm();
setTimeout(() => setSubmitting(false), 3000);
};
return (
<div className={styles["form-wrapper"]}>
@ -63,15 +47,16 @@ const ContactForm = () => {
initialValues={initialValues}
validationSchema={FormSchema}
>
{({ errors, touched, resetForm }) => (
{({
isSubmitting,
}) => (
<Form>
<div className={styles["form-col"]}>
<label htmlFor="name">Nome</label>
<Field
<CustomInput
id="name"
name="name"
placeholder="Seu nome completo"
// className={errors.name && touched.name && "invalid"}
/>
<ErrorMessage
component="span"
@ -81,12 +66,10 @@ const ContactForm = () => {
</div>
<div className={styles["form-col"]}>
<label htmlFor="email">E-mail</label>
<Field
<CustomInput
id="email"
name="email"
placeholder="Seu e-mail"
// className={errors.email && touched.email && `${styles["invalid"]}`}
// className={errors.email && touched.email && "invalid"}
/>
<ErrorMessage
component="span"
@ -95,12 +78,12 @@ const ContactForm = () => {
/>
</div>
<div className={styles["form-col"]}>
<label htmlFor="subject">CPF</label>
<Field
<label htmlFor="cpf">CPF</label>
<CustomInput
mask="999.999.999-99"
id="cpf"
name="cpf"
placeholder="000.000.000-00"
// className={errors.cpf && touched.cpf && "invalid"}
/>
<ErrorMessage
component="span"
@ -110,13 +93,11 @@ const ContactForm = () => {
</div>
<div className={styles["form-col"]}>
<label htmlFor="dateOfBirth">Data de Nascimento:</label>
{/* tem : só nesses campos? */}
<Field
// as="textarea"
<CustomInput
mask={"99.99.9999"}
id="dateOfBirth"
name="dateOfBirth"
placeholder="00.00.0000"
// className={errors.dateOfBirth && touched.dateOfBirth && "invalid"}
/>
<ErrorMessage
component="span"
@ -125,16 +106,12 @@ const ContactForm = () => {
/>
</div>
<div className={styles["form-col"]}>
<label htmlFor="message">Telefone:</label>
{/* tem : só nesses campos? */}
<Field
// as="textarea"
<label htmlFor="phoneNumber">Telefone:</label>
<CustomInput
mask={"(99) 99999-9999"}
id="phoneNumber"
name="phoneNumber"
placeholder="(00) 00000-0000"
// className={
// errors.phoneNumber && touched.phoneNumber && "invalid"
// }
/>
<ErrorMessage
component="span"
@ -143,12 +120,11 @@ const ContactForm = () => {
/>
</div>
<div className={styles["form-col"]}>
<label htmlFor="message">Instagram</label>
<Field
<label htmlFor="instagram">Instagram</label>
<CustomInput
id="instagram"
name="instagram"
placeholder="@seuuser"
// className={errors.instagram && touched.instagram && "invalid"}
/>
<ErrorMessage
component="span"
@ -159,32 +135,28 @@ const ContactForm = () => {
<div className={styles["terms-col"]}>
<span>* </span>
<label htmlFor="acceptTerms">Declaro que li e aceito</label>
<label htmlFor="acceptedTerms">Declaro que li e aceito</label>
{/* <div className={styles["custom-checkbox"]}></div> */}
<Field
type="checkbox"
id="acceptTerms"
name="acceptTerms"
// className={`${"invalid" ? styles["invalid"] : ""}`}
id="acceptedTerms"
name="acceptedTerms"
// className={
// errors.acceptTerms && touched.acceptTerms && "invalid"
// errors.acceptedTerms && touched.acceptedTerms && styles["invalid"]
// }
/>
{/* <ErrorMessage
component="span"
name="acceptTerms"
className={styles["invalid-form-feedback"]}
/> */}
</div>
<button className={styles["submit-button"]} type="submit">
Cadastre-se
</button>
{hasSubmitCompleted && (
<span className={styles["success-message"]}>
*Formulário enviado com sucesso!
</span>
{isSubmitting && (
<div className={styles["success-wrapper"]}>
<span className={styles["success-message"]}>
*Formulário enviado com sucesso!
</span>
</div>
)}
</Form>
)}

View File

@ -0,0 +1,23 @@
import React from "react";
import { useField } from "formik";
import InputMask from "react-input-mask";
interface InputProps {
mask?: string;
id: string;
name: string;
placeholder: string;
}
const CustomInput = (props: InputProps) => {
const { mask } = props;
const [field] = useField(props);
return (
<>
<InputMask mask={mask!} {...field} />
</>
);
};
export { CustomInput };

View File

@ -1,19 +1,51 @@
import { Formik, FormikHelpers } from "formik";
import React from "react";
import { Form } from "react-router-dom";
import NewsletterSchema from "../../../schema/NewsletterSchema";
import styles from "./Newsletter.module.scss";
interface FormValues {
email: string;
}
const initialValues = {
email: "",
};
const newsletterList: Array<any> = [];
const Newsletter = () => {
const handleSubmit = (
values: FormValues,
{ resetForm }: FormikHelpers<FormValues>
) => {
newsletterList.push(values);
console.log(newsletterList);
resetForm();
};
return (
<div className={styles["container"]}>
<h3 className={styles["title"]}>
{/* ASSINE NOSSA NEWSLETTER */}
assine nossa newsletter
</h3>
<form className={styles["form-wrapper"]}>
<input type="email" placeholder="E-mail" />
<button>ENVIAR</button>
</form>
</div>
<>
<Formik
onSubmit={handleSubmit}
initialValues={initialValues}
validationSchema={NewsletterSchema}
>
{({ resetForm }) => (
<div className={styles["container"]}>
<h3 className={styles["title"]}>
{/* ASSINE NOSSA NEWSLETTER */}
assine nossa newsletter
</h3>
<Form className={styles["form-wrapper"]}>
<input type="email" placeholder="E-mail" />
<button>ENVIAR</button>
</Form>
</div>
)}
</Formik>
</>
);
};

View File

@ -6,15 +6,5 @@
display: flex;
flex-direction: column;
gap: 25px;
@media screen and (width > 1024px) {
display: none;
}
}
&.header-desktop {
@media screen and (width <= 1024px) {
display: none;
}
}
}

View File

@ -1,15 +1,25 @@
// import React from "react";
import styles from "./Header.module.scss";
import React, { useContext } from "react";
import { HeaderDesktop } from "./HeaderDesktop/HeaderDesktop";
import { HeaderMobile } from "./HeaderMobile/HeaderMobile";
import { WidthContext } from "../../contexts/WidthContext";
import styles from "./Header.module.scss";
const Header = () => {
const { isMobile } = useContext(WidthContext);
return (
<>
<HeaderMobile className={`${styles["container"]} ${styles["header-mobile"]}`} />
<HeaderDesktop className={`${styles["container"]} ${styles["header-desktop"]}`} />
{isMobile ? (
<HeaderMobile
className={`${styles["container"]} ${styles["header-mobile"]}`}
/>
) : (
<HeaderDesktop
className={`${styles["container"]} ${styles["header-desktop"]}`}
/>
)}
</>
);
};

View File

@ -2,13 +2,13 @@
display: grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
padding: 25px 100px;
padding: 22px 100px;
border-bottom: 1px solid #c4c4c4;
.login-cart-wrapper {
display: flex;
gap: 55px;
align-items: center;
justify-content: right;
gap: 55px;
}
}

View File

@ -1,13 +1,12 @@
import React from "react";
import styles from "./HeaderDesktop.module.scss";
import { HeaderBottom } from "../HeaderBottom/HeaderBottom";
import { Logo } from "../Logo/Logo";
import { Searchbox } from "../Searchbox/Searchbox";
import { LoginButton } from "../LoginButton/LoginButton";
import { Minicart } from "../Minicart/Minicart";
import { HeaderNavbar } from "../HeaderNavbar/HeaderNavbar";
import styles from "./HeaderDesktop.module.scss";
interface HeaderDesktopProps {
className: string;
}
@ -25,7 +24,7 @@ const HeaderDesktop = (props: HeaderDesktopProps) => {
<Minicart />
</div>
</div>
<HeaderBottom />
<HeaderNavbar />
</header>
);
};

View File

@ -1,11 +1,11 @@
import React from "react";
import { Searchbox } from "../Searchbox/Searchbox";
import { Logo } from "../Logo/Logo";
import { MenuButton } from "../MenuButton/MenuButton";
import { Logo } from "../Logo/Logo";
import { Minicart } from "../Minicart/Minicart";
import { Searchbox } from "../Searchbox/Searchbox";
import styles from "./HeaderMobile.module.scss";
interface HeaderMobileProps {
className: string;
}

View File

@ -2,22 +2,24 @@
padding: 14px 100px;
.navbar-list {
display: flex;
gap: 55px;
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 55px;
.navbar-list-item {
a {
text-decoration: none;
font-family: "Roboto";
font-style: normal;
font-weight: 500; // cursos no componente header à esquerda está 400 e nas páginas 500
font-weight: 500;
font-size: 14px;
line-height: 16px;
text-transform: uppercase;
color: #ffffff;
text-transform: uppercase;
&:hover {
filter: brightness(0.5);
}
@media screen and (width >= 2500px) {
font-size: 28px;

View File

@ -1,15 +1,15 @@
import React from "react";
import { Link } from "react-router-dom";
import styles from "./HeaderBottom.module.scss";
import styles from "./HeaderNavbar.module.scss";
const navLinks = [
{ name: "Cursos", value: "cursos" },
{ name: "Saiba Mais", value: "saiba-mais" },
{ name: "Institucionais", value: "intitucionais" },
{ name: "Cursos", value: "/" },
{ name: "Saiba Mais", value: "/" },
{ name: "Institucionais", value: "institucionais/sobre" },
];
const HeaderBottom = () => {
const HeaderNavbar = () => {
return (
<nav className={styles["navbar"]}>
<ul className={styles["navbar-list"]}>
@ -23,4 +23,4 @@ const HeaderBottom = () => {
);
};
export { HeaderBottom };
export { HeaderNavbar };

View File

@ -4,10 +4,14 @@
font-weight: 400;
font-size: 14px;
line-height: 16px;
text-transform: uppercase;
color: #ffffff;
text-transform: uppercase;
text-decoration: none;
&:hover {
filter: brightness(0.5);
}
@media screen and (width >= 2500px) {
font-size: 28px;
line-height: 33px;

View File

@ -4,6 +4,10 @@
height: 25.86px;
display: block;
&:hover {
filter: brightness(0.8);
}
@media screen and (width >= 2500px) {
width: 265.62px;
height: 50.5px;

View File

@ -1,14 +1,14 @@
import React from "react";
import { Link } from "react-router-dom";
import styles from "./Logo.module.scss";
import m3Logo from "../assets/m3-logo.svg";
import styles from "./Logo.module.scss";
const Logo = () => {
return (
<div className={styles["logo-wrapper"]}>
<Link to="/">
<Link to="/institucionais/sobre">
<img src={m3Logo} alt="Logo da M3" />
</Link>
</div>

View File

@ -1,9 +1,9 @@
.open-menu {
width: 28px;
height: 22.5px;
background-color: transparent;
padding: 0;
display: block;
border: 0;
outline: 0;
padding: 0;
background-color: transparent;
}

View File

@ -1,11 +1,9 @@
import React, { useContext } from "react";
import styles from "./MenuButton.module.scss";
import menuButton from "../assets/menu-icon.svg";
import { ModalContext } from "../../../contexts/ModalContext";
import menuButton from "../assets/menu-icon.svg";
// const openMenu = () => {};
import styles from "./MenuButton.module.scss";
const MenuButton = () => {

View File

@ -4,6 +4,10 @@
height: 28px;
display: block;
&:hover {
filter: brightness(0.5);
}
@media screen and (width >= 2500px) {
width: 54.68px;
height: 54.68px;

View File

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
import styles from "./Minicart.module.scss";
import minicart from "../assets/cart-icon.svg";
import minicart from "./assets/cart-icon.svg";
const Minicart = () => {
return (

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -10,17 +10,17 @@
border: 2px solid #f2f2f2;
border-radius: 5px;
outline: 0;
font-family: "Roboto";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 16px;
color: #c6c6c6; // #000000? #c4c4c4?
color: #000000;
@media screen and (width >= 2500px) {
width: 67.26%; //515.62px;
padding: 12px 16px;
font-size: 28px;
line-height: 33px;
}
@ -31,22 +31,23 @@
}
&::placeholder {
color: #c4c4c4; // #c6c6c6;
color: #c4c4c4;
}
}
button {
border: 0;
outline: 0;
position: absolute;
right: 12.78%; //46px;
top: 8px;
padding: 0;
border: 0;
outline: 0;
background: transparent;
cursor: pointer;
img {
height: 18px;
width: 18px;
height: 18px;
display: block;
}
@ -58,8 +59,8 @@
}
@media screen and (width <= 1024px) {
right: 1.61%; //16px;
top: 11px;
}
right: 1.61%; //16px;
top: 11px;
}
}
}

View File

@ -1,10 +1,10 @@
import { Field, Form, Formik } from "formik";
import FormSchema from "../../../schema/FormSchema";
import { Field, Form, Formik, FormikHelpers } from "formik";
import SearchboxSchema from "../../../schema/SearchboxSchema";
import searchIcon from "../assets/search-icon.svg";
import styles from "./Searchbox.module.scss";
interface IFormikValues {
interface FormValues {
searchInput: string;
}
@ -13,8 +13,9 @@ const initialValues = {
};
const Searchbox = () => {
const handleSubmit = (values: IFormikValues) => {
const handleSubmit = (values: FormValues, { resetForm }: FormikHelpers<FormValues>) => {
console.log(values);
resetForm();
};
return (
@ -22,15 +23,14 @@ const Searchbox = () => {
<Formik
onSubmit={handleSubmit}
initialValues={initialValues}
validationSchema={FormSchema}
validationSchema={SearchboxSchema}
>
{({ errors, touched, resetForm }) => (
{({ resetForm }) => (
<Form className={styles["searchbox-wrapper"]}>
<Field
id="searchInput"
name="searchInput"
placeholder="Buscar..."
// className={errors.name && touched.name && "invalid"}
/>
<button className={styles["submit-button"]} type="submit">
<img src={searchIcon} alt="Ícone de busca" />

View File

@ -1,7 +1,8 @@
import React, { useContext } from "react";
import { Outlet } from "react-router-dom";
import { LocationProvider } from "../../contexts/LocationContext";
import { ModalContext } from "../../contexts/ModalContext";
import { AsideMenu } from "../AsideMenu/AsideMenu";
import { MainMenu } from "../MainMenu/MainMenu";
import { Footer } from "../Footer/Footer";
import { Header } from "../Header/Header";
import { Modal } from "../Modal/Modal";
@ -14,18 +15,20 @@ const MainLayout = () => {
return (
<>
<Modal isOpen={isOpen} setIsOpen={setIsOpen} />
<Header />
<main className={styles["main-container"]}>
<Breadcrumbs />
<h1 className={styles["main-title"]}>Institucional</h1>
<div className={styles["main-content"]}>
<AsideMenu />
<div className={styles["vertical-divider"]}></div>
<Outlet />
</div>
</main>
<Footer />
<LocationProvider>
<Modal isOpen={isOpen} setIsOpen={setIsOpen} />
<Header />
<main className={styles["main-container"]}>
<Breadcrumbs />
<h1 className={styles["main-title"]}>Institucional</h1>
<div className={styles["main-content"]}>
<MainMenu />
<div className={styles["vertical-divider"]}></div>
<Outlet />
</div>
</main>
<Footer />
</LocationProvider>
</>
);
};

View File

@ -11,7 +11,6 @@
li {
padding: 10px 16px;
transition: all 0.2s ease-in-out;
// background-color: yellow;
@media screen and (width <= 1024px) {
width: calc(100% - 32px);
@ -32,23 +31,17 @@
}
}
// &.active {
// background: #000000;
// a {
// font-weight: 700;
// color: #ffffff;
// }
// }
&:hover {
// &.active
background: #292929; // #000000;
&.active {
background: #000000;
a {
font-weight: 700;
color: #ffffff;
}
}
&:hover {
filter: brightness(1.5);
}
}
}

View File

@ -0,0 +1,132 @@
import React, { useState, useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { LocationContext } from "../../contexts/LocationContext";
import styles from "./MainMenu.module.scss";
const MainMenuItems = [
{
name: "Sobre",
value: "sobre",
className: "sobre",
},
{
name: "Forma de Pagamento",
value: "forma-de-pagamento",
className: "formaPagamento",
},
{
name: "Entrega",
value: "entrega",
className: "entrega",
},
{
name: "Troca e Devolução",
value: "troca-e-devolucao",
className: "trocaDevolucao",
},
{
name: "Segurança e Privacidade",
value: "seguranca-e-privacidade",
className: "segurancaPrivacidade",
},
{
name: "Contato",
value: "contato",
className: "contato",
},
];
const MainMenu = () => {
const [isActive, setIsActive] = useState({
sobre: false,
formaPagamento: false,
entrega: false,
trocaDevolucao: false,
segurancaPrivacidade: false,
contato: false,
});
const { path } = useContext(LocationContext);
const updateLocation = () => {
if (path === "/sobre") {
setIsActive({
sobre: true,
formaPagamento: false,
entrega: false,
trocaDevolucao: false,
segurancaPrivacidade: false,
contato: false,
});
} else if (path === "/formaPagamento") {
setIsActive({
sobre: false,
formaPagamento: true,
entrega: false,
trocaDevolucao: false,
segurancaPrivacidade: false,
contato: false,
});
} else if (path === "/entrega") {
setIsActive({
sobre: false,
formaPagamento: false,
entrega: true,
trocaDevolucao: false,
segurancaPrivacidade: false,
contato: false,
});
} else if (path === "/trocaDevolucao") {
setIsActive({
sobre: false,
formaPagamento: false,
entrega: false,
trocaDevolucao: true,
segurancaPrivacidade: false,
contato: false,
});
} else if (path === "/segurancaPrivacidade") {
setIsActive({
sobre: false,
formaPagamento: false,
entrega: false,
trocaDevolucao: false,
segurancaPrivacidade: true,
contato: false,
});
} else if (path === "/contato") {
setIsActive({
sobre: false,
formaPagamento: false,
entrega: false,
trocaDevolucao: false,
segurancaPrivacidade: false,
contato: true,
});
}
};
useEffect(() => {
updateLocation();
}, [path]);
return (
<ul className={styles["menu-list"]}>
{MainMenuItems.map((item, index) => (
<li
key={index}
className={
Object.values(isActive)[index]
? `${item.className} ${styles["active"]}`
: `${item.className}`
}
>
<Link to={item.value}>{item.name}</Link>
</li>
))}
</ul>
);
};
export { MainMenu };

View File

@ -0,0 +1,24 @@
import { createContext } from "react";
import { useLocation } from "react-router-dom";
interface LocationContextData {
path: string;
location: object;
}
interface Props {
children: React.ReactNode;
}
export const LocationContext = createContext({} as LocationContextData);
export const LocationProvider: React.FC<Props> = ({ children }) => {
const location = useLocation();
const path = location.pathname;
return (
<LocationContext.Provider value={{ path: path, location: location }}>
{children}
</LocationContext.Provider>
);
};

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import {
createBrowserRouter,
RouterProvider,
@ -19,28 +19,28 @@ const Institucional = () => {
children: [
{
path: "",
loader: () => redirect("/sobre")
loader: () => redirect("sobre"),
},
{
path: "sobre",
element: <About />,
},
// {
// path: "forma-de-pagamento",
// element: <Payment />,
// },
// {
// path: "entrega",
// element: <Shipping />,
// },
// {
// path: "troca-e-devolucao",
// element: <ExchangeAndReturn />,
// },
// {
// path: "seguranca-e-privacidade",
// element: <Security />,
// },
// {
// path: "forma-de-pagamento",
// element: <Payment />,
// },
// {
// path: "entrega",
// element: <Shipping />,
// },
// {
// path: "troca-e-devolucao",
// element: <ExchangeAndReturn />,
// },
// {
// path: "seguranca-e-privacidade",
// element: <Security />,
// },
{
path: "contato",
element: <Contact />,

View File

@ -1,14 +1,34 @@
import * as Yup from "yup";
import { parse, isDate } from "date-fns";
const cpfRegex = /^\d{3}[.]?\d{3}[.]?\d{3}[-]?\d{2}$/;
const phoneRegex = /^\(?([0-9]{2})\)?[ ]?([0-9]{5})[-]?([0-9]{4})$/;
const instagramRegex = /^[@]/;
const today = new Date();
function parseDateString(value: any, originalValue: any) {
const parsedDate = isDate(originalValue)
? originalValue
: parse(originalValue, "dd.MM.yyyy", new Date());
return parsedDate;
}
export default Yup.object().shape({
// melhorar validações: tipo de dado, minlength, formato
name: Yup.string().required("*Campo Obrigatório"),
email: Yup.string().required("*Campo Obrigatório").email("Email inválido"),
cpf: Yup.string().required("*Campo Obrigatório"),
dateOfBirth: Yup.string().required("*Campo Obrigatório"),
phoneNumber: Yup.string().required("*Campo Obrigatório"),
instagram: Yup.string(),
acceptTerms: Yup.boolean().required("*Campo Obrigatório"),
searchInput: Yup.string().required(""),
name: Yup.string().min(2, "*Nome inválido").required("*Campo Obrigatório"),
email: Yup.string().email("*Email inválido").required("*Campo Obrigatório"),
cpf: Yup.string()
.matches(cpfRegex, "*CPF inválido")
.required("*Campo Obrigatório"),
dateOfBirth: Yup.date()
.transform(parseDateString)
.max(today, "*Data de nascimento inválida")
.required("*Campo Obrigatório")
.typeError("*Data de nascimento inválida"),
phoneNumber: Yup.string()
.matches(phoneRegex, "*Telefone inválido")
.required("*Campo Obrigatório"),
instagram: Yup.string().matches(instagramRegex, "*Instagram inválido"),
acceptedTerms: Yup.boolean().required().oneOf([true]),
});

View File

@ -0,0 +1,5 @@
import * as Yup from "yup";
export default Yup.object().shape({
email: Yup.string().email().required(),
});

View File

@ -0,0 +1,5 @@
import * as Yup from "yup";
export default Yup.object().shape({
searchInput: Yup.string().required(),
});

View File

@ -1926,6 +1926,13 @@
dependencies:
"@types/node" "*"
"@types/date-fns@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@types/date-fns/-/date-fns-2.6.0.tgz#b062ca46562002909be0c63a6467ed173136acc1"
integrity sha512-9DSw2ZRzV0Tmpa6PHHJbMcZn79HHus+BBBohcOaDzkK/G3zMjDUDYjJIWBFLbkh+1+/IOS0A59BpQfdr37hASg==
dependencies:
date-fns "*"
"@types/eslint-scope@^3.7.3":
version "3.7.4"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
@ -2097,6 +2104,13 @@
dependencies:
"@types/react" "*"
"@types/react-input-mask@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/react-input-mask/-/react-input-mask-3.0.2.tgz#60df645cdb2415c97a8f97316011eb3ede78dc1e"
integrity sha512-WTli3kUyvUqqaOLYG/so2pLqUvRb+n4qnx2He5klfqZDiQmRyD07jVIt/bco/1BrcErkPMtpOm+bHii4Oed6cQ==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^18.0.26":
version "18.0.26"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
@ -3612,6 +3626,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
date-fns@*, date-fns@^2.29.3:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -5134,6 +5153,13 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
@ -6226,7 +6252,7 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -7611,6 +7637,15 @@ react-fast-compare@^2.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-input-mask@3.0.0-alpha.2:
version "3.0.0-alpha.2"
resolved "https://registry.yarnpkg.com/react-input-mask/-/react-input-mask-3.0.0-alpha.2.tgz#113102942a557edc7a192e66020b8ce1ba699a5c"
integrity sha512-9U7qL+mvDMOJcbOFPdt6Vj+zzmCMNnBjhhjGDrL8BGQmymgvMVKhu/oOVfAkl+5VWOsLr+G3EhZOmae5fBcAkA==
dependencies:
invariant "^2.2.4"
prop-types "^15.7.2"
warning "^4.0.3"
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -8934,6 +8969,13 @@ walker@^1.0.7:
dependencies:
makeerror "1.0.12"
warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
dependencies:
loose-envify "^1.0.0"
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"