mirror of
https://github.com/vercel/commerce.git
synced 2025-05-14 13:47:49 +00:00
cleanup types
This commit is contained in:
parent
27b11abe1e
commit
f6652da4ab
@ -1,6 +1,6 @@
|
|||||||
import { GridTileImage } from 'components/grid/tile';
|
import { GridTileImage } from 'components/grid/tile';
|
||||||
import { getCollectionProducts } from 'lib/fourthwall';
|
import { getCollectionProducts } from 'lib/fourthwall';
|
||||||
import type { Product } from 'lib/shopify/types';
|
import type { Product } from 'lib/types';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
function ThreeItemGridItem({
|
function ThreeItemGridItem({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Cart, CartItem, Image, Money, Product, ProductVariant } from "lib/shopify/types";
|
import { Cart, CartItem, Image, Money, Product, ProductVariant } from "lib/types";
|
||||||
import { FourthwallCart, FourthwallCartItem, FourthwallMoney, FourthwallProduct, FourthwallProductImage, FourthwallProductVariant } from "./types";
|
import { FourthwallCart, FourthwallCartItem, FourthwallMoney, FourthwallProduct, FourthwallProductImage, FourthwallProductVariant } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +41,6 @@ export type FourthwallProductVariant = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type FourthwallCart = {
|
export type FourthwallCart = {
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
items: FourthwallCartItem[];
|
items: FourthwallCartItem[];
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import productFragment from './product';
|
|
||||||
|
|
||||||
const cartFragment = /* GraphQL */ `
|
|
||||||
fragment cart on Cart {
|
|
||||||
id
|
|
||||||
checkoutUrl
|
|
||||||
cost {
|
|
||||||
subtotalAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
totalAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
totalTaxAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines(first: 100) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
quantity
|
|
||||||
cost {
|
|
||||||
totalAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
merchandise {
|
|
||||||
... on ProductVariant {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
selectedOptions {
|
|
||||||
name
|
|
||||||
value
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
...product
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalQuantity
|
|
||||||
}
|
|
||||||
${productFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default cartFragment;
|
|
@ -1,10 +0,0 @@
|
|||||||
const imageFragment = /* GraphQL */ `
|
|
||||||
fragment image on Image {
|
|
||||||
url
|
|
||||||
altText
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default imageFragment;
|
|
@ -1,64 +0,0 @@
|
|||||||
import imageFragment from './image';
|
|
||||||
import seoFragment from './seo';
|
|
||||||
|
|
||||||
const productFragment = /* GraphQL */ `
|
|
||||||
fragment product on Product {
|
|
||||||
id
|
|
||||||
handle
|
|
||||||
availableForSale
|
|
||||||
title
|
|
||||||
description
|
|
||||||
descriptionHtml
|
|
||||||
options {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
values
|
|
||||||
}
|
|
||||||
priceRange {
|
|
||||||
maxVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
minVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
variants(first: 250) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
availableForSale
|
|
||||||
selectedOptions {
|
|
||||||
name
|
|
||||||
value
|
|
||||||
}
|
|
||||||
price {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
featuredImage {
|
|
||||||
...image
|
|
||||||
}
|
|
||||||
images(first: 20) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seo {
|
|
||||||
...seo
|
|
||||||
}
|
|
||||||
tags
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
${imageFragment}
|
|
||||||
${seoFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default productFragment;
|
|
@ -1,8 +0,0 @@
|
|||||||
const seoFragment = /* GraphQL */ `
|
|
||||||
fragment seo on SEO {
|
|
||||||
description
|
|
||||||
title
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default seoFragment;
|
|
@ -1,278 +0,0 @@
|
|||||||
import { HIDDEN_PRODUCT_TAG, SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants';
|
|
||||||
import { isShopifyError } from 'lib/type-guards';
|
|
||||||
import { ensureStartsWith } from 'lib/utils';
|
|
||||||
import { revalidateTag } from 'next/cache';
|
|
||||||
import { headers } from 'next/headers';
|
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import {
|
|
||||||
getCollectionQuery,
|
|
||||||
getCollectionsQuery
|
|
||||||
} from './queries/collection';
|
|
||||||
import { getPageQuery, getPagesQuery } from './queries/page';
|
|
||||||
import {
|
|
||||||
getProductsQuery
|
|
||||||
} from './queries/product';
|
|
||||||
import {
|
|
||||||
Collection,
|
|
||||||
Connection,
|
|
||||||
Image,
|
|
||||||
Page,
|
|
||||||
Product,
|
|
||||||
ShopifyCollection,
|
|
||||||
ShopifyCollectionOperation,
|
|
||||||
ShopifyCollectionsOperation,
|
|
||||||
ShopifyPageOperation,
|
|
||||||
ShopifyPagesOperation,
|
|
||||||
ShopifyProduct,
|
|
||||||
ShopifyProductsOperation
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
const domain = process.env.SHOPIFY_STORE_DOMAIN
|
|
||||||
? ensureStartsWith(process.env.SHOPIFY_STORE_DOMAIN, 'https://')
|
|
||||||
: '';
|
|
||||||
const endpoint = `${domain}${SHOPIFY_GRAPHQL_API_ENDPOINT}`;
|
|
||||||
const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!;
|
|
||||||
|
|
||||||
type ExtractVariables<T> = T extends { variables: object } ? T['variables'] : never;
|
|
||||||
|
|
||||||
export async function shopifyFetch<T>({
|
|
||||||
cache = 'force-cache',
|
|
||||||
headers,
|
|
||||||
query,
|
|
||||||
tags,
|
|
||||||
variables
|
|
||||||
}: {
|
|
||||||
cache?: RequestCache;
|
|
||||||
headers?: HeadersInit;
|
|
||||||
query: string;
|
|
||||||
tags?: string[];
|
|
||||||
variables?: ExtractVariables<T>;
|
|
||||||
}): Promise<{ status: number; body: T } | never> {
|
|
||||||
try {
|
|
||||||
const result = await fetch(endpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Shopify-Storefront-Access-Token': key,
|
|
||||||
...headers
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
...(query && { query }),
|
|
||||||
...(variables && { variables })
|
|
||||||
}),
|
|
||||||
cache,
|
|
||||||
...(tags && { next: { tags } })
|
|
||||||
});
|
|
||||||
|
|
||||||
const body = await result.json();
|
|
||||||
|
|
||||||
if (body.errors) {
|
|
||||||
throw body.errors[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: result.status,
|
|
||||||
body
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
if (isShopifyError(e)) {
|
|
||||||
throw {
|
|
||||||
cause: e.cause?.toString() || 'unknown',
|
|
||||||
status: e.status || 500,
|
|
||||||
message: e.message,
|
|
||||||
query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw {
|
|
||||||
error: e,
|
|
||||||
query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeEdgesAndNodes = <T>(array: Connection<T>): T[] => {
|
|
||||||
return array.edges.map((edge) => edge?.node);
|
|
||||||
};
|
|
||||||
|
|
||||||
const reshapeCollection = (collection: ShopifyCollection): Collection | undefined => {
|
|
||||||
if (!collection) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...collection,
|
|
||||||
path: `/search/${collection.handle}`
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const reshapeCollections = (collections: ShopifyCollection[]) => {
|
|
||||||
const reshapedCollections = [];
|
|
||||||
|
|
||||||
for (const collection of collections) {
|
|
||||||
if (collection) {
|
|
||||||
const reshapedCollection = reshapeCollection(collection);
|
|
||||||
|
|
||||||
if (reshapedCollection) {
|
|
||||||
reshapedCollections.push(reshapedCollection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 getCollection(handle: string): Promise<Collection | undefined> {
|
|
||||||
const res = await shopifyFetch<ShopifyCollectionOperation>({
|
|
||||||
query: getCollectionQuery,
|
|
||||||
tags: [TAGS.collections],
|
|
||||||
variables: {
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return reshapeCollection(res.body.data.collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCollections(): Promise<Collection[]> {
|
|
||||||
const res = await shopifyFetch<ShopifyCollectionsOperation>({
|
|
||||||
query: getCollectionsQuery,
|
|
||||||
tags: [TAGS.collections]
|
|
||||||
});
|
|
||||||
const shopifyCollections = removeEdgesAndNodes(res.body?.data?.collections);
|
|
||||||
const collections = [
|
|
||||||
{
|
|
||||||
handle: '',
|
|
||||||
title: 'All',
|
|
||||||
description: 'All products',
|
|
||||||
seo: {
|
|
||||||
title: 'All',
|
|
||||||
description: 'All products'
|
|
||||||
},
|
|
||||||
path: '/search',
|
|
||||||
updatedAt: new Date().toISOString()
|
|
||||||
},
|
|
||||||
// Filter out the `hidden` collections.
|
|
||||||
// Collections that start with `hidden-*` need to be hidden on the search page.
|
|
||||||
...reshapeCollections(shopifyCollections).filter(
|
|
||||||
(collection) => !collection.handle.startsWith('hidden')
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
return collections;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPage(handle: string): Promise<Page> {
|
|
||||||
const res = await shopifyFetch<ShopifyPageOperation>({
|
|
||||||
query: getPageQuery,
|
|
||||||
cache: 'no-store',
|
|
||||||
variables: { handle }
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.body.data.pageByHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPages(): Promise<Page[]> {
|
|
||||||
const res = await shopifyFetch<ShopifyPagesOperation>({
|
|
||||||
query: getPagesQuery,
|
|
||||||
cache: 'no-store'
|
|
||||||
});
|
|
||||||
|
|
||||||
return removeEdgesAndNodes(res.body.data.pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getProducts({
|
|
||||||
query,
|
|
||||||
reverse,
|
|
||||||
sortKey
|
|
||||||
}: {
|
|
||||||
query?: string;
|
|
||||||
reverse?: boolean;
|
|
||||||
sortKey?: string;
|
|
||||||
}): Promise<Product[]> {
|
|
||||||
const res = await shopifyFetch<ShopifyProductsOperation>({
|
|
||||||
query: getProductsQuery,
|
|
||||||
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.
|
|
||||||
export async function revalidate(req: NextRequest): Promise<NextResponse> {
|
|
||||||
// We always need to respond with a 200 status code to Shopify,
|
|
||||||
// otherwise it will continue to retry the request.
|
|
||||||
const collectionWebhooks = ['collections/create', 'collections/delete', 'collections/update'];
|
|
||||||
const productWebhooks = ['products/create', 'products/delete', 'products/update'];
|
|
||||||
const topic = headers().get('x-shopify-topic') || 'unknown';
|
|
||||||
const secret = req.nextUrl.searchParams.get('secret');
|
|
||||||
const isCollectionUpdate = collectionWebhooks.includes(topic);
|
|
||||||
const isProductUpdate = productWebhooks.includes(topic);
|
|
||||||
|
|
||||||
if (!secret || secret !== process.env.SHOPIFY_REVALIDATION_SECRET) {
|
|
||||||
console.error('Invalid revalidation secret.');
|
|
||||||
return NextResponse.json({ status: 200 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isCollectionUpdate && !isProductUpdate) {
|
|
||||||
// We don't need to revalidate anything for any other topics.
|
|
||||||
return NextResponse.json({ status: 200 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCollectionUpdate) {
|
|
||||||
revalidateTag(TAGS.collections);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isProductUpdate) {
|
|
||||||
revalidateTag(TAGS.products);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({ status: 200, revalidated: true, now: Date.now() });
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import cartFragment from '../fragments/cart';
|
|
||||||
|
|
||||||
export const addToCartMutation = /* GraphQL */ `
|
|
||||||
mutation addToCart($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
||||||
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
||||||
cart {
|
|
||||||
...cart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const createCartMutation = /* GraphQL */ `
|
|
||||||
mutation createCart($lineItems: [CartLineInput!]) {
|
|
||||||
cartCreate(input: { lines: $lineItems }) {
|
|
||||||
cart {
|
|
||||||
...cart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const editCartItemsMutation = /* GraphQL */ `
|
|
||||||
mutation editCartItems($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
||||||
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
||||||
cart {
|
|
||||||
...cart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const removeFromCartMutation = /* GraphQL */ `
|
|
||||||
mutation removeFromCart($cartId: ID!, $lineIds: [ID!]!) {
|
|
||||||
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
||||||
cart {
|
|
||||||
...cart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartFragment}
|
|
||||||
`;
|
|
@ -1,10 +0,0 @@
|
|||||||
import cartFragment from '../fragments/cart';
|
|
||||||
|
|
||||||
export const getCartQuery = /* GraphQL */ `
|
|
||||||
query getCart($cartId: ID!) {
|
|
||||||
cart(id: $cartId) {
|
|
||||||
...cart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartFragment}
|
|
||||||
`;
|
|
@ -1,56 +0,0 @@
|
|||||||
import productFragment from '../fragments/product';
|
|
||||||
import seoFragment from '../fragments/seo';
|
|
||||||
|
|
||||||
const collectionFragment = /* GraphQL */ `
|
|
||||||
fragment collection on Collection {
|
|
||||||
handle
|
|
||||||
title
|
|
||||||
description
|
|
||||||
seo {
|
|
||||||
...seo
|
|
||||||
}
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
${seoFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getCollectionQuery = /* GraphQL */ `
|
|
||||||
query getCollection($handle: String!) {
|
|
||||||
collection(handle: $handle) {
|
|
||||||
...collection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${collectionFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getCollectionsQuery = /* GraphQL */ `
|
|
||||||
query getCollections {
|
|
||||||
collections(first: 100, sortKey: TITLE) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...collection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${collectionFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getCollectionProductsQuery = /* GraphQL */ `
|
|
||||||
query getCollectionProducts(
|
|
||||||
$handle: String!
|
|
||||||
$sortKey: ProductCollectionSortKeys
|
|
||||||
$reverse: Boolean
|
|
||||||
) {
|
|
||||||
collection(handle: $handle) {
|
|
||||||
products(sortKey: $sortKey, reverse: $reverse, first: 100) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...product
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${productFragment}
|
|
||||||
`;
|
|
@ -1,10 +0,0 @@
|
|||||||
export const getMenuQuery = /* GraphQL */ `
|
|
||||||
query getMenu($handle: String!) {
|
|
||||||
menu(handle: $handle) {
|
|
||||||
items {
|
|
||||||
title
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
@ -1,41 +0,0 @@
|
|||||||
import seoFragment from '../fragments/seo';
|
|
||||||
|
|
||||||
const pageFragment = /* GraphQL */ `
|
|
||||||
fragment page on Page {
|
|
||||||
... on Page {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
handle
|
|
||||||
body
|
|
||||||
bodySummary
|
|
||||||
seo {
|
|
||||||
...seo
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${seoFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getPageQuery = /* GraphQL */ `
|
|
||||||
query getPage($handle: String!) {
|
|
||||||
pageByHandle(handle: $handle) {
|
|
||||||
...page
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${pageFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getPagesQuery = /* GraphQL */ `
|
|
||||||
query getPages {
|
|
||||||
pages(first: 100) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...page
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${pageFragment}
|
|
||||||
`;
|
|
@ -1,32 +0,0 @@
|
|||||||
import productFragment from '../fragments/product';
|
|
||||||
|
|
||||||
export const getProductQuery = /* GraphQL */ `
|
|
||||||
query getProduct($handle: String!) {
|
|
||||||
product(handle: $handle) {
|
|
||||||
...product
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${productFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getProductsQuery = /* GraphQL */ `
|
|
||||||
query getProducts($sortKey: ProductSortKeys, $reverse: Boolean, $query: String) {
|
|
||||||
products(sortKey: $sortKey, reverse: $reverse, query: $query, first: 100) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...product
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${productFragment}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const getProductRecommendationsQuery = /* GraphQL */ `
|
|
||||||
query getProductRecommendations($productId: ID!) {
|
|
||||||
productRecommendations(productId: $productId) {
|
|
||||||
...product
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${productFragment}
|
|
||||||
`;
|
|
@ -1,273 +0,0 @@
|
|||||||
export type Maybe<T> = T | null;
|
|
||||||
|
|
||||||
export type Connection<T> = {
|
|
||||||
edges: Array<Edge<T>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Edge<T> = {
|
|
||||||
node: T;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Cart = Omit<ShopifyCart, 'lines'> & {
|
|
||||||
lines: CartItem[];
|
|
||||||
currency: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CartProduct = {
|
|
||||||
id: string;
|
|
||||||
handle: string;
|
|
||||||
title: string;
|
|
||||||
featuredImage: Image;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CartItem = {
|
|
||||||
id: string | undefined;
|
|
||||||
quantity: number;
|
|
||||||
cost: {
|
|
||||||
totalAmount: Money;
|
|
||||||
};
|
|
||||||
merchandise: {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
selectedOptions: {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
product: CartProduct;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Collection = ShopifyCollection & {
|
|
||||||
path: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Image = {
|
|
||||||
url: string;
|
|
||||||
altText: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Menu = {
|
|
||||||
title: string;
|
|
||||||
path: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Money = {
|
|
||||||
amount: string;
|
|
||||||
currencyCode: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Page = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
handle: string;
|
|
||||||
body: string;
|
|
||||||
bodySummary: string;
|
|
||||||
seo?: SEO;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Product = Omit<ShopifyProduct, 'variants' | 'images'> & {
|
|
||||||
variants: ProductVariant[];
|
|
||||||
images: Image[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProductOption = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
values: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProductVariant = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
availableForSale: boolean;
|
|
||||||
selectedOptions: {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
price: Money;
|
|
||||||
images: Image[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SEO = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCart = {
|
|
||||||
id: string | undefined;
|
|
||||||
checkoutUrl: string;
|
|
||||||
cost: {
|
|
||||||
subtotalAmount: Money;
|
|
||||||
totalAmount: Money;
|
|
||||||
};
|
|
||||||
lines: Connection<CartItem>;
|
|
||||||
totalQuantity: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCollection = {
|
|
||||||
handle: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
seo: SEO;
|
|
||||||
updatedAt: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyProduct = {
|
|
||||||
id: string;
|
|
||||||
handle: string;
|
|
||||||
availableForSale: boolean;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
descriptionHtml: string;
|
|
||||||
options: ProductOption[];
|
|
||||||
priceRange: {
|
|
||||||
maxVariantPrice: Money;
|
|
||||||
minVariantPrice: Money;
|
|
||||||
};
|
|
||||||
variants: Connection<ProductVariant>;
|
|
||||||
featuredImage: Image;
|
|
||||||
images: Connection<Image>;
|
|
||||||
seo: SEO;
|
|
||||||
tags: string[];
|
|
||||||
updatedAt: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCartOperation = {
|
|
||||||
data: {
|
|
||||||
cart: ShopifyCart;
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
cartId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCreateCartOperation = {
|
|
||||||
data: { cartCreate: { cart: ShopifyCart } };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyAddToCartOperation = {
|
|
||||||
data: {
|
|
||||||
cartLinesAdd: {
|
|
||||||
cart: ShopifyCart;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
cartId: string;
|
|
||||||
lines: {
|
|
||||||
merchandiseId: string;
|
|
||||||
quantity: number;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyRemoveFromCartOperation = {
|
|
||||||
data: {
|
|
||||||
cartLinesRemove: {
|
|
||||||
cart: ShopifyCart;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
cartId: string;
|
|
||||||
lineIds: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyUpdateCartOperation = {
|
|
||||||
data: {
|
|
||||||
cartLinesUpdate: {
|
|
||||||
cart: ShopifyCart;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
cartId: string;
|
|
||||||
lines: {
|
|
||||||
id: string;
|
|
||||||
merchandiseId: string;
|
|
||||||
quantity: number;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCollectionOperation = {
|
|
||||||
data: {
|
|
||||||
collection: ShopifyCollection;
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
handle: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCollectionProductsOperation = {
|
|
||||||
data: {
|
|
||||||
collection: {
|
|
||||||
products: Connection<ShopifyProduct>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
handle: string;
|
|
||||||
reverse?: boolean;
|
|
||||||
sortKey?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyCollectionsOperation = {
|
|
||||||
data: {
|
|
||||||
collections: Connection<ShopifyCollection>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyMenuOperation = {
|
|
||||||
data: {
|
|
||||||
menu?: {
|
|
||||||
items: {
|
|
||||||
title: string;
|
|
||||||
url: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
handle: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyPageOperation = {
|
|
||||||
data: { pageByHandle: Page };
|
|
||||||
variables: { handle: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyPagesOperation = {
|
|
||||||
data: {
|
|
||||||
pages: Connection<Page>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyProductOperation = {
|
|
||||||
data: { product: ShopifyProduct };
|
|
||||||
variables: {
|
|
||||||
handle: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyProductRecommendationsOperation = {
|
|
||||||
data: {
|
|
||||||
productRecommendations: ShopifyProduct[];
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
productId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ShopifyProductsOperation = {
|
|
||||||
data: {
|
|
||||||
products: Connection<ShopifyProduct>;
|
|
||||||
};
|
|
||||||
variables: {
|
|
||||||
query?: string;
|
|
||||||
reverse?: boolean;
|
|
||||||
sortKey?: string;
|
|
||||||
};
|
|
||||||
};
|
|
125
lib/types.ts
Normal file
125
lib/types.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
export type Maybe<T> = T | null;
|
||||||
|
|
||||||
|
export type Connection<T> = {
|
||||||
|
edges: Array<Edge<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Edge<T> = {
|
||||||
|
node: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Cart = {
|
||||||
|
id: string | undefined;
|
||||||
|
checkoutUrl: string;
|
||||||
|
cost: {
|
||||||
|
subtotalAmount: Money;
|
||||||
|
totalAmount: Money;
|
||||||
|
};
|
||||||
|
totalQuantity: number;
|
||||||
|
lines: CartItem[];
|
||||||
|
currency: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CartProduct = {
|
||||||
|
id: string;
|
||||||
|
handle: string;
|
||||||
|
title: string;
|
||||||
|
featuredImage: Image;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CartItem = {
|
||||||
|
id: string | undefined;
|
||||||
|
quantity: number;
|
||||||
|
cost: {
|
||||||
|
totalAmount: Money;
|
||||||
|
};
|
||||||
|
merchandise: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
selectedOptions: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
product: CartProduct;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Collection = {
|
||||||
|
handle: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
seo: SEO;
|
||||||
|
updatedAt: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Image = {
|
||||||
|
url: string;
|
||||||
|
altText: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Menu = {
|
||||||
|
title: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Money = {
|
||||||
|
amount: string;
|
||||||
|
currencyCode: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Page = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
handle: string;
|
||||||
|
body: string;
|
||||||
|
bodySummary: string;
|
||||||
|
seo?: SEO;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Product = {
|
||||||
|
id: string;
|
||||||
|
handle: string;
|
||||||
|
availableForSale: boolean;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
descriptionHtml: string;
|
||||||
|
options: ProductOption[];
|
||||||
|
priceRange: {
|
||||||
|
maxVariantPrice: Money;
|
||||||
|
minVariantPrice: Money;
|
||||||
|
};
|
||||||
|
featuredImage: Image;
|
||||||
|
seo: SEO;
|
||||||
|
tags: string[];
|
||||||
|
updatedAt: string;
|
||||||
|
variants: ProductVariant[];
|
||||||
|
images: Image[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProductOption = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
values: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProductVariant = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
availableForSale: boolean;
|
||||||
|
selectedOptions: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
price: Money;
|
||||||
|
images: Image[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SEO = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user