4
0
forked from crowetic/commerce

Improved types and added method for product

This commit is contained in:
Luis Alvarez 2020-10-01 14:53:29 -05:00
parent 2971b4f802
commit fdb306a988
5 changed files with 245 additions and 20 deletions

View File

@ -1,9 +1,9 @@
import { import type {
GetAllProductsQuery, GetAllProductsQuery,
GetAllProductsQueryVariables, GetAllProductsQueryVariables,
} from 'lib/bigcommerce/schema'; } from 'lib/bigcommerce/schema';
import type { RecursivePartial, RecursiveRequired } from '../types';
import { getConfig, Images, ProductImageVariables } from '..'; import { getConfig, Images, ProductImageVariables } from '..';
import { RecursivePartial } from '../types';
export const getAllProductsQuery = /* GraphQL */ ` export const getAllProductsQuery = /* GraphQL */ `
query getAllProducts( query getAllProducts(
@ -104,42 +104,45 @@ export const getAllProductsQuery = /* GraphQL */ `
export interface GetAllProductsResult<T> { export interface GetAllProductsResult<T> {
products: T extends GetAllProductsQuery products: T extends GetAllProductsQuery
? T['site']['products']['edges'] ? NonNullable<T['site']['products']['edges']>
: unknown; : unknown;
} }
export type ProductVariables = Images & export type ProductVariables = Images &
Omit<GetAllProductsQueryVariables, keyof ProductImageVariables>; Omit<GetAllProductsQueryVariables, keyof ProductImageVariables>;
async function getAllProducts<T, V = any>(opts: {
query: string;
variables?: V;
}): Promise<GetAllProductsResult<T>>;
async function getAllProducts(opts?: { async function getAllProducts(opts?: {
query?: string; query?: string;
variables?: ProductVariables; variables?: ProductVariables;
}): Promise<GetAllProductsResult<GetAllProductsQuery>>; }): Promise<GetAllProductsResult<GetAllProductsQuery>>;
async function getAllProducts<T, V = any>(opts: {
query: string;
variables?: V;
}): Promise<GetAllProductsResult<T>>;
async function getAllProducts({ async function getAllProducts({
query = getAllProductsQuery, query = getAllProductsQuery,
variables: vars, variables: vars,
}: { }: {
query?: string; query?: string;
variables?: ProductVariables; variables?: ProductVariables;
} = {}): Promise<GetAllProductsResult<RecursivePartial<GetAllProductsQuery>>> { } = {}): Promise<GetAllProductsResult<GetAllProductsQuery>> {
const config = getConfig(); const config = getConfig();
const variables: GetAllProductsQueryVariables = { const variables: GetAllProductsQueryVariables = {
...config.imageVariables, ...config.imageVariables,
...vars, ...vars,
}; };
// RecursivePartial forces the method to check for every prop in the data, which is
// required in case there's a custom `query`
const data = await config.fetch<RecursivePartial<GetAllProductsQuery>>( const data = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
query, query,
{ variables } { variables }
); );
const products = data.site?.products?.edges;
return { return {
products: data?.site?.products?.edges, products: (products as RecursiveRequired<typeof products>) ?? [],
}; };
} }

View File

@ -0,0 +1,131 @@
import type {
GetProductQuery,
GetProductQueryVariables,
} from 'lib/bigcommerce/schema';
import type { RecursivePartial, RecursiveRequired } from '../types';
import { getConfig, Images, ProductImageVariables } from '..';
export const getProductQuery = /* GraphQL */ `
query getProduct(
$slug: String!
$imgSmallWidth: Int = 320
$imgSmallHeight: Int
$imgMediumWidth: Int = 640
$imgMediumHeight: Int
$imgLargeWidth: Int = 960
$imgLargeHeight: Int
$imgXLWidth: Int = 1280
$imgXLHeight: Int
) {
site {
route(path: $slug) {
node {
__typename
... on Product {
entityId
name
path
brand {
name
}
description
prices {
price {
currencyCode
value
}
salePrice {
currencyCode
value
}
}
images {
edges {
node {
urlSmall: url(width: $imgSmallWidth, height: $imgSmallHeight)
urlMedium: url(
width: $imgMediumWidth
height: $imgMediumHeight
)
urlLarge: url(width: $imgLargeWidth, height: $imgLargeHeight)
urlXL: url(width: $imgXLWidth, height: $imgXLHeight)
}
}
}
variants {
edges {
node {
entityId
}
}
}
options {
edges {
node {
entityId
displayName
isRequired
values {
edges {
node {
entityId
label
}
}
}
}
}
}
}
}
}
}
}
`;
export interface GetProductResult<T> {
product?: T extends GetProductQuery
? Extract<T['site']['route']['node'], { __typename: 'Product' }>
: unknown;
}
export type ProductVariables = Images &
Omit<GetProductQueryVariables, keyof ProductImageVariables>;
async function getProduct(opts: {
query?: string;
variables: ProductVariables;
}): Promise<GetProductResult<GetProductQuery>>;
async function getProduct<T, V = any>(opts: {
query: string;
variables: V;
}): Promise<GetProductResult<T>>;
async function getProduct({
query = getProductQuery,
variables: vars,
}: {
query?: string;
variables: ProductVariables;
}): Promise<GetProductResult<GetProductQuery>> {
const config = getConfig();
const variables: GetProductQueryVariables = {
...config.imageVariables,
...vars,
};
const data = await config.fetch<RecursivePartial<GetProductQuery>>(query, {
variables,
});
const product = data.site?.route?.node;
if (product?.__typename === 'Product') {
return {
product: product as RecursiveRequired<typeof product>,
};
}
return {};
}
export default getProduct;

View File

@ -1,3 +1,7 @@
export type RecursivePartial<T> = { export type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>; [P in keyof T]?: RecursivePartial<T[P]>;
}; };
export type RecursiveRequired<T> = {
[P in keyof T]-?: RecursiveRequired<T[P]>;
};

View File

@ -1736,3 +1736,80 @@ export type GetAllProductsQuery = (
) } ) }
) } ) }
); );
export type GetProductQueryVariables = Exact<{
slug: Scalars['String'];
imgSmallWidth?: Maybe<Scalars['Int']>;
imgSmallHeight?: Maybe<Scalars['Int']>;
imgMediumWidth?: Maybe<Scalars['Int']>;
imgMediumHeight?: Maybe<Scalars['Int']>;
imgLargeWidth?: Maybe<Scalars['Int']>;
imgLargeHeight?: Maybe<Scalars['Int']>;
imgXLWidth?: Maybe<Scalars['Int']>;
imgXLHeight?: Maybe<Scalars['Int']>;
}>;
export type GetProductQuery = (
{ __typename?: 'Query' }
& { site: (
{ __typename?: 'Site' }
& { route: (
{ __typename?: 'Route' }
& { node?: Maybe<{ __typename: 'Brand' } | { __typename: 'Category' } | (
{ __typename: 'Product' }
& Pick<Product, 'entityId' | 'name' | 'path' | 'description'>
& { brand?: Maybe<(
{ __typename?: 'Brand' }
& Pick<Brand, 'name'>
)>, prices?: Maybe<(
{ __typename?: 'Prices' }
& { price: (
{ __typename?: 'Money' }
& Pick<Money, 'currencyCode' | 'value'>
), salePrice?: Maybe<(
{ __typename?: 'Money' }
& Pick<Money, 'currencyCode' | 'value'>
)> }
)>, images: (
{ __typename?: 'ImageConnection' }
& { edges?: Maybe<Array<Maybe<(
{ __typename?: 'ImageEdge' }
& { node: (
{ __typename?: 'Image' }
& { urlSmall: Image['url'], urlMedium: Image['url'], urlLarge: Image['url'], urlXL: Image['url'] }
) }
)>>> }
), variants: (
{ __typename?: 'VariantConnection' }
& { edges?: Maybe<Array<Maybe<(
{ __typename?: 'VariantEdge' }
& { node: (
{ __typename?: 'Variant' }
& Pick<Variant, 'entityId'>
) }
)>>> }
), options: (
{ __typename?: 'OptionConnection' }
& { edges?: Maybe<Array<Maybe<(
{ __typename?: 'OptionEdge' }
& { node: (
{ __typename?: 'ProductOption' }
& Pick<ProductOption, 'entityId' | 'displayName' | 'isRequired'>
& { values: (
{ __typename?: 'OptionValueConnection' }
& { edges?: Maybe<Array<Maybe<(
{ __typename?: 'OptionValueEdge' }
& { node: (
{ __typename?: 'ProductOptionValue' }
& Pick<ProductOptionValue, 'entityId' | 'label'>
) }
)>>> }
) }
) }
)>>> }
) }
) | { __typename: 'Variant' }> }
) }
) }
);

View File

@ -1,10 +1,18 @@
import { useRouter } from "next/router"; import { GetStaticPropsContext, InferGetStaticPropsType } from 'next';
import { Layout } from "@components/core"; import { useRouter } from 'next/router';
import { ProductView } from "@components/product"; import getProduct from 'lib/bigcommerce/api/operations/get-product';
import { Layout } from '@components/core';
import { ProductView } from '@components/product';
export async function getStaticProps({
params,
}: GetStaticPropsContext<{ slug: string }>) {
const { product } = await getProduct({ variables: { slug: params!.slug } });
console.log('PRODUCT', product);
export async function getStaticProps() {
const productData = { const productData = {
title: "T-Shirt", title: 'T-Shirt',
description: ` description: `
Nothing undercover about this tee. Nope. This is the official Bad Nothing undercover about this tee. Nope. This is the official Bad
Boys tee. Printed in white or black ink on Black, Brown, or Oatmeal. Boys tee. Printed in white or black ink on Black, Brown, or Oatmeal.
@ -13,9 +21,9 @@ export async function getStaticProps() {
run. Printing starts when the drop ends. Reminder: Bad Boys For run. Printing starts when the drop ends. Reminder: Bad Boys For
Life. Shipping may take 10+ days due to COVID-19. Life. Shipping may take 10+ days due to COVID-19.
`, `,
price: "$50", price: '$50',
colors: ["black", "white", "pink"], colors: ['black', 'white', 'pink'],
sizes: ["s", "m", "l", "xl", "xxl"], sizes: ['s', 'm', 'l', 'xl', 'xxl'],
}; };
return { return {
props: { props: {
@ -28,11 +36,13 @@ export async function getStaticProps() {
export async function getStaticPaths() { export async function getStaticPaths() {
return { return {
paths: [], paths: [],
fallback: "unstable_blocking", fallback: 'unstable_blocking',
}; };
} }
export default function Home({ productData }) { export default function Home({
productData,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const router = useRouter(); const router = useRouter();
return ( return (
<Layout> <Layout>