Browse Source

Merge branch 'master' of https://git.dilig.net/ntasicc/coffee-ts into products-typescript

products-typescript
Selena Simic 3 years ago
parent
commit
efd9c69a19

+ 20
- 19
components/cards/card-container/CardContainer.tsx View File

import { Box } from '@mui/system'; import { Box } from '@mui/system';
import { ReactNode } from 'react';


interface CardContainerProps { interface CardContainerProps {
children: JSX.Element;
children: ReactNode;
} }


const CardContainer: React.FC<CardContainerProps> = ({ children }) => { const CardContainer: React.FC<CardContainerProps> = ({ children }) => {
return (
<Box
sx={{
ml: { md: 2 },
mt: { xs: 5, md: 0 },
display: 'flex',
flexDirection: {
xs: 'column',
sm: 'row',
lg: 'column',
},
justifyContent: { sm: 'flex-start' },
flexWrap: 'wrap',
}}
>
{children}
</Box>
);
return (
<Box
sx={{
ml: { md: 2 },
mt: { xs: 5, md: 0 },
display: 'flex',
flexDirection: {
xs: 'column',
sm: 'row',
lg: 'column',
},
justifyContent: { sm: 'flex-start' },
flexWrap: 'wrap',
}}
>
{children}
</Box>
);
}; };


export default CardContainer; export default CardContainer;

+ 62
- 62
components/cards/data-card/DataCard.tsx View File

import { Box, Card, Typography } from '@mui/material'; import { Box, Card, Typography } from '@mui/material';
import Image from 'next/image'; import Image from 'next/image';
import { ProductDataDB } from '../../../utils/interface/productInterface';
import { ProductData } from '../../../utils/interface/productInterface';


interface DataCardProps { interface DataCardProps {
data: ProductDataDB;
quantity: number;
data: ProductData;
quantity: number;
} }


const DataCard: React.FC<DataCardProps> = ({ data, quantity }) => { const DataCard: React.FC<DataCardProps> = ({ data, quantity }) => {
return (
<Card
return (
<Card
sx={{
backgroundColor: '#f2f2f2',
mb: 2,
p: 2,
mx: { xs: 0, sm: 1 },
width: { xs: '100%', sm: '44%', md: '100%', lg: '100%' },
}}
>
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', lg: 'row' },
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Image src={data.image} alt="profile" width={200} height={200} />
</Box>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyItems: 'center',
}}
>
<Typography
sx={{ sx={{
backgroundColor: '#f2f2f2',
mb: 2,
p: 2,
mx: { xs: 0, sm: 1 },
width: { xs: '100%', sm: '44%', md: '100%', lg: '100%' },
textAlign: 'center',
fontWeight: 600,
fontSize: { md: 20, xs: 16 },
pt: { xs: 2 },
}} }}
>
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', lg: 'row' },
}}
>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Image src={data.image} alt="profile" width={200} height={200} />
</Box>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyItems: 'center',
}}
>
<Typography
sx={{
textAlign: 'center',
fontWeight: 600,
fontSize: { md: 20, xs: 16 },
pt: { xs: 2 },
}}
>
{data.name}
</Typography>
<Typography
sx={{
width: '100%',
textAlign: 'center',
fontWeight: 600,
fontSize: { md: 20, xs: 16 },
}}
>
x{quantity}
</Typography>
<Typography
sx={{
mt: { lg: 3, xs: 1 },
textAlign: 'center',
fontSize: 14,
}}
>
${data.price} (per unit)
</Typography>
</Box>
</Box>
</Card>
);
>
{data.name}
</Typography>
<Typography
sx={{
width: '100%',
textAlign: 'center',
fontWeight: 600,
fontSize: { md: 20, xs: 16 },
}}
>
x{quantity}
</Typography>
<Typography
sx={{
mt: { lg: 3, xs: 1 },
textAlign: 'center',
fontSize: 14,
}}
>
${data.price} (per unit)
</Typography>
</Box>
</Box>
</Card>
);
}; };


export default DataCard; export default DataCard;

+ 42
- 40
components/cards/order-card/OrderCard.tsx View File

import { Card, Divider, Typography } from '@mui/material'; import { Card, Divider, Typography } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { OrderCard } from '../../../utils/interface/orderInterface';


interface OrderCardProps { interface OrderCardProps {
data: OrderCard;
data: {
date: string;
name: string;
totalPrice: number;
};
} }


const OrderCard: React.FC<OrderCardProps> = ({ data }) => { const OrderCard: React.FC<OrderCardProps> = ({ data }) => {
const { t } = useTranslation('profile');
return (
<Card
sx={{
backgroundColor: '#f2f2f2',
mb: 2,
p: 2,
mx: { xs: 0, sm: 1 },
width: { xs: '100%', sm: '47%', md: '100%', lg: '100%' },
height: "100%"
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: { xs: 'center', md: 'flex-start' },
}}
>
<Typography sx={{ fontWeight: 600 }}>
<>
{t('profile:orderDate')}
{data.date}
</>
</Typography>
<Divider />
<Typography sx={{ mt: 1 }}>
{t('profile:by')}
{data.name}
</Typography>
<Typography>
{t('profile:total')}
{data.totalPrice.toFixed(2)}
</Typography>
</Box>
</Card>
);
const { t } = useTranslation('profile');
return (
<Card
sx={{
backgroundColor: '#f2f2f2',
mb: 2,
p: 2,
mx: { xs: 0, sm: 1 },
width: { xs: '100%', sm: '47%', md: '100%', lg: '100%' },
height: '100%',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: { xs: 'center', md: 'flex-start' },
}}
>
<Typography sx={{ fontWeight: 600 }}>
<>
{t('profile:orderDate')}
{data.date}
</>
</Typography>
<Divider />
<Typography sx={{ mt: 1 }}>
{t('profile:by')}
{data.name}
</Typography>
<Typography>
{t('profile:total')}
{data.totalPrice.toFixed(2)}
</Typography>
</Box>
</Card>
);
}; };



export default OrderCard; export default OrderCard;

+ 1
- 1
components/checkout-content/CheckoutContent.tsx View File

<ContentContainer> <ContentContainer>
<Box flexGrow={1} sx={{ minWidth: '65%' }}> <Box flexGrow={1} sx={{ minWidth: '65%' }}>
<ShippingDetailsForm <ShippingDetailsForm
enableBtn={false}
enableBtn={true}
backBtn={true} backBtn={true}
isCheckout={true} isCheckout={true}
submitHandler={submitHandler} submitHandler={submitHandler}

+ 65
- 62
components/forms/contact/ContactForm.tsx View File

import { Box, Button, Paper, TextField } from '@mui/material'; import { Box, Button, Paper, TextField } from '@mui/material';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import React, { useState } from 'react';
import React, { useState, FC } from 'react';
import { contactSchema } from '../../../schemas/contactSchema'; import { contactSchema } from '../../../schemas/contactSchema';
import { useCheckoutData } from '../../../store/checkout-context'; import { useCheckoutData } from '../../../store/checkout-context';
import ErrorMessageComponent from '../../mui/ErrorMessageComponent'; import ErrorMessageComponent from '../../mui/ErrorMessageComponent';


const ContactForm = ({ submitHandler }) => {
const [error] = useState({ hasError: false, errorMessage: '' });
const { checkoutStorage } = useCheckoutData();
interface Props {
submitHandler: (x: string) => void;
}
const ContactForm: FC<Props> = ({ submitHandler }) => {
const [error] = useState({ hasError: false, errorMessage: '' });
const { checkoutStorage } = useCheckoutData();


const handleSubmit = async (values) => {
submitHandler(values.email);
};
const handleSubmit = async (values: { email: string }) => {
submitHandler(values.email);
};


const formik = useFormik({
initialValues: {
email: checkoutStorage ? checkoutStorage.userInfo.email : '',
},
validationSchema: contactSchema,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});
const formik = useFormik({
initialValues: {
email: checkoutStorage ? checkoutStorage.userInfo.email : '',
},
validationSchema: contactSchema,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});


return (
<Paper
sx={{ p: 3, width: '90%', ml: 12, mt: 2, backgroundColor: '#f2f2f2' }}
elevation={3}
return (
<Paper
sx={{ p: 3, width: '90%', ml: 12, mt: 2, backgroundColor: '#f2f2f2' }}
elevation={3}
>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
{error.hasError && <ErrorMessageComponent error={error.errorMessage} />}
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: 'relative', mt: 1, p: 1 }}
> >
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
{error.hasError && <ErrorMessageComponent error={error.errorMessage} />}
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: 'relative', mt: 1, p: 1 }}
>
<TextField
name="email"
label="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}
fullWidth
/>
<Button
type="submit"
variant="contained"
sx={{
mt: 3,
mb: 2,
backgroundColor: '#CBA213',
height: 50,
width: 150,
textTransform: 'none',
color: 'white',
}}
>
Submit Details
</Button>
</Box>
</Box>
</Paper>
);
<TextField
name="email"
label="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}
fullWidth
/>
<Button
type="submit"
variant="contained"
sx={{
mt: 3,
mb: 2,
backgroundColor: '#CBA213',
height: 50,
width: 150,
textTransform: 'none',
color: 'white',
}}
>
Submit Details
</Button>
</Box>
</Box>
</Paper>
);
}; };


export default ContactForm; export default ContactForm;

+ 130
- 130
components/forms/contact/ContactPageForm.tsx View File

import { 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, { useState } from 'react';
import { BASE_PAGE } from '../../../constants/pages';
import { postQuestion } from '../../../requests/question/postQuestionRequest';
import { contactPageSchema } from '../../../schemas/contactSchema';
import Notification from '../../notification/Notification';
const ContactPageForm = () => {
const { t } = useTranslation('contact');
const [open, setOpen] = useState(false);
const handleSubmit = async (values) => {
try {
postQuestion(values);
setOpen(true);
} catch (error) {
console.log(error);
}
};
const handleCloseNotification = () => {
setOpen(false);
};
const formik = useFormik({
initialValues: {
firstName: '',
lastName: '',
email: '',
message: '',
},
validationSchema: contactPageSchema,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});
return (
<Container component="main" maxWidth="md" sx={{ mb: '60px' }}>
<Notification
open={open}
notification={t('contact:notification')}
handleCloseNotification={handleCloseNotification}
/>
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, { useState } from 'react';
import { BASE_PAGE } from '../../../constants/pages';
import { postQuestion } from '../../../requests/question/postQuestionRequest';
import { contactPageSchema } from '../../../schemas/contactSchema';
import { QuestionData } from '../../../utils/interface/questionInterface';
import Notification from '../../notification/Notification';

const ContactPageForm = () => {
const { t } = useTranslation('contact');
const [open, setOpen] = useState(false);

const handleSubmit = async (values: QuestionData) => {
try {
postQuestion(values);
setOpen(true);
} catch (error) {
console.log(error);
}
};

const handleCloseNotification = () => {
setOpen(false);
};

const formik = useFormik({
initialValues: {
firstName: '',
lastName: '',
email: '',
message: '',
},
validationSchema: contactPageSchema,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});

return (
<Container component="main" maxWidth="md" sx={{ mb: '60px' }}>
<Notification
open={open}
notification={t('contact:notification')}
handleCloseNotification={handleCloseNotification}
/>
<Box
sx={{
marginTop: 32,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography fontSize={48}>{t('contact:title')}</Typography>
<Box <Box
sx={{
marginTop: 32,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: 'relative', mt: 1, p: 1 }}
> >
<Typography fontSize={48}>{t('contact:title')}</Typography>
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: 'relative', mt: 1, p: 1 }}
<TextField
name="firstName"
label={t('contact: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('contact: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('contact: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('contact: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
> >
<TextField
name="firstName"
label={t('contact: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('contact: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('contact: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('contact: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}>
<Typography>{t('contact:back')}</Typography>
</Link>
</Grid>
</Box>
{t('contact:sendBtn')}
</Button>
<Grid container justifyContent="center">
<Link href={BASE_PAGE}>
<Typography>{t('contact:back')}</Typography>
</Link>
</Grid>
</Box> </Box>
</Container>
);
};
export default ContactPageForm;
</Box>
</Container>
);
};

export default ContactPageForm;

+ 4
- 3
components/forms/shipping-details/ShippingDetailsForm.tsx View File

import { useState, FC } from 'react'; import { useState, FC } from 'react';
import { registerSchema } from '../../../schemas/shippingDetailsSchema'; import { registerSchema } from '../../../schemas/shippingDetailsSchema';
import { useUserData } from '../../../store/user-context'; import { useUserData } from '../../../store/user-context';
import { ShippingData } from '../../../utils/interface/orderInterface';
import ErrorMessageComponent from '../../mui/ErrorMessageComponent'; import ErrorMessageComponent from '../../mui/ErrorMessageComponent';


interface FormValues {
export interface FormValues {
fullName: string; fullName: string;
address: string; address: string;
address2: string; address2: string;
} }


interface Props { interface Props {
submitHandler: (x: FormValues) => void;
submitHandler: (x: FormValues | ShippingData) => void;
backBtn: boolean; backBtn: boolean;
isCheckout: boolean; isCheckout: boolean;
enableBtn: boolean; enableBtn: boolean;
const { userStorage } = useUserData(); const { userStorage } = useUserData();
const router = useRouter(); const router = useRouter();


const formikSubmitHandler = async (values: FormValues) => {
const formikSubmitHandler = async (values: FormValues | ShippingData) => {
submitHandler(values); submitHandler(values);
}; };



+ 84
- 66
components/profile-content/ProfileContent.tsx View File

import { Box } from '@mui/system'; import { Box } from '@mui/system';
import { useSession } from 'next-auth/react'; import { useSession } from 'next-auth/react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useState } from 'react';
import { useState, FC } from 'react';
import { updateUser } from '../../requests/user/userUpdateRequest'; import { updateUser } from '../../requests/user/userUpdateRequest';
import { useUserUpdate } from '../../store/user-context'; import { useUserUpdate } from '../../store/user-context';
import { OrderDataDB } from '../../utils/interface/orderInterface';
import CardContainer from '../cards/card-container/CardContainer'; import CardContainer from '../cards/card-container/CardContainer';
import OrderCard from '../cards/order-card/OrderCard'; import OrderCard from '../cards/order-card/OrderCard';
import ShippingDetailsForm from '../forms/shipping-details/ShippingDetailsForm'; import ShippingDetailsForm from '../forms/shipping-details/ShippingDetailsForm';
import StepTitle from '../layout/steps-title/StepTitle'; import StepTitle from '../layout/steps-title/StepTitle';
import Notification from '../notification/Notification'; import Notification from '../notification/Notification';


const ProfileContent = ({ orders }) => {
const { t } = useTranslation('profile');
const { data: session } = useSession();
const { updateUserInfo } = useUserUpdate();
const [enableBtn, setEnableBtn] = useState(true);
const [open, setOpen] = useState(false);
interface Props {
orders: Array<OrderDataDB>;
}


const updateUserHandler = async (values) => {
try {
setEnableBtn(false);
updateUserInfo(values);
await updateUser(values, session.user._id);
setOpen(true);
setTimeout(() => {
setEnableBtn(true);
}, 5000);
} catch (error) {
console.log(error);
setTimeout(() => {
setEnableBtn(true);
}, 3000);
}
};
interface FormValues {
fullName: string;
address: string;
address2: string;
city: string;
country: string;
postcode: string;
}


const handleCloseNotification = () => {
setOpen(false);
};
const ProfileContent: FC<Props> = ({ orders }) => {
const { t } = useTranslation('profile');
const { data: session } = useSession();
const { updateUserInfo } = useUserUpdate();
const [enableBtn, setEnableBtn] = useState(true);
const [open, setOpen] = useState(false);


const mapOrdersToDom = () =>
orders.slice(-4).map((order, i) => (
<OrderCard
key={i}
data={{
date: order.time.split('T')[0],
name: order.shippingAddress.fullName,
totalPrice: order.totalPrice,
}}
></OrderCard>
));
const updateUserHandler = async (values: FormValues) => {
if (session?.user) {
try {
setEnableBtn(false);
updateUserInfo(values);
await updateUser(values, session.user._id);
setOpen(true);
setTimeout(() => {
setEnableBtn(true);
}, 5000);
} catch (error) {
console.log(error);
setTimeout(() => {
setEnableBtn(true);
}, 3000);
}
}
};


return (
<PageWrapper>
<StepTitle title={t('profile:title')} />
<Notification
open={open}
handleCloseNotification={handleCloseNotification}
notification={t('profile:notification')}
/>
const handleCloseNotification = () => {
setOpen(false);
};


<ContentContainer>
<Box flexGrow={1} sx={{ minWidth: '65%' }}>
<Typography sx={{ fontSize: 20, mb: 3 }}>
{t('profile:subtitle1')}
</Typography>
<ShippingDetailsForm
submitHandler={updateUserHandler}
enableBtn={enableBtn}
></ShippingDetailsForm>
</Box>
<Box sx={{ mt: { xs: 5, md: 0 } }}>
<Typography
sx={{ fontSize: 20, mb: { xs: -2, md: 3 }, ml: { md: 3 } }}
>
{t('profile:subtitle2')}
</Typography>
<CardContainer>{mapOrdersToDom()}</CardContainer>
</Box>
</ContentContainer>
</PageWrapper>
);
const mapOrdersToDom = () =>
orders.slice(-4).map((order, i) => (
<OrderCard
key={i}
data={{
date: order.time.toLocaleString().split('T')[0],
name: order.shippingAddress.fullName,
totalPrice: order.totalPrice,
}}
></OrderCard>
));

return (
<PageWrapper>
<StepTitle title={t('profile:title')} breadcrumbsArray={[]} />
<Notification
open={open}
handleCloseNotification={handleCloseNotification}
notification={t('profile:notification')}
/>

<ContentContainer>
<Box flexGrow={1} sx={{ minWidth: '65%' }}>
<Typography sx={{ fontSize: 20, mb: 3 }}>
{t('profile:subtitle1')}
</Typography>
<ShippingDetailsForm
submitHandler={updateUserHandler}
enableBtn={enableBtn}
backBtn={false}
isCheckout={false}
></ShippingDetailsForm>
</Box>
<Box sx={{ mt: { xs: 5, md: 0 } }}>
<Typography
sx={{ fontSize: 20, mb: { xs: -2, md: 3 }, ml: { md: 3 } }}
>
{t('profile:subtitle2')}
</Typography>
<CardContainer>{mapOrdersToDom()}</CardContainer>
</Box>
</ContentContainer>
</PageWrapper>
);
}; };


export default ProfileContent; export default ProfileContent;

+ 3
- 2
components/review-content/ReviewContent.tsx View File

useCheckoutData, useCheckoutData,
useCheckoutDataUpdate, useCheckoutDataUpdate,
} from '../../store/checkout-context'; } from '../../store/checkout-context';
import { OrderData } from '../../utils/interface/orderInterface';
import PageWrapper from '../layout/page-wrapper/PageWrapper'; import PageWrapper from '../layout/page-wrapper/PageWrapper';
import StepTitle from '../layout/steps-title/StepTitle'; import StepTitle from '../layout/steps-title/StepTitle';


const { checkoutStorage } = useCheckoutData(); const { checkoutStorage } = useCheckoutData();
const { parseCheckoutValue, clearCheckout } = useCheckoutDataUpdate(); const { parseCheckoutValue, clearCheckout } = useCheckoutDataUpdate();
const { clearCart } = useStoreUpdate(); const { clearCart } = useStoreUpdate();
const [orderData, setOrderData] = useState({});
const [orderData, setOrderData] = useState<OrderData>({} as OrderData);


const router = useRouter(); const router = useRouter();


> >
<Typography sx={{ fontSize: 18, fontWeight: 600 }}> <Typography sx={{ fontSize: 18, fontWeight: 600 }}>
{t('review:date')} {t('review:date')}
{orderData.time}
{orderData?.time?.toLocaleDateString()}
</Typography> </Typography>
</Box> </Box>
<Box <Box

+ 10
- 6
components/shipping-content/ShippingContent.tsx View File

import { setCookie } from 'nookies'; import { setCookie } from 'nookies';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import {
CheckoutData,
useCheckoutData, useCheckoutData,
useCheckoutDataUpdate, useCheckoutDataUpdate,
} from '../../store/checkout-context'; } from '../../store/checkout-context';
import { stripe } from '../../utils/helpers/stripe'; import { stripe } from '../../utils/helpers/stripe';
import { ShippingData as IShippingData } from '../../utils/interface/orderInterface';
import CardContainer from '../cards/card-container/CardContainer'; import CardContainer from '../cards/card-container/CardContainer';
import DataCard from '../cards/data-card/DataCard'; import DataCard from '../cards/data-card/DataCard';
import { FormValues } from '../forms/shipping-details/ShippingDetailsForm';
import ContentContainer from '../layout/content-wrapper/ContentContainer'; import ContentContainer from '../layout/content-wrapper/ContentContainer';
import PageWrapper from '../layout/page-wrapper/PageWrapper'; import PageWrapper from '../layout/page-wrapper/PageWrapper';
import StepTitle from '../layout/steps-title/StepTitle'; import StepTitle from '../layout/steps-title/StepTitle';
const { checkoutStorage } = useCheckoutData(); const { checkoutStorage } = useCheckoutData();
const { changeContact, changeShippingData } = useCheckoutDataUpdate(); const { changeContact, changeShippingData } = useCheckoutDataUpdate();
const [open, setOpen] = useState({ isOpen: false, type: '' }); const [open, setOpen] = useState({ isOpen: false, type: '' });
const [checkoutData, setCheckoutData] = useState({});
const [checkoutData, setCheckoutData] = useState<CheckoutData>(
{} as CheckoutData
);


const router = useRouter(); const router = useRouter();


setCheckoutData(checkoutStorage); setCheckoutData(checkoutStorage);
}, [checkoutStorage]); }, [checkoutStorage]);


const handleOpen = (type) => setOpen({ isOpen: true, type });
const handleOpen = (type: string) => setOpen({ isOpen: true, type });
const handleClose = () => setOpen({ isOpen: false, type: '' }); const handleClose = () => setOpen({ isOpen: false, type: '' });


const handleChangeShipping = (values) => {
changeShippingData(values);
const handleChangeShipping = (values: IShippingData | FormValues) => {
changeShippingData(values as IShippingData);
handleClose(); handleClose();
}; };


const handleChangeContact = (values) => {
const handleChangeContact = (values: string) => {
changeContact(values); changeContact(values);
handleClose(); handleClose();
}; };
price: el.product.stripeID, price: el.product.stripeID,
quantity: el.quantity, quantity: el.quantity,
})), })),
userInfo: checkoutData.userInfo,
}); });
setCookie(null, 'review-session', 'active', { setCookie(null, 'review-session', 'active', {
maxAge: 3600, maxAge: 3600,

+ 50
- 44
components/shipping-content/shipping-btnGroup/ButtonGroup.tsx View File

import { Box, Button } from '@mui/material'; import { Box, Button } from '@mui/material';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { FC } from 'react';


const ButtonGroup = ({ handleBackToCart, handleStripePayment }) => {
const { t } = useTranslation('shipping');
return (
<Box
sx={{
display: 'flex',
mb: 2,
borderRadius: 2,
}}
>
<Button
variant="contained"
sx={{
mt: 3,
mb: 2,
height: 50,
width: 150,
textTransform: 'none',
backgroundColor: 'primary.main',
color: 'white',
mr: 2,
}}
onClick={handleBackToCart}
>
{t('shipping:back')}
</Button>
<Button
type="submit"
variant="contained"
sx={{
mt: 3,
mb: 2,
backgroundColor: '#CBA213',
height: 50,
width: 200,
textTransform: 'none',
color: 'white',
}}
onClick={handleStripePayment}
>
{t('shipping:continue')}
</Button>
</Box>
);
interface Props {
handleBackToCart: () => void;
handleStripePayment: () => void;
}

const ButtonGroup: FC<Props> = ({ handleBackToCart, handleStripePayment }) => {
const { t } = useTranslation('shipping');
return (
<Box
sx={{
display: 'flex',
mb: 2,
borderRadius: 2,
}}
>
<Button
variant="contained"
sx={{
mt: 3,
mb: 2,
height: 50,
width: 150,
textTransform: 'none',
backgroundColor: 'primary.main',
color: 'white',
mr: 2,
}}
onClick={handleBackToCart}
>
{t('shipping:back')}
</Button>
<Button
type="submit"
variant="contained"
sx={{
mt: 3,
mb: 2,
backgroundColor: '#CBA213',
height: 50,
width: 200,
textTransform: 'none',
color: 'white',
}}
onClick={handleStripePayment}
>
{t('shipping:continue')}
</Button>
</Box>
);
}; };


export default ButtonGroup; export default ButtonGroup;

+ 91
- 76
components/shipping-content/shipping-data/ShippingData.tsx View File

import { Button, Typography } from '@mui/material'; import { Button, Typography } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { FC } from 'react';


const ShippingData = ({ email, address, city, postcode, handleOpen }) => {
const { t } = useTranslation('shipping');
interface Props {
email: string;
address: string;
city: string;
postcode: string;
handleOpen: (x: string) => void;
}


return (
<>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
backgroundColor: '#f2f2f2',
alignItems: 'center',
mb: 2,
borderRadius: 2,
p: 1,
}}
>
<Typography sx={{ fontSize: 18, fontWeight: 600 }}>
{t('shipping:contact')}
</Typography>
<Typography>{email}</Typography>
<Button
sx={{
height: 35,
minWidth: { md: 125, xs: 90 },
fontSize: 15,
textTransform: 'none',
backgroundColor: '#CBA213',
color: 'white',
}}
onClick={() => {
handleOpen('Contact');
}}
>
{t('shipping:changeBtn')}
</Button>
</Box>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
backgroundColor: '#f2f2f2',
alignItems: 'center',
mb: 2,
borderRadius: 2,
p: 1,
}}
>
<Typography
sx={{
fontSize: { md: 18, xs: 16 },
fontWeight: 600,
mr: { xs: 1, sm: 0 },
}}
>
{t('shipping:shipping')}
</Typography>
<Typography>
{address} | {city} | {postcode}
</Typography>
<Button
sx={{
height: 35,
minWidth: { md: 125, xs: 90 },
fontSize: 15,
textTransform: 'none',
backgroundColor: '#CBA213',
color: 'white',
}}
onClick={() => {
handleOpen('Shipping');
}}
>
{t('shipping:changeBtn')}
</Button>
</Box>
</>
);
const ShippingData: FC<Props> = ({
email,
address,
city,
postcode,
handleOpen,
}) => {
const { t } = useTranslation('shipping');

return (
<>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
backgroundColor: '#f2f2f2',
alignItems: 'center',
mb: 2,
borderRadius: 2,
p: 1,
}}
>
<Typography sx={{ fontSize: 18, fontWeight: 600 }}>
{t('shipping:contact')}
</Typography>
<Typography>{email}</Typography>
<Button
sx={{
height: 35,
minWidth: { md: 125, xs: 90 },
fontSize: 15,
textTransform: 'none',
backgroundColor: '#CBA213',
color: 'white',
}}
onClick={() => {
handleOpen('Contact');
}}
>
{t('shipping:changeBtn')}
</Button>
</Box>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
backgroundColor: '#f2f2f2',
alignItems: 'center',
mb: 2,
borderRadius: 2,
p: 1,
}}
>
<Typography
sx={{
fontSize: { md: 18, xs: 16 },
fontWeight: 600,
mr: { xs: 1, sm: 0 },
}}
>
{t('shipping:shipping')}
</Typography>
<Typography>
{address} | {city} | {postcode}
</Typography>
<Button
sx={{
height: 35,
minWidth: { md: 125, xs: 90 },
fontSize: 15,
textTransform: 'none',
backgroundColor: '#CBA213',
color: 'white',
}}
onClick={() => {
handleOpen('Shipping');
}}
>
{t('shipping:changeBtn')}
</Button>
</Box>
</>
);
}; };


export default ShippingData; export default ShippingData;

+ 19
- 3
components/shipping-content/shipping-modal/ShippingModal.tsx View File

import { Modal } from '@mui/material'; import { Modal } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import ContactForm from '../../forms/contact/ContactForm'; import ContactForm from '../../forms/contact/ContactForm';
import ShippingDetailsForm from '../../forms/shipping-details/ShippingDetailsForm';
import ShippingDetailsForm, {
FormValues,
} from '../../forms/shipping-details/ShippingDetailsForm';
import { FC } from 'react';
import { ShippingData } from '../../../utils/interface/orderInterface';


const ShippingModal = ({
interface Props {
open: { isOpen: boolean; type: string };
handleClose: () => void;
handleChangeShipping: (x: ShippingData | FormValues) => void;
handleChangeContact: (values: string) => void;
}

const ShippingModal: FC<Props> = ({
open, open,
handleClose, handleClose,
handleChangeShipping, handleChangeShipping,
}} }}
> >
{open.type === 'Shipping' && ( {open.type === 'Shipping' && (
<ShippingDetailsForm submitHandler={handleChangeShipping} />
<ShippingDetailsForm
submitHandler={handleChangeShipping}
backBtn={false}
isCheckout={false}
enableBtn={true}
/>
)} )}
{open.type === 'Contact' && ( {open.type === 'Contact' && (
<ContactForm submitHandler={handleChangeContact} /> <ContactForm submitHandler={handleChangeContact} />

+ 3
- 3
pages/contact/index.tsx View File

import type { NextPage } from 'next';
import type { NextPage, GetStaticProps } from 'next';


import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import ContactPageForm from '../../components/forms/contact/ContactPageForm'; import ContactPageForm from '../../components/forms/contact/ContactPageForm';
return <ContactPageForm />; return <ContactPageForm />;
}; };


export const getStaticProps = async ({ locale }: any) => {
export const getStaticProps: GetStaticProps = async ({ locale }: any) => {
return { return {
props: { props: {
...(await serverSideTranslations(locale, ['contact'])), ...(await serverSideTranslations(locale, ['contact'])),
}, },
}; };
}
};


export default Contact; export default Contact;

+ 5
- 4
pages/profile/index.tsx View File

import { NextPage } from 'next';
import { NextPage, GetServerSideProps } from 'next';
import { getSession } from 'next-auth/react'; import { getSession } from 'next-auth/react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import ProfileContent from '../../components/profile-content/ProfileContent'; import ProfileContent from '../../components/profile-content/ProfileContent';
import { LOGIN_PAGE } from '../../constants/pages'; import { LOGIN_PAGE } from '../../constants/pages';
import { getOrdersForOwner } from '../../requests/orders/getOrdersForOwnerRequest'; import { getOrdersForOwner } from '../../requests/orders/getOrdersForOwnerRequest';
import { OrderResponseGet } from '../../utils/interface/orderInterface';


const ProfilePage: NextPage = (props) => {
const ProfilePage: NextPage = (props: any) => {
return <ProfileContent orders={props.orders.orders}></ProfileContent>; return <ProfileContent orders={props.orders.orders}></ProfileContent>;
}; };


export async function getServerSideProps(context: any) {
export const getServerSideProps: GetServerSideProps = async (context: any) => {
const session = await getSession({ req: context.req }); const session = await getSession({ req: context.req });


if (!session) { if (!session) {
orders, orders,
}, },
}; };
}
};


export default ProfilePage; export default ProfilePage;

+ 16
- 16
pages/review/index.tsx View File

import { NextPage } from 'next';
import { NextPage, GetServerSideProps } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import nookies from 'nookies'; import nookies from 'nookies';
import ReviewContent from '../../components/review-content/ReviewContent'; import ReviewContent from '../../components/review-content/ReviewContent';


const ReviewPage: NextPage = () => { const ReviewPage: NextPage = () => {
return <ReviewContent></ReviewContent>;
return <ReviewContent></ReviewContent>;
}; };


export const getServerSideProps = async (ctx: any) => {
const cookies = nookies.get(ctx);

if (!cookies['review-session']) {
return {
redirect: {
destination: '/cart',
permanent: false,
},
};
}
export const getServerSideProps: GetServerSideProps = async (ctx: any) => {
const cookies = nookies.get(ctx);


if (!cookies['review-session']) {
return { return {
props: {
...(await serverSideTranslations(ctx.locale, ['review'])),
},
redirect: {
destination: '/cart',
permanent: false,
},
}; };
}

return {
props: {
...(await serverSideTranslations(ctx.locale, ['review'])),
},
};
}; };


export default ReviewPage; export default ReviewPage;

+ 19
- 19
pages/shipping/index.tsx View File

import { NextPage } from 'next';
import { NextPage, GetServerSideProps } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import nookies from 'nookies'; import nookies from 'nookies';
import ShippingContent from '../../components/shipping-content/ShippingContent'; import ShippingContent from '../../components/shipping-content/ShippingContent';


const ShippingPage: NextPage = () => { const ShippingPage: NextPage = () => {
return <ShippingContent></ShippingContent>;
return <ShippingContent></ShippingContent>;
}; };


export const getServerSideProps = async (ctx: any) => {
const cookies = nookies.get(ctx);

if (!cookies['shipping-session']) {
return {
redirect: {
destination: '/cart',
permanent: false,
},
};
}
export const getServerSideProps: GetServerSideProps = async (ctx: any) => {
const cookies = nookies.get(ctx);


if (!cookies['shipping-session']) {
return { return {
props: {
...(await serverSideTranslations(ctx.locale, [
'shipping',
'addressForm',
])),
},
redirect: {
destination: '/cart',
permanent: false,
},
}; };
}

return {
props: {
...(await serverSideTranslations(ctx.locale, [
'shipping',
'addressForm',
])),
},
};
}; };
export default ShippingPage; export default ShippingPage;

+ 1
- 1
public/locales/en/review.json View File

"note": "Thank you for placing your order with us. We wll get to work on sending your order as soon as possible", "note": "Thank you for placing your order with us. We wll get to work on sending your order as soon as possible",
"title": "Order Summary", "title": "Order Summary",
"date": "Order placed on: ", "date": "Order placed on: ",
"email": "Email",
"email": "Email: ",
"total": "Total: $", "total": "Total: $",
"shipping": "Shipping Address: ", "shipping": "Shipping Address: ",
"back": "Back to Home" "back": "Back to Home"

+ 3
- 15
store/checkout-context.tsx View File

import { createContext, useContext, useState, FC, ReactNode } from 'react'; import { createContext, useContext, useState, FC, ReactNode } from 'react';
import { getSStorage, setSStorage } from '../utils/helpers/storage'; import { getSStorage, setSStorage } from '../utils/helpers/storage';
import { ShippingData } from '../utils/interface/orderInterface';
import { OrderData, ShippingData } from '../utils/interface/orderInterface';
import { ProductData } from '../utils/interface/productInterface'; import { ProductData } from '../utils/interface/productInterface';
import { UserData } from '../utils/interface/userInterface';


interface Props { interface Props {
children: ReactNode; children: ReactNode;
quantity: number; quantity: number;
} }


interface CheckoutData {
export interface CheckoutData {
products: Products[]; products: Products[];
userInfo: ShippingData; userInfo: ShippingData;
userID: string; userID: string;
} }


interface OrderData {
products: Array<ProductData>;
time: string;
shippingAddress: ShippingData;
totalPrice: number;
numberOfItems: number;
fulfilled: boolean;
owner: string;
stripeCheckoutId: string;
}

interface ICheckout { interface ICheckout {
checkoutStorage: CheckoutData; checkoutStorage: CheckoutData;
} }
const date = new Date(); const date = new Date();
const dataToStore = { const dataToStore = {
products: items?.products?.map((el) => el.product), products: items?.products?.map((el) => el.product),
time: date.toLocaleDateString(),
time: date,
shippingAddress: items?.userInfo, shippingAddress: items?.userInfo,
totalPrice: items?.products totalPrice: items?.products
?.map((entry) => entry?.product.price * entry?.quantity) ?.map((entry) => entry?.product.price * entry?.quantity)

+ 5
- 1
utils/helpers/storage.ts View File



const storedItems = window.localStorage.getItem(key); const storedItems = window.localStorage.getItem(key);


return storedItems ? JSON.parse(storedItems) : [];
return storedItems
? Object.keys(JSON.parse(storedItems)).length
? JSON.parse(storedItems)
: []
: [];
}; };


export const removeStorage = (key: string) => { export const removeStorage = (key: string) => {

+ 1
- 1
utils/interface/orderInterface.ts View File

totalPrice: number; totalPrice: number;
numberOfItems: number; numberOfItems: number;
fulfilled: boolean; fulfilled: boolean;
owner: ObjectId;
owner: ObjectId | string;
stripeCheckoutId: string; stripeCheckoutId: string;
} }



Loading…
Cancel
Save