forked from crowetic/commerce
Merge branch 'master' of github.com:okbel/e-comm-example
This commit is contained in:
commit
97dd5abc30
24
codegen.json
Normal file
24
codegen.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema": {
|
||||||
|
"https://buybutton.store/graphql": {
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlYXQiOjE3NjcxMzkyMDAsInN1Yl90eXBlIjoyLCJ0b2tlbl90eXBlIjoxLCJjb3JzIjpbImh0dHBzOi8vZGV2ZWxvcGVyLmJpZ2NvbW1lcmNlLmNvbSJdLCJjaWQiOjEsImlhdCI6MTU3NjI1MzgyNCwic3ViIjoiM3dtZThrcWtrNjQwNzZueWljMGkzamk0NG5wajQ2byIsInNpZCI6OTk5MzMxNzg0LCJpc3MiOiJCQyJ9.Rqt6hNI2W-XSOzHl4pqtfhAOygwka6atCIaIZ_WAa9v3dOctnBlZpBV5wzd3ICCy4sTCOZ9mJwcFH5_CHmJpNQ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"documents": [
|
||||||
|
{
|
||||||
|
"./lib/bigcommerce/api/queries/**/*.ts": {
|
||||||
|
"noRequire": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"generates": {
|
||||||
|
"./lib/bigcommerce/schema.d.ts": {
|
||||||
|
"plugins": ["typescript", "typescript-operations"]
|
||||||
|
},
|
||||||
|
"./lib/bigcommerce/schema.graphql": {
|
||||||
|
"plugins": ["schema-ast"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
lib/bigcommerce/api/index.ts
Normal file
52
lib/bigcommerce/api/index.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
CommerceAPI,
|
||||||
|
CommerceAPIOptions,
|
||||||
|
CommerceAPIFetchOptions,
|
||||||
|
} from 'lib/commerce/api';
|
||||||
|
import { GetAllProductsQuery } from '../schema';
|
||||||
|
import { getAllProductsQuery } from './operations/get-all-products';
|
||||||
|
|
||||||
|
type RecursivePartial<T> = {
|
||||||
|
[P in keyof T]?: RecursivePartial<T[P]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class BigcommerceAPI implements CommerceAPI {
|
||||||
|
commerceUrl: string;
|
||||||
|
apiToken: string;
|
||||||
|
|
||||||
|
constructor({ commerceUrl, apiToken }: CommerceAPIOptions) {
|
||||||
|
this.commerceUrl = commerceUrl;
|
||||||
|
this.apiToken = apiToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch<T>(
|
||||||
|
query: string,
|
||||||
|
{ variables, preview }: CommerceAPIFetchOptions = {}
|
||||||
|
): Promise<T> {
|
||||||
|
const res = await fetch(this.commerceUrl + (preview ? '/preview' : ''), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${this.apiToken}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = await res.json();
|
||||||
|
if (json.errors) {
|
||||||
|
console.error(json.errors);
|
||||||
|
throw new Error('Failed to fetch API');
|
||||||
|
}
|
||||||
|
return json.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllProducts<T = GetAllProductsQuery>(
|
||||||
|
query: string = getAllProductsQuery
|
||||||
|
): Promise<T> {
|
||||||
|
const data = await this.fetch<RecursivePartial<GetAllProductsQuery>>(query);
|
||||||
|
return data as T;
|
||||||
|
}
|
||||||
|
}
|
79
lib/bigcommerce/api/operations/get-all-products.ts
Normal file
79
lib/bigcommerce/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
export const responsiveImageFragment = /* GraphQL */ `
|
||||||
|
fragment responsiveImage on Image {
|
||||||
|
url320wide: url(width: 320)
|
||||||
|
url640wide: url(width: 640)
|
||||||
|
url960wide: url(width: 960)
|
||||||
|
url1280wide: url(width: 1280)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const getAllProductsQuery = /* GraphQL */ `
|
||||||
|
query getAllProducts {
|
||||||
|
site {
|
||||||
|
products(first: 4) {
|
||||||
|
pageInfo {
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
cursor
|
||||||
|
node {
|
||||||
|
entityId
|
||||||
|
name
|
||||||
|
path
|
||||||
|
brand {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
description
|
||||||
|
prices {
|
||||||
|
price {
|
||||||
|
value
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
salePrice {
|
||||||
|
value
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
images {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...responsiveImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variants {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
entityId
|
||||||
|
defaultImage {
|
||||||
|
...responsiveImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
entityId
|
||||||
|
displayName
|
||||||
|
isRequired
|
||||||
|
values {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
entityId
|
||||||
|
label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${responsiveImageFragment}
|
||||||
|
`;
|
@ -1,8 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
CartProvider as CommerceCartProvider,
|
CartProvider as CommerceCartProvider,
|
||||||
useCart as useCommerceCart,
|
useCart as useCommerceCart,
|
||||||
} from '../commerce/cart';
|
} from 'lib/commerce/cart';
|
||||||
import { Cart } from './index';
|
|
||||||
|
export type Cart = any;
|
||||||
|
|
||||||
export function CartProvider({ children }) {
|
export function CartProvider({ children }) {
|
||||||
return <CommerceCartProvider>{children}</CommerceCartProvider>;
|
return <CommerceCartProvider>{children}</CommerceCartProvider>;
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
CommerceProvider,
|
CommerceProvider as CoreCommerceProvider,
|
||||||
Connector,
|
Connector,
|
||||||
HookResolver,
|
useCommerce as useCoreCommerce,
|
||||||
useCommerce as useComm,
|
} from 'lib/commerce';
|
||||||
} from '../commerce';
|
|
||||||
|
|
||||||
export type Cart = any;
|
|
||||||
|
|
||||||
async function getText(res: Response) {
|
async function getText(res: Response) {
|
||||||
try {
|
try {
|
||||||
@ -23,11 +20,7 @@ async function getError(res: Response) {
|
|||||||
return { message: await getText(res) };
|
return { message: await getText(res) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetcher(
|
async function fetcher(url: string, query: string) {
|
||||||
url: string,
|
|
||||||
query: string,
|
|
||||||
resolver: HookResolver<Cart>
|
|
||||||
) {
|
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@ -37,42 +30,18 @@ async function fetcher(
|
|||||||
throw await getError(res);
|
throw await getError(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bigcommerce: Connector<Cart> = {
|
export const bigcommerce: Connector = {
|
||||||
hooks: {
|
|
||||||
useCart: {
|
|
||||||
query: '',
|
|
||||||
resolver() {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
useAddItem: {
|
|
||||||
query: '',
|
|
||||||
resolver() {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
useUpdateItem: {
|
|
||||||
query: '',
|
|
||||||
resolver() {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
useRemoveItem: {
|
|
||||||
query: '',
|
|
||||||
resolver() {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
locale: 'en-us',
|
locale: 'en-us',
|
||||||
fetcher,
|
fetcher,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: The connector should be extendable when a developer is using it
|
// TODO: The connector should be extendable when a developer is using it
|
||||||
export function BigcommerceProvider({ children }) {
|
export function CommerceProvider({ children }) {
|
||||||
return (
|
return (
|
||||||
<CommerceProvider connector={bigcommerce}>{children}</CommerceProvider>
|
<CoreCommerceProvider connector={bigcommerce}>
|
||||||
|
{children}
|
||||||
|
</CoreCommerceProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCommerce = () => useComm<Cart>();
|
export const useCommerce = () => useCoreCommerce();
|
||||||
|
1733
lib/bigcommerce/schema.d.ts
vendored
Normal file
1733
lib/bigcommerce/schema.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1529
lib/bigcommerce/schema.graphql
Normal file
1529
lib/bigcommerce/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
24
lib/commerce/api/index.ts
Normal file
24
lib/commerce/api/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export interface CommerceAPIOptions {
|
||||||
|
commerceUrl: string;
|
||||||
|
apiToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommerceAPIFetchOptions {
|
||||||
|
variables?: object;
|
||||||
|
preview?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommerceAPI {
|
||||||
|
commerceUrl: string;
|
||||||
|
apiToken: string;
|
||||||
|
|
||||||
|
fetch<T>(query: string, queryData?: CommerceAPIFetchOptions): Promise<T>;
|
||||||
|
|
||||||
|
getAllProducts(query: string): Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export default class CommerceAPI {
|
||||||
|
// getAllProducts(query: string) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
@ -1,6 +1,8 @@
|
|||||||
import { createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
import useSWR, { responseInterface } from 'swr';
|
import useSWR, { responseInterface } from 'swr';
|
||||||
import { Cart, useCommerce } from '.';
|
import { useCommerce } from '.';
|
||||||
|
|
||||||
|
export type Cart = any;
|
||||||
|
|
||||||
export type CartResponse<C extends Cart> = responseInterface<C, Error> & {
|
export type CartResponse<C extends Cart> = responseInterface<C, Error> & {
|
||||||
isEmpty: boolean;
|
isEmpty: boolean;
|
||||||
|
@ -1,44 +1,20 @@
|
|||||||
import { createContext, ReactNode, useContext } from 'react';
|
import { createContext, ReactNode, useContext } from 'react';
|
||||||
|
|
||||||
const Commerce = createContext<Connector<any>>(null);
|
const Commerce = createContext<Connector>(null);
|
||||||
|
|
||||||
export type Cart = any;
|
export type CommerceProps = {
|
||||||
|
|
||||||
export type CommerceProps<C extends Cart> = {
|
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
connector: Connector<C>;
|
connector: Connector;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Connector<C extends Cart> = {
|
export type Connector = {
|
||||||
hooks: {
|
|
||||||
useCart: Hook<C>;
|
|
||||||
useAddItem: Hook<C>;
|
|
||||||
useUpdateItem: Hook<C>;
|
|
||||||
useRemoveItem: Hook<C>;
|
|
||||||
};
|
|
||||||
fetcher: Fetcher<any>;
|
fetcher: Fetcher<any>;
|
||||||
locale: string;
|
locale: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Hook<T extends any> = {
|
|
||||||
query?: string;
|
|
||||||
url?: string;
|
|
||||||
resolver: HookResolver<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HookResolver<T> = (
|
|
||||||
fetcher: Fetcher<T>,
|
|
||||||
context: ResolverContext
|
|
||||||
) => T | Promise<T>;
|
|
||||||
|
|
||||||
export type Fetcher<T> = (...args: any) => T | Promise<T>;
|
export type Fetcher<T> = (...args: any) => T | Promise<T>;
|
||||||
|
|
||||||
export type ResolverContext = {
|
export function CommerceProvider({ children, connector }: CommerceProps) {
|
||||||
query?: string;
|
|
||||||
locale: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CommerceProvider({ children, connector }: CommerceProps<Cart>) {
|
|
||||||
if (!connector) {
|
if (!connector) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'CommerceProvider requires a valid headless commerce connector'
|
'CommerceProvider requires a valid headless commerce connector'
|
||||||
@ -48,6 +24,6 @@ export function CommerceProvider({ children, connector }: CommerceProps<Cart>) {
|
|||||||
return <Commerce.Provider value={connector}>{children}</Commerce.Provider>;
|
return <Commerce.Provider value={connector}>{children}</Commerce.Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useCommerce<C extends Cart>() {
|
export function useCommerce<T extends Connector>() {
|
||||||
return useContext(Commerce) as Connector<C>;
|
return useContext(Commerce) as T;
|
||||||
}
|
}
|
||||||
|
18
package.json
18
package.json
@ -5,7 +5,12 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start",
|
||||||
|
"generate": "graphql-codegen"
|
||||||
|
},
|
||||||
|
"graphql": {
|
||||||
|
"schema": "lib/bigcommerce/schema.graphql",
|
||||||
|
"documents": "lib/bigcommerce/**/*.{graphql,js,ts,jsx,tsx}"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/ui": "^0.6.2",
|
"@tailwindcss/ui": "^0.6.2",
|
||||||
@ -18,15 +23,20 @@
|
|||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"swr": "^0.3.3"
|
"swr": "^0.3.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
|
||||||
"webpack": "^5.0.0-beta.30"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@graphql-codegen/cli": "^1.17.10",
|
||||||
|
"@graphql-codegen/schema-ast": "^1.17.8",
|
||||||
|
"@graphql-codegen/typescript": "^1.17.10",
|
||||||
|
"@graphql-codegen/typescript-operations": "^1.17.8",
|
||||||
"@types/node": "^14.11.2",
|
"@types/node": "^14.11.2",
|
||||||
"@types/react": "^16.9.49",
|
"@types/react": "^16.9.49",
|
||||||
|
"graphql": "^15.3.0",
|
||||||
"postcss-flexbugs-fixes": "^4.2.1",
|
"postcss-flexbugs-fixes": "^4.2.1",
|
||||||
"postcss-preset-env": "^6.7.0",
|
"postcss-preset-env": "^6.7.0",
|
||||||
"tailwindcss": "^1.8.10",
|
"tailwindcss": "^1.8.10",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"webpack": "^5.0.0-beta.30"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user