forked from M3-Academy/desafio-react-e-typescript
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:
commit
b33c70b7be
@ -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",
|
||||
|
@ -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 };
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
)}
|
||||
|
23
src/components/CustomInput/CustomInput.tsx
Normal file
23
src/components/CustomInput/CustomInput.tsx
Normal 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 };
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"]}`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
@ -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 };
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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 = () => {
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
height: 28px;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.5);
|
||||
}
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
width: 54.68px;
|
||||
height: 54.68px;
|
||||
|
@ -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 (
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
132
src/components/MainMenu/MainMenu.tsx
Normal file
132
src/components/MainMenu/MainMenu.tsx
Normal 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 };
|
24
src/contexts/LocationContext.tsx
Normal file
24
src/contexts/LocationContext.tsx
Normal 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>
|
||||
);
|
||||
};
|
@ -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 />,
|
||||
|
@ -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]),
|
||||
});
|
||||
|
5
src/schema/NewsletterSchema.ts
Normal file
5
src/schema/NewsletterSchema.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import * as Yup from "yup";
|
||||
|
||||
export default Yup.object().shape({
|
||||
email: Yup.string().email().required(),
|
||||
});
|
5
src/schema/SearchboxSchema.ts
Normal file
5
src/schema/SearchboxSchema.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import * as Yup from "yup";
|
||||
|
||||
export default Yup.object().shape({
|
||||
searchInput: Yup.string().required(),
|
||||
});
|
44
yarn.lock
44
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user