Преглед изворни кода

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…
Откажи
Сачувај