ソースを参照

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

products-typescript
Selena Simic 3年前
コミット
efd9c69a19

+ 20
- 19
components/cards/card-container/CardContainer.tsx ファイルの表示

@@ -1,28 +1,29 @@
import { Box } from '@mui/system';
import { ReactNode } from 'react';

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

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;

+ 62
- 62
components/cards/data-card/DataCard.tsx ファイルの表示

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

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

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={{
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;

+ 42
- 40
components/cards/order-card/OrderCard.tsx ファイルの表示

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

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

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;

+ 1
- 1
components/checkout-content/CheckoutContent.tsx ファイルの表示

@@ -73,7 +73,7 @@ const CheckoutContent = () => {
<ContentContainer>
<Box flexGrow={1} sx={{ minWidth: '65%' }}>
<ShippingDetailsForm
enableBtn={false}
enableBtn={true}
backBtn={true}
isCheckout={true}
submitHandler={submitHandler}

+ 65
- 62
components/forms/contact/ContactForm.tsx ファイルの表示

@@ -1,75 +1,78 @@
import { Box, Button, Paper, TextField } from '@mui/material';
import { useFormik } from 'formik';
import React, { useState } from 'react';
import React, { useState, FC } from 'react';
import { contactSchema } from '../../../schemas/contactSchema';
import { useCheckoutData } from '../../../store/checkout-context';
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;

+ 130
- 130
components/forms/contact/ContactPageForm.tsx ファイルの表示

@@ -1,135 +1,135 @@
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
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>
</Container>
);
};
export default ContactPageForm;
</Box>
</Container>
);
};

export default ContactPageForm;

+ 4
- 3
components/forms/shipping-details/ShippingDetailsForm.tsx ファイルの表示

@@ -5,9 +5,10 @@ import { useRouter } from 'next/router';
import { useState, FC } from 'react';
import { registerSchema } from '../../../schemas/shippingDetailsSchema';
import { useUserData } from '../../../store/user-context';
import { ShippingData } from '../../../utils/interface/orderInterface';
import ErrorMessageComponent from '../../mui/ErrorMessageComponent';

interface FormValues {
export interface FormValues {
fullName: string;
address: string;
address2: string;
@@ -17,7 +18,7 @@ interface FormValues {
}

interface Props {
submitHandler: (x: FormValues) => void;
submitHandler: (x: FormValues | ShippingData) => void;
backBtn: boolean;
isCheckout: boolean;
enableBtn: boolean;
@@ -34,7 +35,7 @@ const ShippingDetailsForm: FC<Props> = ({
const { userStorage } = useUserData();
const router = useRouter();

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


+ 84
- 66
components/profile-content/ProfileContent.tsx ファイルの表示

@@ -2,9 +2,10 @@ import { Typography } from '@mui/material';
import { Box } from '@mui/system';
import { useSession } from 'next-auth/react';
import { useTranslation } from 'next-i18next';
import { useState } from 'react';
import { useState, FC } from 'react';
import { updateUser } from '../../requests/user/userUpdateRequest';
import { useUserUpdate } from '../../store/user-context';
import { OrderDataDB } from '../../utils/interface/orderInterface';
import CardContainer from '../cards/card-container/CardContainer';
import OrderCard from '../cards/order-card/OrderCard';
import ShippingDetailsForm from '../forms/shipping-details/ShippingDetailsForm';
@@ -13,76 +14,93 @@ import PageWrapper from '../layout/page-wrapper/PageWrapper';
import StepTitle from '../layout/steps-title/StepTitle';
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;

+ 3
- 2
components/review-content/ReviewContent.tsx ファイルの表示

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

@@ -21,7 +22,7 @@ const ReviewContent = () => {
const { checkoutStorage } = useCheckoutData();
const { parseCheckoutValue, clearCheckout } = useCheckoutDataUpdate();
const { clearCart } = useStoreUpdate();
const [orderData, setOrderData] = useState({});
const [orderData, setOrderData] = useState<OrderData>({} as OrderData);

const router = useRouter();

@@ -104,7 +105,7 @@ const ReviewContent = () => {
>
<Typography sx={{ fontSize: 18, fontWeight: 600 }}>
{t('review:date')}
{orderData.time}
{orderData?.time?.toLocaleDateString()}
</Typography>
</Box>
<Box

+ 10
- 6
components/shipping-content/ShippingContent.tsx ファイルの表示

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

const router = useRouter();

@@ -32,15 +37,15 @@ const ShippingContent = () => {
setCheckoutData(checkoutStorage);
}, [checkoutStorage]);

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

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

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

+ 50
- 44
components/shipping-content/shipping-btnGroup/ButtonGroup.tsx ファイルの表示

@@ -1,50 +1,56 @@
import { Box, Button } from '@mui/material';
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;

+ 91
- 76
components/shipping-content/shipping-data/ShippingData.tsx ファイルの表示

@@ -1,84 +1,99 @@
import { Button, Typography } from '@mui/material';
import { Box } from '@mui/system';
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;

+ 19
- 3
components/shipping-content/shipping-modal/ShippingModal.tsx ファイルの表示

@@ -1,9 +1,20 @@
import { Modal } from '@mui/material';
import { Box } from '@mui/system';
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,
handleClose,
handleChangeShipping,
@@ -26,7 +37,12 @@ const ShippingModal = ({
}}
>
{open.type === 'Shipping' && (
<ShippingDetailsForm submitHandler={handleChangeShipping} />
<ShippingDetailsForm
submitHandler={handleChangeShipping}
backBtn={false}
isCheckout={false}
enableBtn={true}
/>
)}
{open.type === 'Contact' && (
<ContactForm submitHandler={handleChangeContact} />

+ 3
- 3
pages/contact/index.tsx ファイルの表示

@@ -1,4 +1,4 @@
import type { NextPage } from 'next';
import type { NextPage, GetStaticProps } from 'next';

import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import ContactPageForm from '../../components/forms/contact/ContactPageForm';
@@ -7,12 +7,12 @@ const Contact: NextPage = () => {
return <ContactPageForm />;
};

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

export default Contact;

+ 5
- 4
pages/profile/index.tsx ファイルの表示

@@ -1,15 +1,16 @@
import { NextPage } from 'next';
import { NextPage, GetServerSideProps } from 'next';
import { getSession } from 'next-auth/react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import ProfileContent from '../../components/profile-content/ProfileContent';
import { LOGIN_PAGE } from '../../constants/pages';
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>;
};

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

if (!session) {
@@ -32,6 +33,6 @@ export async function getServerSideProps(context: any) {
orders,
},
};
}
};

export default ProfilePage;

+ 16
- 16
pages/review/index.tsx ファイルの表示

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

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 {
props: {
...(await serverSideTranslations(ctx.locale, ['review'])),
},
redirect: {
destination: '/cart',
permanent: false,
},
};
}

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

export default ReviewPage;

+ 19
- 19
pages/shipping/index.tsx ファイルの表示

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

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 {
props: {
...(await serverSideTranslations(ctx.locale, [
'shipping',
'addressForm',
])),
},
redirect: {
destination: '/cart',
permanent: false,
},
};
}

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

+ 1
- 1
public/locales/en/review.json ファイルの表示

@@ -3,7 +3,7 @@
"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",
"date": "Order placed on: ",
"email": "Email",
"email": "Email: ",
"total": "Total: $",
"shipping": "Shipping Address: ",
"back": "Back to Home"

+ 3
- 15
store/checkout-context.tsx ファイルの表示

@@ -1,8 +1,7 @@
import { createContext, useContext, useState, FC, ReactNode } from 'react';
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 { UserData } from '../utils/interface/userInterface';

interface Props {
children: ReactNode;
@@ -13,23 +12,12 @@ interface Products {
quantity: number;
}

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

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

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

+ 5
- 1
utils/helpers/storage.ts ファイルの表示

@@ -9,7 +9,11 @@ export const getStorage = (key: string) => {

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) => {

+ 1
- 1
utils/interface/orderInterface.ts ファイルの表示

@@ -13,7 +13,7 @@ export interface OrderData {
totalPrice: number;
numberOfItems: number;
fulfilled: boolean;
owner: ObjectId;
owner: ObjectId | string;
stripeCheckoutId: string;
}


読み込み中…
キャンセル
保存