194 lines
5.8 KiB
JavaScript
194 lines
5.8 KiB
JavaScript
import { Link } from '@chakra-ui/next-js'
|
|
import {
|
|
Alert,
|
|
AlertIcon,
|
|
Box,
|
|
Button,
|
|
ButtonGroup,
|
|
Card,
|
|
CardBody,
|
|
Flex,
|
|
HStack,
|
|
Heading,
|
|
IconButton,
|
|
Image,
|
|
LinkBox,
|
|
LinkOverlay,
|
|
SimpleGrid,
|
|
Skeleton,
|
|
Spinner,
|
|
Table,
|
|
Tbody,
|
|
Td,
|
|
Text,
|
|
Tr,
|
|
} from '@chakra-ui/react'
|
|
import { useLocalStorageValue } from '@react-hookz/web'
|
|
import { useRouter } from 'next/router'
|
|
import { TbLayoutGrid, TbLayoutList } from 'react-icons/tb'
|
|
import useSWR from 'swr'
|
|
import Pagination from '~/components/pagination'
|
|
|
|
const PER_PAGE = 12
|
|
|
|
export default function VendorsPage() {
|
|
const router = useRouter()
|
|
const {
|
|
query: { page = 1 },
|
|
} = router
|
|
|
|
const { value: perPage, set: setPerPage } = useLocalStorageValue('perPage', {
|
|
defaultValue: PER_PAGE,
|
|
initializeWithValue: false,
|
|
})
|
|
|
|
const { value: layout, set: setLayout } = useLocalStorageValue('layout', {
|
|
defaultValue: 'GRID',
|
|
initializeWithValue: false,
|
|
})
|
|
|
|
const { data, error, isLoading, isValidating } = useSWR(['vendors', page, perPage], async () => {
|
|
// await new Promise((r) => setTimeout(r, 2000))
|
|
const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/vendors`)
|
|
url.searchParams.append('fields[]', '*')
|
|
url.searchParams.append('limit', perPage)
|
|
url.searchParams.append('page', page)
|
|
url.searchParams.append('sort', 'name')
|
|
url.searchParams.append('meta[]', 'filter_count')
|
|
const res = await fetch(url.toString())
|
|
if (!res.ok) {
|
|
throw new Error('Oops')
|
|
}
|
|
return res.json()
|
|
})
|
|
|
|
const { meta, data: vendors } = data || { meta: {}, data: [] }
|
|
|
|
return (
|
|
<div>
|
|
<Flex alignItems="center" justifyContent="space-between">
|
|
<Heading size="lg" my={8}>
|
|
Vendors
|
|
</Heading>
|
|
<Box>{isValidating && <Spinner />}</Box>
|
|
<ButtonGroup isAttached>
|
|
<IconButton icon={<TbLayoutGrid />} onClick={() => setLayout('GRID')} isDisabled={layout === 'GRID'} />
|
|
<IconButton icon={<TbLayoutList />} onClick={() => setLayout('LIST')} isDisabled={layout === 'LIST'} />
|
|
</ButtonGroup>
|
|
</Flex>
|
|
|
|
{error && (
|
|
<Alert status="error" marginY={3}>
|
|
<AlertIcon />
|
|
There was an error processing your request
|
|
</Alert>
|
|
)}
|
|
|
|
{layout === 'GRID' && (
|
|
<SimpleGrid columns={[1, 2, 3, 4]} spacing={3}>
|
|
{isLoading &&
|
|
new Array(perPage).fill(true).map((v, k) => (
|
|
<Card key={k}>
|
|
<Skeleton h="40" />
|
|
<CardBody>
|
|
<Skeleton h="6" mb="2" />
|
|
<Skeleton h="4" mb="1" />
|
|
<Skeleton h="4" />
|
|
</CardBody>
|
|
</Card>
|
|
))}
|
|
{!isLoading &&
|
|
vendors.map((v) => (
|
|
<LinkBox as={Card} rounded={4} key={v.id}>
|
|
<Image
|
|
roundedTop={4}
|
|
objectFit="cover"
|
|
src={`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/assets/${v.logo}?key=logo-card`}
|
|
alt={v.name}
|
|
width="250"
|
|
height="150"
|
|
style={{ objectFit: 'contain', background: '#fff' }}
|
|
/>
|
|
<CardBody>
|
|
<LinkOverlay as={Link} href={`/vendors/${v.slug}`} prefetch={false}>
|
|
<Text mb="2" fontWeight="900">
|
|
{v.name}
|
|
</Text>
|
|
<Text fontSize="sm">
|
|
{v?.description?.substring(0, v?.description?.substring(0, 80).lastIndexOf(' '))} …
|
|
</Text>
|
|
</LinkOverlay>
|
|
</CardBody>
|
|
</LinkBox>
|
|
))}
|
|
</SimpleGrid>
|
|
)}
|
|
|
|
{layout === 'LIST' && (
|
|
<>
|
|
{isLoading && (
|
|
<Table>
|
|
<Tbody>
|
|
{new Array(perPage).fill(true).map((v, k) => (
|
|
<Tr key={k}>
|
|
<Td w="10%">
|
|
<Skeleton h="12" w="12" />
|
|
</Td>
|
|
<Td w="30%">
|
|
<Skeleton h="6" />
|
|
</Td>
|
|
<Td w="60%">
|
|
<Skeleton h="6" />
|
|
</Td>
|
|
</Tr>
|
|
))}
|
|
</Tbody>
|
|
</Table>
|
|
)}
|
|
{!isLoading && (
|
|
<Table>
|
|
<Tbody>
|
|
{vendors.map((v) => (
|
|
<Link key={v.id} href={`/vendors/${v.slug}`} prefetch={false} display="contents">
|
|
<Tr verticalAlign="middle">
|
|
<Td>
|
|
<Image
|
|
rounded="md"
|
|
src={`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/assets/${v.logo}?key=logo-card`}
|
|
alt={v.name}
|
|
width="12"
|
|
height="12"
|
|
style={{ objectFit: 'contain', background: '#fff' }}
|
|
/>
|
|
</Td>
|
|
<Td>{v.name}</Td>
|
|
<Td>
|
|
<Text fontSize="sm">
|
|
{v?.description?.substring(0, v?.description?.substring(0, 80).lastIndexOf(' '))} …
|
|
</Text>
|
|
</Td>
|
|
</Tr>
|
|
</Link>
|
|
))}
|
|
</Tbody>
|
|
</Table>
|
|
)}
|
|
</>
|
|
)}
|
|
|
|
<Pagination page={Number(page)} itemsPerPage={perPage} totalItems={meta.filter_count} />
|
|
|
|
<HStack>
|
|
<Text>Results per page:</Text>
|
|
<ButtonGroup isAttached>
|
|
{[12, 24, 48].map((n) => (
|
|
<Button key={n} onClick={() => setPerPage(n)} isDisabled={perPage === n}>
|
|
{n}
|
|
</Button>
|
|
))}
|
|
</ButtonGroup>
|
|
</HStack>
|
|
</div>
|
|
)
|
|
}
|