diff --git a/frontend/package.json b/frontend/package.json index bfee0ab..f653162 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,13 +12,14 @@ "@chakra-ui/icons": "2.0.19", "@chakra-ui/next-js": "2.1.4", "@chakra-ui/react": "2.7.1", - "@directus/sdk": "10.3.3", "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", - "@tanstack/react-query": "4.29.19", + "@react-hookz/web": "23.1.0", "clsx": "1.2.1", "next": "13.4.8", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-icons": "4.10.1", + "swr": "2.2.0" } } diff --git a/frontend/pages/[slug]/index.js b/frontend/pages/[slug]/index.js index ec2c8e4..6809f7b 100644 --- a/frontend/pages/[slug]/index.js +++ b/frontend/pages/[slug]/index.js @@ -1,6 +1,7 @@ export const getStaticPaths = async () => { const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/pages`) url.searchParams.append('fields[]', 'slug') + url.searchParams.append('limit', -1) const res = await fetch(url.toString()) const { data: pages } = await res.json() diff --git a/frontend/pages/_app.js b/frontend/pages/_app.js index a037570..4dcb81b 100644 --- a/frontend/pages/_app.js +++ b/frontend/pages/_app.js @@ -1,18 +1,13 @@ import { ChakraProvider } from '@chakra-ui/react' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import Layout from '~/components/layout' import theme from '~/src/theme' -const queryClient = new QueryClient() - export default function MyApp({ Component, pageProps }) { return ( - - - - - - - + + + + + ) } diff --git a/frontend/pages/vendors/index.js b/frontend/pages/vendors/index.js index 758568c..f195ca6 100644 --- a/frontend/pages/vendors/index.js +++ b/frontend/pages/vendors/index.js @@ -1,7 +1,32 @@ import { Link } from '@chakra-ui/next-js' -import { Card, CardBody, Heading, Image, LinkBox, LinkOverlay, SimpleGrid, Skeleton } from '@chakra-ui/react' -import { useQuery } from '@tanstack/react-query' +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 @@ -12,65 +37,157 @@ export default function VendorsPage() { query: { page = 1 }, } = router - const { data, isFetching } = useQuery({ - queryKey: ['vendors', page], - queryFn: async (...props2) => { - const url = new URL(`${process.env.NEXT_PUBLIC_DIRECTUS_API_URL}/items/vendors`) - url.searchParams.append('fields[]', '*') - url.searchParams.append('limit', PER_PAGE) - url.searchParams.append('page', page) - url.searchParams.append('sort', 'name') - url.searchParams.append('meta[]', 'filter_count') + const { value: perPage, set: setPerPage } = useLocalStorageValue('perPage', { + defaultValue: PER_PAGE, + initializeWithValue: false, + }) - const res = await fetch(url.toString()) - if (!res.ok) { - throw new Error('Oops') - } + const { value: layout, set: setLayout } = useLocalStorageValue('layout', { + defaultValue: 'GRID', + initializeWithValue: false, + }) - return res.json() - }, - keepPreviousData: true, - cacheTime: 1000 * 60 * 30, - staleTime: 1000 * 60 * 30, + 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 (
- - Vendors - + + + Vendors + + {isValidating && } + + } onClick={() => setLayout('GRID')} isDisabled={layout === 'GRID'} /> + } onClick={() => setLayout('LIST')} isDisabled={layout === 'LIST'} /> + + - - {isFetching && - new Array(PER_PAGE).fill(true).map((v, k) => ( - - - - - - - ))} - {!isFetching && - vendors.map((v) => ( - - {v.name} - - - {v.name} - - - - ))} - + {error && ( + + + There was an error processing your request + + )} - + {layout === 'GRID' && ( + + {isLoading && + new Array(perPage).fill(true).map((v, k) => ( + + + + + + + + + ))} + {!isLoading && + vendors.map((v) => ( + + {v.name} + + + + {v.name} + + + {v?.description?.substring(0, v?.description?.substring(0, 80).lastIndexOf(' '))} … + + + + + ))} + + )} + + {layout === 'LIST' && ( + <> + {isLoading && ( + + + {new Array(perPage).fill(true).map((v, k) => ( + + + + + + ))} + +
+ + + + + +
+ )} + {!isLoading && ( + + + {vendors.map((v) => ( + + + + + + + + ))} + +
+ {v.name} + {v.name} + + {v?.description?.substring(0, v?.description?.substring(0, 80).lastIndexOf(' '))} … + +
+ )} + + )} + + + + + Results per page: + + {[12, 24, 48].map((n) => ( + + ))} + +
) } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 4667115..0865876 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -14,18 +14,15 @@ dependencies: '@chakra-ui/react': 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) - '@directus/sdk': - specifier: 10.3.3 - version: 10.3.3 '@emotion/react': specifier: 11.11.1 version: 11.11.1(react@18.2.0) '@emotion/styled': specifier: 11.11.0 version: 11.11.0(@emotion/react@11.11.1)(react@18.2.0) - '@tanstack/react-query': - specifier: 4.29.19 - version: 4.29.19(react-dom@18.2.0)(react@18.2.0) + '@react-hookz/web': + specifier: 23.1.0 + version: 23.1.0(react-dom@18.2.0)(react@18.2.0) clsx: specifier: 1.2.1 version: 1.2.1 @@ -38,6 +35,12 @@ dependencies: react-dom: specifier: 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: @@ -1203,14 +1206,6 @@ packages: react: 18.2.0 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: resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} dependencies: @@ -1474,34 +1469,31 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} 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: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: tslib: 2.6.0 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: resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==} dependencies: @@ -1538,19 +1530,6 @@ packages: tslib: 2.5.3 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: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -1608,13 +1587,6 @@ packages: resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==} 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: resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} dev: false @@ -1650,11 +1622,6 @@ packages: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: false - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false - /detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false @@ -1686,25 +1653,6 @@ packages: tslib: 2.5.3 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): resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} peerDependencies: @@ -1821,18 +1769,6 @@ packages: js-tokens: 4.0.0 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: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1984,6 +1920,14 @@ packages: use-sidecar: 1.1.2(react@18.2.0) 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: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: false @@ -2123,6 +2067,15 @@ packages: engines: {node: '>= 0.4'} 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: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} dev: false diff --git a/frontend/src/import/index.js b/frontend/src/import/index.js deleted file mode 100644 index f4372db..0000000 --- a/frontend/src/import/index.js +++ /dev/null @@ -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 ( -
-

Import {new Date().toISOString()}

- {/*
{JSON.stringify(res, null, 2)} 
*/} -
- ) -}