mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 15:36:58 +00:00
update to agility/next
This commit is contained in:
parent
17f458b45b
commit
e836a5950f
@ -1,20 +1,20 @@
|
|||||||
import pageTemplates from "components/agility-pageTemplates"
|
import pageTemplates from "components/agility-pageTemplates"
|
||||||
|
|
||||||
const AgilityPage = (props:any) => {
|
const AgilityPage = ({ header, agilityProps, error, revalidate }: {header:any, agilityProps: any, error?: any, revalidate?: any}) => {
|
||||||
|
|
||||||
if (!props || !props.pageTemplateName) {
|
if (!agilityProps) {
|
||||||
console.error(`Page object or template was not found.`)
|
console.error(`Page object or template was not found.`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
let AgilityPageTemplate = pageTemplates(props.pageTemplateName)
|
let AgilityPageTemplate = pageTemplates(agilityProps.pageTemplateName)
|
||||||
if (! AgilityPageTemplate) {
|
if (! AgilityPageTemplate) {
|
||||||
console.error(`${props.pageTemplateName} not found.`)
|
console.error(`${agilityProps.pageTemplateName} not found.`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AgilityPageTemplate {...props} />
|
<AgilityPageTemplate {...agilityProps} />
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import moduleComponents from "components/agility-modules"
|
import {getModule} from "components/agility-modules"
|
||||||
|
|
||||||
|
|
||||||
function ContentZone({ name, page, dynamicPageItem }) {
|
function ContentZone({ name, page, dynamicPageItem }) {
|
||||||
@ -11,7 +11,7 @@ import moduleComponents from "components/agility-modules"
|
|||||||
|
|
||||||
const modulesToRender = modules.map(m => {
|
const modulesToRender = modules.map(m => {
|
||||||
|
|
||||||
const AgilityModule = moduleComponents(m.moduleName)
|
const AgilityModule = getModule(m.moduleName)
|
||||||
|
|
||||||
if (AgilityModule) {
|
if (AgilityModule) {
|
||||||
return <AgilityModule key={m.item.contentID} page={page} dynamicPageItem={dynamicPageItem} {...m.item} />
|
return <AgilityModule key={m.item.contentID} page={page} dynamicPageItem={dynamicPageItem} {...m.item} />
|
||||||
|
@ -2,34 +2,36 @@ import React, { FC } from 'react'
|
|||||||
|
|
||||||
import { ProductCard } from '@components/product'
|
import { ProductCard } from '@components/product'
|
||||||
import { Grid, Marquee, Hero } from '@components/ui'
|
import { Grid, Marquee, Hero } from '@components/ui'
|
||||||
|
import { ModuleWithInit } from '@agility/nextjs'
|
||||||
|
|
||||||
interface Fields {
|
interface ICustomData {
|
||||||
|
bestSelling: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface IModule {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BestsellingProducts:FC<Props> = ({fields, customData}) => {
|
|
||||||
|
|
||||||
const bestSelling = customData.bestSelling
|
const BestsellingProducts: ModuleWithInit<IModule, ICustomData> = ({ customData }) => {
|
||||||
|
|
||||||
|
const bestSelling = customData.bestSelling
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Marquee variant="secondary">
|
<Marquee variant="secondary">
|
||||||
{bestSelling.slice(0, 12).map(({ node }:any) => (
|
{bestSelling.slice(0, 12).map(({ node }: any) => (
|
||||||
<ProductCard
|
<ProductCard
|
||||||
key={node.path}
|
key={node.path}
|
||||||
product={node}
|
product={node}
|
||||||
variant="slim"
|
variant="slim"
|
||||||
imgWidth={320}
|
imgWidth={320}
|
||||||
imgHeight={320}
|
imgHeight={320}
|
||||||
imgLayout="fixed"
|
imgLayout="fixed"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Marquee>
|
</Marquee>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default BestsellingProducts
|
export default BestsellingProducts
|
||||||
|
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import { Grid, Marquee, Hero } from '@components/ui'
|
import { Grid, Marquee, Hero } from '@components/ui'
|
||||||
import { ProductCard } from '@components/product'
|
import { ProductCard } from '@components/product'
|
||||||
|
import { ModuleWithInit } from "@agility/nextjs"
|
||||||
|
|
||||||
interface Fields {
|
interface ICustomData {
|
||||||
|
featured: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface IModule {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeaturedProducts:FC<Props> = ({fields, customData}) => {
|
|
||||||
|
const FeaturedProducts: ModuleWithInit<IModule, ICustomData> = ({ customData }) => {
|
||||||
|
|
||||||
|
if (! customData) {
|
||||||
|
return <div>No featured products returned.</div>
|
||||||
|
}
|
||||||
|
|
||||||
const featured:any = customData.featured
|
const featured:any = customData.featured
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
import { Hero } from '@components/ui'
|
import { Hero } from '@components/ui'
|
||||||
import * as AgilityTypes from "@agility/types"
|
import * as AgilityTypes from "@agility/types"
|
||||||
|
import { Module } from '@agility/nextjs'
|
||||||
|
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
@ -9,11 +10,7 @@ interface Fields {
|
|||||||
cTA?:AgilityTypes.URLField
|
cTA?:AgilityTypes.URLField
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
const HeroModule:Module<Fields> = ({ module: {fields }}) => {
|
||||||
fields: Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
const HeroModule:FC<Props> = ({fields}) => {
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Hero
|
<Hero
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
import React, { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid'
|
import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid'
|
||||||
|
import { ModuleWithInit } from '@agility/nextjs'
|
||||||
|
|
||||||
interface Fields {
|
|
||||||
|
|
||||||
|
interface ICustomData {
|
||||||
|
categories: any
|
||||||
|
newestProducts: any
|
||||||
|
brands: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface IModule {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HomeAllProductsGridModule:FC<Props> = ({fields, customData}) => {
|
|
||||||
|
const HomeAllProductsGridModule: ModuleWithInit<IModule, ICustomData> = ({ customData }) => {
|
||||||
|
|
||||||
const categories = customData.categories
|
const categories = customData.categories
|
||||||
const newestProducts = customData.newestProducts
|
const newestProducts = customData.newestProducts
|
||||||
const brands = customData.brands
|
const brands = customData.brands
|
||||||
|
|
||||||
|
if (!categories) return <div>No data</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomeAllProductsGrid
|
<HomeAllProductsGrid
|
||||||
categories={categories}
|
categories={categories}
|
||||||
|
@ -2,17 +2,13 @@ import React, { FC } from 'react'
|
|||||||
|
|
||||||
import { Container, Text } from '@components/ui'
|
import { Container, Text } from '@components/ui'
|
||||||
import { Bag } from '@components/icons'
|
import { Bag } from '@components/icons'
|
||||||
|
import { Module } from '@agility/nextjs'
|
||||||
|
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
const Orders: Module<Fields> = ({ }) => {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
|
||||||
|
|
||||||
const Orders: FC<Props> = ({ fields, customData }) => {
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Text variant="pageHeading">My Orders</Text>
|
<Text variant="pageHeading">My Orders</Text>
|
||||||
|
@ -3,19 +3,16 @@ import { Hero } from '@components/ui'
|
|||||||
import * as AgilityTypes from "@agility/types"
|
import * as AgilityTypes from "@agility/types"
|
||||||
import { GetProductResult } from '@framework/api/operations/get-product'
|
import { GetProductResult } from '@framework/api/operations/get-product'
|
||||||
import { ProductView } from '@components/product'
|
import { ProductView } from '@components/product'
|
||||||
|
import { Module } from '@agility/nextjs'
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
|
||||||
fields: Fields,
|
|
||||||
dynamicPageItem:any
|
|
||||||
}
|
|
||||||
|
|
||||||
const HeroModule:FC<Props> = ({fields, dynamicPageItem}) => {
|
|
||||||
|
|
||||||
|
const HeroModule:Module<Fields> = ({ dynamicPageItem }) => {
|
||||||
|
const product:any = dynamicPageItem
|
||||||
return (
|
return (
|
||||||
<ProductView product={dynamicPageItem} />
|
<ProductView product={product} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ import {
|
|||||||
getDesignerPath,
|
getDesignerPath,
|
||||||
useSearchMeta,
|
useSearchMeta,
|
||||||
} from '@lib/search'
|
} from '@lib/search'
|
||||||
|
import { ModuleWithInit } from '@agility/nextjs'
|
||||||
|
|
||||||
|
|
||||||
const SORT = Object.entries({
|
const SORT = Object.entries({
|
||||||
'latest-desc': 'Latest arrivals',
|
'latest-desc': 'Latest arrivals',
|
||||||
@ -23,21 +25,24 @@ const SORT = Object.entries({
|
|||||||
'price-desc': 'Price: High to low',
|
'price-desc': 'Price: High to low',
|
||||||
})
|
})
|
||||||
|
|
||||||
interface Fields {
|
|
||||||
|
interface ICustomData {
|
||||||
|
categories: any
|
||||||
|
brands: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface IModule {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductSearch: FC<Props> = ({ fields, customData }) => {
|
|
||||||
|
const ProductSearch: ModuleWithInit<IModule, ICustomData> = ({ customData }) => {
|
||||||
|
|
||||||
const categories:[any] = customData.categories
|
const categories:[any] = customData.categories
|
||||||
const brands:[any] = customData.brands
|
const brands:[any] = customData.brands
|
||||||
|
|
||||||
const [activeFilter, setActiveFilter] = useState('')
|
const [activeFilter, setActiveFilter] = useState('')
|
||||||
const [toggleFilter, setToggleFilter] = useState(false)
|
const [toggleFilter, setToggleFilter] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { asPath } = router
|
const { asPath } = router
|
||||||
@ -447,4 +452,6 @@ const ProductSearch: FC<Props> = ({ fields, customData }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default ProductSearch
|
export default ProductSearch
|
@ -2,6 +2,7 @@ import React, { FC } from 'react'
|
|||||||
|
|
||||||
import useCustomer from '@framework/use-customer'
|
import useCustomer from '@framework/use-customer'
|
||||||
import { Container, Text } from '@components/ui'
|
import { Container, Text } from '@components/ui'
|
||||||
|
import { Module } from '@agility/nextjs'
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
heading: string,
|
heading: string,
|
||||||
@ -10,11 +11,8 @@ interface Fields {
|
|||||||
notLoggedInMessage: string
|
notLoggedInMessage: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
|
||||||
fields: Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProfileModule:FC<Props> = ({fields}) => {
|
const ProfileModule:Module<Fields> = ({ module: {fields}}) => {
|
||||||
|
|
||||||
const { data } = useCustomer()
|
const { data } = useCustomer()
|
||||||
return (
|
return (
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import React, {FC} from 'react';
|
import React, {FC} from 'react';
|
||||||
import { Text, Container } from '@components/ui'
|
import { Text, Container } from '@components/ui'
|
||||||
|
import { Module } from '@agility/nextjs';
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
textblob:string,
|
textblob:string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
const RichTextArea:Module<Fields> = ({ module: {fields} }) => {
|
||||||
fields: Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
const RichTextArea:FC<Props> = ({fields}) => {
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
@ -5,6 +5,7 @@ import useWishlist from '@framework/wishlist/use-wishlist'
|
|||||||
import { Heart } from '@components/icons'
|
import { Heart } from '@components/icons'
|
||||||
import { Text, Container } from '@components/ui'
|
import { Text, Container } from '@components/ui'
|
||||||
import { WishlistCard } from '@components/wishlist'
|
import { WishlistCard } from '@components/wishlist'
|
||||||
|
import { Module } from '@agility/nextjs'
|
||||||
|
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
@ -13,40 +14,35 @@ interface Fields {
|
|||||||
addItemsMessage?: string
|
addItemsMessage?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
const Wishlist: Module<Fields> = ({ module: { fields } }) => {
|
||||||
fields: Fields,
|
|
||||||
customData: any
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wishlist: FC<Props> = ({ fields, customData }) => {
|
|
||||||
const { data, isEmpty } = useWishlist({ includeProducts: true })
|
const { data, isEmpty } = useWishlist({ includeProducts: true })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<div className="mt-3 mb-20">
|
<div className="mt-3 mb-20">
|
||||||
<Text variant="pageHeading">{fields.heading}</Text>
|
<Text variant="pageHeading">{fields.heading}</Text>
|
||||||
<div className="group flex flex-col">
|
<div className="group flex flex-col">
|
||||||
{isEmpty ? (
|
{isEmpty ? (
|
||||||
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
|
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
|
||||||
<span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
|
<span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
|
||||||
<Heart className="absolute" />
|
<Heart className="absolute" />
|
||||||
</span>
|
</span>
|
||||||
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
|
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
|
||||||
{fields.emptyMessage}
|
{fields.emptyMessage}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-accents-6 px-10 text-center pt-2">
|
<p className="text-accents-6 px-10 text-center pt-2">
|
||||||
{fields.addItemsMessage}
|
{fields.addItemsMessage}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
data &&
|
data &&
|
||||||
data.items?.map((item) => (
|
data.items?.map((item) => (
|
||||||
<WishlistCard key={item.id} item={item} />
|
<WishlistCard key={item.id} item={item} />
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,10 +33,8 @@ const allModules = [
|
|||||||
* Find the component for a module by name.
|
* Find the component for a module by name.
|
||||||
* @param moduleName
|
* @param moduleName
|
||||||
*/
|
*/
|
||||||
const getModule = (moduleName: string): any | null => {
|
export const getModule = (moduleName: string): any | null => {
|
||||||
const obj = allModules.find(m => m.name.toLowerCase() === moduleName.toLowerCase())
|
const obj = allModules.find(m => m.name.toLowerCase() === moduleName.toLowerCase())
|
||||||
if (!obj) return null
|
if (!obj) return null
|
||||||
return obj.module
|
return obj.module
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getModule
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ContentZone from 'components/agility-global/ContentZone'
|
import { ContentZone } from '@agility/nextjs'
|
||||||
|
|
||||||
|
import { getModule } from "components/agility-modules"
|
||||||
|
|
||||||
const MainTemplate = (props:any) => {
|
const MainTemplate = (props:any) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="one-column-template">
|
<div className="one-column-template">
|
||||||
<ContentZone name='MainContentZone' {...props} />
|
<ContentZone name='MainContentZone' {...props} getModule={getModule} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import rangeMap from '@lib/range-map'
|
|||||||
|
|
||||||
const nonNullable = (v: any) => v
|
const nonNullable = (v: any) => v
|
||||||
|
|
||||||
const BestsellingProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
const getCustomInitialProps = async function ({ }):Promise<{bestSelling:any}> {
|
||||||
//TODO: pass the locale and preview mode as props...
|
//TODO: pass the locale and preview mode as props...
|
||||||
|
|
||||||
|
|
||||||
@ -52,4 +52,4 @@ const BestsellingProductsInit = async function ({ item, agility, languageCode, c
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BestsellingProductsInit
|
export default {getCustomInitialProps}
|
@ -7,7 +7,7 @@ import rangeMap from '@lib/range-map'
|
|||||||
|
|
||||||
const nonNullable = (v: any) => v
|
const nonNullable = (v: any) => v
|
||||||
|
|
||||||
const FeaturedProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
const getCustomInitialProps = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
||||||
//TODO: pass the locale and preview mode as props...
|
//TODO: pass the locale and preview mode as props...
|
||||||
|
|
||||||
|
|
||||||
@ -31,4 +31,4 @@ const FeaturedProductsInit = async function ({ item, agility, languageCode, chan
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FeaturedProductsInit
|
export default { getCustomInitialProps }
|
@ -8,7 +8,7 @@ import rangeMap from '@lib/range-map'
|
|||||||
|
|
||||||
const nonNullable = (v: any) => v
|
const nonNullable = (v: any) => v
|
||||||
|
|
||||||
const HomeAllProductsGridData = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
const getCustomInitialProps = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
||||||
//TODO: pass the locale and preview mode as props...
|
//TODO: pass the locale and preview mode as props...
|
||||||
|
|
||||||
const locale = "en-US"
|
const locale = "en-US"
|
||||||
@ -35,4 +35,4 @@ const HomeAllProductsGridData = async function ({ item, agility, languageCode, c
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HomeAllProductsGridData
|
export default { getCustomInitialProps }
|
@ -1,7 +1,8 @@
|
|||||||
import { getConfig } from '@framework/api'
|
import { getConfig } from '@framework/api'
|
||||||
import getSiteInfo from '@framework/api/operations/get-site-info'
|
import getSiteInfo from '@framework/api/operations/get-site-info'
|
||||||
|
|
||||||
const ProductSearchData = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
const getCustomInitialProps = async ({ agility, channelName, languageCode }:any) => {
|
||||||
|
|
||||||
//TODO: pass the locale and preview mode as props...
|
//TODO: pass the locale and preview mode as props...
|
||||||
|
|
||||||
const locale = "en-US"
|
const locale = "en-US"
|
||||||
@ -18,4 +19,6 @@ const ProductSearchData = async function ({ item, agility, languageCode, channel
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProductSearchData
|
export default {
|
||||||
|
getCustomInitialProps
|
||||||
|
}
|
||||||
|
@ -14,10 +14,8 @@ const allModules:any =[
|
|||||||
* Find the data method for a module by module reference name.
|
* Find the data method for a module by module reference name.
|
||||||
* @param moduleName
|
* @param moduleName
|
||||||
*/
|
*/
|
||||||
const getInitMethod = (moduleName:string):any => {
|
export const getModuleData = (moduleName:string):any => {
|
||||||
const obj = allModules.find((m: { name: string }) => m.name.toLowerCase() === moduleName.toLowerCase())
|
const obj = allModules.find((m: { name: string }) => m.name.toLowerCase() === moduleName.toLowerCase())
|
||||||
if (!obj) return null
|
if (!obj) return null
|
||||||
return obj.init
|
return obj.init
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getInitMethod
|
|
||||||
|
@ -1,42 +1,50 @@
|
|||||||
const bundleAnalyzer = require('@next/bundle-analyzer')({
|
|
||||||
enabled: !!process.env.BUNDLE_ANALYZE,
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = bundleAnalyzer({
|
|
||||||
|
|
||||||
images: {
|
module.exports = {
|
||||||
domains: ['cdn11.bigcommerce.com', 'cdn.aglty.io'],
|
webpack5: true,
|
||||||
},
|
images: {
|
||||||
i18n: {
|
domains: ['cdn11.bigcommerce.com', 'cdn.aglty.io'],
|
||||||
locales: ['en-US', 'es'],
|
},
|
||||||
defaultLocale: 'en-US',
|
i18n: {
|
||||||
},
|
locales: ['en-US', 'es'],
|
||||||
rewrites() {
|
defaultLocale: 'en-US',
|
||||||
return [
|
},
|
||||||
{
|
webpack: (config, { isServer }) => {
|
||||||
source: '/checkout',
|
if (!isServer) {
|
||||||
destination: '/api/bigcommerce/checkout',
|
config.node = {
|
||||||
},
|
net: 'empty',
|
||||||
// The logout is also an action so this route is not required, but it's also another way
|
dns: 'empty'
|
||||||
// you can allow a logout!
|
};
|
||||||
{
|
}
|
||||||
source: '/logout',
|
|
||||||
destination: '/api/bigcommerce/customers/logout?redirect_to=/',
|
return config;
|
||||||
},
|
},
|
||||||
// Rewrites for /search
|
rewrites() {
|
||||||
{
|
return [
|
||||||
source: '/search/designers/:name',
|
{
|
||||||
destination: '/search',
|
source: '/checkout',
|
||||||
},
|
destination: '/api/bigcommerce/checkout',
|
||||||
{
|
},
|
||||||
source: '/search/designers/:name/:category',
|
// The logout is also an action so this route is not required, but it's also another way
|
||||||
destination: '/search',
|
// you can allow a logout!
|
||||||
},
|
{
|
||||||
{
|
source: '/logout',
|
||||||
// This rewrite will also handle `/search/designers`
|
destination: '/api/bigcommerce/customers/logout?redirect_to=/',
|
||||||
source: '/search/:category',
|
},
|
||||||
destination: '/search',
|
// Rewrites for /search
|
||||||
},
|
{
|
||||||
]
|
source: '/search/designers/:name',
|
||||||
},
|
destination: '/search',
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
source: '/search/designers/:name/:category',
|
||||||
|
destination: '/search',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// This rewrite will also handle `/search/designers`
|
||||||
|
source: '/search/:category',
|
||||||
|
destination: '/search',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
20581
package-lock.json
generated
Normal file
20581
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@ -2,18 +2,19 @@
|
|||||||
"name": "nextjs-commerce",
|
"name": "nextjs-commerce",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"predev": "node framework/agility/agility.sync.js sync",
|
"predev": "agility-next sync",
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"prebuild": "node framework/agility/agility.sync.js prebuild",
|
"prebuild": "agility-next prebuild",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"postbuild": "node framework/agility/agility.sync.js postbuild",
|
"export": "next export",
|
||||||
|
"postbuild": "agility-next postbuild",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
|
"cms-pull": "agility-next sync",
|
||||||
|
"cms-clean": "agility-next clean",
|
||||||
"analyze": "BUNDLE_ANALYZE=both yarn build",
|
"analyze": "BUNDLE_ANALYZE=both yarn build",
|
||||||
"find:unused": "next-unused",
|
"find:unused": "next-unused",
|
||||||
"generate": "graphql-codegen",
|
"generate": "graphql-codegen",
|
||||||
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js",
|
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js"
|
||||||
"cms-pull": "NODE_ENV=development && node framework/agility/agility.sync.js sync",
|
|
||||||
"cms-clear": "node framework/agility/agility.sync.js clear"
|
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"semi": false,
|
"semi": false,
|
||||||
@ -49,7 +50,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@agility/content-sync": "^0.1.23",
|
"@agility/content-sync": "^1.0.3",
|
||||||
|
"@agility/nextjs": "^0.1.8",
|
||||||
"@reach/portal": "^0.11.2",
|
"@reach/portal": "^0.11.2",
|
||||||
"@tailwindcss/ui": "^0.6.2",
|
"@tailwindcss/ui": "^0.6.2",
|
||||||
"@vercel/fetch": "^6.1.0",
|
"@vercel/fetch": "^6.1.0",
|
||||||
@ -64,12 +66,12 @@
|
|||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.random": "^3.2.0",
|
"lodash.random": "^3.2.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"next": "^10.0.5-canary.11",
|
"next": "^10.2.3",
|
||||||
"next-seo": "^4.11.0",
|
"next-seo": "^4.11.0",
|
||||||
"next-themes": "^0.0.4",
|
"next-themes": "^0.0.4",
|
||||||
"postcss-nesting": "^7.0.1",
|
"postcss-nesting": "^7.0.1",
|
||||||
"react": "^16.14.0",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^17.0.2",
|
||||||
"react-merge-refs": "^1.1.0",
|
"react-merge-refs": "^1.1.0",
|
||||||
"react-ticker": "^1.2.2",
|
"react-ticker": "^1.2.2",
|
||||||
"swr": "^0.4.0",
|
"swr": "^0.4.0",
|
||||||
|
@ -6,69 +6,91 @@ import type {
|
|||||||
} from 'next'
|
} from 'next'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { Layout } from '@components/common'
|
import { Layout } from '@components/common'
|
||||||
import { missingLocaleInPages } from '@lib/usage-warns'
|
import { missingLocaleInPages } from '@lib/usage-warns'
|
||||||
|
|
||||||
|
import { getAgilityPageProps, getAgilityPaths } from "@agility/nextjs/node"
|
||||||
|
import { handlePreview } from "@agility/nextjs"
|
||||||
|
|
||||||
import { defaultPageProps } from '@lib/defaults'
|
import { defaultPageProps } from '@lib/defaults'
|
||||||
|
|
||||||
import AgilityPage from "components/agility-global/AgilityPage"
|
import AgilityPage from "components/agility-global/AgilityPage"
|
||||||
|
|
||||||
import { getConfig } from '@framework/api'
|
import { getConfig } from '@framework/api'
|
||||||
import getProduct from '@framework/api/operations/get-product'
|
import getProduct from '@framework/api/operations/get-product'
|
||||||
|
import { getModuleData } from "framework/module-data"
|
||||||
|
|
||||||
import { getAgilityPageProps, getAgilityPaths } from "framework/agility/agility.node";
|
|
||||||
import getAllProductPaths from '@framework/api/operations/get-all-product-paths'
|
import getAllProductPaths from '@framework/api/operations/get-all-product-paths'
|
||||||
|
|
||||||
|
|
||||||
export async function getStaticProps({ preview, params, locale }: GetStaticPropsContext<{ slug: string[] }>) {
|
export async function getStaticProps({ preview, params, locale, locales, defaultLocale }: GetStaticPropsContext<{ slug: string[] }>) {
|
||||||
|
try {
|
||||||
|
let productCode: string | null = null
|
||||||
|
|
||||||
let productCode: string | null = null
|
//check if this page is a product...
|
||||||
|
if (params?.slug.length === 2
|
||||||
//check if this page is a product...
|
&& params?.slug[0] === "product") {
|
||||||
if (params?.slug.length === 2
|
productCode = params.slug[1]
|
||||||
&& params?.slug[0] === "product") {
|
params.slug[1] = "product-details"
|
||||||
productCode = params.slug[1]
|
|
||||||
params.slug[1] = "product-details"
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = await getAgilityPageProps({ preview, params, locale });
|
|
||||||
|
|
||||||
let rebuildFrequency = 10
|
|
||||||
|
|
||||||
if (productCode) {
|
|
||||||
const config = getConfig({ locale })
|
|
||||||
const { product } = await getProduct({
|
|
||||||
variables: { slug: productCode },
|
|
||||||
config,
|
|
||||||
preview,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (product !== null) {
|
|
||||||
page.dynamicPageItem = product
|
|
||||||
rebuildFrequency = 60 * 60 //once per hour for products
|
|
||||||
} else {
|
|
||||||
throw new Error(`Product not found`)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const pages = await getAgilityPaths(preview)
|
//add any global components (header, footer) that need agility data here
|
||||||
|
const globalComponents = {
|
||||||
|
// "header": GlobalHeader,
|
||||||
|
// "footer": GlobalFooter
|
||||||
|
}
|
||||||
|
|
||||||
if (!page) {
|
const agilityProps = await getAgilityPageProps({ preview, params, locale, getModule: getModuleData, defaultLocale, globalComponents });
|
||||||
// We throw to make sure this fails at build time as this is never expected to happen
|
|
||||||
throw new Error(`Page not found`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: { ...defaultPageProps, pages, page },
|
let rebuildFrequency = 10
|
||||||
revalidate: rebuildFrequency
|
|
||||||
|
let productDetail:any = null
|
||||||
|
|
||||||
|
if (productCode) {
|
||||||
|
const config = getConfig({ locale })
|
||||||
|
const { product } = await getProduct({
|
||||||
|
variables: { slug: productCode },
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (product !== null) {
|
||||||
|
|
||||||
|
//moderate hack: use the Product as the dynamic page item for product detail pages
|
||||||
|
agilityProps.dynamicPageItem = product
|
||||||
|
rebuildFrequency = 60 * 60 //once per hour for products
|
||||||
|
} else {
|
||||||
|
throw new Error(`Product not found`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!agilityProps) {
|
||||||
|
// We throw to make sure this fails at build time as this is never expected to happen
|
||||||
|
throw new Error(`Page not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: { ...defaultPageProps, agilityProps },
|
||||||
|
revalidate: rebuildFrequency
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
var e = new Error();
|
||||||
|
const st = e.stack;
|
||||||
|
|
||||||
|
console.log("Error getting page props", params, err)
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
error: `Params: ${params}, Error: ${err}, Stack: ${st}`,
|
||||||
|
revalidate: 60000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticPaths({ locales }: GetStaticPathsContext) {
|
export async function getStaticPaths({ defaultLocale, locales }: GetStaticPathsContext) {
|
||||||
|
|
||||||
//get the paths configured in agility
|
//get the paths configured in agility
|
||||||
let agilityPaths = await getAgilityPaths(false)
|
let agilityPaths = await getAgilityPaths({ preview: false, defaultLocale, locales })
|
||||||
|
|
||||||
//remove product/product-details from the agility paths (special details page...)
|
//remove product/product-details from the agility paths (special details page...)
|
||||||
agilityPaths = agilityPaths.filter(p => p !== "/product/product-details")
|
agilityPaths = agilityPaths.filter(p => p !== "/product/product-details")
|
||||||
@ -85,10 +107,10 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Pages({ page }: InferGetStaticPropsType<typeof getStaticProps>) {
|
export default function Pages(props: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AgilityPage {...page} />
|
<AgilityPage {...props} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
pages/api/bigcommerce/search-products.ts
Normal file
39
pages/api/bigcommerce/search-products.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// import { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
// import getProducts from "../../shopify-api/get-products"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const searchProducts = async (req:NextApiRequest, res:NextApiResponse) => {
|
||||||
|
|
||||||
|
// //cors stuff
|
||||||
|
// res.setHeader('Access-Control-Allow-Credentials', "true")
|
||||||
|
// res.setHeader('Access-Control-Allow-Origin', '*')
|
||||||
|
// res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT')
|
||||||
|
// res.setHeader(
|
||||||
|
// 'Access-Control-Allow-Headers',
|
||||||
|
// 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
|
||||||
|
// )
|
||||||
|
|
||||||
|
// if (req.method === 'OPTIONS') {
|
||||||
|
// res.status(200).end()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const filter = req.query.filter ?? ""
|
||||||
|
|
||||||
|
// const products = await getProducts({filter})
|
||||||
|
|
||||||
|
// res.statusCode = 200
|
||||||
|
// res.json(products)
|
||||||
|
|
||||||
|
// } catch (e) {
|
||||||
|
|
||||||
|
// res.statusCode = 500
|
||||||
|
// res.json({ message: "An error occurred ", error: e })
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// export default searchProducts
|
188
public/agility-ui-ext/custom-fields.js
Normal file
188
public/agility-ui-ext/custom-fields.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
//
|
||||||
|
// API Item Picker
|
||||||
|
//
|
||||||
|
|
||||||
|
var baseAPIUrl = "http://localhost:3000"
|
||||||
|
|
||||||
|
var ChooseProductCustomField = function () {
|
||||||
|
/// <summary>The type definition of this Agility Custom Field Type.</summary>
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
/// <field name="Label" type="String">The display name of the Custom Field Type</field>
|
||||||
|
self.Label = "Choose Product";
|
||||||
|
|
||||||
|
/// <field name="ReferenceName" type="String">The internal reference name of the Custom Field Type. Must not contain any special characters.</field>
|
||||||
|
self.ReferenceName = "ChooseProduct";
|
||||||
|
|
||||||
|
/// <field name="Render" type="Function">This function runs every time the field is rendered</field>
|
||||||
|
self.Render = function (options) {
|
||||||
|
/// <summary>
|
||||||
|
/// The Render handler for this field. Create any elements and bindings that you might need, pull down resources.
|
||||||
|
/// This method will be called everytime to the field value changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options" type="ContentManager.Global.CustomInputFieldParams">The options used to render this field.</param>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//get our base element
|
||||||
|
var $pnl = $(".product-picker-field", options.$elem);
|
||||||
|
|
||||||
|
if ($pnl.size() == 0) {
|
||||||
|
|
||||||
|
var htmlContent = `
|
||||||
|
<style>
|
||||||
|
.prod-img {
|
||||||
|
display:inline-block;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 40px;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.prod-img-small {
|
||||||
|
margin-top: 2px;
|
||||||
|
display:inline-block;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 20px;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.prod-text {
|
||||||
|
display:inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<input class='product-picker-field' type='hidden' style='width:100%' data-bind='value:selectedValue,select2:select2'>`;
|
||||||
|
//pull down the html template and load it into the element
|
||||||
|
options.$elem.append(htmlContent)
|
||||||
|
|
||||||
|
$pnl = $(".product-picker-field", options.$elem);
|
||||||
|
|
||||||
|
|
||||||
|
//bind our viewmodel to this
|
||||||
|
var viewModel = function () {
|
||||||
|
|
||||||
|
/// <summary>The KO ViewModel that will be binded to your HTML template.</summary>
|
||||||
|
/// <param name="options" type="Object">
|
||||||
|
/// <field name="$elem" type="jQueryElem">The .field-row jQuery Dom Element.</field>
|
||||||
|
/// <field name="contentItem" type="ContentItem Object">The entire Content Item object including Values and their KO Observable properties of all other fields on the form.</field>
|
||||||
|
/// <field name="fieldBinding" type="KO Observable">The value binding of thie Custom Field Type. Get and set this field's value by using this property.</field>
|
||||||
|
/// <field name="fieldSetting" type="Object">Object representing the field's settings such as 'Hidden', 'Label', and 'Description'</field>
|
||||||
|
/// <field name="readonly" type="boolean">Represents if this field should be readonly or not.</field>
|
||||||
|
/// </param>
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.ajaxRequest = null;
|
||||||
|
|
||||||
|
self.selectedValue = options.fieldBinding.extend({ throttle: 500 });
|
||||||
|
|
||||||
|
self.formatResult = function (item) {
|
||||||
|
|
||||||
|
return $(`<div class='prod-img' style="background-image:url('${item.node.featuredImage.transformedSrc}')"/></div><div class='prod-text'>${item.node.title}</div>`);
|
||||||
|
//return item.node.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.formatSelection = function (item) {
|
||||||
|
|
||||||
|
return $(`<div class='prod-img-small' style="background-image:url('${item.node.featuredImage.transformedSrc}')"/></div><div class='prod-text'>${item.node.title}</div>`);
|
||||||
|
|
||||||
|
};
|
||||||
|
self.ajaxRequest = null;
|
||||||
|
|
||||||
|
self.select2 = {
|
||||||
|
label: 'Product',
|
||||||
|
readOnly: false,
|
||||||
|
value: options.fieldBinding,
|
||||||
|
multiple: false,
|
||||||
|
maximumSelectionSize: 1,
|
||||||
|
minimumInputLength: 0,
|
||||||
|
placeholder: 'Find product...',
|
||||||
|
formatResult: self.formatResult,
|
||||||
|
formatSelection: self.formatSelection,
|
||||||
|
templateResult: self.templateResult,
|
||||||
|
|
||||||
|
matcher: function (term, text) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
id: function (obj) {
|
||||||
|
//set content of the Agility CMS Content Item
|
||||||
|
|
||||||
|
//options.contentItem.Values.ExternalID(obj.ID)
|
||||||
|
|
||||||
|
//options.contentItem.Values.MyField1(obj.Value1)
|
||||||
|
//options.contentItem.Values.MyField2(obj.Value2)
|
||||||
|
//etc...
|
||||||
|
|
||||||
|
//save the whole thing as JSON
|
||||||
|
return JSON.stringify(obj.node)
|
||||||
|
},
|
||||||
|
|
||||||
|
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
|
||||||
|
url: `${baseAPIUrl}/api/search-products`,
|
||||||
|
dataType: 'json',
|
||||||
|
type: "get",
|
||||||
|
quietMillis: 250,
|
||||||
|
|
||||||
|
originalValue: ko.unwrap(options.fieldBinding),
|
||||||
|
term: "",
|
||||||
|
data: function (term, page, params) {
|
||||||
|
return {
|
||||||
|
filter: term, // search term
|
||||||
|
};
|
||||||
|
},
|
||||||
|
results: function (data, page) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: data
|
||||||
|
};
|
||||||
|
},
|
||||||
|
current: function (data) {
|
||||||
|
|
||||||
|
},
|
||||||
|
cache: true
|
||||||
|
},
|
||||||
|
initSelection: function (element, callback) {
|
||||||
|
//use the hidden "product name" field
|
||||||
|
var json = ko.unwrap(options.fieldBinding);
|
||||||
|
console.log({ json })
|
||||||
|
if (json && json.length > 0) {
|
||||||
|
|
||||||
|
var node = JSON.parse(json)
|
||||||
|
console.log({ node })
|
||||||
|
callback({ node })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// console.log(val)
|
||||||
|
|
||||||
|
// var label = ko.unwrap(options.contentItem.Values.ProductName);
|
||||||
|
|
||||||
|
// if (val && label) {
|
||||||
|
// var data = {
|
||||||
|
// node: {
|
||||||
|
// id: val,
|
||||||
|
// title: label
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// callback(data);
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
allowClear: false,
|
||||||
|
dropdownCssClass: "bigdrop"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ko.applyBindings(viewModel, $pnl.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentManager.Global.CustomInputFormFields.push(new ChooseProductCustomField());
|
Loading…
x
Reference in New Issue
Block a user