start moving over

This commit is contained in:
Jieren Chen 2024-09-08 12:01:49 -04:00
parent 694c5c17ba
commit 2c6e46b4d9
No known key found for this signature in database
GPG Key ID: 2FF322D21B5DB10B
9 changed files with 229 additions and 54 deletions

View File

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

View File

@ -1,4 +1,4 @@
import { getCollectionProducts } from 'lib/shopify';
import { getCollectionProducts } from 'lib/fourthwall';
import Link from 'next/link';
import { GridTileImage } from './grid/tile';

View File

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

View File

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

View File

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

View File

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