#3 single-product

Unito
lazarkostic ha unito 2 commit da single-product a master 3 anni fa

+ 3
- 0
.vscode/settings.json Vedi File

"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true, "source.fixAll": true,
"source.organizeImports": true "source.organizeImports": true
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
} }
} }

+ 29
- 0
components/filter-sort/FilterSort.jsx Vedi File

import { Box } from '@mui/system';
import ProductType from '../product-type/ProductType';
import Sort from '../sort/sort';

const FilterSort = ({
sort,
handleSortChange,
productType,
handleProductTypeChange,
}) => {
return (
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
justifyContent: { xs: 'center' },
alignItems: { xs: 'center' },
}}
>
<Sort sort={sort} handleSortChange={handleSortChange} />
<ProductType
productType={productType}
handleProductTypeChange={handleProductTypeChange}
/>
</Box>
);
};

export default FilterSort;

+ 35
- 0
components/loader/Loader.js Vedi File

import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';

const Loader = ({ loading }) => {
return (
loading && (
<Box
sx={{
display: 'flex',
zIndex: 99,
height: '100vh',
width: '100vw',
justifyContent: 'center',
alignItems: 'center',
position: 'fixed',
top: 0,
left: 0,
}}
>
<Box
sx={{
position: 'absolute',
top: '48%',
left: '48%',
marginX: 'auto',
}}
>
<CircularProgress color="inherit" size={60} thickness={4} />
</Box>
</Box>
)
);
};

export default Loader;

+ 30
- 13
components/product-card/ProductCard.jsx Vedi File

import { Button, Typography } from '@mui/material'; import { Button, Typography } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import Image from 'next/image'; import Image from 'next/image';
import NextLink from 'next/link';
import { useStore, useStoreUpdate } from '../../store/cart-context';


const ProductCard = () => {
const ProductCard = ({ product }) => {
const { addCartValue } = useStoreUpdate();
const { cartStorage } = useStore();
const addProductToCart = (quantity) => addCartValue(product, quantity);
const inCart = cartStorage?.some(
(item) => item.product.customID === product.customID
)
? true
: false;
return ( return (
<Box <Box
sx={{ sx={{
width: '100%', width: '100%',
height: '590px',
height: '100%',
border: 'none', border: 'none',
mb: '75px', mb: '75px',
backgroundColor: '#F5ECD4', backgroundColor: '#F5ECD4',
}} }}
> >
<Box width="100%"> <Box width="100%">
<Image
src="/images/product-card-image.jpg"
alt="product image"
width={373.33}
height={249}
/>
<NextLink
style={{ cursor: 'pointer' }}
href={`/products/${product.customID}`}
passHref
>
<Image
src="/images/product-card-image.jpg"
alt="product image"
width={630}
height={390}
/>
</NextLink>
</Box> </Box>
<Box <Box
width="100%" width="100%"
}} }}
> >
<Typography fontSize="24px" align="center" pt={1} pb={3}> <Typography fontSize="24px" align="center" pt={1} pb={3}>
MINIMALIST PRINTED MUG
{product.name}
</Typography> </Typography>
<Typography align="center" fontSize="18px" m={2}> <Typography align="center" fontSize="18px" m={2}>
Our simple and sturdy mugs are made to last. With a minimalist desings
you will soon be enjoying your next brew.
{product.description}
</Typography> </Typography>
<Typography fontSize="24px" align="center" pt={4}> <Typography fontSize="24px" align="center" pt={4}>
$20
${product.price}
</Typography> </Typography>
<Box textAlign="center" mt={1}> <Box textAlign="center" mt={1}>
<Button <Button
disabled={inCart}
onClick={() => addProductToCart(1)}
sx={{ sx={{
backgroundColor: '#CBA213', backgroundColor: '#CBA213',
height: 50, height: 50,
color: 'white', color: 'white',
}} }}
> >
Add to cart
{inCart ? 'In Cart' : 'Add to cart'}
</Button> </Button>
</Box> </Box>
</Box> </Box>

+ 3
- 2
components/product-type/ProductType.jsx Vedi File

value={productType} value={productType}
onChange={handleProductTypeChange} onChange={handleProductTypeChange}
> >
<MenuItem value="asc">Name - A-Z</MenuItem>
<MenuItem value="desc">Name - Z-A</MenuItem>
<MenuItem value="All">All</MenuItem>
<MenuItem value="Coffee">Coffee</MenuItem>
<MenuItem value="Mug">Mug</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
</> </>

+ 119
- 0
components/products-grid/ProductsGrid.jsx Vedi File

import { Button, Container, Grid } from '@mui/material';
import { Box } from '@mui/system';
import Image from 'next/image';
import { useMemo, useState } from 'react';
import { useFetchProductsByCategory } from '../../hooks/useFetchProductData';
import { compare } from '../../utils/helpers/sortHelpers';
import ProductCard from '../product-card/ProductCard';

const ProductsGrid = ({
allProducts,
hasNextPage,
productType,
fetchNextPage,
sort,
}) => {
const productsPerPage = 9;
const [next, setNext] = useState(productsPerPage);

const { data: filteredData } = useFetchProductsByCategory(productType);

const allItems = useMemo(
() => allProducts?.pages?.flatMap((page) => page.product),
[allProducts]
);

const dataToDisplay =
productType === 'All' || productType === ''
? allItems.sort(compare('name', sort)).map((item) => (
<Grid key={item._id} item md={4} sm={6} xs={12} sx={{ mb: '100px' }}>
<ProductCard product={item} />
</Grid>
))
: filteredData?.productsByCategory
.slice(0, next)
.sort(compare('name', sort))
.map((item) => (
<Grid
key={item._id}
item
md={4}
sm={6}
xs={12}
sx={{ mb: '100px' }}
>
<ProductCard product={item} />
</Grid>
));

const handleMoreProducts = () => {
setNext(next + productsPerPage);
};

return (
<Container
sx={{
mt: 10,
}}
>
<Grid container spacing={2}>
{dataToDisplay}
</Grid>
<Box textAlign="center" mt={-5} mb={5}>
{hasNextPage && (productType === 'All' || productType === '') && (
<Button
onClick={fetchNextPage}
startIcon={
<Image
src="/images/arrow.svg"
alt="arrow down"
width={29}
height={29}
/>
}
sx={{
backgroundColor: 'primary.main',
height: 50,
width: 150,
color: 'white',
':hover': {
bgcolor: 'primary.main', // theme.palette.primary.main
color: 'white',
},
}}
>
Load More
</Button>
)}

{filteredData && next < filteredData.productsByCategory.length && (
<Button
onClick={handleMoreProducts}
startIcon={
<Image
src="/images/arrow.svg"
alt="arrow down"
width={29}
height={29}
/>
}
sx={{
backgroundColor: 'primary.main',
height: 50,
width: 150,
color: 'white',
':hover': {
bgcolor: 'primary.main', // theme.palette.primary.main
color: 'white',
},
}}
>
Load More
</Button>
)}
</Box>
</Container>
);
};

export default ProductsGrid;

+ 11
- 75
components/products-hero/ProductsHero.jsx Vedi File

import { Button, Container, Grid, Typography } from '@mui/material';
import { Container, Typography } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import Image from 'next/image';
import ProductCard from '../product-card/ProductCard';
import ProductType from '../product-type/ProductType';
import Sort from '../sort/sort';


const ProductsHero = () => { const ProductsHero = () => {
return ( return (
<Box <Box
sx={{ sx={{
width: '100%',
height: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
}} }}
> >
<Container <Container
maxWidth="lg"
sx={{ sx={{
width: '1273px',
height: '350px',
mt: 25, mt: 25,
mb: 10,
}} }}
> >
<Typography <Typography
fontFamily={'body1.fontFamily'} fontFamily={'body1.fontFamily'}
height="120px"
fontSize="64px"
align="center" align="center"
color="primary.main" color="primary.main"
mb={3}
sx={{
fontSize: { md: '64px', sm: '46px', xs: '32px' },
}}
> >
Welcome to our Store! Welcome to our Store!
</Typography> </Typography>
<Typography fontSize="24px" align="center">
<Typography
sx={{ fontSize: { xs: '16px', sm: '18px', md: '24px' } }}
align="center"
>
Our focus is to bring you the very best in the world of coffee. Our focus is to bring you the very best in the world of coffee.
Everything from fresh coffee beans, the best coffee powders and Everything from fresh coffee beans, the best coffee powders and
capsules as well as other miscellaneous items such as cups and mugs. capsules as well as other miscellaneous items such as cups and mugs.
Take a look to see if anything takes your fancy. Take a look to see if anything takes your fancy.
</Typography> </Typography>
</Container> </Container>
<Box textAlign="center" width="100%">
<Sort />
<ProductType />
</Box>
<Container
sx={{
mt: 10,
}}
>
<Grid container spacing={2}>
<Grid item md={4} xs={12} sx={{ height: '500px' }}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
<Grid item md={4} xs={12}>
<ProductCard />
</Grid>
</Grid>
<Box textAlign="center" mt={-3} mb={5}>
<Button
startIcon={
<Image
src="/images/arrow.svg"
alt="arrow down"
width={29}
height={29}
/>
}
sx={{
backgroundColor: 'primary.main',
height: 50,
width: 150,
color: 'white',
':hover': {
bgcolor: 'primary.main', // theme.palette.primary.main
color: 'white',
},
}}
>
Load More
</Button>
</Box>
</Container>
</Box> </Box>
); );
}; };

+ 1
- 1
components/products/featured-product/ProductInfo.jsx Vedi File

bColor: PropType.string, bColor: PropType.string,
side: PropType.string, side: PropType.string,
addProductToCart: PropType.func, addProductToCart: PropType.func,
inCart: PropType.Boolean | PropType.undefined,
inCart: PropType.bool,
}; };
export default ProductInfo; export default ProductInfo;

+ 7
- 1
components/sort/Sort.jsx Vedi File

const Sort = ({ sort, handleSortChange }) => { const Sort = ({ sort, handleSortChange }) => {
return ( return (
<> <>
<FormControl sx={{ width: '200px', paddingRight: '15px' }}>
<FormControl
sx={{
width: '200px',
mb: { xs: '10px', sm: '0px' },
mr: { sm: '10px' },
}}
>
<InputLabel id="sort-label">Sort</InputLabel> <InputLabel id="sort-label">Sort</InputLabel>
<Select <Select
MenuProps={{ MenuProps={{

+ 8
- 3
components/tab-panel/TabPanel.jsx Vedi File

import { Typography } from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';


const TabPanel = ({ children, value, index, ...other }) => { const TabPanel = ({ children, value, index, ...other }) => {
id={`simple-tabpanel-${index}`} id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`} aria-labelledby={`simple-tab-${index}`}
{...other} {...other}
style={{ height: '80%' }}
> >
{value === index && ( {value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
<Box
display="flex"
flexDirection="column"
alignContent="space-between"
sx={{ pt: 3, pl: 3, width: '100%', height: '100%' }}
>
{children}
</Box> </Box>
)} )}
</div> </div>

+ 28
- 0
hooks/useFetchProductData.js Vedi File

import { useQuery } from '@tanstack/react-query';
import { getProductData } from '../requests/products/producDataRequest';
import { getProductsByCategory } from '../requests/products/productsByCategoryRequest';

export const useFetchSingleProduct = (customID) => {
return useQuery(
['product', customID],
async () => await getProductData(customID)
);
};

export const useFetchSimilarProducts = (category) => {
return useQuery(
['products', category],
async () => await getProductsByCategory(category),
{
enabled: !!category,
}
);
};

export const useFetchProductsByCategory = (productType) => {
return useQuery(
['filteredProducts', productType],
async () => await getProductsByCategory(productType),
{ enabled: productType === 'Mug' || productType === 'Coffee' }
);
};

+ 21
- 0
hooks/useInfiniteQuery.js Vedi File

import { useInfiniteQuery } from '@tanstack/react-query';
import { getAllProducts } from '../requests/products/productRequest';

export const useInfiniteProducts = (filter) => {
return useInfiniteQuery(
['products'],
async ({ pageParam = 1 }) => await getAllProducts(pageParam),
{
getNextPageParam: (lastPage, pages) => {
const maxPages = Math.ceil(lastPage?.productCount / 9);
const nextPage = pages.length + 1;
if (nextPage <= maxPages) {
return nextPage;
}
},
enabled: filter === 'All' || filter === '',
staleTime: 0,
cacheTime: 0,
}
);
};

+ 2
- 0
pages/_app.js Vedi File

QueryClient, QueryClient,
QueryClientProvider, QueryClientProvider,
} from '@tanstack/react-query'; } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { SessionProvider } from 'next-auth/react'; import { SessionProvider } from 'next-auth/react';
import { appWithTranslation } from 'next-i18next'; import { appWithTranslation } from 'next-i18next';
import Head from 'next/head'; import Head from 'next/head';
</Providers> </Providers>
</ThemeProvider> </ThemeProvider>
</SessionProvider> </SessionProvider>
<ReactQueryDevtools initialIsOpen={false}></ReactQueryDevtools>
</Hydrate> </Hydrate>
</QueryClientProvider> </QueryClientProvider>
); );

+ 199
- 0
pages/products/[customId].js Vedi File

import { Button, Grid, Tab, Tabs, Typography } from '@mui/material';
import { Box, Container } from '@mui/system';
import { dehydrate, QueryClient } from '@tanstack/react-query';
import Image from 'next/image';
import { useRouter } from 'next/router';
import React, { useState } from 'react';
import Loader from '../../components/loader/Loader';
import ProductCard from '../../components/product-card/ProductCard';
import TabPanel from '../../components/tab-panel/TabPanel';
import {
useFetchSimilarProducts,
useFetchSingleProduct,
} from '../../hooks/useFetchProductData';
import { getProductData } from '../../requests/products/producDataRequest';
import { useStore, useStoreUpdate } from '../../store/cart-context';
import { shuffle } from '../../utils/helpers/shuffle';

const SingleProduct = () => {
const { addCartValue } = useStoreUpdate();
const { cartStorage } = useStore();

const router = useRouter();

const { customId } = router.query;

const { data, isLoading } = useFetchSingleProduct(customId);

const productCategory = data?.product.category;

const { data: similarProducts, isLoading: similarLoading } =
useFetchSimilarProducts(productCategory);

const [value, setValue] = useState(0);

const addProductToCart = (quantity) => addCartValue(data.product, quantity);
const inCart = cartStorage?.some(
(item) => item.product.customID === data?.product.customID
)
? true
: false;

const handleChange = (event, newValue) => {
setValue(newValue);
};

function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}

if (isLoading) {
return <Loader loading={isLoading} />;
}

if (similarLoading) {
return <Loader loading={similarLoading} />;
}

const productsToShow = (id) => {
const filtered = shuffle(similarProducts?.productsByCategory)
.filter((product) => product.customID !== id)
.slice(0, 3)
.map((item) => (
<Grid
key={item._id}
item
lg={4}
md={6}
sm={6}
xs={12}
sx={{ mb: '100px' }}
>
<ProductCard product={item} />
</Grid>
));

return filtered;
};

return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
}}
>
<Container>
<Typography
fontFamily={'body1.fontFamily'}
fontSize="32px"
sx={{ mt: 25, height: '100%', color: 'primary.main' }}
>
{data.product.name}
</Typography>
<Grid container spacing={2}>
<Grid item md={6} sm={12}>
<Image
src="/images/product-card-image.jpg"
alt="product"
width={900}
height={600}
/>
</Grid>
<Grid item xs={12} md={6}>
<Tabs
sx={{
'& button:focus': {
borderTop: '1px solid black',
borderLeft: '1px solid black',
borderRight: '1px solid black',
borderRadius: '5px 5px 0 0',
borderBottom: 'none',
},
}}
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
<Tab
sx={{
width: '50%',
}}
label="Purchase"
{...a11yProps(0)}
/>
<Tab sx={{ width: '50%' }} label="Category" {...a11yProps(1)} />
</Tabs>
<TabPanel value={value} index={0}>
<Box flexGrow={2} sx={{ pb: { xs: '70px' } }}>
<Typography>{data.product.description}</Typography>
</Box>
<Box
sx={{
display: { xs: 'flex' },
flexDirection: { xs: 'column' },
justifyContent: { xs: 'center' },
alignItems: { xs: 'center', md: 'flex-end' },
}}
>
<Typography mb={2}>${data.product.price}</Typography>
<Button
disabled={inCart}
onClick={() => addProductToCart(1)}
sx={{
backgroundColor: '#CBA213',
height: 50,
width: { xs: '300px', md: '150px' },
color: 'white',
}}
>
{inCart ? 'In Cart' : 'Add to cart'}
</Button>
</Box>
</TabPanel>
<TabPanel value={value} index={1}>
<Box sx={{ mb: { xs: '60px' } }}>{data.product.category}</Box>
</TabPanel>
</Grid>
</Grid>

<Typography
sx={{
mt: { xs: '60px', md: '100px', lg: '150px' },
mb: 5,
color: 'primary.main',
fontSize: '32px',
}}
>
Other Product You May Like
</Typography>
<Grid container spacing={2}>
{productsToShow(customId)}
</Grid>
</Container>
</Box>
);
};

export const getServerSideProps = async (context) => {
const { params } = context;
const { customId } = params;

const queryClient = new QueryClient();

await queryClient.prefetchQuery(
['product', customId],
async () => await getProductData(customId)
);

return {
props: {
dehydratatedState: dehydrate(queryClient),
},
};
};

export default SingleProduct;

+ 0
- 119
pages/products/[slug].js Vedi File

import { Grid, Tab, Tabs, Typography } from '@mui/material';
import { Box, Container } from '@mui/system';
import Image from 'next/image';
import React, { useState } from 'react';
import ProductCard from '../../components/product-card/ProductCard';
import TabPanel from '../../components/tab-panel/TabPanel';

const SingleProduct = () => {
const [value, setValue] = useState(0);

const handleChange = (event, newValue) => {
setValue(newValue);
};

function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}

return (
<Box
sx={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
<Container
sx={{
width: '1273px',
}}
>
<Typography
fontFamily={'body1.fontFamily'}
fontSize="32px"
sx={{ mt: 25, height: '100%', color: 'primary.main' }}
>
Minimalist Printed Mug
</Typography>
<Grid container spacing={2} sx={{ height: '100%', width: '100%' }}>
<Grid item lg={6}>
<Image
src="/images/product-card-image.jpg"
alt="product"
width={630}
height={390}
/>
</Grid>
<Grid item lg={6}>
<Tabs
sx={{
'& button:focus': {
borderTop: '1px solid black',
borderLeft: '1px solid black',
borderRight: '1px solid black',
borderRadius: '5px 5px 0 0',
borderBottom: 'none',
},
}}
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
<Tab
sx={{
width: '50%',
}}
label="Purchase"
{...a11yProps(0)}
/>
<Tab sx={{ width: '50%' }} label="Category" {...a11yProps(1)} />
</Tabs>
<TabPanel value={value} index={0}>
<Box display="flex" flexDirection="row" justifyContent="right">
<Box>
<Typography>
Our simple and sturdy mugs are made to last. With a
minimalist desings you will soon be enjoying your next brew.
</Typography>
</Box>
<Box
justifyContent="flex-end"
sx={{ display: 'flex', flexDirection: 'column' }}
>
<Typography align="right">$20</Typography>
</Box>
</Box>
</TabPanel>
<TabPanel value={value} index={1}>
Mugs & Cups
</TabPanel>
</Grid>
</Grid>

<Typography
sx={{ mt: 25, mb: 5, color: 'primary.main', fontSize: '32px' }}
>
Other Product You May Like
</Typography>
<Grid container spacing={2} sx={{ height: '100%', width: '100%' }}>
<Grid item lg={4}>
<ProductCard />
</Grid>
<Grid item lg={4}>
<ProductCard />
</Grid>
<Grid item lg={4}>
<ProductCard />
</Grid>
</Grid>
</Container>
</Box>
);
};

export default SingleProduct;

+ 49
- 22
pages/products/index.js Vedi File

import { Box } from '@mui/system'; import { Box } from '@mui/system';
import Head from 'next/head'; import Head from 'next/head';
import { useState } from 'react';
import FilterSort from '../../components/filter-sort/FilterSort';
import Loader from '../../components/loader/Loader';
import ProductsGrid from '../../components/products-grid/ProductsGrid';
import ProductsHero from '../../components/products-hero/ProductsHero'; import ProductsHero from '../../components/products-hero/ProductsHero';
import { useInfiniteProducts } from '../../hooks/useInfiniteQuery';


const Products = () => { const Products = () => {
return (
<>
<Box sx={{ width: '100%', height: '100%' }}>
<Head>
<title>NextJS template</title>
<meta name="description" content="Random data with pagination..." />
</Head>
<ProductsHero />
</Box>
</>
);
};
const [filter, setFilter] = useState('');
const [sort, setSort] = useState('');
const { data, isLoading, fetchNextPage, hasNextPage } =
useInfiniteProducts(filter);

const handleProductTypeChange = (event) => {
const filterText = event.target.value;
setFilter(filterText);
};


// export async function getStaticProps({ locale }) {
// const queryClient = new QueryClient();
const handleSortChange = (event) => {
const sort = event.target.value;
setSort(sort);
};


// await queryClient.prefetchQuery(['randomData', 1], () => getData(1));
if (isLoading) {
return <Loader loading={isLoading} />;
}


// return {
// props: {
// dehydratedState: dehydrate(queryClient),
// ...(await serverSideTranslations(locale, ['pagination'])),
// },
// };
// }
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
}}
>
<Head>
<title>NextJS template</title>
<meta name="description" content="Random data with pagination..." />
</Head>
<ProductsHero />
<FilterSort
handleProductTypeChange={handleProductTypeChange}
productType={filter}
sort={sort}
handleSortChange={handleSortChange}
/>
<ProductsGrid
allProducts={data}
sort={sort}
productType={filter}
fetchNextPage={fetchNextPage}
hasNextPage={hasNextPage}
/>
</Box>
);
};


export default Products; export default Products;

+ 10
- 0
utils/helpers/shuffle.js Vedi File

export const shuffle = (array) => {
const newArray = [...array];

newArray.reverse().forEach((item, index) => {
const j = Math.floor(Math.random() * (index + 1));
[newArray[index], newArray[j]] = [newArray[j], newArray[index]];
});

return newArray;
};

+ 19
- 10
utils/helpers/sortHelpers.js Vedi File

export const compare = (a, b, sort) => {
if (sort === 'asc') {
if (a > b) return 1;
else if (b > a) return -1;
return 0;
} else if (sort === 'desc') {
if (a > b) return -1;
else if (b > a) return 1;
return 0;
}
/* eslint-disable no-prototype-builtins */
export const compare = (key, order = 'asc') => {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
// property doesn't exist on either object
return 0;
}

const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key];
const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key];

let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return order === 'desc' ? comparison * -1 : comparison;
};
}; };

Loading…
Annulla
Salva