4
0
forked from crowetic/commerce

Improved Categories (#339)

* Improved Categories

* Improved Categories

* Improved Categories

* Improved Categories

* Improved Categories

* Improved Categories
This commit is contained in:
B 2021-05-31 19:44:08 -03:00 committed by GitHub
parent 84a72718d2
commit 1bc721de83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 94 additions and 88 deletions

View File

@ -8,10 +8,9 @@ import { Navbar, Footer } from '@components/common'
import { useAcceptCookies } from '@lib/hooks/useAcceptCookies' import { useAcceptCookies } from '@lib/hooks/useAcceptCookies'
import { Sidebar, Button, Modal, LoadingDots } from '@components/ui' import { Sidebar, Button, Modal, LoadingDots } from '@components/ui'
import CartSidebarView from '@components/cart/CartSidebarView' import CartSidebarView from '@components/cart/CartSidebarView'
import type { Page, Category } from '@commerce/types'
import LoginView from '@components/auth/LoginView' import LoginView from '@components/auth/LoginView'
import { CommerceProvider } from '@framework' import { CommerceProvider } from '@framework'
import type { Page } from '@framework/common/get-all-pages'
const Loading = () => ( const Loading = () => (
<div className="w-80 h-80 flex items-center text-center justify-center p-3"> <div className="w-80 h-80 flex items-center text-center justify-center p-3">
@ -41,13 +40,13 @@ const FeatureBar = dynamic(
interface Props { interface Props {
pageProps: { pageProps: {
pages?: Page[] pages?: Page[]
commerceFeatures: Record<string, boolean> categories: Category[]
} }
} }
const Layout: FC<Props> = ({ const Layout: FC<Props> = ({
children, children,
pageProps: { commerceFeatures, ...pageProps }, pageProps: { categories = [], ...pageProps },
}) => { }) => {
const { const {
displaySidebar, displaySidebar,
@ -58,10 +57,16 @@ const Layout: FC<Props> = ({
} = useUI() } = useUI()
const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
const { locale = 'en-US' } = useRouter() const { locale = 'en-US' } = useRouter()
const navBarlinks = categories.slice(0, 2).map((c) => ({
label: c.name,
href: `/search/${c.slug}`,
}))
return ( return (
<CommerceProvider locale={locale}> <CommerceProvider locale={locale}>
<div className={cn(s.root)}> <div className={cn(s.root)}>
<Navbar /> <Navbar links={navBarlinks} />
<main className="fit">{children}</main> <main className="fit">{children}</main>
<Footer pages={pageProps.pages} /> <Footer pages={pageProps.pages} />

View File

@ -5,7 +5,15 @@ import { Searchbar, UserNav } from '@components/common'
import NavbarRoot from './NavbarRoot' import NavbarRoot from './NavbarRoot'
import s from './Navbar.module.css' import s from './Navbar.module.css'
const Navbar: FC = () => ( interface Link {
href: string
label: string
}
interface NavbarProps {
links?: Link[]
}
const Navbar: FC<NavbarProps> = ({ links }) => (
<NavbarRoot> <NavbarRoot>
<Container> <Container>
<div className="relative flex flex-row justify-between py-4 align-center md:py-6"> <div className="relative flex flex-row justify-between py-4 align-center md:py-6">
@ -19,15 +27,13 @@ const Navbar: FC = () => (
<Link href="/search"> <Link href="/search">
<a className={s.link}>All</a> <a className={s.link}>All</a>
</Link> </Link>
<Link href="/search?q=clothes"> {links
<a className={s.link}>Clothes</a> ? links.map((l) => (
</Link> <Link href={l.href}>
<Link href="/search?q=accessories"> <a className={s.link}>{l.label}</a>
<a className={s.link}>Accessories</a>
</Link>
<Link href="/search?q=shoes">
<a className={s.link}>Shoes</a>
</Link> </Link>
))
: null}
</nav> </nav>
</div> </div>

View File

@ -3,6 +3,8 @@ import type { RecursivePartial, RecursiveRequired } from '../api/utils/types'
import filterEdges from '../api/utils/filter-edges' import filterEdges from '../api/utils/filter-edges'
import { BigcommerceConfig, getConfig } from '../api' import { BigcommerceConfig, getConfig } from '../api'
import { categoryTreeItemFragment } from '../api/fragments/category-tree' import { categoryTreeItemFragment } from '../api/fragments/category-tree'
import { Category } from '@commerce/types'
import getSlug from '@lib/get-slug'
// Get 3 levels of categories // Get 3 levels of categories
export const getSiteInfoQuery = /* GraphQL */ ` export const getSiteInfoQuery = /* GraphQL */ `
@ -56,7 +58,7 @@ export type Brands = BrandEdge[]
export type GetSiteInfoResult< export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = { T extends { categories: any[]; brands: any[] } = {
categories: CategoriesTree categories: Category[]
brands: Brands brands: Brands
} }
> = T > = T
@ -68,7 +70,7 @@ async function getSiteInfo(opts?: {
}): Promise<GetSiteInfoResult> }): Promise<GetSiteInfoResult>
async function getSiteInfo< async function getSiteInfo<
T extends { categories: any[]; brands: any[] }, T extends { categories: Category[]; brands: any[] },
V = any V = any
>(opts: { >(opts: {
query: string query: string
@ -94,11 +96,20 @@ async function getSiteInfo({
query, query,
{ variables } { variables }
) )
const categories = data.site?.categoryTree
let categories = data!.site!.categoryTree?.map(
({ entityId, name, path }: any) => ({
id: `${entityId}`,
name,
slug: getSlug(path),
path,
})
)
const brands = data.site?.brands?.edges const brands = data.site?.brands?.edges
return { return {
categories: (categories as RecursiveRequired<typeof categories>) ?? [], categories: categories ?? [],
brands: filterEdges(brands as RecursiveRequired<typeof brands>), brands: filterEdges(brands as RecursiveRequired<typeof brands>),
} }
} }

View File

@ -6,7 +6,7 @@ export default useSearch as UseSearch<typeof handler>
export type SearchProductsInput = { export type SearchProductsInput = {
search?: string search?: string
categoryId?: number categoryId?: number | string
brandId?: number brandId?: number
sort?: string sort?: string
} }

View File

@ -151,6 +151,15 @@ export type RemoveCartItemHandlerBody = Partial<RemoveCartItemBody> & {
cartId?: string cartId?: string
} }
export type Category = {
id: string
name: string
slug: string
path: string
}
export type Page = any
/** /**
* Temporal types * Temporal types
*/ */

View File

@ -1,7 +1,7 @@
import getCategories, { Category } from '../utils/get-categories' import { Category } from '@commerce/types'
import getVendors, { Brands } from '../utils/get-vendors'
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import getCategories from '../utils/get-categories'
import getVendors, { Brands } from '../utils/get-vendors'
export type GetSiteInfoResult< export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = { T extends { categories: any[]; brands: any[] } = {

View File

@ -1,12 +1,7 @@
import { ShopifyConfig } from '../api' import { ShopifyConfig } from '../api'
import { CollectionEdge } from '../schema' import { CollectionEdge } from '../schema'
import getSiteCollectionsQuery from './queries/get-all-collections-query' import getSiteCollectionsQuery from './queries/get-all-collections-query'
import { Category } from '@commerce/types'
export type Category = {
entityId: string
name: string
path: string
}
const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, { const { data } = await config.fetch(getSiteCollectionsQuery, {
@ -17,9 +12,10 @@ const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
return ( return (
data.collections?.edges?.map( data.collections?.edges?.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ ({ node: { id, title: name, handle } }: CollectionEdge) => ({
entityId, id,
name, name,
slug: handle,
path: `/${handle}`, path: `/${handle}`,
}) })
) ?? [] ) ?? []

View File

@ -1,6 +1,6 @@
import getCategories, { Category } from '../utils/get-categories' import getCategories from '../utils/get-categories'
import getVendors, { Brands } from '../utils/get-vendors' import getVendors, { Brands } from '../utils/get-vendors'
import { Category } from '@commerce/types'
import { getConfig, SwellConfig } from '../api' import { getConfig, SwellConfig } from '../api'
export type GetSiteInfoResult< export type GetSiteInfoResult<

View File

@ -1,17 +1,13 @@
import { SwellConfig } from '../api' import { SwellConfig } from '../api'
import { Category } from '@commerce/types'
export type Category = {
entityId: string
name: string
path: string
}
const getCategories = async (config: SwellConfig): Promise<Category[]> => { const getCategories = async (config: SwellConfig): Promise<Category[]> => {
const data = await config.fetch('categories', 'get') const data = await config.fetch('categories', 'get')
return ( return (
data.results.map(({ id: entityId, name, slug }: any) => ({ data.results.map(({ id, name, slug }: any) => ({
entityId, id,
name, name,
slug,
path: `/${slug}`, path: `/${slug}`,
})) ?? [] })) ?? []
) )

View File

@ -2,13 +2,7 @@ import { getConfig, VendureConfig } from '../api'
import { GetCollectionsQuery } from '../schema' import { GetCollectionsQuery } from '../schema'
import { arrayToTree } from '../lib/array-to-tree' import { arrayToTree } from '../lib/array-to-tree'
import { getCollectionsQuery } from '../lib/queries/get-collections-query' import { getCollectionsQuery } from '../lib/queries/get-collections-query'
import { Category } from '@commerce/types'
export type Category = {
entityId: string
name: string
path: string
productCount: number
}
export type GetSiteInfoResult< export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = { T extends { categories: any[]; brands: any[] } = {

View File

@ -1,14 +0,0 @@
// Fallback to CMS Data
export const defaultPageProps = {
header: {
links: [
{
link: {
title: 'New Arrivals',
url: '/',
},
},
],
},
}

View File

@ -10,7 +10,7 @@ import { missingLocaleInPages } from '@lib/usage-warns'
import { getConfig } from '@framework/api' import { getConfig } from '@framework/api'
import getPage from '@framework/common/get-page' import getPage from '@framework/common/get-page'
import getAllPages from '@framework/common/get-all-pages' import getAllPages from '@framework/common/get-all-pages'
import { defaultPageProps } from '@lib/defaults' import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
@ -19,9 +19,9 @@ export async function getStaticProps({
}: GetStaticPropsContext<{ pages: string[] }>) { }: GetStaticPropsContext<{ pages: string[] }>) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { pages } = await getAllPages({ preview, config }) const { pages } = await getAllPages({ preview, config })
const { categories } = await getSiteInfo({ config, preview })
const path = params?.pages.join('/') const path = params?.pages.join('/')
const slug = locale ? `${locale}/${path}` : path const slug = locale ? `${locale}/${path}` : path
const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false)) const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false))
const data = const data =
pageItem && pageItem &&
@ -34,7 +34,7 @@ export async function getStaticProps({
} }
return { return {
props: { ...defaultPageProps, pages, page }, props: { pages, page, categories },
revalidate: 60 * 60, // Every hour revalidate: 60 * 60, // Every hour
} }
} }

View File

@ -7,15 +7,17 @@ import { Layout } from '@components/common'
import { Button, Text } from '@components/ui' import { Button, Text } from '@components/ui'
import { Bag, Cross, Check, MapPin, CreditCard } from '@components/icons' import { Bag, Cross, Check, MapPin, CreditCard } from '@components/icons'
import { CartItem } from '@components/cart' import { CartItem } from '@components/cart'
import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { categories } = await getSiteInfo({ config, preview })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages, categories },
} }
} }

View File

@ -1,9 +1,8 @@
import { Layout } from '@components/common' import { Layout } from '@components/common'
import { Grid, Marquee, Hero } from '@components/ui'
import { ProductCard } from '@components/product' import { ProductCard } from '@components/product'
import { Grid, Marquee, Hero } from '@components/ui'
// import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' // import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid'
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
import { getConfig } from '@framework/api' import { getConfig } from '@framework/api'
import getAllProducts from '@framework/product/get-all-products' import getAllProducts from '@framework/product/get-all-products'
import getSiteInfo from '@framework/common/get-site-info' import getSiteInfo from '@framework/common/get-site-info'
@ -14,6 +13,8 @@ export async function getStaticProps({
locale, locale,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview })
const { categories } = await getSiteInfo({ config, preview })
const { products } = await getAllProducts({ const { products } = await getAllProducts({
variables: { first: 12 }, variables: { first: 12 },
@ -21,15 +22,12 @@ export async function getStaticProps({
preview, preview,
}) })
// const { categories, brands } = await getSiteInfo({ config, preview })
// const { pages } = await getAllPages({ config, preview })
return { return {
props: { props: {
products, products,
categories: [], categories,
brands: [], brands: [],
pages: [], pages,
}, },
revalidate: 14400, revalidate: 14400,
} }
@ -37,8 +35,6 @@ export async function getStaticProps({
export default function Home({ export default function Home({
products, products,
brands,
categories,
}: InferGetStaticPropsType<typeof getStaticProps>) { }: InferGetStaticPropsType<typeof getStaticProps>) {
return ( return (
<> <>

View File

@ -1,18 +1,21 @@
import type { GetStaticPropsContext } from 'next' import type { GetStaticPropsContext } from 'next'
import { Bag } from '@components/icons' import { Bag } from '@components/icons'
import { getConfig } from '@framework/api'
import { Layout } from '@components/common' import { Layout } from '@components/common'
import { Container, Text } from '@components/ui' import { Container, Text } from '@components/ui'
import { getConfig } from '@framework/api'
import getAllPages from '@framework/common/get-all-pages' import getAllPages from '@framework/common/get-all-pages'
import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { categories } = await getSiteInfo({ config, preview })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages, categories },
} }
} }

View File

@ -11,6 +11,7 @@ import { getConfig } from '@framework/api'
import getProduct from '@framework/product/get-product' import getProduct from '@framework/product/get-product'
import getAllPages from '@framework/common/get-all-pages' import getAllPages from '@framework/common/get-all-pages'
import getAllProductPaths from '@framework/product/get-all-product-paths' import getAllProductPaths from '@framework/product/get-all-product-paths'
import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
params, params,
@ -24,6 +25,7 @@ export async function getStaticProps({
config, config,
preview, preview,
}) })
const { categories } = await getSiteInfo({ config, preview })
if (!product) { if (!product) {
throw new Error(`Product with slug '${params!.slug}' not found`) throw new Error(`Product with slug '${params!.slug}' not found`)
@ -33,6 +35,7 @@ export async function getStaticProps({
props: { props: {
pages, pages,
product, product,
categories,
}, },
revalidate: 200, revalidate: 200,
} }

View File

@ -4,15 +4,17 @@ import getAllPages from '@framework/common/get-all-pages'
import useCustomer from '@framework/customer/use-customer' import useCustomer from '@framework/customer/use-customer'
import { Layout } from '@components/common' import { Layout } from '@components/common'
import { Container, Text } from '@components/ui' import { Container, Text } from '@components/ui'
import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale })
const { categories } = await getSiteInfo({ config, preview })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages, categories },
} }
} }

View File

@ -13,12 +13,9 @@ import useSearch from '@framework/product/use-search'
import getAllPages from '@framework/common/get-all-pages' import getAllPages from '@framework/common/get-all-pages'
import getSiteInfo from '@framework/common/get-site-info' import getSiteInfo from '@framework/common/get-site-info'
import getSlug from '@lib/get-slug'
import rangeMap from '@lib/range-map' import rangeMap from '@lib/range-map'
// TODO(bc) Remove this. This should come from the API
import getSlug from '@lib/get-slug'
// TODO (bc) : Remove or standarize this.
const SORT = Object.entries({ const SORT = Object.entries({
'latest-desc': 'Latest arrivals', 'latest-desc': 'Latest arrivals',
'trending-desc': 'Trending', 'trending-desc': 'Trending',
@ -75,7 +72,7 @@ export default function Search({
const { data } = useSearch({ const { data } = useSearch({
search: typeof q === 'string' ? q : '', search: typeof q === 'string' ? q : '',
categoryId: activeCategory?.entityId, categoryId: activeCategory?.id,
brandId: (activeBrand as any)?.entityId, brandId: (activeBrand as any)?.entityId,
sort: typeof sort === 'string' ? sort : '', sort: typeof sort === 'string' ? sort : '',
}) })
@ -164,8 +161,7 @@ export default function Search({
className={cn( className={cn(
'block text-sm leading-5 text-gray-700 hover:bg-gray-100 lg:hover:bg-transparent hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900', 'block text-sm leading-5 text-gray-700 hover:bg-gray-100 lg:hover:bg-transparent hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900',
{ {
underline: underline: activeCategory?.id === cat.id,
activeCategory?.entityId === cat.entityId,
} }
)} )}
> >

View File

@ -1,13 +1,13 @@
import type { GetStaticPropsContext } from 'next' import type { GetStaticPropsContext } from 'next'
import { Heart } from '@components/icons' import { Heart } from '@components/icons'
import { getConfig } from '@framework/api'
import { Layout } from '@components/common' import { Layout } from '@components/common'
import { Text, Container } from '@components/ui' import { Text, Container } from '@components/ui'
import { defaultPageProps } from '@lib/defaults'
import { getConfig } from '@framework/api'
import { useCustomer } from '@framework/customer' import { useCustomer } from '@framework/customer'
import { WishlistCard } from '@components/wishlist' import { WishlistCard } from '@components/wishlist'
import useWishlist from '@framework/wishlist/use-wishlist' import useWishlist from '@framework/wishlist/use-wishlist'
import getAllPages from '@framework/common/get-all-pages' import getAllPages from '@framework/common/get-all-pages'
import getSiteInfo from '@framework/common/get-site-info'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
@ -21,11 +21,12 @@ export async function getStaticProps({
} }
const config = getConfig({ locale }) const config = getConfig({ locale })
const { categories } = await getSiteInfo({ config, preview })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { props: {
pages, pages,
...defaultPageProps, categories,
}, },
} }
} }