bug fixes

This commit is contained in:
Joel Varty 2021-06-18 13:31:02 -04:00
parent 52f2cccc11
commit a88b52b00a
13 changed files with 44 additions and 1059 deletions

View File

@ -1,6 +1,6 @@
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) {
console.error(`Page object or template was not found.`)

View File

@ -16,8 +16,8 @@ const HeroModule:Module<Fields> = ({ module: {fields }}) => {
<Hero
headline={fields.title}
description={fields.description}
ctaText={fields.cTA?.text}
ctaUrl={fields.cTA?.href}
ctaText={fields.cTA?.text || ""}
ctaUrl={fields.cTA?.href || ""}
/>
)
}

View File

@ -1,11 +1,12 @@
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 { 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 {
@ -15,35 +16,41 @@ interface Fields {
}
const Wishlist: Module<Fields> = ({ module: { fields } }) => {
const { data, isEmpty } = useWishlist({ includeProducts: true })
return (
<Container>
<div className="mt-3 mb-20">
<Text variant="pageHeading">{fields.heading}</Text>
<div className="group flex flex-col">
{isEmpty ? (
<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">
<Heart className="absolute" />
</span>
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
{fields.emptyMessage}
</h2>
<p className="text-accents-6 px-10 text-center pt-2">
{fields.addItemsMessage}
</p>
</div>
) : (
data &&
data.items?.map((item) => (
<WishlistCard key={item.id} item={item} />
))
)}
</div>
</div>
</Container>
)
const { data: customer } = useCustomer()
// @ts-ignore Shopify - Fix this types
const { data, isLoading, isEmpty } = useWishlist({ includeProducts: true })
return (
<Container>
<div className="mt-3 mb-20">
<Text variant="pageHeading">My Wishlist</Text>
<div className="group flex flex-col">
{isLoading || isEmpty ? (
<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">
<Heart className="absolute" />
</span>
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
Your wishlist is empty
</h2>
<p className="text-accent-6 px-10 text-center pt-2">
Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
</p>
</div>
) : (
data &&
// @ts-ignore Shopify - Fix this types
data.items?.map((item) => (
<WishlistCard key={item.id} product={item.product! as any} />
))
)}
</div>
</div>
</Container>
)
}
export default Wishlist

View File

@ -1,5 +1,3 @@
import { FC } from "react"
import * as AgilityTypes from "@agility/types"
import RichTextArea from "./RichTextArea"
import BestsellingProducts from "./BestsellingProducts"
import ProductDetails from "./ProductDetails"

View File

@ -13,7 +13,7 @@ import useAddItem from '@framework/cart/use-add-item'
import useRemoveItem from '@framework/wishlist/use-remove-item'
interface Props {
product: Product
product: Product | any
}
const placeholderImg = '/product-img-placeholder.svg'

View File

@ -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
};

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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. Its 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

View File

@ -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. Its 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