This commit is contained in:
Kristian Duda 2024-06-28 08:16:08 +02:00
parent b94e46d796
commit 8b1218fc35
4 changed files with 60 additions and 115 deletions

View File

@ -1,6 +1,6 @@
import { HIDDEN_PRODUCT_TAG, SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants'; import { SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants';
import { find } from 'lib/shopify/payload'; import { find, findByID } from 'lib/shopify/payload';
import { Media, Option, Product } from 'lib/shopify/payload-types'; import { Media, Option, Product, Tag } from 'lib/shopify/payload-types';
import { isShopifyError } from 'lib/type-guards'; import { isShopifyError } from 'lib/type-guards';
import { ensureStartsWith } from 'lib/utils'; import { ensureStartsWith } from 'lib/utils';
import { revalidateTag } from 'next/cache'; import { revalidateTag } from 'next/cache';
@ -15,11 +15,6 @@ import {
import { getCartQuery } from './queries/cart'; import { getCartQuery } from './queries/cart';
import { getCollectionQuery, getCollectionsQuery } from './queries/collection'; import { getCollectionQuery, getCollectionsQuery } from './queries/collection';
import { getPageQuery, getPagesQuery } from './queries/page'; import { getPageQuery, getPagesQuery } from './queries/page';
import {
getProductQuery,
getProductRecommendationsQuery,
getProductsQuery
} from './queries/product';
import { import {
Cart, Cart,
Collection, Collection,
@ -30,6 +25,7 @@ import {
Money, Money,
Page, Page,
ProductOption, ProductOption,
ProductVariant,
ShopifyAddToCartOperation, ShopifyAddToCartOperation,
ShopifyCart, ShopifyCart,
ShopifyCartOperation, ShopifyCartOperation,
@ -39,10 +35,6 @@ import {
ShopifyCreateCartOperation, ShopifyCreateCartOperation,
ShopifyPageOperation, ShopifyPageOperation,
ShopifyPagesOperation, ShopifyPagesOperation,
ShopifyProduct,
ShopifyProductOperation,
ShopifyProductRecommendationsOperation,
ShopifyProductsOperation,
ShopifyRemoveFromCartOperation, ShopifyRemoveFromCartOperation,
ShopifyUpdateCartOperation ShopifyUpdateCartOperation
} from './types'; } from './types';
@ -156,48 +148,6 @@ const reshapeCollections = (collections: ShopifyCollection[]) => {
return reshapedCollections; return reshapedCollections;
}; };
const reshapeImages = (images: Connection<Image>, productTitle: string) => {
const flattened = removeEdgesAndNodes(images);
return flattened.map((image) => {
const filename = image.url.match(/.*\/(.*)\..*/)[1];
return {
...image,
altText: image.altText || `${productTitle} - ${filename}`
};
});
};
const reshapeProduct = (product: ShopifyProduct, filterHiddenProducts: boolean = true) => {
if (!product || (filterHiddenProducts && product.tags.includes(HIDDEN_PRODUCT_TAG))) {
return undefined;
}
const { images, variants, ...rest } = product;
return {
...rest,
images: reshapeImages(images, product.title),
variants: removeEdgesAndNodes(variants)
};
};
const reshapeProducts = (products: ShopifyProduct[]) => {
const reshapedProducts = [];
for (const product of products) {
if (product) {
const reshapedProduct = reshapeProduct(product);
if (reshapedProduct) {
reshapedProducts.push(reshapedProduct);
}
}
}
return reshapedProducts;
};
export async function createCart(): Promise<Cart> { export async function createCart(): Promise<Cart> {
const res = await shopifyFetch<ShopifyCreateCartOperation>({ const res = await shopifyFetch<ShopifyCreateCartOperation>({
query: createCartMutation, query: createCartMutation,
@ -282,7 +232,9 @@ export async function getCollection(handle: string): Promise<Collection | undefi
const reshapeImage = (media: Media): Image => { const reshapeImage = (media: Media): Image => {
return { return {
url: media.url!, url: media.url!,
altText: media.alt altText: media.alt,
width: media.width,
height: media.height
}; };
}; };
@ -293,24 +245,49 @@ type Price = {
const reshapePrice = (price: Price): Money => { const reshapePrice = (price: Price): Money => {
return { return {
amount: (price.amount / 100).toString(), amount: price.amount.toString(),
currencyCode: price.currencyCode currencyCode: price.currencyCode
}; };
}; };
const reshapeP = (product: Product): ExProduct => { const reshapeOptions = (variants: Product['variants']): ProductOption[] => {
const options: ProductOption[] = []; const options = new Map<string, Option>();
const map = new Map();
product.variants.forEach((variant) => { variants.forEach((variant) => {
variant.selectedOptions?.forEach((selectedOption) => { variant.selectedOptions?.forEach((selectedOption) => {
const option = selectedOption.option as Option; const option = selectedOption.option as Option;
map.set(option.id, option.values); options.set(option.id, option);
}); });
}); });
// console.log(map); return Array.from(options, ([id, option]) => ({
id,
name: option.name,
values: option.values.map((value) => value.label)
}));
};
const reshapeVariants = (variants: Product['variants']): ProductVariant[] => {
return variants.map((variant) => ({
id: variant.id!,
title: `${variant.price.amount} ${variant.price.currencyCode}`,
availableForSale: true,
selectedOptions: (variant.selectedOptions ?? []).map((selectedOption) => {
const option = selectedOption.option as Option;
return {
name: option.name,
value: option.values.find(({ value }) => value === selectedOption.value)?.label!
};
}),
price: reshapePrice(variant.price)
}));
};
const reshapeTags = (tags: Tag[]): string[] => {
return tags.map((tag) => tag.name);
};
const reshapeProduct = (product: Product): ExProduct => {
return { return {
id: product.id, id: product.id,
handle: product.id, handle: product.id,
@ -318,20 +295,20 @@ const reshapeP = (product: Product): ExProduct => {
title: product.title, title: product.title,
description: product.description, description: product.description,
descriptionHtml: product.description, descriptionHtml: product.description,
options, options: reshapeOptions(product.variants),
priceRange: { priceRange: {
maxVariantPrice: reshapePrice(product.variants[0]?.price!), maxVariantPrice: reshapePrice(product.variants[0]?.price!),
minVariantPrice: reshapePrice(product.variants[0]?.price!) minVariantPrice: reshapePrice(product.variants[0]?.price!)
}, },
featuredImage: {} as any, featuredImage: reshapeImage(product.media as Media),
images: [], images: [],
seo: { seo: {
title: product.title, title: product.title,
description: product.description description: product.description
}, },
// tags: product.tags ?? [], tags: reshapeTags(product.tags as Tag[]),
updatedAt: product.updatedAt, variants: reshapeVariants(product.variants),
createdAt: product.createdAt updatedAt: product.updatedAt
}; };
}; };
@ -344,19 +321,8 @@ export async function getCollectionProducts({
reverse?: boolean; reverse?: boolean;
sortKey?: string; sortKey?: string;
}): Promise<ExProduct[]> { }): Promise<ExProduct[]> {
const m = await find<Product>('products', { const products = await find<Product>('products', {});
where: { return products.docs.map(reshapeProduct);
title: {
equals: 'test'
}
}
});
const products: ExProduct[] = m.docs.map(reshapeP);
console.log(products);
return products;
} }
export async function getCollections(): Promise<Collection[]> { export async function getCollections(): Promise<Collection[]> {
@ -425,27 +391,12 @@ export async function getPages(): Promise<Page[]> {
} }
export async function getProduct(handle: string): Promise<ExProduct | undefined> { export async function getProduct(handle: string): Promise<ExProduct | undefined> {
const res = await shopifyFetch<ShopifyProductOperation>({ const product = await findByID<Product>('products', handle);
query: getProductQuery, return reshapeProduct(product);
tags: [TAGS.products],
variables: {
handle
}
});
return reshapeProduct(res.body.data.product, false);
} }
export async function getProductRecommendations(productId: string): Promise<ExProduct[]> { export async function getProductRecommendations(productId: string): Promise<ExProduct[]> {
const res = await shopifyFetch<ShopifyProductRecommendationsOperation>({ return [];
query: getProductRecommendationsQuery,
tags: [TAGS.products],
variables: {
productId
}
});
return reshapeProducts(res.body.data.productRecommendations);
} }
export async function getProducts({ export async function getProducts({
@ -457,17 +408,8 @@ export async function getProducts({
reverse?: boolean; reverse?: boolean;
sortKey?: string; sortKey?: string;
}): Promise<ExProduct[]> { }): Promise<ExProduct[]> {
const res = await shopifyFetch<ShopifyProductsOperation>({ const products = await find<Product>('products', {});
query: getProductsQuery, return products.docs.map(reshapeProduct);
tags: [TAGS.products],
variables: {
query,
reverse,
sortKey
}
});
return reshapeProducts(removeEdgesAndNodes(res.body.data.products));
} }
// This is called from `app/api/revalidate.ts` so providers can control revalidation logic. // This is called from `app/api/revalidate.ts` so providers can control revalidation logic.

View File

@ -59,3 +59,8 @@ export const find = <T>(collection: string, params: FindParams) => {
const url = `${process.env.CMS_URL}/api/${collection}${query}`; const url = `${process.env.CMS_URL}/api/${collection}${query}`;
return ajax<PaginatedDocs<T>>('GET', url); return ajax<PaginatedDocs<T>>('GET', url);
}; };
export const findByID = <T>(collection: string, id: string) => {
const url = `${process.env.CMS_URL}/api/${collection}/${id}`;
return ajax<T>('GET', url);
};

View File

@ -36,8 +36,8 @@ export type Collection = ShopifyCollection & {
export type Image = { export type Image = {
url: string; url: string;
altText: string; altText: string;
width?: number; width?: number | null;
height?: number; height?: number | null;
}; };
export type Menu = { export type Menu = {

View File

@ -5,12 +5,10 @@ module.exports = {
ignoreDuringBuilds: true ignoreDuringBuilds: true
}, },
images: { images: {
formats: ['image/avif', 'image/webp'],
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: 'http',
hostname: 'cdn.shopify.com', hostname: 'localhost'
pathname: '/s/files/**'
} }
] ]
}, },