SWR, improved vendors list
This commit is contained in:
@@ -12,13 +12,14 @@
|
|||||||
"@chakra-ui/icons": "2.0.19",
|
"@chakra-ui/icons": "2.0.19",
|
||||||
"@chakra-ui/next-js": "2.1.4",
|
"@chakra-ui/next-js": "2.1.4",
|
||||||
"@chakra-ui/react": "2.7.1",
|
"@chakra-ui/react": "2.7.1",
|
||||||
"@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",
|
||||||
"@tanstack/react-query": "4.29.19",
|
"@react-hookz/web": "23.1.0",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"next": "13.4.8",
|
"next": "13.4.8",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0"
|
"react-dom": "18.2.0",
|
||||||
|
"react-icons": "4.10.1",
|
||||||
|
"swr": "2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export const getStaticPaths = async () => {
|
export const getStaticPaths = async () => {
|
||||||
const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/pages`)
|
const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/pages`)
|
||||||
url.searchParams.append('fields[]', 'slug')
|
url.searchParams.append('fields[]', 'slug')
|
||||||
|
url.searchParams.append('limit', -1)
|
||||||
const res = await fetch(url.toString())
|
const res = await fetch(url.toString())
|
||||||
|
|
||||||
const { data: pages } = await res.json()
|
const { data: pages } = await res.json()
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
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 '~/src/theme'
|
import theme from '~/src/theme'
|
||||||
|
|
||||||
const queryClient = new QueryClient()
|
|
||||||
|
|
||||||
export default function MyApp({ Component, pageProps }) {
|
export default function MyApp({ Component, pageProps }) {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<ChakraProvider theme={theme}>
|
<ChakraProvider theme={theme}>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</Layout>
|
</Layout>
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
</QueryClientProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
151
frontend/pages/vendors/index.js
vendored
151
frontend/pages/vendors/index.js
vendored
@@ -1,7 +1,32 @@
|
|||||||
import { Link } from '@chakra-ui/next-js'
|
import { Link } from '@chakra-ui/next-js'
|
||||||
import { Card, CardBody, Heading, Image, LinkBox, LinkOverlay, SimpleGrid, Skeleton } from '@chakra-ui/react'
|
import {
|
||||||
import { useQuery } from '@tanstack/react-query'
|
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 { useRouter } from 'next/router'
|
||||||
|
import { TbLayoutGrid, TbLayoutList } from 'react-icons/tb'
|
||||||
|
import useSWR from 'swr'
|
||||||
import Pagination from '~/components/pagination'
|
import Pagination from '~/components/pagination'
|
||||||
|
|
||||||
const PER_PAGE = 12
|
const PER_PAGE = 12
|
||||||
@@ -12,65 +37,157 @@ export default function VendorsPage() {
|
|||||||
query: { page = 1 },
|
query: { page = 1 },
|
||||||
} = router
|
} = router
|
||||||
|
|
||||||
const { data, isFetching } = useQuery({
|
const { value: perPage, set: setPerPage } = useLocalStorageValue('perPage', {
|
||||||
queryKey: ['vendors', page],
|
defaultValue: PER_PAGE,
|
||||||
queryFn: async (...props2) => {
|
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`)
|
const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/vendors`)
|
||||||
url.searchParams.append('fields[]', '*')
|
url.searchParams.append('fields[]', '*')
|
||||||
url.searchParams.append('limit', PER_PAGE)
|
url.searchParams.append('limit', perPage)
|
||||||
url.searchParams.append('page', page)
|
url.searchParams.append('page', page)
|
||||||
url.searchParams.append('sort', 'name')
|
url.searchParams.append('sort', 'name')
|
||||||
url.searchParams.append('meta[]', 'filter_count')
|
url.searchParams.append('meta[]', 'filter_count')
|
||||||
|
|
||||||
const res = await fetch(url.toString())
|
const res = await fetch(url.toString())
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error('Oops')
|
throw new Error('Oops')
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json()
|
return res.json()
|
||||||
},
|
|
||||||
keepPreviousData: true,
|
|
||||||
cacheTime: 1000 * 60 * 30,
|
|
||||||
staleTime: 1000 * 60 * 30,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const { meta, data: vendors } = data || { meta: {}, data: [] }
|
const { meta, data: vendors } = data || { meta: {}, data: [] }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
<Heading size="lg" my={8}>
|
<Heading size="lg" my={8}>
|
||||||
Vendors
|
Vendors
|
||||||
</Heading>
|
</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}>
|
<SimpleGrid columns={[1, 2, 3, 4]} spacing={3}>
|
||||||
{isFetching &&
|
{isLoading &&
|
||||||
new Array(PER_PAGE).fill(true).map((v, k) => (
|
new Array(perPage).fill(true).map((v, k) => (
|
||||||
<Card key={k}>
|
<Card key={k}>
|
||||||
<Skeleton h="40" />
|
<Skeleton h="40" />
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<Skeleton h="6" mb="2" />
|
||||||
|
<Skeleton h="4" mb="1" />
|
||||||
<Skeleton h="4" />
|
<Skeleton h="4" />
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
{!isFetching &&
|
{!isLoading &&
|
||||||
vendors.map((v) => (
|
vendors.map((v) => (
|
||||||
<LinkBox as={Card} rounded={4} key={v.id}>
|
<LinkBox as={Card} rounded={4} key={v.id}>
|
||||||
<Image
|
<Image
|
||||||
roundedTop={4}
|
roundedTop={4}
|
||||||
objectFit="cover"
|
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"
|
src={`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/assets/${v.logo}?key=logo-card`}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
|
width="250"
|
||||||
|
height="150"
|
||||||
|
style={{ objectFit: 'contain', background: '#fff' }}
|
||||||
/>
|
/>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<LinkOverlay as={Link} href={`/vendors/${v.slug}`} prefetch={false}>
|
<LinkOverlay as={Link} href={`/vendors/${v.slug}`} prefetch={false}>
|
||||||
|
<Text mb="2" fontWeight="900">
|
||||||
{v.name}
|
{v.name}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="sm">
|
||||||
|
{v?.description?.substring(0, v?.description?.substring(0, 80).lastIndexOf(' '))} …
|
||||||
|
</Text>
|
||||||
</LinkOverlay>
|
</LinkOverlay>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</LinkBox>
|
</LinkBox>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
)}
|
||||||
|
|
||||||
<Pagination page={Number(page)} itemsPerPage={PER_PAGE} totalItems={meta.filter_count} />
|
{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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
137
frontend/pnpm-lock.yaml
generated
137
frontend/pnpm-lock.yaml
generated
@@ -14,18 +14,15 @@ dependencies:
|
|||||||
'@chakra-ui/react':
|
'@chakra-ui/react':
|
||||||
specifier: 2.7.1
|
specifier: 2.7.1
|
||||||
version: 2.7.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(framer-motion@6.5.1)(react-dom@18.2.0)(react@18.2.0)
|
version: 2.7.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(framer-motion@6.5.1)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@directus/sdk':
|
|
||||||
specifier: 10.3.3
|
|
||||||
version: 10.3.3
|
|
||||||
'@emotion/react':
|
'@emotion/react':
|
||||||
specifier: 11.11.1
|
specifier: 11.11.1
|
||||||
version: 11.11.1(react@18.2.0)
|
version: 11.11.1(react@18.2.0)
|
||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: 11.11.0
|
specifier: 11.11.0
|
||||||
version: 11.11.0(@emotion/react@11.11.1)(react@18.2.0)
|
version: 11.11.0(@emotion/react@11.11.1)(react@18.2.0)
|
||||||
'@tanstack/react-query':
|
'@react-hookz/web':
|
||||||
specifier: 4.29.19
|
specifier: 23.1.0
|
||||||
version: 4.29.19(react-dom@18.2.0)(react@18.2.0)
|
version: 23.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
clsx:
|
clsx:
|
||||||
specifier: 1.2.1
|
specifier: 1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
@@ -38,6 +35,12 @@ dependencies:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
|
react-icons:
|
||||||
|
specifier: 4.10.1
|
||||||
|
version: 4.10.1(react@18.2.0)
|
||||||
|
swr:
|
||||||
|
specifier: 2.2.0
|
||||||
|
version: 2.2.0(react@18.2.0)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1203,14 +1206,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@directus/sdk@10.3.3:
|
|
||||||
resolution: {integrity: sha512-58gw+QjkuIr0lJFRx5HwSp1ewAf7rjfV++eJqAmC13p7vif9wfJEcWcJwqXstYdvtJVUg+nB4O/CE0OBEtp5HQ==}
|
|
||||||
dependencies:
|
|
||||||
axios: 0.27.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/babel-plugin@11.11.0:
|
/@emotion/babel-plugin@11.11.0:
|
||||||
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
|
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1474,34 +1469,31 @@ packages:
|
|||||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@react-hookz/deep-equal@1.0.4:
|
||||||
|
resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@react-hookz/web@23.1.0(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-fvbURdsa1ukttbLR1ASE/XmqXP09vZ1PiCYppYeR1sNMzCrdkG0iBnjxniFSVjJ8gIw2fRs6nqMTbeBz2uAkuA==}
|
||||||
|
peerDependencies:
|
||||||
|
js-cookie: ^3.0.5
|
||||||
|
react: ^16.8 || ^17 || ^18
|
||||||
|
react-dom: ^16.8 || ^17 || ^18
|
||||||
|
peerDependenciesMeta:
|
||||||
|
js-cookie:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@react-hookz/deep-equal': 1.0.4
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@swc/helpers@0.5.1:
|
/@swc/helpers@0.5.1:
|
||||||
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
|
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.6.0
|
tslib: 2.6.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@tanstack/query-core@4.29.19:
|
|
||||||
resolution: {integrity: sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@tanstack/react-query@4.29.19(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-XiTIOHHQ5Cw1WUlHaD4fmVUMhoWjuNJlAeJGq7eM4BraI5z7y8WkZO+NR8PSuRnQGblpuVdjClQbDFtwxTtTUw==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react-native: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
react-dom:
|
|
||||||
optional: true
|
|
||||||
react-native:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@tanstack/query-core': 4.29.19
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/lodash.mergewith@4.6.7:
|
/@types/lodash.mergewith@4.6.7:
|
||||||
resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==}
|
resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1538,19 +1530,6 @@ packages:
|
|||||||
tslib: 2.5.3
|
tslib: 2.5.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/asynckit@0.4.0:
|
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/axios@0.27.2:
|
|
||||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
|
||||||
dependencies:
|
|
||||||
follow-redirects: 1.15.2
|
|
||||||
form-data: 4.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/babel-plugin-macros@3.1.0:
|
/babel-plugin-macros@3.1.0:
|
||||||
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
|
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
|
||||||
engines: {node: '>=10', npm: '>=6'}
|
engines: {node: '>=10', npm: '>=6'}
|
||||||
@@ -1608,13 +1587,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==}
|
resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/combined-stream@1.0.8:
|
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
delayed-stream: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/compute-scroll-into-view@1.0.20:
|
/compute-scroll-into-view@1.0.20:
|
||||||
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
|
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -1650,11 +1622,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
|
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/delayed-stream@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
|
||||||
engines: {node: '>=0.4.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/detect-node-es@1.1.0:
|
/detect-node-es@1.1.0:
|
||||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -1686,25 +1653,6 @@ packages:
|
|||||||
tslib: 2.5.3
|
tslib: 2.5.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/follow-redirects@1.15.2:
|
|
||||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
|
||||||
engines: {node: '>=4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
debug: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
debug:
|
|
||||||
optional: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/form-data@4.0.0:
|
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
dependencies:
|
|
||||||
asynckit: 0.4.0
|
|
||||||
combined-stream: 1.0.8
|
|
||||||
mime-types: 2.1.35
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/framer-motion@6.5.1(react-dom@18.2.0)(react@18.2.0):
|
/framer-motion@6.5.1(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==}
|
resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1821,18 +1769,6 @@ packages:
|
|||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/mime-db@1.52.0:
|
|
||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-types@2.1.35:
|
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.52.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/nanoid@3.3.6:
|
/nanoid@3.3.6:
|
||||||
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
|
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
|
||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
@@ -1984,6 +1920,14 @@ packages:
|
|||||||
use-sidecar: 1.1.2(react@18.2.0)
|
use-sidecar: 1.1.2(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-icons@4.10.1(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-is@16.13.1:
|
/react-is@16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -2123,6 +2067,15 @@ packages:
|
|||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/swr@2.2.0(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.11.0 || ^17.0.0 || ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tiny-invariant@1.3.1:
|
/tiny-invariant@1.3.1:
|
||||||
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
|
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
import directus from '~/lib/directus'
|
|
||||||
import all from '~/public/data.json'
|
|
||||||
|
|
||||||
function sluggify(str = '') {
|
|
||||||
return (
|
|
||||||
str
|
|
||||||
.toLocaleLowerCase()
|
|
||||||
.normalize('NFKC')
|
|
||||||
.trim()
|
|
||||||
//
|
|
||||||
.split('.')
|
|
||||||
.join(' ')
|
|
||||||
.split('(')
|
|
||||||
.join('')
|
|
||||||
.split(')')
|
|
||||||
.join('')
|
|
||||||
.split('|')
|
|
||||||
.join(' ')
|
|
||||||
.trim()
|
|
||||||
.split(',')
|
|
||||||
.join('')
|
|
||||||
.split(' ')
|
|
||||||
.join('-')
|
|
||||||
.split('-/-')
|
|
||||||
.join('-')
|
|
||||||
.split(' &')
|
|
||||||
.join('')
|
|
||||||
.split('&')
|
|
||||||
.join('and')
|
|
||||||
.split('/')
|
|
||||||
.join('-')
|
|
||||||
.replace(/\-\-+/, '-')
|
|
||||||
.trim()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitize(str = '') {
|
|
||||||
return (
|
|
||||||
str
|
|
||||||
//
|
|
||||||
.split(' &')
|
|
||||||
.join('')
|
|
||||||
.split(' / ')
|
|
||||||
.join('/')
|
|
||||||
.split('/')
|
|
||||||
.join(' / ')
|
|
||||||
.trim()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importCategories() {
|
|
||||||
all.results.map((one) => {
|
|
||||||
one.raw.marketplaceexhibitorcategory?.map(async (cat) => {
|
|
||||||
const name = sanitize(cat)
|
|
||||||
const slug = sluggify(name)
|
|
||||||
try {
|
|
||||||
await directus.items('categories').createOne({
|
|
||||||
slug,
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
} catch (error) {}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importSubcategories() {
|
|
||||||
all.results.map((one) => {
|
|
||||||
one.raw.marketplaceexhibitorsubcategory?.map(async (cat) => {
|
|
||||||
const name = sanitize(cat)
|
|
||||||
const slug = sluggify(name)
|
|
||||||
try {
|
|
||||||
await directus.items('categories').createOne({
|
|
||||||
slug,
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
} catch (error) {}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importCategoryStructure() {
|
|
||||||
const { data: categories } = await directus.items('categories').readByQuery({ limit: -1 })
|
|
||||||
|
|
||||||
all.results.map((one) => {
|
|
||||||
one.raw.marketplaceexhibitorcategorysubcategory?.map(async (cat) => {
|
|
||||||
const [parentTitle, catTitle] = cat.split('|')
|
|
||||||
const catSlug = sluggify(catTitle)
|
|
||||||
const parentSlug = sluggify(parentTitle)
|
|
||||||
if (parentSlug) {
|
|
||||||
try {
|
|
||||||
const category = categories.find((c) => catSlug === c.slug)
|
|
||||||
const parent = categories.find((c) => parentSlug === c.slug)
|
|
||||||
await directus.items('categories').updateOne(category.id, {
|
|
||||||
parent_id: parent.id,
|
|
||||||
})
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importVendors() {
|
|
||||||
const { data: categories } = await directus.items('categories').readByQuery({ limit: -1 })
|
|
||||||
|
|
||||||
return Promise.all(
|
|
||||||
all.results.map(async (one) => {
|
|
||||||
const name = sanitize(one.raw.name)
|
|
||||||
let slug = sluggify(name)
|
|
||||||
const {
|
|
||||||
data: [existingVendor],
|
|
||||||
} = await directus.items('vendors').readByQuery({
|
|
||||||
fields: ['slug'],
|
|
||||||
limit: -1,
|
|
||||||
filter: {
|
|
||||||
slug: {
|
|
||||||
_eq: slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
let i = 1
|
|
||||||
while (existingVendor?.slug === slug) {
|
|
||||||
slug = sluggify(name) + '-' + i++
|
|
||||||
}
|
|
||||||
|
|
||||||
const categoryIds = new Set(
|
|
||||||
one.raw.marketplaceexhibitorcategory
|
|
||||||
?.map((t) => sluggify(sanitize(t)))
|
|
||||||
.map((s) => categories.filter((t) => !t.parent_id).find((t) => s === t.slug)?.id)
|
|
||||||
.filter((s) => !!s)
|
|
||||||
)
|
|
||||||
|
|
||||||
const vendor = {
|
|
||||||
slug,
|
|
||||||
name,
|
|
||||||
status: 'published',
|
|
||||||
description: one.raw?.description,
|
|
||||||
address_line_1: one.raw?.addressline1,
|
|
||||||
address_line_2: one.raw?.addressline2,
|
|
||||||
city: one.raw?.city,
|
|
||||||
state: one.raw?.state,
|
|
||||||
country: one.raw?.country,
|
|
||||||
website: one.raw?.website,
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const res = await directus.items('vendors').createOne(vendor)
|
|
||||||
categoryIds.forEach(async (cid) => {
|
|
||||||
await directus.items('vendors_categories').createOne({ vendors_id: res.id, categories_id: cid })
|
|
||||||
})
|
|
||||||
} catch (error) {}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
// importCategories()
|
|
||||||
// importSubcategories()
|
|
||||||
// importCategoryStructure()
|
|
||||||
// importVendors()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<h1>Import {new Date().toISOString()}</h1>
|
|
||||||
{/* <pre>{JSON.stringify(res, null, 2)} </pre> */}
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user