| import styles from './hover-image-card.module.css'; | |||||
| const HoverImageCard = () => { | |||||
| return ( | |||||
| <div className={styles.container}> | |||||
| <div className={styles.card}> | |||||
| <div className={styles.content}> | |||||
| <p>Next JS Path</p> | |||||
| <p>18-8-2022</p> | |||||
| <button className={styles.btn}>More Details</button> | |||||
| </div> | |||||
| {/*Change with Next Image*/} | |||||
| <img src="/images/image-one.jpg" alt="text" /> | |||||
| </div> | |||||
| <div className={styles.card}> | |||||
| <div className={styles.content}> | |||||
| <p>Text 1</p> | |||||
| <p>Text 2</p> | |||||
| <button className={styles.btn}>Button Text</button> | |||||
| </div> | |||||
| {/*Change with Next Image*/} | |||||
| <img src="/images/image-one.jpg" alt="text" /> | |||||
| </div> | |||||
| <div className={styles.card}> | |||||
| <div className={styles.content}> | |||||
| <p>Text 1</p> | |||||
| <p>Text 2</p> | |||||
| <button className={styles.btn}>Button Text</button> | |||||
| </div> | |||||
| {/*Change with Next Image*/} | |||||
| <img src="/images/image-one.jpg" alt="text" /> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export default HoverImageCard; |
| .container { | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| margin-top: 30px; | |||||
| } | |||||
| .card { | |||||
| position: relative; | |||||
| width: 230px; | |||||
| height: 260px; | |||||
| margin: 0 5px; | |||||
| background-color: red; | |||||
| transition: 0.3s; | |||||
| overflow: hidden; | |||||
| cursor: pointer; | |||||
| } | |||||
| .card img { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| object-fit: cover; | |||||
| transition: 0.3s; | |||||
| } | |||||
| .card::after { | |||||
| content: ''; | |||||
| position: absolute; | |||||
| left: 0; | |||||
| bottom: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| opacity: 0; | |||||
| transition: 0.3s; | |||||
| background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)); | |||||
| } | |||||
| .content { | |||||
| position: absolute; | |||||
| bottom: 0; | |||||
| width: 100%; | |||||
| padding: 1rem; | |||||
| z-index: 1; | |||||
| color: #fff; | |||||
| transition: 0.3s; | |||||
| opacity: 0; | |||||
| } | |||||
| .btn { | |||||
| padding: 0.3rem 0.8rem; | |||||
| font-size: 0.6rem; | |||||
| border: none; | |||||
| cursor: pointer; | |||||
| outline: none; | |||||
| color: #fff; | |||||
| background: transparent; | |||||
| border: 2px solid #fff; | |||||
| } | |||||
| .content p { | |||||
| font-size: 0.6rem; | |||||
| margin: 0.5rem 0; | |||||
| } | |||||
| .card:hover { | |||||
| width: 350px; | |||||
| } | |||||
| .card:hover img { | |||||
| transform: scale(1.1); | |||||
| } | |||||
| .card:hover:after, | |||||
| .card:hover .content { | |||||
| opacity: 1; | |||||
| } |
| import { | |||||
| Box, | |||||
| Button, | |||||
| Container, | |||||
| Grid, | |||||
| TextField, | |||||
| Typography | |||||
| } from '@mui/material'; | |||||
| import { useFormik } from 'formik'; | |||||
| import { useTranslation } from 'next-i18next'; | |||||
| import Link from 'next/link'; | |||||
| import React from 'react'; | |||||
| import { BASE_PAGE } from '../../../constants/pages'; | |||||
| import { contactSchema } from '../../../schemas/contactSchema'; | |||||
| const ContactForm = () => { | |||||
| const { t } = useTranslation('forms', 'contact', 'common'); | |||||
| const handleSubmit = (values) => { | |||||
| console.log('Values', values); | |||||
| }; | |||||
| const formik = useFormik({ | |||||
| initialValues: { | |||||
| firstName: '', | |||||
| lastName: '', | |||||
| email: '', | |||||
| message: '' | |||||
| }, | |||||
| validationSchema: contactSchema, | |||||
| onSubmit: handleSubmit, | |||||
| validateOnBlur: true, | |||||
| enableReinitialize: true, | |||||
| }); | |||||
| return ( | |||||
| <Container component="main" maxWidth="md"> | |||||
| <Box | |||||
| sx={{ | |||||
| marginTop: 32, | |||||
| display: 'flex', | |||||
| flexDirection: 'column', | |||||
| alignItems: 'center', | |||||
| }} | |||||
| > | |||||
| <Typography component="h1" variant="h5"> | |||||
| {t('contact:Title')} | |||||
| </Typography> | |||||
| <Box | |||||
| component="form" | |||||
| onSubmit={formik.handleSubmit} | |||||
| sx={{ position: 'relative', mt: 1, p: 1 }} | |||||
| > | |||||
| <TextField | |||||
| name="firstName" | |||||
| label={t('forms:FirstName')} | |||||
| margin="normal" | |||||
| value={formik.values.firstName} | |||||
| onChange={formik.handleChange} | |||||
| error={formik.touched.firstName && Boolean(formik.errors.firstName)} | |||||
| helperText={formik.touched.firstName && formik.errors.firstName} | |||||
| autoFocus | |||||
| fullWidth | |||||
| /> | |||||
| <TextField | |||||
| name="lastName" | |||||
| label={t('forms:LastName')} | |||||
| margin="normal" | |||||
| value={formik.values.lastName} | |||||
| onChange={formik.handleChange} | |||||
| error={formik.touched.lastName && Boolean(formik.errors.lastName)} | |||||
| helperText={formik.touched.lastName && formik.errors.lastName} | |||||
| autoFocus | |||||
| fullWidth | |||||
| /> | |||||
| <TextField | |||||
| name="email" | |||||
| label={t('forms:Email')} | |||||
| margin="normal" | |||||
| value={formik.values.email} | |||||
| onChange={formik.handleChange} | |||||
| error={formik.touched.email && Boolean(formik.errors.email)} | |||||
| helperText={formik.touched.email && formik.errors.email} | |||||
| autoFocus | |||||
| fullWidth | |||||
| /> | |||||
| <TextField | |||||
| name="message" | |||||
| label={t('forms:Message')} | |||||
| multiline | |||||
| margin="normal" | |||||
| value={formik.values.message} | |||||
| onChange={formik.handleChange} | |||||
| error={formik.touched.message && Boolean(formik.errors.message)} | |||||
| helperText={formik.touched.message && formik.errors.message} | |||||
| rows={4} | |||||
| autoFocus | |||||
| fullWidth | |||||
| /> | |||||
| <Button | |||||
| type="submit" | |||||
| variant="contained" | |||||
| sx={{ mt: 3, mb: 2 }} | |||||
| fullWidth | |||||
| > | |||||
| {t('contact:SendBtn')} | |||||
| </Button> | |||||
| <Grid container justifyContent="center"> | |||||
| <Link href={BASE_PAGE}>{t('common:Back')}</Link> | |||||
| </Grid> | |||||
| </Box> | |||||
| </Box> | |||||
| </Container> | |||||
| ); | |||||
| }; | |||||
| export default ContactForm; | |||||
| import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; | |||||
| import ContactForm from '../components/forms/contact/ContactForm'; | |||||
| const ContactPage = () => { | |||||
| return <ContactForm />; | |||||
| }; | |||||
| export async function getStaticProps({ locale }) { | |||||
| return { | |||||
| props: { | |||||
| ...(await serverSideTranslations(locale, ['forms', 'contact', 'common'])), | |||||
| }, | |||||
| }; | |||||
| } | |||||
| export default ContactPage; |
| import { dehydrate, QueryClient } from '@tanstack/react-query'; | import { dehydrate, QueryClient } from '@tanstack/react-query'; | ||||
| import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; | import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; | ||||
| import Head from 'next/head'; | import Head from 'next/head'; | ||||
| import HoverImageCard from '../components/cards/hover-image-card/hover-image-card'; | |||||
| import PaginationComponentRQ from '../components/pagination/react-query/PaginationComponentRQ'; | import PaginationComponentRQ from '../components/pagination/react-query/PaginationComponentRQ'; | ||||
| import { getData } from '../requests/dataRequest'; | import { getData } from '../requests/dataRequest'; | ||||
| <meta name="description" content="Random data with pagination..." /> | <meta name="description" content="Random data with pagination..." /> | ||||
| </Head> | </Head> | ||||
| <PaginationComponentRQ></PaginationComponentRQ> | <PaginationComponentRQ></PaginationComponentRQ> | ||||
| <HoverImageCard /> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; |
| { | |||||
| "Title": "Contact", | |||||
| "SendBtn": "Submit" | |||||
| } |
| { | { | ||||
| "FirstName": "First name", | |||||
| "LastName": "Last name", | |||||
| "Message": "Message", | |||||
| "FullName": "Full name", | "FullName": "Full name", | ||||
| "Username": "Username", | "Username": "Username", | ||||
| "Email": "Email", | "Email": "Email", |
| import * as Yup from 'yup'; | |||||
| export const contactSchema = Yup.object().shape({ | |||||
| firstName: Yup.string().required('First name is required'), | |||||
| lastName: Yup.string().required('Last name is required'), | |||||
| email: Yup.string().email('Enter valid email').required('Email is required'), | |||||
| message: Yup.string().required('Message is required'), | |||||
| }); |