Преглед на файлове

feat: account page, cart page, shipping page

shipping
ntasicc преди 3 години
родител
ревизия
2caaf4c60a

+ 141
- 0
components/cards/cart-card/CartCard.jsx Целия файл

@@ -0,0 +1,141 @@
import { Box, Button, ButtonGroup, Paper, Typography } from '@mui/material';
import Image from 'next/image';

const CartCard = () => {
return (
<Paper
sx={{
p: 1,
width: '88%',
mb: 2,
ml: 12,
backgroundColor: '#f2f2f2',
display: 'flex',
}}
elevation={3}
>
<Box sx={{ width: '30%' }}>
<Image
src="/images/coffee-mug.svg"
alt="profile"
width={500}
height={300}
/>
</Box>
<Box
sx={{
ml: -4,
mr: 2,
display: 'flex',
alignItems: 'center',
width: '30%',
}}
>
<Typography
sx={{
width: '100%',
textAlign: 'center',
height: 25,
fontWeight: 600,
fontSize: 20,
}}
>
Begin Mug in White
</Typography>
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
width: '20%',
alignItems: 'center',
}}
>
<Typography
sx={{
width: '100%',
textAlign: 'center',
height: 16,
fontSize: 14,
}}
>
Quantity
</Typography>
<ButtonGroup
size="small"
aria-label="small outlined button group"
sx={{
height: 35,
width: 125,
mt: 1,
backgroundColor: 'primary.main',
color: 'white',
border: 0,
}}
>
<Button
sx={{
color: 'white',
fontSize: 17,
width: 25,
}}
onClick={() => {}}
>
-
</Button>
<Button
sx={{
color: 'white',
fontSize: 15,
width: 25,
}}
>
1
</Button>
<Button
sx={{
color: 'white',
fontSize: 17,
width: 25,
}}
onClick={() => {}}
>
+
</Button>
</ButtonGroup>
<Button
sx={{
height: 35,
mt: 1,
width: 125,
fontSize: 15,
textTransform: 'none',
backgroundColor: '#C6453E',
color: 'white',
}}
startIcon={
<Image src="/images/x.svg" alt="remove" width={15} height={15} />
}
>
Remove
</Button>
</Box>
<Box
sx={{ ml: 3, display: 'flex', flexDirection: 'column', width: '20%' }}
>
<Typography
sx={{
width: '100%',
textAlign: 'center',
height: 25,
fontSize: 20,
}}
>
Total: $20
</Typography>
</Box>
</Paper>
);
};

export default CartCard;

+ 45
- 22
components/cards/data-card/DataCard.jsx Целия файл

@@ -1,29 +1,52 @@
import { Divider, Paper, Typography } from '@mui/material';
import PropType from 'prop-types';
import { Box, Paper, Typography } from '@mui/material';
import Image from 'next/image';

const DataCard = ({ data, t }) => {
const DataCard = () => {
return (
<Paper sx={{ p: 3, height: '100%' }} elevation={3}>
<Typography sx={{ fontWeight: 600 }}>{t('Name')}</Typography>
<Typography display="inline"> {data.name}</Typography>
<Divider />
<Typography sx={{ fontWeight: 600 }}>{t('Age')}</Typography>
<Typography display="inline"> {data.age}</Typography>
<Divider />
<Typography sx={{ fontWeight: 600 }}>{t('Gender')}</Typography>
<Typography display="inline"> {data.gender}</Typography>
<Divider />
<Paper
sx={{
p: 3,
width: '100%',
mb: 2,
backgroundColor: '#f2f2f2',
display: 'flex',
}}
elevation={3}
>
<Box sx={{ width: '30%', borderRadius: 4, overflow: 'hidden' }}>
<Image
src="/images/coffee-mug.svg"
alt="profile"
width={200}
height={250}
/>
</Box>
<Box
sx={{ ml: 3, display: 'flex', flexDirection: 'column', width: '60%' }}
>
<Typography
sx={{
width: '100%',
textAlign: 'center',
height: 25,
fontWeight: 600,
fontSize: 20,
}}
>
Begin Mug in White
</Typography>
<Typography
sx={{
mt: 3,
fontSize: 14,
}}
>
Simple and beautiful Begin mug. Perfect companion for your next
delicious cup of coffee.
</Typography>
</Box>
</Paper>
);
};

DataCard.propTypes = {
data: PropType.shape({
name: PropType.string,
age: PropType.number,
gender: PropType.string,
}),
t: PropType.func,
};

export default DataCard;

+ 28
- 0
components/cards/order-card/OrderCard.jsx Целия файл

@@ -0,0 +1,28 @@
import { Divider, Paper, Typography } from '@mui/material';
import PropType from 'prop-types';

const OrderCard = ({ data }) => {
return (
<Paper
sx={{ p: 3, width: '100%', mb: 2, backgroundColor: '#f2f2f2' }}
elevation={3}
>
<Typography sx={{ fontWeight: 600 }}>
Order placed on {data.date}
</Typography>
<Divider />
<Typography sx={{ mt: 1 }}>By: {data.name}</Typography>
<Typography>Total: ${data.totalPrice}</Typography>
</Paper>
);
};

OrderCard.propTypes = {
data: PropType.shape({
date: PropType.string,
name: PropType.string,
totalPrice: PropType.number,
}),
};

export default OrderCard;

+ 57
- 0
components/cards/order-summary-card/OrderSummaryCard.jsx Целия файл

@@ -0,0 +1,57 @@
import { Button, Divider, Paper, Typography } from '@mui/material';
import { Box } from '@mui/system';
import Image from 'next/image';
import PropType from 'prop-types';

const OrderSummaryCard = ({ data }) => {
return (
<Paper
sx={{ p: 3, width: '100%', mb: 2, backgroundColor: '#f1f1f1' }}
elevation={3}
>
<Typography
sx={{
fontSize: 26,
color: 'primary.main',
textAlign: 'center',
width: '100%',
}}
>
Order Summary
</Typography>
<Typography sx={{ mt: 4 }}>Items total:${data.totalPrice}</Typography>
<Typography sx={{ mt: 1.5 }}>Shipping Costs: FREE</Typography>
<Typography sx={{ mt: 1.5, mb: 1.5 }}>
Total: ${data.totalPrice}
</Typography>
<Divider />
<Box sx={{ textAlign: 'center', mt: 4, width: '100%' }}>
<Button
sx={{
backgroundColor: '#0066ff',
color: 'white',
textTransform: 'none',
px: 2,
}}
startIcon={
<Image src="/images/lock.svg" alt="lock" width={18} height={18} />
}
>
Proceed to Checkout
</Button>
</Box>
<Typography sx={{ mt: 3, fontSize: 13 }}>
Once the checkout process begins you will have an hour to complete your
checkout otherwise you will be returned back to the cart to start over.
</Typography>
</Paper>
);
};

OrderSummaryCard.propTypes = {
data: PropType.shape({
totalPrice: PropType.number,
}),
};

export default OrderSummaryCard;

+ 44
- 0
components/cart-content/CartContent.jsx Целия файл

@@ -0,0 +1,44 @@
import { Breadcrumbs, Divider, Grid, Typography } from '@mui/material';
import { Box } from '@mui/system';
import CartCard from '../cards/cart-card/CartCard';
import OrderSummaryCard from '../cards/order-summary-card/OrderSummaryCard';

const CartContent = () => {
return (
<Grid container spacing={2} sx={{ py: 10, height: '100%', width: '100%' }}>
<Grid item xs={12}>
<Typography
variant="h3"
sx={{ pl: 12, mt: 12, height: '100%', color: 'primary.main' }}
>
Items in Your Cart
</Typography>
</Grid>
<Grid item xs={12}>
<Divider sx={{ backgroundColor: 'primary.main', mx: 12 }} />
</Grid>
<Grid item xs={12} sx={{ mt: 4 }}>
<Breadcrumbs
aria-label="breadcrumb"
separator="›"
sx={{ pl: 12, fontSize: 20 }}
>
<Typography color="red">Cart</Typography>
<Typography></Typography>
</Breadcrumbs>
</Grid>
<Grid item xs={8}>
<CartCard></CartCard>
<CartCard></CartCard>
<CartCard></CartCard>
</Grid>
<Grid item xs={4}>
<Box sx={{ width: '80%', mt: 2 }}>
<OrderSummaryCard data={{ totalPrice: 60 }}></OrderSummaryCard>
</Box>
</Grid>
</Grid>
);
};

export default CartContent;

+ 50
- 0
components/checkout-content/CheckoutContent.jsx Целия файл

@@ -0,0 +1,50 @@
import { Breadcrumbs, Divider, Grid, Typography } from '@mui/material';
import { Box } from '@mui/system';
import DataCard from '../cards/data-card/DataCard';
import ShippingDetailsForm from '../forms/shipping-details/ShippingDetailsForm';

const ProfileContent = () => {
return (
<Grid container spacing={2} sx={{ py: 10, height: '100%', width: '100%' }}>
<Grid item xs={12}>
<Typography
variant="h3"
sx={{ pl: 12, mt: 12, height: '100%', color: 'primary.main' }}
>
Checkout
</Typography>
</Grid>
<Grid item xs={12}>
<Divider sx={{ backgroundColor: 'primary.main', mx: 12 }} />
</Grid>
<Grid item xs={12} sx={{ mt: 4 }}>
<Breadcrumbs
aria-label="breadcrumb"
separator="›"
sx={{ pl: 12, fontSize: 20 }}
>
<Typography>Cart</Typography>
<Typography color="red">Checkout</Typography>
</Breadcrumbs>
</Grid>
<Grid item xs={12} sx={{ mt: 1 }}>
<Typography sx={{ pl: 12, fontSize: 20 }}>
The following fields will be used as the shipping details for your
order
</Typography>
</Grid>
<Grid item xs={8}>
<ShippingDetailsForm backBtn={true}></ShippingDetailsForm>
</Grid>
<Grid item xs={4}>
<Box sx={{ width: '80%', mt: 2 }}>
<DataCard></DataCard>
<DataCard></DataCard>
<DataCard></DataCard>
</Box>
</Grid>
</Grid>
);
};

export default ProfileContent;

+ 166
- 0
components/forms/shipping-details/ShippingDetailsForm.jsx Целия файл

@@ -0,0 +1,166 @@
import { Box, Button, Paper, TextField } from '@mui/material';
import { useFormik } from 'formik';
import PropType from 'prop-types';
import { useState } from 'react';
import { shippingDetailsSchema } from '../../../schemas/shippingDetailsSchema';
import ErrorMessageComponent from '../../mui/ErrorMessageComponent';

const ShippingDetailsForm = ({ backBtn = false }) => {
const [error] = useState({ hasError: false, errorMessage: '' });

const submitHandler = async (values) => {
console.log(values);
};

const formik = useFormik({
initialValues: {
fullName: '',
email: '',
address: '',
address2: '',
city: '',
country: '',
poostalCode: '',
},
validationSchema: shippingDetailsSchema,
onSubmit: submitHandler,
validateOnBlur: true,
enableReinitialize: true,
});

return (
<Paper
sx={{ p: 3, width: '90%', ml: 12, 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 }}
>
<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}
autoFocus
fullWidth
/>
<TextField
name="fullName"
label="Name"
margin="normal"
value={formik.values.fullName}
onChange={formik.handleChange}
error={formik.touched.fullName && Boolean(formik.errors.fullName)}
helperText={formik.touched.fullName && formik.errors.fullName}
fullWidth
/>
<TextField
name="address"
label="Address"
margin="normal"
value={formik.values.address}
onChange={formik.handleChange}
error={formik.touched.address && Boolean(formik.errors.address)}
helperText={formik.touched.address && formik.errors.address}
fullWidth
/>
<TextField
name="address2"
label="Address Line 2"
margin="normal"
value={formik.values.address2}
onChange={formik.handleChange}
error={formik.touched.address2 && Boolean(formik.errors.address2)}
helperText={formik.touched.address2 && formik.errors.address2}
fullWidth
/>
<TextField
name="city"
label="City"
margin="normal"
value={formik.values.city}
onChange={formik.handleChange}
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
fullWidth
/>
<Box sx={{ display: 'flex' }}>
<TextField
name="country"
label="Country"
margin="normal"
value={formik.values.country}
onChange={formik.handleChange}
error={formik.touched.country && Boolean(formik.errors.country)}
helperText={formik.touched.country && formik.errors.country}
fullWidth
sx={{ mr: 1.5 }}
/>
<TextField
name="city"
label="City"
margin="normal"
value={formik.values.city}
onChange={formik.handleChange}
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
fullWidth
/>
</Box>

{backBtn && (
<Button
variant="contained"
sx={{
mt: 3,
mb: 2,
height: 50,
width: 150,
textTransform: 'none',
backgroundColor: 'primary.main',
color: 'white',
mr: 2,
}}
>
Back to cart
</Button>
)}
<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>
);
};

ShippingDetailsForm.propTypes = {
backBtn: PropType.Boolean,
};

export default ShippingDetailsForm;

+ 1
- 1
components/layout/navbar/Navbar.jsx Целия файл

@@ -54,7 +54,7 @@ const Navbar = () => {
<Typography
key={page}
textAlign="center"
sx={{ mx: 'auto', fontSize: 20, fontWeight: 500 }}
sx={{ mx: 'auto', fontSize: 20, fontWeight: 500, color: 'black' }}
>
{page}
</Typography>

+ 45
- 0
components/profile-content/ProfileContent.jsx Целия файл

@@ -0,0 +1,45 @@
import { Grid, Typography } from '@mui/material';
import { Box } from '@mui/system';
import OrderCard from '../cards/order-card/OrderCard';
import ShippingDetailsForm from '../forms/shipping-details/ShippingDetailsForm';

const ProfileContent = () => {
return (
<Grid container spacing={2} sx={{ py: 10, height: '100%', width: '100%' }}>
<Grid item xs={12}>
<Typography
variant="h3"
sx={{ pl: 12, mt: 12, height: '100%', color: 'primary.main' }}
>
Welcome to your user account
</Typography>
</Grid>
<Grid item xs={8} sx={{ mt: 4 }}>
<Typography sx={{ pl: 12, fontSize: 20 }}>
Save details for later
</Typography>
</Grid>
<Grid item xs={4} sx={{ mt: 4 }}>
<Typography sx={{ fontSize: 20 }}>Previous Orders</Typography>
</Grid>
<Grid item xs={8}>
<ShippingDetailsForm></ShippingDetailsForm>
</Grid>
<Grid item xs={4}>
<Box sx={{ width: '60%', mt: 2 }}>
<OrderCard
data={{ date: '2022-09-02', name: 'John Doe', totalPrice: 30 }}
></OrderCard>
<OrderCard
data={{ date: '2022-09-02', name: 'John Doe', totalPrice: 30 }}
></OrderCard>
<OrderCard
data={{ date: '2022-09-02', name: 'John Doe', totalPrice: 30 }}
></OrderCard>
</Box>
</Grid>
</Grid>
);
};

export default ProfileContent;

+ 1
- 0
constants/pages.js Целия файл

@@ -1,4 +1,5 @@
export const BASE_PAGE = '/';
export const CHECKOUT_PAGE = '/checkout';
export const LOGIN_PAGE = '/auth';
export const PROFILE_PAGE = '/profile';
export const REGISTER_PAGE = '/auth/register';

+ 1
- 1
pages/_app.js Целия файл

@@ -23,7 +23,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
<ThemeProvider theme={theme}>
<Layout>
<Head>
<title>NextJS template</title>
<title>Coffee Shop</title>
<meta name="description" content="NextJS template" />
<meta
name="viewport"

+ 7
- 0
pages/cart/index.js Целия файл

@@ -0,0 +1,7 @@
import CartContent from '../../components/cart-content/CartContent';

const CartPage = () => {
return <CartContent></CartContent>;
};

export default CartPage;

+ 7
- 0
pages/checkout/index.js Целия файл

@@ -0,0 +1,7 @@
import CheckoutContent from '../../components/checkout-content/CheckoutContent';

const CheckoutPage = () => {
return <CheckoutContent></CheckoutContent>;
};

export default CheckoutPage;

+ 17
- 18
pages/profile/index.js Целия файл

@@ -1,28 +1,27 @@
import { getSession, useSession } from 'next-auth/react';
import ProfileCard from '../../components/cards/profile-card/ProfileCard';
import { LOGIN_PAGE } from '../../constants/pages';
import { useSession } from 'next-auth/react';
import ProfileContent from '../../components/profile-content/ProfileContent';

const ProfilePage = () => {
const { data: session } = useSession();

return <ProfileCard profileData={{ name: session.user.name }} />;
return <ProfileContent></ProfileContent>;
};

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

if (!session) {
return {
redirect: {
destination: LOGIN_PAGE,
permanent: false,
},
};
}
// if (!session) {
// return {
// redirect: {
// destination: LOGIN_PAGE,
// permanent: false,
// },
// };
// }

return {
props: { session },
};
}
// return {
// props: { session },
// };
// }

export default ProfilePage;

+ 9
- 0
public/images/coffee-mug.svg
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 3
- 0
public/images/lock.svg Целия файл

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8043 6.49485V3.80412C12.8043 1.70651 11.0978 0 9.00021 0C6.90259 0 5.19608 1.70651 5.19608 3.80412V6.49485H2.59814V18H15.4023V6.49485H12.8043ZM6.30948 3.80412C6.30948 2.32044 7.51652 1.1134 9.00021 1.1134C10.4839 1.1134 11.6909 2.32044 11.6909 3.80412V6.49485H6.30948V3.80412ZM14.2889 16.8866H3.71155V7.60825H14.2889V16.8866Z" fill="white"/>
</svg>

+ 3
- 0
public/images/x.svg Целия файл

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.14623 4.15914C7.23998 4.0655 7.36706 4.01291 7.49956 4.01291C7.63206 4.01291 7.75914 4.0655 7.85289 4.15914L11.3329 7.63914L14.8129 4.15914C14.8587 4.11001 14.9139 4.07061 14.9752 4.04328C15.0365 4.01596 15.1027 4.00126 15.1699 4.00008C15.237 3.99889 15.3037 4.01124 15.366 4.03639C15.4282 4.06154 15.4848 4.09897 15.5323 4.14645C15.5797 4.19393 15.6172 4.25048 15.6423 4.31274C15.6675 4.375 15.6798 4.44169 15.6786 4.50882C15.6774 4.57596 15.6627 4.64216 15.6354 4.7035C15.6081 4.76483 15.5687 4.82003 15.5196 4.86581L12.0396 8.34581L15.5196 11.8258C15.5687 11.8716 15.6081 11.9268 15.6354 11.9881C15.6627 12.0494 15.6774 12.1157 15.6786 12.1828C15.6798 12.2499 15.6675 12.3166 15.6423 12.3789C15.6172 12.4411 15.5797 12.4977 15.5323 12.5452C15.4848 12.5926 15.4282 12.6301 15.366 12.6552C15.3037 12.6804 15.237 12.6927 15.1699 12.6915C15.1027 12.6903 15.0365 12.6757 14.9752 12.6483C14.9139 12.621 14.8587 12.5816 14.8129 12.5325L11.3329 9.05247L7.85289 12.5325C7.75811 12.6208 7.63275 12.6689 7.50321 12.6666C7.37368 12.6643 7.25009 12.6118 7.15848 12.5202C7.06687 12.4286 7.0144 12.305 7.01211 12.1755C7.00982 12.046 7.05791 11.9206 7.14623 11.8258L10.6262 8.34581L7.14623 4.86581C7.05259 4.77206 7 4.64497 7 4.51247C7 4.37997 7.05259 4.25289 7.14623 4.15914Z" fill="white"/>
</svg>

+ 11
- 0
schemas/shippingDetailsSchema.js Целия файл

@@ -0,0 +1,11 @@
import * as Yup from 'yup';

export const registerSchema = Yup.object().shape({
fullName: Yup.string().required('Full name is required'),
email: Yup.string().email().required('Email is required'),
address: Yup.string().required('Address is required'),
address2: Yup.string(),
city: Yup.string().required('City is required'),
country: Yup.string().required('Country name is required'),
postalCode: Yup.string().required('Postal code name is required'),
});

+ 0
- 1
styles/globals.css Целия файл

@@ -4,7 +4,6 @@
* {
box-sizing: border-box;
margin: 0;
height: 100%;
}

body {

Loading…
Отказ
Запис