forked from M3-Academy/desafio-react-e-typescript
feat(main): cria página de contato com formulário
This commit is contained in:
parent
2146b9edc6
commit
14db077644
@ -8,7 +8,7 @@
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
line-height: 28px;
|
||||
color: #292929;
|
||||
color: #292929; // #000000 ??
|
||||
margin: 0 0 12px;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
|
@ -3,7 +3,6 @@
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
text-decoration: none;
|
||||
border-right: 1px solid #000000;
|
||||
|
||||
li {
|
||||
padding: 10px 16px;
|
||||
@ -24,8 +23,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
// &.active {
|
||||
// background: #000000;
|
||||
|
||||
// a {
|
||||
// font-weight: 700;
|
||||
// color: #ffffff;
|
||||
// }
|
||||
// }
|
||||
|
||||
&:hover {
|
||||
background: #000000;
|
||||
background: #292929;
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
|
19
src/components/Contact/Contact.module.scss
Normal file
19
src/components/Contact/Contact.module.scss
Normal file
@ -0,0 +1,19 @@
|
||||
.section-container {
|
||||
margin-left: 30px;
|
||||
padding: 10px 0;
|
||||
|
||||
.section-title {
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
line-height: 28px;
|
||||
color: #000000; // #292929 ??
|
||||
margin: 0 0 12px;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 48px;
|
||||
line-height: 56px;
|
||||
}
|
||||
}
|
||||
}
|
18
src/components/Contact/Contact.tsx
Normal file
18
src/components/Contact/Contact.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import { ContactForm } from "../ContactForm/ContactForm";
|
||||
// import { Formik, Form, Field, ErrorMessage, FormikHelpers } from "formik";
|
||||
// import FormSchema from "../../schema/FormSchema";
|
||||
|
||||
import styles from "./Contact.module.scss";
|
||||
|
||||
const Contact = () => {
|
||||
return (
|
||||
<section className={styles["section-container"]}>
|
||||
<h2 className={styles["section-title"]}>Preencha o formulário </h2>
|
||||
{/* <div className={styles["section-description"]}></div> */}
|
||||
<ContactForm />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export { Contact };
|
181
src/components/ContactForm/ContactForm.module.scss
Normal file
181
src/components/ContactForm/ContactForm.module.scss
Normal file
@ -0,0 +1,181 @@
|
||||
.form-wrapper {
|
||||
width: 100%;
|
||||
padding-bottom: 26.56px;
|
||||
position: relative;
|
||||
// max-width: 720px;
|
||||
// min-height: 100vh;
|
||||
// margin: 0 auto;
|
||||
// padding: 0 12px;
|
||||
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
@media screen and (width >= 2500px) {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-col {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 12px;
|
||||
|
||||
label {
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: #100d0e;
|
||||
padding: 0 15px 12px;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 28px;
|
||||
line-height: 33px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
background: #ffffff;
|
||||
border: 1px solid #100d0e;
|
||||
border-radius: 25px;
|
||||
padding: 15px 20px;
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: #000000;
|
||||
outline: 0;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 28px;
|
||||
line-height: 33px;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #b9b7b7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.invalid-form-feedback {
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
color: #ff0000;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 24px;
|
||||
line-height: 28px;
|
||||
top: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.terms-col {
|
||||
margin: 1.58px 0 12.6px;
|
||||
text-align: center;
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 28px;
|
||||
line-height: 33px;
|
||||
margin: 0 0 12.85px;
|
||||
}
|
||||
|
||||
span {
|
||||
// font-family: "Proxima Nova"; Adobe Fonts
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
label {
|
||||
color: #100d0e;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// .custom-checkbox {
|
||||
// border: 1px solid #000000;
|
||||
// border-radius: 3px;
|
||||
// width: 34.4px;
|
||||
// height: 33.15px;
|
||||
// margin: 0 0 0 4.28px;
|
||||
|
||||
// // display: inline-block;
|
||||
// // position: absolute;
|
||||
// // bottom: 2.2px;
|
||||
// }
|
||||
|
||||
input {
|
||||
// talvez tenha que substituir o checkbox original - não tá pegando as bordas direito e o alinhamento está errado, tem que ser no underline. considerar as bordas na medida do input original ou do custom?
|
||||
width: 18.64px;
|
||||
height: 18px;
|
||||
margin: 0 0 0 4.28px;
|
||||
// opacity: 0;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
width: 36.4px;
|
||||
height: 35.15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
background: #000000;
|
||||
border-radius: 25px;
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
width: 100%;
|
||||
padding: 17px 0;
|
||||
letter-spacing: 0.05em;
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
font-size: 32px;
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #292929;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
194
src/components/ContactForm/ContactForm.tsx
Normal file
194
src/components/ContactForm/ContactForm.tsx
Normal file
@ -0,0 +1,194 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import {
|
||||
Formik,
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
// FormikHelpers,
|
||||
// FormikBag,
|
||||
// FormikState,
|
||||
} from "formik";
|
||||
import FormSchema from "../../schema/FormSchema";
|
||||
import "./ContactForm.module.scss";
|
||||
|
||||
import styles from "./ContactForm.module.scss";
|
||||
|
||||
interface IFormikValues {
|
||||
name: string;
|
||||
email: string;
|
||||
cpf: string;
|
||||
dateOfBirth: string;
|
||||
phoneNumber: string;
|
||||
instagram: string;
|
||||
acceptTerms: boolean;
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
name: "",
|
||||
email: "",
|
||||
cpf: "",
|
||||
dateOfBirth: "",
|
||||
phoneNumber: "",
|
||||
instagram: "",
|
||||
acceptTerms: false,
|
||||
};
|
||||
|
||||
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);
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className={styles["form-wrapper"]}>
|
||||
<Formik
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={initialValues}
|
||||
validationSchema={FormSchema}
|
||||
>
|
||||
{({ errors, touched, resetForm }) => (
|
||||
<Form>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="name">Nome</label>
|
||||
<Field
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Seu nome completo"
|
||||
// className={errors.name && touched.name && "invalid"}
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="name"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="email">E-mail</label>
|
||||
<Field
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Seu e-mail"
|
||||
// className={errors.email && touched.email && `${styles["invalid"]}`}
|
||||
// className={errors.email && touched.email && "invalid"}
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="email"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="subject">CPF</label>
|
||||
<Field
|
||||
id="cpf"
|
||||
name="cpf"
|
||||
placeholder="000.000.000-00"
|
||||
// className={errors.cpf && touched.cpf && "invalid"}
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="cpf"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="dateOfBirth">Data de Nascimento:</label>
|
||||
{/* tem : só nesses campos? */}
|
||||
<Field
|
||||
// as="textarea"
|
||||
id="dateOfBirth"
|
||||
name="dateOfBirth"
|
||||
placeholder="00.00.0000"
|
||||
// className={errors.dateOfBirth && touched.dateOfBirth && "invalid"}
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="dateOfBirth"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="message">Telefone:</label>
|
||||
{/* tem : só nesses campos? */}
|
||||
<Field
|
||||
// as="textarea"
|
||||
id="phoneNumber"
|
||||
name="phoneNumber"
|
||||
placeholder="(00) 00000-0000"
|
||||
// className={
|
||||
// errors.phoneNumber && touched.phoneNumber && "invalid"
|
||||
// }
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="phoneNumber"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["form-col"]}>
|
||||
<label htmlFor="message">Instagram</label>
|
||||
<Field
|
||||
id="instagram"
|
||||
name="instagram"
|
||||
placeholder="@seuuser"
|
||||
// className={errors.instagram && touched.instagram && "invalid"}
|
||||
/>
|
||||
<ErrorMessage
|
||||
component="span"
|
||||
name="instagram"
|
||||
className={styles["invalid-form-feedback"]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles["terms-col"]}>
|
||||
<span>* </span>
|
||||
<label htmlFor="acceptTerms">Declaro que li e aceito</label>
|
||||
{/* <div className={styles["custom-checkbox"]}></div> */}
|
||||
<Field
|
||||
type="checkbox"
|
||||
id="acceptTerms"
|
||||
name="acceptTerms"
|
||||
// className={`${"invalid" ? styles["invalid"] : ""}`}
|
||||
// className={
|
||||
// errors.acceptTerms && touched.acceptTerms && "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>
|
||||
)}
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ContactForm };
|
@ -78,10 +78,20 @@
|
||||
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 302px 1fr;
|
||||
grid-template-columns: 302px 1px 1fr;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
grid-template-columns: 590px 1fr;
|
||||
grid-template-columns: 590px 1px 1fr;
|
||||
}
|
||||
|
||||
.horizontal-divider {
|
||||
width: 1px;
|
||||
height: 285px;
|
||||
background: #000000;
|
||||
|
||||
@media screen and (width >= 2500px) {
|
||||
height: 465px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import { AsideMenu } from "../AsideMenu/AsideMenu";
|
||||
|
||||
import styles from "./MainLayout.module.scss";
|
||||
|
||||
// import "../../assets/svg/Vector.svg";
|
||||
|
||||
const MainLayout = () => {
|
||||
return (
|
||||
<main className={styles["main-container"]}>
|
||||
@ -17,6 +15,7 @@ const MainLayout = () => {
|
||||
<h1 className={styles["main-title"]}>Institucional</h1>
|
||||
<div className={styles["main-content"]}>
|
||||
<AsideMenu />
|
||||
<div className={styles["horizontal-divider"]}></div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
|
12
src/schema/FormSchema.ts
Normal file
12
src/schema/FormSchema.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import * as Yup from "yup";
|
||||
|
||||
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"),
|
||||
});
|
Loading…
Reference in New Issue
Block a user