task: prettier

This commit is contained in:
Zaiste 2021-05-18 14:20:07 +02:00
parent bf68f6aff7
commit 4a652a9b6a
No known key found for this signature in database
GPG Key ID: 15DF7EBC7F2FFE35
56 changed files with 635 additions and 1480 deletions

View File

@ -2,5 +2,13 @@
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"tabWidth": 2, "tabWidth": 2,
"useTabs": false "useTabs": false,
"overrides": [
{
"files": ["framework/saleor/**/*"],
"options": {
"printWidth": 120
}
}
]
} }

View File

@ -127,4 +127,3 @@ a {
opacity: 1; opacity: 1;
} }
} }

View File

@ -49,13 +49,8 @@ const Layout: FC<Props> = ({
children, children,
pageProps: { categories = [], ...pageProps }, pageProps: { categories = [], ...pageProps },
}) => { }) => {
const { const { displaySidebar, displayModal, closeSidebar, closeModal, modalView } =
displaySidebar, useUI()
displayModal,
closeSidebar,
closeModal,
modalView,
} = useUI()
const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
const { locale = 'en-US' } = useRouter() const { locale = 'en-US' } = useRouter()

View File

@ -32,7 +32,7 @@ const ProductView: FC<Props> = ({ product }) => {
useEffect(() => { useEffect(() => {
// Selects the default option // Selects the default option
const options = product.variants[0].options || []; const options = product.variants[0].options || []
options.forEach((v) => { options.forEach((v) => {
setChoices((choices) => ({ setChoices((choices) => ({
...choices, ...choices,
@ -128,7 +128,8 @@ const ProductView: FC<Props> = ({ product }) => {
setChoices((choices) => { setChoices((choices) => {
return { return {
...choices, ...choices,
[opt.displayName.toLowerCase()]: v.label.toLowerCase(), [opt.displayName.toLowerCase()]:
v.label.toLowerCase(),
} }
}) })
}} }}

View File

@ -13,9 +13,8 @@ const Container: FC<Props> = ({ children, className, el = 'div', clean }) => {
'mx-auto max-w-8xl px-6': !clean, 'mx-auto max-w-8xl px-6': !clean,
}) })
let Component: React.ComponentType< let Component: React.ComponentType<React.HTMLAttributes<HTMLDivElement>> =
React.HTMLAttributes<HTMLDivElement> el as any
> = el as any
return <Component className={rootClassName}>{children}</Component> return <Component className={rootClassName}>{children}</Component>
} }

View File

@ -157,7 +157,9 @@ export const handler: SWRHook<
const data = cartId ? await fetch(options) : null const data = cartId ? await fetch(options) : null
return data && normalizeCart(data) return data && normalizeCart(data)
}, },
useHook: ({ useData }) => (input) => { useHook:
({ useData }) =>
(input) => {
const response = useData({ const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
}) })
@ -218,7 +220,9 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = {
return normalizeCart(data) return normalizeCart(data)
}, },
useHook: ({ fetch }) => () => { useHook:
({ fetch }) =>
() => {
const { mutate } = useCart() const { mutate } = useCart()
return useCallback( return useCallback(

View File

@ -11,10 +11,8 @@ type InferValue<Prop extends PropertyKey, Desc> = Desc extends {
? Record<Prop, T> ? Record<Prop, T>
: never : never
type DefineProperty< type DefineProperty<Prop extends PropertyKey, Desc extends PropertyDescriptor> =
Prop extends PropertyKey, Desc extends { writable: any; set(val: any): any }
Desc extends PropertyDescriptor
> = Desc extends { writable: any; set(val: any): any }
? never ? never
: Desc extends { writable: any; get(): any } : Desc extends { writable: any; get(): any }
? never ? never

View File

@ -42,7 +42,7 @@ const config = new Config({
cartCookie: Const.CHECKOUT_ID_COOKIE, cartCookie: Const.CHECKOUT_ID_COOKIE,
cartCookieMaxAge: 60 * 60 * 24 * 30, cartCookieMaxAge: 60 * 60 * 24 * 30,
fetch: fetchGraphqlApi, fetch: fetchGraphqlApi,
customerCookie: "", customerCookie: '',
storeChannel: Const.API_CHANNEL, storeChannel: Const.API_CHANNEL,
}) })

View File

@ -1,11 +1,7 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { SaleorConfig, getConfig } from '..' import { SaleorConfig, getConfig } from '..'
export type SaleorApiHandler< export type SaleorApiHandler<T = any, H extends SaleorHandlers = {}, Options extends {} = {}> = (
T = any,
H extends SaleorHandlers = {},
Options extends {} = {}
> = (
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse<SaleorApiResponse<T>>, res: NextApiResponse<SaleorApiResponse<T>>,
config: SaleorConfig, config: SaleorConfig,
@ -30,11 +26,7 @@ export type SaleorApiResponse<T> = {
errors?: { message: string; code?: string }[] errors?: { message: string; code?: string }[]
} }
export default function createApiHandler< export default function createApiHandler<T = any, H extends SaleorHandlers = {}, Options extends {} = {}>(
T = any,
H extends SaleorHandlers = {},
Options extends {} = {}
>(
handler: SaleorApiHandler<T, H, Options>, handler: SaleorApiHandler<T, H, Options>,
handlers: H, handlers: H,
defaultOptions: Options defaultOptions: Options

View File

@ -6,14 +6,10 @@ import { getError } from '../../utils/handle-fetch-response'
import { getConfig } from '..' import { getConfig } from '..'
import { getToken } from '@framework/utils' import { getToken } from '@framework/utils'
const fetchGraphqlApi: GraphQLFetcher = async ( const fetchGraphqlApi: GraphQLFetcher = async (query: string, { variables } = {}, fetchOptions) => {
query: string,
{ variables } = {},
fetchOptions
) => {
// FIXME @zaiste follow the bigcommerce example // FIXME @zaiste follow the bigcommerce example
const config = getConfig() const config = getConfig()
const token = getToken(); const token = getToken()
const res = await fetch(API_URL || '', { const res = await fetch(API_URL || '', {
...fetchOptions, ...fetchOptions,

View File

@ -1,13 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next' import type { NextApiRequest, NextApiResponse } from 'next'
export default function isAllowedMethod( export default function isAllowedMethod(req: NextApiRequest, res: NextApiResponse, allowedMethods: string[]) {
req: NextApiRequest, const methods = allowedMethods.includes('OPTIONS') ? allowedMethods : [...allowedMethods, 'OPTIONS']
res: NextApiResponse,
allowedMethods: string[]
) {
const methods = allowedMethods.includes('OPTIONS')
? allowedMethods
: [...allowedMethods, 'OPTIONS']
if (!req.method || !methods.includes(req.method)) { if (!req.method || !methods.includes(req.method)) {
res.status(405) res.status(405)

View File

@ -4,10 +4,7 @@ import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
import * as mutation from '../utils/mutations' import * as mutation from '../utils/mutations'
import { import { Mutation, MutationTokenCreateArgs } from '../schema'
Mutation,
MutationTokenCreateArgs,
} from '../schema'
import useLogin, { UseLogin } from '@commerce/auth/use-login' import useLogin, { UseLogin } from '@commerce/auth/use-login'
import { setCSRFToken, setToken, throwUserErrors, checkoutAttach, getCheckoutId } from '../utils' import { setCSRFToken, setToken, throwUserErrors, checkoutAttach, getCheckoutId } from '../utils'
@ -20,39 +17,37 @@ export const handler: MutationHook<null, {}, MutationTokenCreateArgs> = {
async fetcher({ input: { email, password }, options, fetch }) { async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) { if (!(email && password)) {
throw new CommerceError({ throw new CommerceError({
message: message: 'A first name, last name, email and password are required to login',
'A first name, last name, email and password are required to login',
}) })
} }
const { tokenCreate } = await fetch< const { tokenCreate } = await fetch<Mutation, MutationTokenCreateArgs>({
Mutation,
MutationTokenCreateArgs
>({
...options, ...options,
variables: { email, password }, variables: { email, password },
}) })
throwUserErrors(tokenCreate?.errors) throwUserErrors(tokenCreate?.errors)
const { token, csrfToken } = tokenCreate!; const { token, csrfToken } = tokenCreate!
if (token && csrfToken) { if (token && csrfToken) {
setToken(token) setToken(token)
setCSRFToken(csrfToken) setCSRFToken(csrfToken)
const { checkoutId } = getCheckoutId(); const { checkoutId } = getCheckoutId()
checkoutAttach(fetch, { checkoutAttach(fetch, {
variables: { checkoutId }, variables: { checkoutId },
headers: { headers: {
Authorization: `JWT ${token}` Authorization: `JWT ${token}`,
} },
}) })
} }
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer() const { revalidate } = useCustomer()
return useCallback( return useCallback(

View File

@ -23,7 +23,9 @@ export const handler: MutationHook<null> = {
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
({ fetch }) =>
() => {
const { mutate } = useCustomer() const { mutate } = useCustomer()
return useCallback( return useCallback(

View File

@ -3,42 +3,25 @@ import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useSignup, { UseSignup } from '@commerce/auth/use-signup'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
import { import { AccountRegisterInput, Mutation, MutationAccountRegisterArgs } from '../schema'
AccountRegisterInput,
Mutation,
MutationAccountRegisterArgs
} from '../schema'
import * as mutation from '../utils/mutations' import * as mutation from '../utils/mutations'
import { handleAutomaticLogin, throwUserErrors } from '../utils' import { handleAutomaticLogin, throwUserErrors } from '../utils'
export default useSignup as UseSignup<typeof handler> export default useSignup as UseSignup<typeof handler>
export const handler: MutationHook< export const handler: MutationHook<null, {}, AccountRegisterInput, AccountRegisterInput> = {
null,
{},
AccountRegisterInput,
AccountRegisterInput
> = {
fetchOptions: { fetchOptions: {
query: mutation.AccountCreate query: mutation.AccountCreate,
}, },
async fetcher({ async fetcher({ input: { email, password }, options, fetch }) {
input: { email, password },
options,
fetch,
}) {
if (!(email && password)) { if (!(email && password)) {
throw new CommerceError({ throw new CommerceError({
message: message: 'A first name, last name, email and password are required to signup',
'A first name, last name, email and password are required to signup',
}) })
} }
const { customerCreate } = await fetch< const { customerCreate } = await fetch<Mutation, MutationAccountRegisterArgs>({
Mutation,
MutationAccountRegisterArgs
>({
...options, ...options,
variables: { variables: {
input: { input: {
@ -53,7 +36,9 @@ export const handler: MutationHook<
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer() const { revalidate } = useCustomer()
return useCallback( return useCallback(

View File

@ -6,10 +6,7 @@ import useCart from './use-cart'
import * as mutation from '../utils/mutations' import * as mutation from '../utils/mutations'
import { import { getCheckoutId, checkoutToCart } from '../utils'
getCheckoutId,
checkoutToCart,
} from '../utils'
import { Cart, CartItemBody } from '../types' import { Cart, CartItemBody } from '../types'
import { Mutation, MutationCheckoutLinesAddArgs } from '../schema' import { Mutation, MutationCheckoutLinesAddArgs } from '../schema'
@ -19,19 +16,13 @@ export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<Cart, {}, CartItemBody> = { export const handler: MutationHook<Cart, {}, CartItemBody> = {
fetchOptions: { query: mutation.CheckoutLineAdd }, fetchOptions: { query: mutation.CheckoutLineAdd },
async fetcher({ input: item, options, fetch }) { async fetcher({ input: item, options, fetch }) {
if ( if (item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1)) {
item.quantity &&
(!Number.isInteger(item.quantity) || item.quantity! < 1)
) {
throw new CommerceError({ throw new CommerceError({
message: 'The item quantity has to be a valid integer greater than 0', message: 'The item quantity has to be a valid integer greater than 0',
}) })
} }
const { checkoutLinesAdd } = await fetch< const { checkoutLinesAdd } = await fetch<Mutation, MutationCheckoutLinesAddArgs>({
Mutation,
MutationCheckoutLinesAddArgs
>({
...options, ...options,
variables: { variables: {
checkoutId: getCheckoutId().checkoutId, checkoutId: getCheckoutId().checkoutId,
@ -46,7 +37,9 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = {
return checkoutToCart(checkoutLinesAdd) return checkoutToCart(checkoutLinesAdd)
}, },
useHook: ({ fetch }) => () => { useHook:
({ fetch }) =>
() => {
const { mutate } = useCart() const { mutate } = useCart()
return useCallback( return useCallback(

View File

@ -1,8 +1,5 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import useCommerceCart, { import useCommerceCart, { FetchCartInput, UseCart } from '@commerce/cart/use-cart'
FetchCartInput,
UseCart,
} from '@commerce/cart/use-cart'
import { Cart } from '../types' import { Cart } from '../types'
import { SWRHook } from '@commerce/utils/types' import { SWRHook } from '@commerce/utils/types'
@ -11,12 +8,7 @@ import * as query from '../utils/queries'
export default useCommerceCart as UseCart<typeof handler> export default useCommerceCart as UseCart<typeof handler>
export const handler: SWRHook< export const handler: SWRHook<Cart | null, {}, FetchCartInput, { isEmpty?: boolean }> = {
Cart | null,
{},
FetchCartInput,
{ isEmpty?: boolean }
> = {
fetchOptions: { fetchOptions: {
query: query.CheckoutOne, query: query.CheckoutOne,
}, },
@ -24,22 +16,24 @@ export const handler: SWRHook<
let checkout let checkout
if (checkoutId) { if (checkoutId) {
const checkoutId = getCheckoutId().checkoutToken; const checkoutId = getCheckoutId().checkoutToken
const data = await fetch({ const data = await fetch({
...options, ...options,
variables: { checkoutId }, variables: { checkoutId },
}) })
checkout = data; checkout = data
} }
if (checkout?.completedAt || !checkoutId) { if (checkout?.completedAt || !checkoutId) {
checkout = await checkoutCreate(fetch) checkout = await checkoutCreate(fetch)
} }
return checkoutToCart(checkout); return checkoutToCart(checkout)
}, },
useHook: ({ useData }) => (input) => { useHook:
({ useData }) =>
(input) => {
const response = useData({ const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
}) })

View File

@ -1,20 +1,11 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import type { import type { MutationHookContext, HookFetcherContext } from '@commerce/utils/types'
MutationHookContext,
HookFetcherContext,
} from '@commerce/utils/types'
import { RemoveCartItemBody } from '@commerce/types' import { RemoveCartItemBody } from '@commerce/types'
import { ValidationError } from '@commerce/utils/errors' import { ValidationError } from '@commerce/utils/errors'
import useRemoveItem, { import useRemoveItem, { RemoveItemInput as RemoveItemInputBase, UseRemoveItem } from '@commerce/cart/use-remove-item'
RemoveItemInput as RemoveItemInputBase,
UseRemoveItem,
} from '@commerce/cart/use-remove-item'
import useCart from './use-cart' import useCart from './use-cart'
import * as mutation from '../utils/mutations' import * as mutation from '../utils/mutations'
import { import { getCheckoutId, checkoutToCart } from '../utils'
getCheckoutId,
checkoutToCart,
} from '../utils'
import { Cart, LineItem } from '../types' import { Cart, LineItem } from '../types'
import { Mutation, MutationCheckoutLineDeleteArgs } from '../schema' import { Mutation, MutationCheckoutLineDeleteArgs } from '../schema'
@ -22,35 +13,25 @@ export type RemoveItemFn<T = any> = T extends LineItem
? (input?: RemoveItemInput<T>) => Promise<Cart | null> ? (input?: RemoveItemInput<T>) => Promise<Cart | null>
: (input: RemoveItemInput<T>) => Promise<Cart | null> : (input: RemoveItemInput<T>) => Promise<Cart | null>
export type RemoveItemInput<T = any> = T extends LineItem export type RemoveItemInput<T = any> = T extends LineItem ? Partial<RemoveItemInputBase> : RemoveItemInputBase
? Partial<RemoveItemInputBase>
: RemoveItemInputBase
export default useRemoveItem as UseRemoveItem<typeof handler> export default useRemoveItem as UseRemoveItem<typeof handler>
export const handler = { export const handler = {
fetchOptions: { query: mutation.CheckoutLineDelete }, fetchOptions: { query: mutation.CheckoutLineDelete },
async fetcher({ async fetcher({ input: { itemId }, options, fetch }: HookFetcherContext<RemoveCartItemBody>) {
input: { itemId },
options,
fetch,
}: HookFetcherContext<RemoveCartItemBody>) {
const data = await fetch<Mutation, MutationCheckoutLineDeleteArgs>({ const data = await fetch<Mutation, MutationCheckoutLineDeleteArgs>({
...options, ...options,
variables: { variables: {
checkoutId: getCheckoutId().checkoutId, checkoutId: getCheckoutId().checkoutId,
lineId: itemId lineId: itemId,
}, },
}) })
return checkoutToCart(data.checkoutLinesUpdate) return checkoutToCart(data.checkoutLinesUpdate)
}, },
useHook: ({ useHook:
fetch, ({ fetch }: MutationHookContext<Cart | null, RemoveCartItemBody>) =>
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => < <T extends LineItem | undefined = undefined>(ctx: { item?: T } = {}) => {
T extends LineItem | undefined = undefined
>(
ctx: { item?: T } = {}
) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => { const removeItem: RemoveItemFn<LineItem> = async (input) => {

View File

@ -1,14 +1,8 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import type { import type { HookFetcherContext, MutationHookContext } from '@commerce/utils/types'
HookFetcherContext,
MutationHookContext,
} from '@commerce/utils/types'
import { ValidationError } from '@commerce/utils/errors' import { ValidationError } from '@commerce/utils/errors'
import useUpdateItem, { import useUpdateItem, { UpdateItemInput as UpdateItemInputBase, UseUpdateItem } from '@commerce/cart/use-update-item'
UpdateItemInput as UpdateItemInputBase,
UseUpdateItem,
} from '@commerce/cart/use-update-item'
import useCart from './use-cart' import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item' import { handler as removeItemHandler } from './use-remove-item'
@ -27,11 +21,7 @@ export default useUpdateItem as UseUpdateItem<typeof handler>
export const handler = { export const handler = {
fetchOptions: { query: mutation.CheckoutLineUpdate }, fetchOptions: { query: mutation.CheckoutLineUpdate },
async fetcher({ async fetcher({ input: { itemId, item }, options, fetch }: HookFetcherContext<UpdateCartItemBody>) {
input: { itemId, item },
options,
fetch,
}: HookFetcherContext<UpdateCartItemBody>) {
if (Number.isInteger(item.quantity)) { if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1 // Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) { if (item.quantity! < 1) {
@ -47,11 +37,8 @@ export const handler = {
}) })
} }
const checkoutId = getCheckoutId().checkoutId; const checkoutId = getCheckoutId().checkoutId
const { checkoutLinesUpdate } = await fetch< const { checkoutLinesUpdate } = await fetch<Mutation, MutationCheckoutLinesUpdateArgs>({
Mutation,
MutationCheckoutLinesUpdateArgs
>({
...options, ...options,
variables: { variables: {
checkoutId, checkoutId,
@ -66,11 +53,9 @@ export const handler = {
return checkoutToCart(checkoutLinesUpdate) return checkoutToCart(checkoutLinesUpdate)
}, },
useHook: ({ useHook:
fetch, ({ fetch }: MutationHookContext<Cart | null, UpdateCartItemBody>) =>
}: MutationHookContext<Cart | null, UpdateCartItemBody>) => < <T extends LineItem | undefined = undefined>(
T extends LineItem | undefined = undefined
>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number

View File

@ -28,13 +28,11 @@ const getAllPages = async (options?: {
const { locale } = config const { locale } = config
const { data } = await config.fetch(query.PageMany, { variables }) const { data } = await config.fetch(query.PageMany, { variables })
const pages = data.pages?.edges?.map( const pages = data.pages?.edges?.map(({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
...node, ...node,
url: `/${locale}/${slug}`, url: `/${locale}/${slug}`,
name, name,
}) }))
)
return { pages } return { pages }
} }

View File

@ -3,5 +3,3 @@ export const API_CHANNEL = process.env.NEXT_PUBLIC_SALEOR_CHANNEL
export const CHECKOUT_ID_COOKIE = 'saleor.CheckoutID' export const CHECKOUT_ID_COOKIE = 'saleor.CheckoutID'
export const SALEOR_TOKEN = 'saleor.Token' export const SALEOR_TOKEN = 'saleor.Token'
export const SALEOR_CRSF_TOKEN = 'saleor.CSRFToken' export const SALEOR_CRSF_TOKEN = 'saleor.CSRFToken'

View File

@ -13,8 +13,7 @@ async function getCustomerId({
const { data } = await config.fetch(query.CustomerOne, { const { data } = await config.fetch(query.CustomerOne, {
variables: { variables: {
customerAccesToken: customerAccesToken: customerAccesToken || Cookies.get(config.customerCookie),
customerAccesToken || Cookies.get(config.customerCookie),
}, },
}) })

View File

@ -17,7 +17,9 @@ export const handler: SWRHook<Customer | null> = {
}) })
return data.me ?? null return data.me ?? null
}, },
useHook: ({ useData }) => (input) => { useHook:
({ useData }) =>
(input) => {
return useData({ return useData({
swrOptions: { swrOptions: {
revalidateOnFocus: false, revalidateOnFocus: false,

View File

@ -2,13 +2,8 @@ import { Fetcher } from '@commerce/utils/types'
import { API_URL } from './const' import { API_URL } from './const'
import { getToken, handleFetchResponse } from './utils' import { getToken, handleFetchResponse } from './utils'
const fetcher: Fetcher = async ({ const fetcher: Fetcher = async ({ url = API_URL, method = 'POST', variables, query }) => {
url = API_URL, const token = getToken()
method = 'POST',
variables,
query,
}) => {
const token = getToken();
return handleFetchResponse( return handleFetchResponse(
await fetch(url!, { await fetch(url!, {

View File

@ -1,21 +1,17 @@
import * as React from 'react' import * as React from 'react'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce } from '@commerce'
CommerceConfig,
CommerceProvider as CoreCommerceProvider,
useCommerce as useCoreCommerce,
} from '@commerce'
import { saleorProvider, SaleorProvider } from './provider' import { saleorProvider, SaleorProvider } from './provider'
import * as Const from './const'; import * as Const from './const'
export { saleorProvider } export { saleorProvider }
export type { SaleorProvider } export type { SaleorProvider }
export const saleorConfig: CommerceConfig = { export const saleorConfig: CommerceConfig = {
locale: 'en-us', locale: 'en-us',
cartCookie: Const.CHECKOUT_ID_COOKIE cartCookie: Const.CHECKOUT_ID_COOKIE,
} }
export type SaleorConfig = Partial<CommerceConfig> export type SaleorConfig = Partial<CommerceConfig>
@ -27,10 +23,7 @@ export type SaleorProps = {
export function CommerceProvider({ children, ...config }: SaleorProps) { export function CommerceProvider({ children, ...config }: SaleorProps) {
return ( return (
<CoreCommerceProvider <CoreCommerceProvider provider={saleorProvider} config={{ ...saleorConfig, ...config }}>
provider={saleorProvider}
config={{ ...saleorConfig, ...config }}
>
{children} {children}
</CoreCommerceProvider> </CoreCommerceProvider>
) )

View File

@ -2,24 +2,18 @@ import { CollectionCountableEdge } from '../schema'
import { getConfig, SaleorConfig } from '../api' import { getConfig, SaleorConfig } from '../api'
import * as query from '../utils/queries' import * as query from '../utils/queries'
const getAllCollections = async (options?: { const getAllCollections = async (options?: { variables?: any; config: SaleorConfig; preview?: boolean }) => {
variables?: any
config: SaleorConfig
preview?: boolean
}) => {
let { config, variables = { first: 100 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const { data } = await config.fetch(query.CollectionMany, { variables }) const { data } = await config.fetch(query.CollectionMany, { variables })
const edges = data.collections?.edges ?? [] const edges = data.collections?.edges ?? []
const categories = edges.map( const categories = edges.map(({ node: { id: entityId, name, slug } }: CollectionCountableEdge) => ({
({ node: { id: entityId, name, slug } }: CollectionCountableEdge) => ({
entityId, entityId,
name, name,
path: `/${slug}`, path: `/${slug}`,
}) }))
)
return { return {
categories, categories,

View File

@ -24,12 +24,11 @@ const getAllProducts = async (options: {
let { config, variables = { first: 100 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const { data }: GraphQLFetcherResult = await config.fetch(query.ProductMany, { variables }) const { data }: GraphQLFetcherResult = await config.fetch(query.ProductMany, {
variables,
})
const products = const products = data.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
data.products?.edges?.map(({ node: p }: ProductCountableEdge) =>
normalizeProduct(p)
) ?? []
return { return {
products, products,

View File

@ -1,7 +1,7 @@
import { GraphQLFetcherResult } from '@commerce/api' import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, SaleorConfig } from '../api' import { getConfig, SaleorConfig } from '../api'
import { normalizeProduct } from '../utils' import { normalizeProduct } from '../utils'
import * as query from '../utils/queries'; import * as query from '../utils/queries'
type Variables = { type Variables = {
slug: string slug: string

View File

@ -3,10 +3,7 @@ import { Product } from '@commerce/types'
import useSearch, { UseSearch } from '@commerce/product/use-search' import useSearch, { UseSearch } from '@commerce/product/use-search'
import { ProductCountableEdge } from '../schema' import { ProductCountableEdge } from '../schema'
import { import { getSearchVariables, normalizeProduct } from '../utils'
getSearchVariables,
normalizeProduct,
} from '../utils'
import * as query from '../utils/queries' import * as query from '../utils/queries'
@ -24,13 +21,9 @@ export type SearchProductsData = {
found: boolean found: boolean
} }
export const handler: SWRHook< export const handler: SWRHook<SearchProductsData, SearchProductsInput, SearchProductsInput> = {
SearchProductsData,
SearchProductsInput,
SearchProductsInput
> = {
fetchOptions: { fetchOptions: {
query: query.ProductMany query: query.ProductMany,
}, },
async fetcher({ input, options, fetch }) { async fetcher({ input, options, fetch }) {
const { categoryId, brandId } = input const { categoryId, brandId } = input
@ -57,13 +50,13 @@ export const handler: SWRHook<
} }
return { return {
products: edges.map(({ node }: ProductCountableEdge) => products: edges.map(({ node }: ProductCountableEdge) => normalizeProduct(node)),
normalizeProduct(node)
),
found: !!edges.length, found: !!edges.length,
} }
}, },
useHook: ({ useData }) => (input = {}) => { useHook:
({ useData }) =>
(input = {}) => {
return useData({ return useData({
input: [ input: [
['search', input.search], ['search', input.search],

View File

@ -14,8 +14,8 @@ import fetcher from './fetcher'
export const saleorProvider = { export const saleorProvider = {
locale: 'en-us', locale: 'en-us',
cartCookie: "", cartCookie: '',
cartCookieToken: "", cartCookieToken: '',
fetcher, fetcher,
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
customer: { useCustomer }, customer: { useCustomer },

View File

@ -2,10 +2,8 @@ export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: unknown }> = { export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K] [K in keyof T]: T[K]
} }
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> }
{ [SubKey in K]?: Maybe<T[SubKey]> } export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> }
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> &
{ [SubKey in K]: Maybe<T[SubKey]> }
/** All built-in and custom scalars, mapped to their actual values */ /** All built-in and custom scalars, mapped to their actual values */
export type Scalars = { export type Scalars = {
ID: string ID: string
@ -11442,15 +11440,11 @@ export type GetAllProductPathsQueryVariables = Exact<{
export type GetAllProductPathsQuery = { __typename?: 'Query' } & { export type GetAllProductPathsQuery = { __typename?: 'Query' } & {
products?: Maybe< products?: Maybe<
{ __typename?: 'ProductCountableConnection' } & { { __typename?: 'ProductCountableConnection' } & {
pageInfo: { __typename?: 'PageInfo' } & Pick< pageInfo: { __typename?: 'PageInfo' } & Pick<PageInfo, 'hasNextPage' | 'hasPreviousPage'>
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array< edges: Array<
{ __typename?: 'ProductCountableEdge' } & Pick< { __typename?: 'ProductCountableEdge' } & Pick<ProductCountableEdge, 'cursor'> & {
ProductCountableEdge, node: { __typename?: 'Product' } & Pick<Product, 'slug'>
'cursor' }
> & { node: { __typename?: 'Product' } & Pick<Product, 'slug'> }
> >
} }
> >
@ -11459,16 +11453,10 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & {
export type ProductConnectionFragment = { export type ProductConnectionFragment = {
__typename?: 'ProductCountableConnection' __typename?: 'ProductCountableConnection'
} & { } & {
pageInfo: { __typename?: 'PageInfo' } & Pick< pageInfo: { __typename?: 'PageInfo' } & Pick<PageInfo, 'hasNextPage' | 'hasPreviousPage'>
PageInfo,
'hasNextPage' | 'hasPreviousPage'
>
edges: Array< edges: Array<
{ __typename?: 'ProductCountableEdge' } & { { __typename?: 'ProductCountableEdge' } & {
node: { __typename?: 'Product' } & Pick< node: { __typename?: 'Product' } & Pick<Product, 'id' | 'name' | 'description' | 'slug'> & {
Product,
'id' | 'name' | 'description' | 'slug'
> & {
pricing?: Maybe< pricing?: Maybe<
{ __typename?: 'ProductPricingInfo' } & { { __typename?: 'ProductPricingInfo' } & {
priceRange?: Maybe< priceRange?: Maybe<
@ -11482,14 +11470,7 @@ export type ProductConnectionFragment = {
> >
} }
> >
media?: Maybe< media?: Maybe<Array<{ __typename?: 'ProductMedia' } & Pick<ProductMedia, 'url' | 'alt'>>>
Array<
{ __typename?: 'ProductMedia' } & Pick<
ProductMedia,
'url' | 'alt'
>
>
>
} }
} }
> >
@ -11503,7 +11484,5 @@ export type GetAllProductsQueryVariables = Exact<{
}> }>
export type GetAllProductsQuery = { __typename?: 'Query' } & { export type GetAllProductsQuery = { __typename?: 'Query' } & {
products?: Maybe< products?: Maybe<{ __typename?: 'ProductCountableConnection' } & ProductConnectionFragment>
{ __typename?: 'ProductCountableConnection' } & ProductConnectionFragment
>
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,12 @@
import * as mutation from './mutations' import * as mutation from './mutations'
import { CheckoutCustomerAttach } from '../schema' import { CheckoutCustomerAttach } from '../schema'
export const checkoutAttach = async ( export const checkoutAttach = async (fetch: any, { variables, headers }: any): Promise<CheckoutCustomerAttach> => {
fetch: any,
{ variables, headers }: any,
): Promise<CheckoutCustomerAttach> => {
const data = await fetch({ const data = await fetch({
query: mutation.CheckoutAttach, query: mutation.CheckoutAttach,
variables, variables,
headers headers,
}) })
return data; return data
} }

View File

@ -4,15 +4,13 @@ import * as mutation from './mutations'
import { CheckoutCreate } from '../schema' import { CheckoutCreate } from '../schema'
import { CHECKOUT_ID_COOKIE } from '@framework/const' import { CHECKOUT_ID_COOKIE } from '@framework/const'
export const checkoutCreate = async ( export const checkoutCreate = async (fetch: any): Promise<CheckoutCreate> => {
fetch: any
): Promise<CheckoutCreate> => {
const data = await fetch({ query: mutation.CheckoutCreate }) const data = await fetch({ query: mutation.CheckoutCreate })
const checkout = data.checkoutCreate?.checkout const checkout = data.checkoutCreate?.checkout
const checkoutId = checkout?.id const checkoutId = checkout?.id
const checkoutToken = checkout?.token const checkoutToken = checkout?.token
const value = `${checkoutId}:${checkoutToken}`; const value = `${checkoutId}:${checkoutToken}`
if (checkoutId) { if (checkoutId) {
const options = { const options = {

View File

@ -1,14 +1,7 @@
import { Cart } from '../types' import { Cart } from '../types'
import { CommerceError } from '@commerce/utils/errors' import { CommerceError } from '@commerce/utils/errors'
import { import { CheckoutLinesAdd, CheckoutLinesUpdate, CheckoutCreate, CheckoutError, Checkout, Maybe } from '../schema'
CheckoutLinesAdd,
CheckoutLinesUpdate,
CheckoutCreate,
CheckoutError,
Checkout,
Maybe,
} from '../schema'
import { normalizeCart } from './normalize' import { normalizeCart } from './normalize'
import throwUserErrors from './throw-user-errors' import throwUserErrors from './throw-user-errors'
@ -18,11 +11,7 @@ export type CheckoutQuery = {
errors?: Array<CheckoutError> errors?: Array<CheckoutError>
} }
export type CheckoutPayload = export type CheckoutPayload = CheckoutLinesAdd | CheckoutLinesUpdate | CheckoutCreate | CheckoutQuery
| CheckoutLinesAdd
| CheckoutLinesUpdate
| CheckoutCreate
| CheckoutQuery
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
if (!checkoutPayload) { if (!checkoutPayload) {

View File

@ -1,5 +1,5 @@
import Cookies, { CookieAttributes } from 'js-cookie' import Cookies, { CookieAttributes } from 'js-cookie'
import * as Const from '../const'; import * as Const from '../const'
export const getToken = () => Cookies.get(Const.SALEOR_TOKEN) export const getToken = () => Cookies.get(Const.SALEOR_TOKEN)
export const setToken = (token?: string, options?: CookieAttributes) => { export const setToken = (token?: string, options?: CookieAttributes) => {

View File

@ -1,2 +1,2 @@
export { ProductConnection } from './product'; export { ProductConnection } from './product'
export { CheckoutDetails } from './checkout-details'; export { CheckoutDetails } from './checkout-details'

View File

@ -1,6 +1,6 @@
import { SaleorConfig } from '../api' import { SaleorConfig } from '../api'
import { CollectionCountableEdge } from '../schema' import { CollectionCountableEdge } from '../schema'
import * as query from './queries'; import * as query from './queries'
export type Category = { export type Category = {
entityId: string entityId: string
@ -16,13 +16,11 @@ const getCategories = async (config: SaleorConfig): Promise<Category[]> => {
}) })
return ( return (
data.collections?.edges?.map( data.collections?.edges?.map(({ node: { id: entityId, name, slug } }: CollectionCountableEdge) => ({
({ node: { id: entityId, name, slug } }: CollectionCountableEdge) => ({
entityId, entityId,
name, name,
path: `/${slug}`, path: `/${slug}`,
}) })) ?? []
) ?? []
) )
} }

View File

@ -2,7 +2,7 @@ import Cookies from 'js-cookie'
import { CHECKOUT_ID_COOKIE } from '../const' import { CHECKOUT_ID_COOKIE } from '../const'
const getCheckoutId = (id?: string) => { const getCheckoutId = (id?: string) => {
const r = Cookies.get(CHECKOUT_ID_COOKIE)?.split(":") || []; const r = Cookies.get(CHECKOUT_ID_COOKIE)?.split(':') || []
return { checkoutId: r[0], checkoutToken: r[1] } return { checkoutId: r[0], checkoutToken: r[1] }
} }

View File

@ -1,17 +1,17 @@
import { getSortVariables } from './get-sort-variables' import { getSortVariables } from './get-sort-variables'
import type { SearchProductsInput } from '../product/use-search' import type { SearchProductsInput } from '../product/use-search'
export const getSearchVariables = ({ export const getSearchVariables = ({ brandId, search, categoryId, sort }: SearchProductsInput) => {
brandId, const sortBy = {
search, field: 'NAME',
categoryId, direction: 'ASC',
sort, ...getSortVariables(sort, !!categoryId),
}: SearchProductsInput) => { channel: 'default-channel',
const sortBy = { field: "NAME", direction: 'ASC', ...getSortVariables(sort, !!categoryId), channel: "default-channel"}; }
return { return {
categoryId, categoryId,
filter: { search }, filter: { search },
sortBy sortBy,
} }
} }

View File

@ -4,7 +4,7 @@ export const getSortVariables = (sort?: string, isCategory: boolean = false) =>
case 'price-asc': case 'price-asc':
output = { output = {
field: 'PRICE', field: 'PRICE',
direction: 'ASC' direction: 'ASC',
} }
break break
case 'price-desc': case 'price-desc':
@ -22,10 +22,9 @@ export const getSortVariables = (sort?: string, isCategory: boolean = false) =>
case 'latest-desc': case 'latest-desc':
output = { output = {
field: 'DATE', field: 'DATE',
direction: 'DESC' direction: 'DESC',
} }
break break
} }
return output return output
} }

View File

@ -22,10 +22,7 @@ export const handleAutomaticLogin = async (
input: MutationTokenCreateArgs input: MutationTokenCreateArgs
) => { ) => {
try { try {
const { tokenCreate } = await fetch< const { tokenCreate } = await fetch<Mutation, MutationTokenCreateArgs>({
Mutation,
MutationTokenCreateArgs
>({
query: mutation.SessionCreate, query: mutation.SessionCreate,
variables: { ...input }, variables: { ...input },
}) })

View File

@ -7,7 +7,7 @@ export { default as getCategories } from './get-categories'
export { default as getCheckoutId } from './get-checkout-id' export { default as getCheckoutId } from './get-checkout-id'
export { default as checkoutCreate } from './checkout-create' export { default as checkoutCreate } from './checkout-create'
export { checkoutAttach } from './checkout-attach'; export { checkoutAttach } from './checkout-attach'
export { default as checkoutToCart } from './checkout-to-cart' export { default as checkoutToCart } from './checkout-to-cart'
export { default as handleLogin, handleAutomaticLogin } from './handle-login' export { default as handleLogin, handleAutomaticLogin } from './handle-login'

View File

@ -1,4 +1,4 @@
import * as fragment from '../fragments'; import * as fragment from '../fragments'
export const CheckoutLineUpdate = /* GraphQL */ ` export const CheckoutLineUpdate = /* GraphQL */ `
mutation CheckoutLineUpdate($checkoutId: ID!, $lineItems: [CheckoutLineInput!]!) { mutation CheckoutLineUpdate($checkoutId: ID!, $lineItems: [CheckoutLineInput!]!) {

View File

@ -1,12 +1,6 @@
import { Product } from '@commerce/types' import { Product } from '@commerce/types'
import { import { Product as SaleorProduct, Checkout, CheckoutLine, Money, ProductVariant } from '../schema'
Product as SaleorProduct,
Checkout,
CheckoutLine,
Money,
ProductVariant,
} from '../schema'
import type { Cart, LineItem } from '../types' import type { Cart, LineItem } from '../types'
@ -25,9 +19,7 @@ const normalizeProductOptions = (options: ProductVariant[]) => {
?.map((option) => option?.attributes) ?.map((option) => option?.attributes)
.flat(1) .flat(1)
.reduce<any>((acc, x) => { .reduce<any>((acc, x) => {
if ( if (acc.find(({ displayName }: any) => displayName === x.attribute.name)) {
acc.find(({ displayName }: any) => displayName === x.attribute.name)
) {
return acc.map((opt: any) => { return acc.map((opt: any) => {
return opt.displayName === x.attribute.name return opt.displayName === x.attribute.name
? { ? {
@ -72,36 +64,20 @@ const normalizeProductVariants = (variants: ProductVariant[]) => {
} }
export function normalizeProduct(productNode: SaleorProduct): Product { export function normalizeProduct(productNode: SaleorProduct): Product {
const { const { id, name, media, variants, description, slug, pricing, ...rest } = productNode
id,
name,
media,
variants,
description,
slug,
pricing,
...rest
} = productNode
const product = { const product = {
id, id,
name, name,
vendor: '', vendor: '',
description: description description: description ? JSON.parse(description)?.blocks[0]?.data.text : '',
? JSON.parse(description)?.blocks[0]?.data.text
: '',
path: `/${slug}`, path: `/${slug}`,
slug: slug?.replace(/^\/+|\/+$/g, ''), slug: slug?.replace(/^\/+|\/+$/g, ''),
price: price: (pricing?.priceRange?.start?.net && money(pricing.priceRange.start.net)) || 0,
(pricing?.priceRange?.start?.net &&
money(pricing.priceRange.start.net)) ||
0,
// TODO: Check nextjs-commerce bug if no images are added for a product // TODO: Check nextjs-commerce bug if no images are added for a product
images: media?.length ? media : [{ url: placeholderImg }], images: media?.length ? media : [{ url: placeholderImg }],
variants: variants: variants && variants.length > 0 ? normalizeProductVariants(variants) : [],
variants && variants.length > 0 ? normalizeProductVariants(variants) : [], options: variants && variants.length > 0 ? normalizeProductOptions(variants) : [],
options:
variants && variants.length > 0 ? normalizeProductOptions(variants) : [],
...rest, ...rest,
} }
@ -109,8 +85,8 @@ export function normalizeProduct(productNode: SaleorProduct): Product {
} }
export function normalizeCart(checkout: Checkout): Cart { export function normalizeCart(checkout: Checkout): Cart {
const lines = checkout.lines as CheckoutLine[]; const lines = checkout.lines as CheckoutLine[]
const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []; const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
return { return {
id: checkout.id, id: checkout.id,
@ -118,7 +94,7 @@ export function normalizeCart(checkout: Checkout): Cart {
email: '', email: '',
createdAt: checkout.created, createdAt: checkout.created,
currency: { currency: {
code: checkout.totalPrice?.currency! code: checkout.totalPrice?.currency!,
}, },
taxesIncluded: false, taxesIncluded: false,
lineItems, lineItems,
@ -145,7 +121,7 @@ function normalizeLineItem({ id, variant, quantity }: CheckoutLine): LineItem {
}, },
requiresShipping: false, requiresShipping: false,
price: variant?.pricing?.price?.gross.amount!, price: variant?.pricing?.price?.gross.amount!,
listPrice: 0 listPrice: 0,
}, },
path: String(variant?.product?.slug), path: String(variant?.product?.slug),
discounts: [], discounts: [],

View File

@ -1,4 +1,4 @@
import * as fragment from '../fragments'; import * as fragment from '../fragments'
export const CheckoutOne = /* GraphQL */ ` export const CheckoutOne = /* GraphQL */ `
query CheckoutOne($checkoutId: UUID!) { query CheckoutOne($checkoutId: UUID!) {

View File

@ -1,11 +1,7 @@
import * as fragment from '../fragments' import * as fragment from '../fragments'
export const CollectionOne = /* GraphQL */ ` export const CollectionOne = /* GraphQL */ `
query getProductsFromCollection( query getProductsFromCollection($categoryId: ID!, $first: Int = 100, $channel: String = "default-channel") {
$categoryId: ID!
$first: Int = 100
$channel: String = "default-channel"
) {
collection(id: $categoryId, channel: $channel) { collection(id: $categoryId, channel: $channel) {
id id
products(first: $first) { products(first: $first) {

View File

@ -1,9 +1,5 @@
export const getAllProductsPathsQuery = /* GraphQL */ ` export const getAllProductsPathsQuery = /* GraphQL */ `
query getAllProductPaths( query getAllProductPaths($first: Int = 100, $cursor: String, $channel: String = "default-channel") {
$first: Int = 100
$cursor: String
$channel: String = "default-channel"
) {
products(first: $first, after: $cursor, channel: $channel) { products(first: $first, after: $cursor, channel: $channel) {
pageInfo { pageInfo {
hasNextPage hasNextPage

View File

@ -1,4 +1,4 @@
import * as fragment from '../fragments'; import * as fragment from '../fragments'
export const ProductMany = /* GraphQL */ ` export const ProductMany = /* GraphQL */ `
query ProductMany( query ProductMany(

View File

@ -41,4 +41,3 @@ export const ProductOneBySlug = /* GraphQL */ `
} }
} }
` `

View File

@ -4,18 +4,14 @@ import { CheckoutError, CheckoutErrorCode, AppError, AccountError, AccountErrorC
export type UserErrors = Array<CheckoutError | AccountError | AppError> export type UserErrors = Array<CheckoutError | AccountError | AppError>
export type UserErrorCode = export type UserErrorCode = CheckoutErrorCode | AccountErrorCode | null | undefined
| CheckoutErrorCode
| AccountErrorCode
| null
| undefined
export const throwUserErrors = (errors?: UserErrors) => { export const throwUserErrors = (errors?: UserErrors) => {
if (errors && errors.length) { if (errors && errors.length) {
throw new ValidationError({ throw new ValidationError({
errors: errors.map(({ code, message }) => ({ errors: errors.map(({ code, message }) => ({
code: code ?? 'validation_error', code: code ?? 'validation_error',
message: message || "", message: message || '',
})), })),
}) })
} }

View File

@ -48,7 +48,10 @@ export async function getStaticProps({
} }
} }
export default function Search({ categories, brands }: InferGetStaticPropsType<typeof getStaticProps>) { export default function Search({
categories,
brands,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const [activeFilter, setActiveFilter] = useState('') const [activeFilter, setActiveFilter] = useState('')
const [toggleFilter, setToggleFilter] = useState(false) const [toggleFilter, setToggleFilter] = useState(false)