WIP: Pagination

This commit is contained in:
2023-07-05 21:13:51 +04:00
parent cd79d25d56
commit 849ef57227
11 changed files with 1293 additions and 1223 deletions

View File

@@ -7,7 +7,7 @@
"start": "directus start" "start": "directus start"
}, },
"dependencies": { "dependencies": {
"directus": "10.3.0", "directus": "10.4.2",
"sqlite3": "5.1.6" "sqlite3": "5.1.6"
}, },
"devDependencies": { "devDependencies": {

545
backend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# The URL where your API can be reached on the web. # The URL where your API can be reached on the web.
DIRECTUS_API_URL="http://0.0.0.0:8055" NEXT_PUBLIC_DIRECTUS_API_URL="http://0.0.0.0:8055"
GLOBALS_ID="4f8d9e66-ec95-4bdd-a5e7-34df8ea68a45" NEXT_PUBLIC_GLOBALS_ID="4f8d9e66-ec95-4bdd-a5e7-34df8ea68a45"

View File

@@ -1,5 +1,3 @@
'use client'
import { MoonIcon, SunIcon } from '@chakra-ui/icons' import { MoonIcon, SunIcon } from '@chakra-ui/icons'
import { Box, Button, Flex, Heading, Spacer, Stack, useColorMode, useColorModeValue } from '@chakra-ui/react' import { Box, Button, Flex, Heading, Spacer, Stack, useColorMode, useColorModeValue } from '@chakra-ui/react'
import { Link } from '@chakra-ui/next-js' import { Link } from '@chakra-ui/next-js'

View File

@@ -0,0 +1,78 @@
import { Flex, Link, Select, Text } from '@chakra-ui/react'
import clsx from 'clsx'
import NextLink from 'next/link'
import { useRouter } from 'next/router'
import { Fragment } from 'react'
export default function Pagination({ page = 1, itemsPerPage = 12, totalItems = 0, limit = 4 }) {
const router = useRouter()
const totalPages = Math.ceil(totalItems / itemsPerPage)
if (totalPages === 1) return <></>
const links = new Array(totalPages)
.fill(true)
.map((_v, i) => (i < limit || i >= totalPages - limit || (i >= page - limit && i < page + limit - 1)) && i + 1)
.filter((i) => i !== false)
return (
<>
<Flex justifyContent="space-between" direction={['column', 'row']} mt={6} mb={6} gap={3}>
<Link
//
as={NextLink}
href={{ query: { ...router.query, page: Math.max(1, page - 1) } }}
variant="pagination"
>
&larr; Previous
</Link>
<Flex gap={1} display={['none', 'none', 'flex']}>
{links.map((v, i) => (
<Fragment key={i}>
{v > 1 && v - 1 !== links[i - 1] && (
<Text marginX={3} lineHeight={10}>
&hellip;
</Text>
)}
<Link
//
key={v}
as={NextLink}
prefetch={false}
href={{ query: { ...router.query, page: v } }}
className={clsx({
current: v === page,
})}
variant="pagination"
>
{v}
</Link>
</Fragment>
))}
</Flex>
<Select
value={page}
onChange={(v) => router.push({ query: { ...router.query, page: v.target.value } })}
display={['flex', 'flex', 'none']}
textAlign="center"
>
{new Array(totalPages).fill(true).map((v, i) => (
<option value={i + 1} key={i}>
{i + 1}
</option>
))}
</Select>
<Link
//
as={NextLink}
href={{ query: { ...router.query, page: Math.min(totalPages, page + 1) } }}
variant="pagination"
textAlign="right"
>
Next &rarr;
</Link>
</Flex>
</>
)
}

View File

@@ -1,5 +0,0 @@
import { Directus } from '@directus/sdk'
const directus = new Directus(process.env.DIRECTUS_API_URL)
export default directus

View File

@@ -16,7 +16,9 @@
"@directus/sdk": "10.3.3", "@directus/sdk": "10.3.3",
"@emotion/react": "11.11.1", "@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0", "@emotion/styled": "11.11.0",
"next": "13.4.7", "@tanstack/react-query": "4.29.19",
"clsx": "1.2.1",
"next": "13.4.8",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
} }

View File

@@ -1,12 +1,18 @@
import { ChakraProvider } from '@chakra-ui/react' import { ChakraProvider } from '@chakra-ui/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import Layout from '~/components/layout' import Layout from '~/components/layout'
import theme from './theme'
const queryClient = new QueryClient()
export default function MyApp({ Component, pageProps }) { export default function MyApp({ Component, pageProps }) {
return ( return (
<ChakraProvider> <QueryClientProvider client={queryClient}>
<Layout> <ChakraProvider theme={theme}>
<Component {...pageProps} /> <Layout>
</Layout> <Component {...pageProps} />
</ChakraProvider> </Layout>
</ChakraProvider>
</QueryClientProvider>
) )
} }

40
frontend/pages/theme.js Normal file
View File

@@ -0,0 +1,40 @@
import { extendTheme } from '@chakra-ui/react'
import { defineStyle, defineStyleConfig } from '@chakra-ui/react'
const pagination = defineStyle({
whiteSpace: 'nowrap',
bg: 'transparent',
gap: 3,
paddingInlineStart: 3,
paddingInlineEnd: 3,
lineHeight: 10,
borderRadius: 'md',
border: '1px solid',
borderColor: 'inherit',
width: '100%',
_hover: {
textDecor: 'none',
},
'&.current': {
bg: 'var(--chakra-colors-gray-300)',
border: '1px solid transparent',
},
_dark: {
bg: 'var(--chakra-colors-gray-900)',
'&.current': {
bg: 'var(--chakra-colors-gray-700)',
},
},
})
const linkTheme = defineStyleConfig({
variants: { pagination },
})
const theme = extendTheme({
components: {
Link: linkTheme,
},
})
export default theme

View File

@@ -1,108 +1,73 @@
import {
Pagination,
PaginationContainer,
PaginationNext,
PaginationPage,
PaginationPageGroup,
PaginationPrevious,
usePagination,
} from '@ajna/pagination'
import { Link } from '@chakra-ui/next-js' import { Link } from '@chakra-ui/next-js'
import { Card, CardBody, Heading, Image, LinkBox, LinkOverlay, SimpleGrid, Text } from '@chakra-ui/react' import { Card, CardBody, Heading, Image, LinkBox, LinkOverlay, SimpleGrid, Text, Skeleton } from '@chakra-ui/react'
import { useRouter } from 'next/navigation' import { useQuery } from '@tanstack/react-query'
import directus from '~/lib/directus' import { useRouter } from 'next/router'
import Pagination from '~/components/pagination'
const PER_PAGE = 12 const PER_PAGE = 12
export const getServerSideProps = async ({ query }) => { export default function VendorsPage() {
const page = query?.page || 1
const sort = query?.sort || 'name'
const { data: vendors, meta } = await directus.items('vendors').readByQuery({
fields: [
//
'*',
],
limit: PER_PAGE,
page,
meta: ['filter_count'],
sort,
})
return { props: { vendors, meta, page } }
}
export default function VendorsPage({ vendors, meta, page }) {
const router = useRouter() const router = useRouter()
const {
query: { page = 1 },
} = router
const { pages, pagesCount, currentPage, setCurrentPage, isDisabled } = usePagination({ const { data, isFetching } = useQuery({
total: meta.filter_count, queryKey: ['vendors', page],
limits: { queryFn: async (...props2) => {
outer: 1, await new Promise((r) => setTimeout(r, 2000))
inner: 1,
}, const res = await fetch(
initialState: { `${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/vendors?fields[]=*&limit=12&page=${page}&meta[]=filter_count&sort=name`
pageSize: PER_PAGE, )
isDisabled: false, if (!res.ok) {
currentPage: page, throw new Error('wrong')
}
return res.json()
}, },
keepPreviousData: true,
cacheTime: 1000 * 60 * 30,
staleTime: 1000 * 60 * 30,
}) })
const handlePageChange = (nextPage) => { const { meta, data: vendors } = data || { meta: {}, data: [] }
setCurrentPage(nextPage)
router.replace(`?page=${nextPage}`)
}
return ( return (
<div> <div>
<Heading size="lg" my={8}> <Heading size="lg" my={8}>
Vendors Vendors
</Heading> </Heading>
<SimpleGrid columns={[1, 2, 3, 4]} spacing={3}> <SimpleGrid columns={[1, 2, 3, 4]} spacing={3}>
{vendors.map((v) => ( {isFetching &&
<LinkBox as={Card} rounded={4} key={v.id}> new Array(PER_PAGE).fill(true).map((v, k) => (
<Image <Card key={k}>
roundedTop={4} <Skeleton h="40" />
objectFit="cover" <CardBody>
src="https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80" <Skeleton h="4" />
alt="Chakra UI" </CardBody>
/> </Card>
<CardBody> ))}
<LinkOverlay as={Link} href={`/vendors/${v.slug}`} prefetch={false}> {!isFetching &&
{v.name} vendors.map((v) => (
</LinkOverlay> <LinkBox as={Card} rounded={4} key={v.id}>
</CardBody> <Image
</LinkBox> roundedTop={4}
))} objectFit="cover"
src="https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80"
alt={v.name}
/>
<CardBody>
<LinkOverlay as={Link} href={`/vendors/${v.slug}`} prefetch={false}>
{v.name}
</LinkOverlay>
</CardBody>
</LinkBox>
))}
</SimpleGrid> </SimpleGrid>
<Pagination <Pagination page={Number(page)} itemsPerPage={PER_PAGE} totalItems={meta.filter_count} />
pagesCount={pagesCount}
currentPage={currentPage}
isDisabled={isDisabled}
onPageChange={handlePageChange}
>
<PaginationContainer align="center" my={4} w={'full'} justifyContent={'space-between'}>
<PaginationPrevious>
<Text>«</Text>
</PaginationPrevious>
<PaginationPageGroup align="center" mx={4}>
{pages.map((page) => (
<PaginationPage
w={10}
key={`pagination_page_${page}`}
page={page}
fontSize="sm"
_current={{
bg: 'gray.400',
}}
/>
))}
</PaginationPageGroup>
<PaginationNext>
<Text>»</Text>
</PaginationNext>
</PaginationContainer>
</Pagination>
</div> </div>
) )
} }

1681
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff