mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 23:46:58 +00:00
bug fixes
This commit is contained in:
parent
52f2cccc11
commit
a88b52b00a
@ -1,6 +1,6 @@
|
|||||||
import pageTemplates from "components/agility-pageTemplates"
|
import pageTemplates from "components/agility-pageTemplates"
|
||||||
|
|
||||||
const AgilityPage = ({ header, agilityProps, error, revalidate }: {header:any, agilityProps: any, error?: any, revalidate?: any}) => {
|
const AgilityPage = ({ agilityProps, error, revalidate }: { agilityProps: any, error?: any, revalidate?: any}) => {
|
||||||
|
|
||||||
if (!agilityProps) {
|
if (!agilityProps) {
|
||||||
console.error(`Page object or template was not found.`)
|
console.error(`Page object or template was not found.`)
|
||||||
|
@ -16,8 +16,8 @@ const HeroModule:Module<Fields> = ({ module: {fields }}) => {
|
|||||||
<Hero
|
<Hero
|
||||||
headline={fields.title}
|
headline={fields.title}
|
||||||
description={fields.description}
|
description={fields.description}
|
||||||
ctaText={fields.cTA?.text}
|
ctaText={fields.cTA?.text || ""}
|
||||||
ctaUrl={fields.cTA?.href}
|
ctaUrl={fields.cTA?.href || ""}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
import useWishlist from '@framework/wishlist/use-wishlist'
|
|
||||||
|
|
||||||
import { Heart } from '@components/icons'
|
|
||||||
import { Text, Container } from '@components/ui'
|
|
||||||
import { WishlistCard } from '@components/wishlist'
|
|
||||||
import { Module } from '@agility/nextjs'
|
import { Module } from '@agility/nextjs'
|
||||||
|
import { Heart } from '@components/icons'
|
||||||
|
import { Layout } from '@components/common'
|
||||||
|
import { Text, Container } from '@components/ui'
|
||||||
|
import { useCustomer } from '@framework/customer'
|
||||||
|
import { WishlistCard } from '@components/wishlist'
|
||||||
|
import useWishlist from '@framework/wishlist/use-wishlist'
|
||||||
|
|
||||||
|
|
||||||
interface Fields {
|
interface Fields {
|
||||||
@ -15,35 +16,41 @@ interface Fields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Wishlist: Module<Fields> = ({ module: { fields } }) => {
|
const Wishlist: Module<Fields> = ({ module: { fields } }) => {
|
||||||
const { data, isEmpty } = useWishlist({ includeProducts: true })
|
|
||||||
|
|
||||||
return (
|
const { data: customer } = useCustomer()
|
||||||
<Container>
|
// @ts-ignore Shopify - Fix this types
|
||||||
<div className="mt-3 mb-20">
|
const { data, isLoading, isEmpty } = useWishlist({ includeProducts: true })
|
||||||
<Text variant="pageHeading">{fields.heading}</Text>
|
|
||||||
<div className="group flex flex-col">
|
return (
|
||||||
{isEmpty ? (
|
<Container>
|
||||||
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
|
<div className="mt-3 mb-20">
|
||||||
<span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
|
<Text variant="pageHeading">My Wishlist</Text>
|
||||||
<Heart className="absolute" />
|
<div className="group flex flex-col">
|
||||||
</span>
|
{isLoading || isEmpty ? (
|
||||||
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
|
<div className="flex-1 px-12 py-24 flex flex-col justify-center items-center ">
|
||||||
{fields.emptyMessage}
|
<span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary">
|
||||||
</h2>
|
<Heart className="absolute" />
|
||||||
<p className="text-accents-6 px-10 text-center pt-2">
|
</span>
|
||||||
{fields.addItemsMessage}
|
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
|
||||||
</p>
|
Your wishlist is empty
|
||||||
</div>
|
</h2>
|
||||||
) : (
|
<p className="text-accent-6 px-10 text-center pt-2">
|
||||||
data &&
|
Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
|
||||||
data.items?.map((item) => (
|
</p>
|
||||||
<WishlistCard key={item.id} item={item} />
|
</div>
|
||||||
))
|
) : (
|
||||||
)}
|
data &&
|
||||||
</div>
|
// @ts-ignore Shopify - Fix this types
|
||||||
</div>
|
data.items?.map((item) => (
|
||||||
</Container>
|
<WishlistCard key={item.id} product={item.product! as any} />
|
||||||
)
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Wishlist
|
export default Wishlist
|
@ -1,5 +1,3 @@
|
|||||||
import { FC } from "react"
|
|
||||||
import * as AgilityTypes from "@agility/types"
|
|
||||||
import RichTextArea from "./RichTextArea"
|
import RichTextArea from "./RichTextArea"
|
||||||
import BestsellingProducts from "./BestsellingProducts"
|
import BestsellingProducts from "./BestsellingProducts"
|
||||||
import ProductDetails from "./ProductDetails"
|
import ProductDetails from "./ProductDetails"
|
||||||
|
@ -13,7 +13,7 @@ import useAddItem from '@framework/cart/use-add-item'
|
|||||||
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
product: Product
|
product: Product | any
|
||||||
}
|
}
|
||||||
|
|
||||||
const placeholderImg = '/product-img-placeholder.svg'
|
const placeholderImg = '/product-img-placeholder.svg'
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
const agilityContentSync = require("@agility/content-sync");
|
|
||||||
const nextFileSystem = require("./next.file.sync");
|
|
||||||
|
|
||||||
const path = require("path")
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
const agilityConfig = {
|
|
||||||
guid: process.env.AGILITY_GUID, //Set your guid here
|
|
||||||
fetchAPIKey: process.env.AGILITY_API_FETCH_KEY, //Set your fetch apikey here
|
|
||||||
previewAPIKey: process.env.AGILITY_API_PREVIEW_KEY, //set your preview apikey
|
|
||||||
languageCode: "en-us", //the language for your website in Agility CMS
|
|
||||||
channelName: "website", //the name of your channel in Agility CMS
|
|
||||||
securityKey: process.env.AGILITY_SECURITY_KEY, //the website security key used to validate and generate preview keys
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSyncClient = ({ isPreview, isDevelopmentMode, isIncremental }) => {
|
|
||||||
|
|
||||||
const rootPath = process.cwd()
|
|
||||||
|
|
||||||
let cachePath = `${rootPath}/.next/cache/agility/${isPreview ? "preview" : "live" }`;
|
|
||||||
|
|
||||||
//if we are in "incremental" mode, we need to use the tmp folder...
|
|
||||||
if (isIncremental) {
|
|
||||||
cachePath = `/tmp/agilitycache/${isPreview ? "preview" : "live"}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`AgilityCMS => Content cache path is ${cachePath}`);
|
|
||||||
const apiKey = isPreview
|
|
||||||
? agilityConfig.previewAPIKey
|
|
||||||
: agilityConfig.fetchAPIKey;
|
|
||||||
|
|
||||||
if (!agilityConfig.guid) {
|
|
||||||
console.log("AgilityCMS => No GUID was provided.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return agilityContentSync.getSyncClient({
|
|
||||||
guid: agilityConfig.guid,
|
|
||||||
apiKey: apiKey,
|
|
||||||
isPreview: isPreview,
|
|
||||||
languages: [agilityConfig.languageCode],
|
|
||||||
channels: [agilityConfig.channelName],
|
|
||||||
store: {
|
|
||||||
interface: nextFileSystem,
|
|
||||||
options: {
|
|
||||||
rootPath: cachePath
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const prepIncrementalMode = async () => {
|
|
||||||
|
|
||||||
const rootPath = process.cwd()
|
|
||||||
let cachePath = `${rootPath}/.next/cache/agility/`;
|
|
||||||
const tempPath = `/tmp/agilitycache/`;
|
|
||||||
|
|
||||||
const buildFilePath = path.join(tempPath, "build.log")
|
|
||||||
|
|
||||||
//check for the build file in here...
|
|
||||||
if (!fs.existsSync(buildFilePath)) {
|
|
||||||
console.log(`Copying Agility Content files to temp folder: ${tempPath}`)
|
|
||||||
//copy everything across from cachePath
|
|
||||||
await fs.copy(cachePath, tempPath)
|
|
||||||
} else {
|
|
||||||
console.log(`Agility tmp folder up to date: ${tempPath}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
agilityConfig,
|
|
||||||
getSyncClient,
|
|
||||||
prepIncrementalMode
|
|
||||||
};
|
|
@ -1,302 +0,0 @@
|
|||||||
import fs from "fs"
|
|
||||||
import crypto from 'crypto'
|
|
||||||
import { asyncForEach } from "./utils"
|
|
||||||
|
|
||||||
import { ModuleWithInit } from "@agility/types"
|
|
||||||
|
|
||||||
//Agility API stuff
|
|
||||||
import { agilityConfig, getSyncClient, prepIncrementalMode } from './agility.config'
|
|
||||||
import GlobalFooter from 'components/agility-global/GlobalFooter'
|
|
||||||
import GlobalHeader from 'components/agility-global/GlobalHeader'
|
|
||||||
|
|
||||||
import moduleInitializers from "framework/module-data"
|
|
||||||
|
|
||||||
|
|
||||||
const securityKey = agilityConfig.securityKey
|
|
||||||
const channelName = agilityConfig.channelName
|
|
||||||
const languageCode = agilityConfig.languageCode
|
|
||||||
const isDevelopmentMode = process.env.NODE_ENV === "development"
|
|
||||||
|
|
||||||
interface AgilityPageProps {
|
|
||||||
sitemapNode?: any,
|
|
||||||
page?: any,
|
|
||||||
dynamicPageItem?: any,
|
|
||||||
pageTemplateName?:string|null,
|
|
||||||
globalHeaderProps?:any,
|
|
||||||
globalFooterProps?:any,
|
|
||||||
languageCode?:string|null,
|
|
||||||
channelName?:string|null,
|
|
||||||
isPreview?:boolean,
|
|
||||||
isDevelopmentMode?:boolean,
|
|
||||||
notFound?:boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAgilityPageProps = async ({ params, preview, locale }:any):Promise<AgilityPageProps> => {
|
|
||||||
|
|
||||||
let path = '/';
|
|
||||||
if (params) {
|
|
||||||
//build path by iterating through slugs
|
|
||||||
path = '';
|
|
||||||
params.slug.map((slug: string) => {
|
|
||||||
path += '/' + slug
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//determine if we've already done a full build yet
|
|
||||||
const buildFilePath = `${process.cwd()}/.next/cache/agility/build.log`
|
|
||||||
const isBuildComplete = fs.existsSync(buildFilePath)
|
|
||||||
|
|
||||||
//TODO: use locale to determin LANGUAGECODE (pulled from config at this point...)
|
|
||||||
|
|
||||||
//determine if we are in preview mode
|
|
||||||
const isPreview:boolean = (preview || isDevelopmentMode);
|
|
||||||
|
|
||||||
const agilitySyncClient = getSyncClient({
|
|
||||||
isPreview: isPreview,
|
|
||||||
isDevelopmentMode,
|
|
||||||
isIncremental: isBuildComplete
|
|
||||||
});
|
|
||||||
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return {
|
|
||||||
notFound: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preview || isBuildComplete) {
|
|
||||||
//only do on-demand sync in next's preview mode or incremental build...
|
|
||||||
console.log(`AgilityCMS => Sync On-demand ${isPreview ? "Preview" : "Live"} Mode`)
|
|
||||||
|
|
||||||
await prepIncrementalMode()
|
|
||||||
await agilitySyncClient.runSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`AgilityCMS => Getting page props for '${path}'...`);
|
|
||||||
|
|
||||||
//get sitemap
|
|
||||||
const sitemap = await agilitySyncClient.store.getSitemap({ channelName, languageCode });
|
|
||||||
|
|
||||||
if (sitemap === null) {
|
|
||||||
console.warn("No sitemap found after sync.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageInSitemap = sitemap[path];
|
|
||||||
let page: any = null;
|
|
||||||
let dynamicPageItem: any = null;
|
|
||||||
|
|
||||||
if (path === '/') {
|
|
||||||
let firstPagePathInSitemap = Object.keys(sitemap)[0];
|
|
||||||
pageInSitemap = sitemap[firstPagePathInSitemap];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pageInSitemap) {
|
|
||||||
//get the page
|
|
||||||
page = await agilitySyncClient.store.getPage({
|
|
||||||
pageID: pageInSitemap.pageID,
|
|
||||||
languageCode: languageCode
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Could not find page
|
|
||||||
console.warn('page [' + path + '] not found in sitemap.');
|
|
||||||
return handlePageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!page) {
|
|
||||||
console.warn('page [' + path + '] not found in getpage method.');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//if there is a dynamic page content id on this page, grab it...
|
|
||||||
if (pageInSitemap.contentID > 0) {
|
|
||||||
dynamicPageItem = await agilitySyncClient.store.getContentItem({
|
|
||||||
contentID: pageInSitemap.contentID,
|
|
||||||
languageCode: languageCode
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//resolve the page template
|
|
||||||
const pageTemplateName = page.templateName.replace(/[^0-9a-zA-Z]/g, '');
|
|
||||||
|
|
||||||
//resolve the modules per content zone
|
|
||||||
await asyncForEach(Object.keys(page.zones), async (zoneName: string) => {
|
|
||||||
|
|
||||||
let modules: { moduleName: string; item: any }[] = [];
|
|
||||||
|
|
||||||
//grab the modules for this content zone
|
|
||||||
const modulesForThisContentZone = page.zones[zoneName];
|
|
||||||
|
|
||||||
//loop through the zone's modules
|
|
||||||
await asyncForEach(modulesForThisContentZone, async (moduleItem: { module: string, item: any }) => {
|
|
||||||
|
|
||||||
//find the react component to use for the module
|
|
||||||
const moduleInitializer = moduleInitializers(moduleItem.module)
|
|
||||||
|
|
||||||
if (moduleInitializer) {
|
|
||||||
//resolve any additional data for the modules
|
|
||||||
|
|
||||||
//we have some additional data in the module we'll need, execute that method now, so it can be included in SSG
|
|
||||||
if (isDevelopmentMode) {
|
|
||||||
console.log(`AgilityCMS => Fetching additional data via getCustomInitialProps for ${moduleItem.module}...`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleData = await moduleInitializer({
|
|
||||||
item: moduleItem.item,
|
|
||||||
agility: agilitySyncClient.store,
|
|
||||||
languageCode,
|
|
||||||
channelName,
|
|
||||||
pageInSitemap,
|
|
||||||
dynamicPageItem
|
|
||||||
});
|
|
||||||
|
|
||||||
//if we have additional module data, then add it to the module props using 'customData'
|
|
||||||
if (moduleData != null) {
|
|
||||||
moduleItem.item.customData = moduleData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modules.push({
|
|
||||||
moduleName: moduleItem.module,
|
|
||||||
item: moduleItem.item,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
//store as dictionary
|
|
||||||
page.zones[zoneName] = modules;
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
//resolve data for other shared components
|
|
||||||
const globalHeaderProps = await GlobalHeader.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName });
|
|
||||||
const globalFooterProps = await GlobalFooter.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName });
|
|
||||||
|
|
||||||
return {
|
|
||||||
sitemapNode: pageInSitemap,
|
|
||||||
page,
|
|
||||||
dynamicPageItem,
|
|
||||||
pageTemplateName,
|
|
||||||
globalHeaderProps,
|
|
||||||
globalFooterProps,
|
|
||||||
languageCode,
|
|
||||||
channelName,
|
|
||||||
isPreview,
|
|
||||||
isDevelopmentMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAgilityPaths = async (preview:boolean|undefined) => {
|
|
||||||
|
|
||||||
console.log(`AgilityCMS => Fetching sitemap for getAgilityPaths...`);
|
|
||||||
|
|
||||||
//determine if we are in preview mode
|
|
||||||
const isPreview = isDevelopmentMode || preview;
|
|
||||||
|
|
||||||
//determine if we've already done a full build yet
|
|
||||||
const buildFilePath = `${process.cwd()}/.next/cache/agility/build.log`
|
|
||||||
const isBuildComplete = fs.existsSync(buildFilePath)
|
|
||||||
|
|
||||||
const agilitySyncClient = getSyncClient({
|
|
||||||
isPreview,
|
|
||||||
isDevelopmentMode,
|
|
||||||
isIncremental: isBuildComplete
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const sitemapFlat = await agilitySyncClient.store.getSitemap({
|
|
||||||
channelName,
|
|
||||||
languageCode
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!sitemapFlat) {
|
|
||||||
console.warn("AgilityCMS => No Site map found. Make sure your environment variables are setup correctly.")
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//returns an array of paths as a string (i.e. ['/home', '/posts'] )
|
|
||||||
const paths = Object.keys(sitemapFlat)
|
|
||||||
.filter(path => {
|
|
||||||
const sitemapNode = sitemapFlat[path]
|
|
||||||
return !sitemapNode.redirect
|
|
||||||
&& !sitemapNode.isFolder
|
|
||||||
})
|
|
||||||
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const validatePreview = async({ agilityPreviewKey, slug }: any) => {
|
|
||||||
//Validate the preview key
|
|
||||||
if (!agilityPreviewKey) {
|
|
||||||
return {
|
|
||||||
error: true,
|
|
||||||
message: `Missing agilitypreviewkey.`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//sanitize incoming key (replace spaces with '+')
|
|
||||||
if (agilityPreviewKey.indexOf(` `) > -1) {
|
|
||||||
agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//compare the preview key being used
|
|
||||||
const correctPreviewKey = generatePreviewKey();
|
|
||||||
|
|
||||||
if (agilityPreviewKey !== correctPreviewKey) {
|
|
||||||
return {
|
|
||||||
error: true,
|
|
||||||
message: `Invalid agilitypreviewkey.`
|
|
||||||
//message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//return success
|
|
||||||
return {
|
|
||||||
error: false,
|
|
||||||
message: null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const generatePreviewKey =() => {
|
|
||||||
//the string we want to encode
|
|
||||||
const str = `-1_${securityKey}_Preview`;
|
|
||||||
|
|
||||||
//build our byte array
|
|
||||||
let data = [];
|
|
||||||
for (var i = 0; i < str.length; ++i) {
|
|
||||||
data.push(str.charCodeAt(i));
|
|
||||||
data.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//convert byte array to buffer
|
|
||||||
const strBuffer = Buffer.from(data);
|
|
||||||
|
|
||||||
//encode it!
|
|
||||||
const previewKey = crypto.createHash('sha512').update(strBuffer).digest('base64');
|
|
||||||
return previewKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePageNotFound() {
|
|
||||||
return {
|
|
||||||
notFound: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
getAgilityPageProps,
|
|
||||||
getAgilityPaths,
|
|
||||||
validatePreview,
|
|
||||||
generatePreviewKey
|
|
||||||
}
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
|||||||
|
|
||||||
const fs = require('fs')
|
|
||||||
|
|
||||||
require("dotenv").config({
|
|
||||||
path: `.env.local`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { getSyncClient } = require('./agility.config')
|
|
||||||
|
|
||||||
|
|
||||||
const runSync = async () => {
|
|
||||||
setBuildLog(false)
|
|
||||||
|
|
||||||
const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true })
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await agilitySyncClient.runSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
const setBuildLog = (builtYN) => {
|
|
||||||
//clear out a file saying WE HAVE SYNC'D
|
|
||||||
const rootPath = process.cwd()
|
|
||||||
const filePath = `${rootPath}/.next/cache/agility/build.log`
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
fs.unlinkSync(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builtYN) {
|
|
||||||
//write out the build log so we know that we are up to date
|
|
||||||
fs.writeFileSync(filePath, "BUILT");
|
|
||||||
} else {
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
fs.unlinkSync(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const preBuild = async () => {
|
|
||||||
|
|
||||||
//clear the build log
|
|
||||||
setBuildLog(false)
|
|
||||||
|
|
||||||
//sync preview mode
|
|
||||||
let agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: false })
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await agilitySyncClient.runSync();
|
|
||||||
|
|
||||||
//sync production mode
|
|
||||||
agilitySyncClient = getSyncClient({ isPreview: false, isDevelopmentMode: false })
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await agilitySyncClient.runSync();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const postBuild = async() => {
|
|
||||||
//mark the build log as BUILT
|
|
||||||
setBuildLog(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearSync = async () => {
|
|
||||||
|
|
||||||
setBuildLog(false)
|
|
||||||
|
|
||||||
const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true })
|
|
||||||
if (! agilitySyncClient) {
|
|
||||||
console.log("AgilityCMS => Sync client could not be accessed.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await agilitySyncClient.clearSync();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (process.argv[2]) {
|
|
||||||
if (process.argv[2] === "clear") {
|
|
||||||
//clear everything
|
|
||||||
return clearSync();
|
|
||||||
} else if (process.argv[2] === "sync") {
|
|
||||||
//run the sync
|
|
||||||
return runSync()
|
|
||||||
|
|
||||||
} else if (process.argv[2] === "prebuild") {
|
|
||||||
//pre build actions
|
|
||||||
return preBuild()
|
|
||||||
|
|
||||||
} else if (process.argv[2] === "postbuild") {
|
|
||||||
//post build actions
|
|
||||||
return postBuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
clearSync,
|
|
||||||
runSync
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const os = require('os')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const { lockSync, unlockSync, checkSync, check } = require("proper-lockfile")
|
|
||||||
|
|
||||||
require("dotenv").config({
|
|
||||||
path: `.env.${process.env.NODE_ENV}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const sleep = (ms) => {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const getFilePath = ({ options, itemType, languageCode, itemID }) => {
|
|
||||||
|
|
||||||
const fileName = `${itemID}.json`;
|
|
||||||
return path.join(options.rootPath, languageCode, itemType, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveItem = async ({ options, item, itemType, languageCode, itemID }) => {
|
|
||||||
|
|
||||||
let filePath = getFilePath({ options, itemType, languageCode, itemID });
|
|
||||||
|
|
||||||
let dirPath = path.dirname(filePath);
|
|
||||||
|
|
||||||
|
|
||||||
if (!fs.existsSync(dirPath)) {
|
|
||||||
fs.mkdirSync(dirPath, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
let json = JSON.stringify(item);
|
|
||||||
fs.writeFileSync(filePath, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteItem = async ({ options, itemType, languageCode, itemID }) => {
|
|
||||||
|
|
||||||
let filePath = getFilePath({ options, itemType, languageCode, itemID });
|
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
fs.unlinkSync(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergeItemToList = async ({ options, item, languageCode, itemID, referenceName, definitionName }) => {
|
|
||||||
|
|
||||||
let contentList = await getItem({ options, itemType: "list", languageCode, itemID: referenceName });
|
|
||||||
|
|
||||||
if (contentList == null) {
|
|
||||||
//initialize the list
|
|
||||||
contentList = [item];
|
|
||||||
} else {
|
|
||||||
//replace the item...
|
|
||||||
const cIndex = contentList.findIndex((ci) => {
|
|
||||||
return ci.contentID === itemID;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (item.properties.state === 3) {
|
|
||||||
//*** deleted item (remove from the list) ***
|
|
||||||
if (cIndex >= 0) {
|
|
||||||
//remove the item
|
|
||||||
contentList.splice(cIndex, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//*** regular item (merge) ***
|
|
||||||
if (cIndex >= 0) {
|
|
||||||
//replace the existing item
|
|
||||||
contentList[cIndex] = item;
|
|
||||||
} else {
|
|
||||||
//and it to the end of the
|
|
||||||
contentList.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await saveItem({ options, item: contentList, itemType: "list", languageCode, itemID: referenceName });
|
|
||||||
}
|
|
||||||
|
|
||||||
const getItem = async ({ options, itemType, languageCode, itemID }) => {
|
|
||||||
let filePath = getFilePath({ options, itemType, languageCode, itemID });
|
|
||||||
|
|
||||||
if (!fs.existsSync(filePath)) return null;
|
|
||||||
|
|
||||||
let json = fs.readFileSync(filePath, 'utf8');
|
|
||||||
return JSON.parse(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearItems = async ({ options }) => {
|
|
||||||
fs.rmdirSync(options.rootPath, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
const waitOnLock = async (lockFile) => {
|
|
||||||
|
|
||||||
while (await check(lockFile)) {
|
|
||||||
await sleep(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const mutexLock = async () => {
|
|
||||||
|
|
||||||
|
|
||||||
const dir = os.tmpdir();
|
|
||||||
const lockFile = `${dir}/${"agility-sync"}.mutex`
|
|
||||||
if (! fs.existsSync(lockFile)) {
|
|
||||||
fs.writeFileSync(lockFile, "agility-sync");
|
|
||||||
}
|
|
||||||
|
|
||||||
//THE LOCK IS ALREADY HELD - WAIT UP!
|
|
||||||
await waitOnLock(lockFile)
|
|
||||||
|
|
||||||
try {
|
|
||||||
return lockSync(lockFile)
|
|
||||||
} catch (err) {
|
|
||||||
if (`${err}`.indexOf("Lock file is already being held") !== -1) {
|
|
||||||
|
|
||||||
//this error happens when 2 processes try to get a lock at the EXACT same time (very rare)
|
|
||||||
await sleep(100)
|
|
||||||
await waitOnLock(lockFile)
|
|
||||||
|
|
||||||
try {
|
|
||||||
return lockSync(lockFile)
|
|
||||||
} catch (e2) {
|
|
||||||
if (`${err}`.indexOf("Lock file is already being held") !== -1) {
|
|
||||||
|
|
||||||
//this error happens when 2 processes try to get a lock at the EXACT same time (very rare)
|
|
||||||
await sleep(100)
|
|
||||||
await waitOnLock(lockFile)
|
|
||||||
return lockSync(lockFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error("The mutex lock could not be obtained.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
saveItem,
|
|
||||||
deleteItem,
|
|
||||||
mergeItemToList,
|
|
||||||
getItem,
|
|
||||||
clearItems,
|
|
||||||
mutexLock
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import { FC } from 'react'
|
|
||||||
|
|
||||||
export interface CustomInitPropsArg {
|
|
||||||
item:any,
|
|
||||||
agility:any,
|
|
||||||
languageCode:any,
|
|
||||||
channelName:any,
|
|
||||||
pageInSitemap:any,
|
|
||||||
dynamicPageItem?:any
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CustomInitProps<T> {
|
|
||||||
(props:CustomInitPropsArg): T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModuleWithInit<TProps, TInit> extends FC<TProps> {
|
|
||||||
getCustomInitialProps:CustomInitProps<TInit>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface URLField {
|
|
||||||
href:string,
|
|
||||||
target:string,
|
|
||||||
text:string
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
const renderHTML = (html) => {
|
|
||||||
if (!html) return { __html: "" };
|
|
||||||
return { __html: cleanHTML(html) };
|
|
||||||
}
|
|
||||||
|
|
||||||
const cleanHTML = (html) => {
|
|
||||||
if (!html) return ""
|
|
||||||
|
|
||||||
//fix '~' in links in HTML
|
|
||||||
return html.replace(/href="~\//gi, 'href="/')
|
|
||||||
}
|
|
||||||
|
|
||||||
const asyncForEach = async (array, callback) => {
|
|
||||||
for (let index = 0; index < array.length; index++) {
|
|
||||||
await callback(array[index], index, array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const expandContentList = async ({ agility, contentItems, languageCode, depth }) => {
|
|
||||||
|
|
||||||
asyncForEach(contentItems, async (contentItem) => {
|
|
||||||
await expandContentItem({agility, contentItem, languageCode, depth})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandContentItem = async ({ agility, contentItem, languageCode, depth = 1 }) => {
|
|
||||||
if (!contentItem) return null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const api = agility;
|
|
||||||
|
|
||||||
if (depth > 0) {
|
|
||||||
//make this work for the .fields or the .customFields property...
|
|
||||||
let fields = contentItem.fields;
|
|
||||||
if (!fields) fields = contentItem.customFields;
|
|
||||||
|
|
||||||
for (const fieldName in fields) {
|
|
||||||
const fieldValue = fields[fieldName];
|
|
||||||
|
|
||||||
if (fieldValue.contentid > 0) {
|
|
||||||
//single linked item
|
|
||||||
const childItem = await api.getContentItem({ contentID: fieldValue.contentid, languageCode, depth: depth - 1 });
|
|
||||||
if (childItem != null) fields[fieldName] = childItem;
|
|
||||||
} else if (fieldValue.sortids && fieldValue.sortids.split) {
|
|
||||||
//multi linked item
|
|
||||||
const sortIDAry = fieldValue.sortids.split(',');
|
|
||||||
const childItems = [];
|
|
||||||
for (const childItemID of sortIDAry) {
|
|
||||||
const childItem = await api.getContentItem({ contentID: childItemID, languageCode, depth: depth - 1 });
|
|
||||||
if (childItem != null) childItems.push(childItem);
|
|
||||||
}
|
|
||||||
fields[fieldName] = childItems;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contentItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandLinkedList = async ({ agility, contentItem, languageCode, fieldName, sortIDField }) => {
|
|
||||||
if (!contentItem) return null;
|
|
||||||
|
|
||||||
let fieldObj = contentItem.fields[fieldName]
|
|
||||||
if (! fieldObj) throw Error(`The field ${fieldName} was not found on the content item.`)
|
|
||||||
|
|
||||||
const referenceName = fieldObj.referencename
|
|
||||||
if (! referenceName) throw Error(`A referencename property was not found on the ${fieldName} value.`)
|
|
||||||
|
|
||||||
//grab the content list...
|
|
||||||
let listItems = await agility.getContentList({referenceName, languageCode})
|
|
||||||
if (listItems?.length > 0) {
|
|
||||||
let sortIDs = contentItem.fields[sortIDField]
|
|
||||||
if (sortIDs?.length > 0 && sortIDs?.split) {
|
|
||||||
//if we have sort ids, assemble the values in the list based on those...
|
|
||||||
const sortIDAry = sortIDs.split(',');
|
|
||||||
const sortedItems = [];
|
|
||||||
for (const idStr of sortIDAry) {
|
|
||||||
const childContentID = parseInt(idStr)
|
|
||||||
|
|
||||||
const childItemIndex = listItems.findIndex(item => item.contentID === childContentID)
|
|
||||||
if (childItemIndex >= 0) {
|
|
||||||
sortedItems.push(listItems[childItemIndex]);
|
|
||||||
listItems.splice(childItemIndex, 1)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listItems = sortedItems.concat(listItems)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem.fields[fieldName] = listItems;
|
|
||||||
return contentItem;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
renderHTML,
|
|
||||||
cleanHTML,
|
|
||||||
asyncForEach,
|
|
||||||
expandContentItem,
|
|
||||||
expandContentList,
|
|
||||||
expandLinkedList
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
import type { GetStaticPropsContext } from 'next'
|
|
||||||
import { getConfig } from '@framework/api'
|
|
||||||
import getAllPages from '@framework/api/operations/get-all-pages'
|
|
||||||
import { Layout } from '@components/common'
|
|
||||||
import { Container } from '@components/ui'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
|
||||||
preview,
|
|
||||||
locale,
|
|
||||||
}: GetStaticPropsContext) {
|
|
||||||
const config = getConfig({ locale })
|
|
||||||
const { pages } = await getAllPages({ config, preview })
|
|
||||||
return {
|
|
||||||
props: { pages },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Blog() {
|
|
||||||
return (
|
|
||||||
<div className="pb-20">
|
|
||||||
<div className="text-center pt-40 pb-56 bg-violet">
|
|
||||||
<Container>
|
|
||||||
<h2 className="text-4xl tracking-tight leading-10 font-extrabold text-white sm:text-5xl sm:leading-none md:text-6xl">
|
|
||||||
Welcome to Acme, the simplest way to start publishing with Next.js
|
|
||||||
</h2>
|
|
||||||
<p className="mt-3 max-w-md mx-auto text-gray-100 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl">
|
|
||||||
The Yeezy BOOST 350 V2 lineup continues to grow. We recently had the
|
|
||||||
‘Carbon’ iteration, and now release details have been locked in for
|
|
||||||
this ‘Natural’ joint. Revealed by Yeezy Mafia earlier this year, the
|
|
||||||
shoe was originally called ‘Abez’, which translated to ‘Tin’ in
|
|
||||||
Hebrew. It’s now undergone a name change, and will be referred to as
|
|
||||||
‘Natura`
|
|
||||||
</p>
|
|
||||||
<div className="mt-5 max-w-md mx-auto sm:flex sm:justify-center md:mt-12">
|
|
||||||
<div className="flex">
|
|
||||||
<div className="flex-shrink-0 inline-flex rounded-full border-2 border-white">
|
|
||||||
<img
|
|
||||||
className="h-12 w-12 rounded-full"
|
|
||||||
src="https://vercel.com/api/www/avatar/61182a9f6bda512b4d9263c9c8a60aabe0402f4c?s=204"
|
|
||||||
alt="Avatar"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="ml-4">
|
|
||||||
<div className="leading-6 font-medium text-white">
|
|
||||||
José Rodriguez
|
|
||||||
</div>
|
|
||||||
<div className="leading-6 font-medium text-gray-200">
|
|
||||||
CEO, Acme
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
<Container>
|
|
||||||
<div className="-mt-96 mx-auto">
|
|
||||||
<img src="/jacket.png" alt="Jacket" />
|
|
||||||
</div>
|
|
||||||
{/** Replace by HTML Content */}
|
|
||||||
<div className="text-lg leading-7 font-medium py-6 text-justify max-w-6xl mx-auto">
|
|
||||||
<p className="py-6">
|
|
||||||
Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
|
|
||||||
Candy canes bonbon dragée jujubes chocolate bar. Cotton candy gummi
|
|
||||||
bears toffee cake muffin caramels. Gummi bears danish liquorice ice
|
|
||||||
cream pie chocolate cake lemon drops tootsie roll tart. Biscuit
|
|
||||||
gingerbread fruitcake cake powder pudding cotton candy chocolate
|
|
||||||
bar. Sweet donut marshmallow powder gummies jelly tart powder.
|
|
||||||
Cheesecake bonbon caramels cupcake jujubes halvah donut dessert
|
|
||||||
chocolate bar. Jelly gummies liquorice lollipop chocolate bar
|
|
||||||
chocolate cake sugar plum. Lollipop toffee dragée chocolate bar
|
|
||||||
jelly beans biscuit. Halvah danish cheesecake. Tiramisu donut
|
|
||||||
lollipop pie donut caramels tiramisu. Jujubes candy canes pudding
|
|
||||||
danish fruitcake chupa chups jujubes carrot cake bonbon. Halvah
|
|
||||||
donut jelly halvah bonbon.
|
|
||||||
</p>
|
|
||||||
<p className="py-6">
|
|
||||||
Biscuit sugar plum sweet chocolate cake sesame snaps soufflé
|
|
||||||
topping. Gummies topping bonbon chocolate pudding cookie. Wafer
|
|
||||||
icing cake pastry. Gummies candy dessert chupa chups lemon drops.
|
|
||||||
Soufflé marshmallow oat cake chocolate jelly-o caramels pie marzipan
|
|
||||||
jelly beans. Cheesecake liquorice donut jujubes halvah ice cream
|
|
||||||
cotton candy cupcake sugar plum. Ice cream ice cream sweet roll
|
|
||||||
fruitcake icing. Muffin candy canes bonbon croissant gummies lemon
|
|
||||||
drops pie danish. Oat cake chocolate toffee cake jelly tart
|
|
||||||
caramels. Sweet donut cheesecake pastry pie sweet. Bonbon lollipop
|
|
||||||
brownie. Soufflé pudding macaroon cotton candy gingerbread. Biscuit
|
|
||||||
macaroon gummi bears candy canes chocolate cake lemon drops
|
|
||||||
marshmallow. Chocolate cake cotton candy marshmallow cake sweet
|
|
||||||
tootsie roll bonbon carrot cake sugar plum.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Blog.Layout = Layout
|
|
@ -1,152 +0,0 @@
|
|||||||
import rangeMap from '@lib/range-map'
|
|
||||||
import { Layout } from '@components/common'
|
|
||||||
import { ProductCard } from '@components/product'
|
|
||||||
import { Grid, Marquee, Hero } from '@components/ui'
|
|
||||||
import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid'
|
|
||||||
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
|
||||||
|
|
||||||
import { getConfig } from '@framework/api'
|
|
||||||
import getAllProducts from '@framework/api/operations/get-all-products'
|
|
||||||
import getSiteInfo from '@framework/api/operations/get-site-info'
|
|
||||||
import getAllPages from '@framework/api/operations/get-all-pages'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
|
||||||
preview,
|
|
||||||
locale,
|
|
||||||
}: GetStaticPropsContext) {
|
|
||||||
const config = getConfig({ locale })
|
|
||||||
|
|
||||||
// Get Featured Products
|
|
||||||
const { products: featuredProducts } = await getAllProducts({
|
|
||||||
variables: { field: 'featuredProducts', first: 6 },
|
|
||||||
config,
|
|
||||||
preview,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get Best Selling Products
|
|
||||||
const { products: bestSellingProducts } = await getAllProducts({
|
|
||||||
variables: { field: 'bestSellingProducts', first: 6 },
|
|
||||||
config,
|
|
||||||
preview,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get Best Newest Products
|
|
||||||
const { products: newestProducts } = await getAllProducts({
|
|
||||||
variables: { field: 'newestProducts', first: 12 },
|
|
||||||
config,
|
|
||||||
preview,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { categories, brands } = await getSiteInfo({ config, preview })
|
|
||||||
const { pages } = await getAllPages({ config, preview })
|
|
||||||
|
|
||||||
// These are the products that are going to be displayed in the landing.
|
|
||||||
// We prefer to do the computation at buildtime/servertime
|
|
||||||
const { featured, bestSelling } = (() => {
|
|
||||||
// Create a copy of products that we can mutate
|
|
||||||
const products = [...newestProducts]
|
|
||||||
// If the lists of featured and best selling products don't have enough
|
|
||||||
// products, then fill them with products from the products list, this
|
|
||||||
// is useful for new commerce sites that don't have a lot of products
|
|
||||||
return {
|
|
||||||
featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift())
|
|
||||||
.filter(nonNullable)
|
|
||||||
.sort((a, b) => a.node.prices.price.value - b.node.prices.price.value)
|
|
||||||
.reverse(),
|
|
||||||
bestSelling: rangeMap(
|
|
||||||
6,
|
|
||||||
(i) => bestSellingProducts[i] ?? products.shift()
|
|
||||||
).filter(nonNullable),
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
featured,
|
|
||||||
bestSelling,
|
|
||||||
newestProducts,
|
|
||||||
categories,
|
|
||||||
brands,
|
|
||||||
pages,
|
|
||||||
},
|
|
||||||
revalidate: 14400,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const nonNullable = (v: any) => v
|
|
||||||
|
|
||||||
export default function Home({
|
|
||||||
featured,
|
|
||||||
bestSelling,
|
|
||||||
brands,
|
|
||||||
categories,
|
|
||||||
newestProducts,
|
|
||||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Grid>
|
|
||||||
{featured.slice(0, 3).map(({ node }, i) => (
|
|
||||||
<ProductCard
|
|
||||||
key={node.path}
|
|
||||||
product={node}
|
|
||||||
imgWidth={i === 0 ? 1080 : 540}
|
|
||||||
imgHeight={i === 0 ? 1080 : 540}
|
|
||||||
imgPriority
|
|
||||||
imgLoading="eager"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
<Marquee variant="secondary">
|
|
||||||
{bestSelling.slice(3, 6).map(({ node }) => (
|
|
||||||
<ProductCard
|
|
||||||
key={node.path}
|
|
||||||
product={node}
|
|
||||||
variant="slim"
|
|
||||||
imgWidth={320}
|
|
||||||
imgHeight={320}
|
|
||||||
imgLayout="fixed"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Marquee>
|
|
||||||
<Hero
|
|
||||||
headline="Release Details: The Yeezy BOOST 350 V2 ‘Natural'"
|
|
||||||
description="
|
|
||||||
The Yeezy BOOST 350 V2 lineup continues to grow. We recently had the
|
|
||||||
‘Carbon’ iteration, and now release details have been locked in for
|
|
||||||
this ‘Natural’ joint. Revealed by Yeezy Mafia earlier this year, the
|
|
||||||
shoe was originally called ‘Abez’, which translated to ‘Tin’ in
|
|
||||||
Hebrew. It’s now undergone a name change, and will be referred to as
|
|
||||||
‘Natural’."
|
|
||||||
/>
|
|
||||||
<Grid layout="B">
|
|
||||||
{featured.slice(3, 6).map(({ node }, i) => (
|
|
||||||
<ProductCard
|
|
||||||
key={node.path}
|
|
||||||
product={node}
|
|
||||||
imgWidth={i === 1 ? 1080 : 540}
|
|
||||||
imgHeight={i === 1 ? 1080 : 540}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
<Marquee>
|
|
||||||
{bestSelling.slice(0, 3).map(({ node }) => (
|
|
||||||
<ProductCard
|
|
||||||
key={node.path}
|
|
||||||
product={node}
|
|
||||||
variant="slim"
|
|
||||||
imgWidth={320}
|
|
||||||
imgHeight={320}
|
|
||||||
imgLayout="fixed"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Marquee>
|
|
||||||
<HomeAllProductsGrid
|
|
||||||
categories={categories}
|
|
||||||
brands={brands}
|
|
||||||
newestProducts={newestProducts}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Home.Layout = Layout
|
|
Loading…
x
Reference in New Issue
Block a user