mirror of
https://github.com/vercel/commerce.git
synced 2025-05-08 02:37:58 +00:00
start moving over
This commit is contained in:
parent
694c5c17ba
commit
2c6e46b4d9
@ -1,10 +1,11 @@
|
||||
import { getCollection, getCollectionProducts } from 'lib/shopify';
|
||||
import { getCollection } from 'lib/shopify';
|
||||
import { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
import Grid from 'components/grid';
|
||||
import ProductGridItems from 'components/layout/product-grid-items';
|
||||
import { defaultSort, sorting } from 'lib/constants';
|
||||
import { getCollectionProducts } from 'lib/fourthwall';
|
||||
|
||||
export async function generateMetadata({
|
||||
params
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getCollectionProducts } from 'lib/shopify';
|
||||
import { getCollectionProducts } from 'lib/fourthwall';
|
||||
import Link from 'next/link';
|
||||
import { GridTileImage } from './grid/tile';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { GridTileImage } from 'components/grid/tile';
|
||||
import { getCollectionProducts } from 'lib/shopify';
|
||||
import { getCollectionProducts } from 'lib/fourthwall';
|
||||
import type { Product } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import Link from 'next/link';
|
||||
|
||||
import FooterMenu from 'components/layout/footer-menu';
|
||||
import LogoSquare from 'components/logo-square';
|
||||
import { getMenu } from 'lib/shopify';
|
||||
import { getMenu } from 'lib/fourthwall';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const { COMPANY_NAME, SITE_NAME } = process.env;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import CartModal from 'components/cart/modal';
|
||||
import LogoSquare from 'components/logo-square';
|
||||
import { getMenu } from 'lib/shopify';
|
||||
import { getMenu } from 'lib/fourthwall';
|
||||
import { Menu } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
import { Suspense } from 'react';
|
||||
|
96
lib/fourthwall/index.ts
Normal file
96
lib/fourthwall/index.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { Menu, Product } from "lib/shopify/types";
|
||||
import { reshapeProducts } from "./reshape";
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
async function fourthwallGet<T>(url: string, options: RequestInit = {}): Promise<{ status: number; body: T }> {
|
||||
try {
|
||||
const result = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const body = await result.json();
|
||||
|
||||
return {
|
||||
status: result.status,
|
||||
body
|
||||
};
|
||||
} catch (e) {
|
||||
throw {
|
||||
error: e,
|
||||
url
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function fourthwallPost<T>(url: string, data: any, options: RequestInit = {}): Promise<{ status: number; body: T }> {
|
||||
try {
|
||||
const result = await fetch(url, {
|
||||
method: 'POST',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const body = await result.json();
|
||||
|
||||
return {
|
||||
status: result.status,
|
||||
body
|
||||
};
|
||||
} catch (e) {
|
||||
throw {
|
||||
error: e,
|
||||
url,
|
||||
data
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls
|
||||
*/
|
||||
|
||||
export async function getCollectionProducts({
|
||||
collection,
|
||||
reverse,
|
||||
sortKey
|
||||
}: {
|
||||
collection: string;
|
||||
reverse?: boolean;
|
||||
sortKey?: string;
|
||||
}): Promise<Product[]> {
|
||||
const res = await fourthwallGet<{results: any[]}>(`${process.env.FW_URL}/api/public/v1.0/collections/${collection}/products?secret=${process.env.FW_SECRET}`, {
|
||||
headers: {
|
||||
'X-ShopId': process.env.FW_SHOPID || ''
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.body.results) {
|
||||
console.log(`No collection found for \`${collection}\``);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
return reshapeProducts(res.body.results);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stubbed out
|
||||
*/
|
||||
export async function getMenu(handle: string): Promise<Menu[]> {
|
||||
return [];
|
||||
}
|
95
lib/fourthwall/reshape.ts
Normal file
95
lib/fourthwall/reshape.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { Image, Money, Product, ProductVariant } from "lib/shopify/types";
|
||||
import { FourthwallMoney, FourthwallProduct, FourthwallProductImage, FourthwallProductVariant } from "./types";
|
||||
|
||||
const DEFAULT_IMAGE: Image = {
|
||||
url: '',
|
||||
altText: '',
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
|
||||
export const reshapeProducts = (products: FourthwallProduct[]) => {
|
||||
const reshapedProducts = [];
|
||||
|
||||
for (const product of products) {
|
||||
if (product) {
|
||||
const reshapedProduct = reshapeProduct(product);
|
||||
|
||||
if (reshapedProduct) {
|
||||
reshapedProducts.push(reshapedProduct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reshapedProducts;
|
||||
};
|
||||
|
||||
const reshapeProduct = (product: FourthwallProduct): Product | undefined => {
|
||||
if (!product) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { images, variants, ...rest } = product;
|
||||
|
||||
const minPrice = Math.min(...variants.map((v) => v.unitPrice.value));
|
||||
const maxPrice = Math.max(...variants.map((v) => v.unitPrice.value));
|
||||
|
||||
const currencyCode = variants[0]?.unitPrice.currencyCode || 'USD';
|
||||
|
||||
return {
|
||||
...rest,
|
||||
handle: product.slug,
|
||||
title: product.name,
|
||||
descriptionHtml: product.description,
|
||||
description: product.description,
|
||||
images: reshapeImages(images, product.name),
|
||||
variants: reshapeVariants(variants),
|
||||
// stubbed out
|
||||
availableForSale: true,
|
||||
priceRange: {
|
||||
minVariantPrice: {
|
||||
amount: minPrice.toString(),
|
||||
currencyCode,
|
||||
},
|
||||
maxVariantPrice: {
|
||||
amount: maxPrice.toString(),
|
||||
currencyCode,
|
||||
}
|
||||
},
|
||||
options: [],
|
||||
featuredImage: reshapeImages(images, product.name)[0] || DEFAULT_IMAGE,
|
||||
seo: {
|
||||
title: product.name,
|
||||
description: product.description,
|
||||
},
|
||||
tags: [],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
const reshapeImages = (images: FourthwallProductImage[], productTitle: string): Image[] => {
|
||||
return images.map((image) => {
|
||||
const filename = image.url.match(/.*\/(.*)\..*/)?.[1];
|
||||
return {
|
||||
...image,
|
||||
altText: `${productTitle} - ${filename}`
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const reshapeVariants = (variants: FourthwallProductVariant[]): ProductVariant[] => {
|
||||
return variants.map((v) => ({
|
||||
id: v.id,
|
||||
title: v.name,
|
||||
availableForSale: true,
|
||||
selectedOptions: [],
|
||||
price: reshapeMoney(v.unitPrice),
|
||||
}))
|
||||
}
|
||||
|
||||
const reshapeMoney = (money: FourthwallMoney): Money => {
|
||||
return {
|
||||
amount: money.value.toString(),
|
||||
currencyCode: money.currencyCode
|
||||
};
|
||||
}
|
32
lib/fourthwall/types.ts
Normal file
32
lib/fourthwall/types.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export type FourthwallMoney = {
|
||||
value: number;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export type FourthwallProduct = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
|
||||
images: FourthwallProductImage[];
|
||||
variants: FourthwallProductVariant[];
|
||||
};
|
||||
|
||||
export type FourthwallProductImage = {
|
||||
id: string;
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type FourthwallProductVariant = {
|
||||
id: string;
|
||||
name: string;
|
||||
sku: string;
|
||||
unitPrice: FourthwallMoney;
|
||||
|
||||
images: FourthwallProductImage[];
|
||||
|
||||
// other attr
|
||||
};
|
@ -12,11 +12,9 @@ import {
|
||||
} from './mutations/cart';
|
||||
import { getCartQuery } from './queries/cart';
|
||||
import {
|
||||
getCollectionProductsQuery,
|
||||
getCollectionQuery,
|
||||
getCollectionsQuery
|
||||
} from './queries/collection';
|
||||
import { getMenuQuery } from './queries/menu';
|
||||
import { getPageQuery, getPagesQuery } from './queries/page';
|
||||
import {
|
||||
getProductQuery,
|
||||
@ -28,7 +26,6 @@ import {
|
||||
Collection,
|
||||
Connection,
|
||||
Image,
|
||||
Menu,
|
||||
Page,
|
||||
Product,
|
||||
ShopifyAddToCartOperation,
|
||||
@ -36,10 +33,8 @@ import {
|
||||
ShopifyCartOperation,
|
||||
ShopifyCollection,
|
||||
ShopifyCollectionOperation,
|
||||
ShopifyCollectionProductsOperation,
|
||||
ShopifyCollectionsOperation,
|
||||
ShopifyCreateCartOperation,
|
||||
ShopifyMenuOperation,
|
||||
ShopifyPageOperation,
|
||||
ShopifyPagesOperation,
|
||||
ShopifyProduct,
|
||||
@ -285,33 +280,6 @@ export async function getCollection(handle: string): Promise<Collection | undefi
|
||||
return reshapeCollection(res.body.data.collection);
|
||||
}
|
||||
|
||||
export async function getCollectionProducts({
|
||||
collection,
|
||||
reverse,
|
||||
sortKey
|
||||
}: {
|
||||
collection: string;
|
||||
reverse?: boolean;
|
||||
sortKey?: string;
|
||||
}): Promise<Product[]> {
|
||||
const res = await shopifyFetch<ShopifyCollectionProductsOperation>({
|
||||
query: getCollectionProductsQuery,
|
||||
tags: [TAGS.collections, TAGS.products],
|
||||
variables: {
|
||||
handle: collection,
|
||||
reverse,
|
||||
sortKey: sortKey === 'CREATED_AT' ? 'CREATED' : sortKey
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.body.data.collection) {
|
||||
console.log(`No collection found for \`${collection}\``);
|
||||
return [];
|
||||
}
|
||||
|
||||
return reshapeProducts(removeEdgesAndNodes(res.body.data.collection.products));
|
||||
}
|
||||
|
||||
export async function getCollections(): Promise<Collection[]> {
|
||||
const res = await shopifyFetch<ShopifyCollectionsOperation>({
|
||||
query: getCollectionsQuery,
|
||||
@ -340,23 +308,6 @@ export async function getCollections(): Promise<Collection[]> {
|
||||
return collections;
|
||||
}
|
||||
|
||||
export async function getMenu(handle: string): Promise<Menu[]> {
|
||||
const res = await shopifyFetch<ShopifyMenuOperation>({
|
||||
query: getMenuQuery,
|
||||
tags: [TAGS.collections],
|
||||
variables: {
|
||||
handle
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
res.body?.data?.menu?.items.map((item: { title: string; url: string }) => ({
|
||||
title: item.title,
|
||||
path: item.url.replace(domain, '').replace('/collections', '/search').replace('/pages', '')
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
|
||||
export async function getPage(handle: string): Promise<Page> {
|
||||
const res = await shopifyFetch<ShopifyPageOperation>({
|
||||
query: getPageQuery,
|
||||
|
Loading…
x
Reference in New Issue
Block a user