Explorar el Código

Merge branch 'single-product' of ntasicc/coffee into master

pagination
lazarkostic hace 3 años
padre
commit
5f7cc4d262

+ 3
- 0
.vscode/settings.json Ver fichero

"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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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 Ver fichero

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;
};
}; };

Cargando…
Cancelar
Guardar