From 895fd8eadff89e75008ab70a36747bebbc9e8cd7 Mon Sep 17 00:00:00 2001 From: Sherika Date: Wed, 18 Dec 2024 15:45:21 -0500 Subject: [PATCH 1/4] Work in progress for shopify ui --- src/utils/shopify.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/utils/shopify.js diff --git a/src/utils/shopify.js b/src/utils/shopify.js new file mode 100644 index 00000000..c6eefb6a --- /dev/null +++ b/src/utils/shopify.js @@ -0,0 +1,40 @@ +export async function shopifyFetch({ query, variables }) { + const endpoint = `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2023-10/graphql.json`; + const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN; + + if (!endpoint == !key) { + throw new Error("Shopify Store domain or access token is missing."); + } + + const body = JSON.stringify({ query, variables }); + + try { + const result = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Shopify-Storefront-Access-Token': key, + }, + body, + }); + + if (!result.ok) { + const errorDetails = await result.text(); + throw new Error(`Shopify API error: ${result.status} - ${errorDetails}`); + } + + const responseBody = await result.json(); + + return { + status: result.status, + body: responseBody, + } + } catch (error) { + console.error('Error:', error); + return { + status: 500, + error: error.message || 'Error receiving data', + }; + } + +} \ No newline at end of file From 56ee18fbabac0bf22c7f89145a259b2468dbb4bd Mon Sep 17 00:00:00 2001 From: Sherika Date: Fri, 20 Dec 2024 12:56:25 -0500 Subject: [PATCH 2/4] Create ProductCard component and Shopify data fetching utilities --- src/components/product-card/ProductCard.tsx | 45 +++++++++++++++++++++ src/utils/shopify.js | 20 ++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/components/product-card/ProductCard.tsx diff --git a/src/components/product-card/ProductCard.tsx b/src/components/product-card/ProductCard.tsx new file mode 100644 index 00000000..5fed6eaa --- /dev/null +++ b/src/components/product-card/ProductCard.tsx @@ -0,0 +1,45 @@ +// components/ProductCard.tsx +import Link from 'next/link'; + +type ProductCardProps = { + id: string; + title: string; + description: string; + image: string; + price: string; + path: string; +}; + +const ProductCard = ({ id, title, description, image, price, path }: ProductCardProps) => { + return ( +
+ +
+ {title} +
+ + +
+

{title}

+

{description}

+ + +

{price}

+ + + + View Product + + +
+
+ ); +}; + +export default ProductCard; diff --git a/src/utils/shopify.js b/src/utils/shopify.js index c6eefb6a..246e8e0a 100644 --- a/src/utils/shopify.js +++ b/src/utils/shopify.js @@ -35,6 +35,22 @@ export async function shopifyFetch({ query, variables }) { status: 500, error: error.message || 'Error receiving data', }; - } + } -} \ No newline at end of file +} + +export async function getAllProducts() { + return shopifyFetch({ + query: `{ + products(sortKey: TITLE, first: 100) { + edges{ + node { + id + title + description + } + } + } + }` + }); + } \ No newline at end of file From 9e4185302eced6ac73f813d5a2e50cb70e2566bf Mon Sep 17 00:00:00 2001 From: Sherika Date: Sat, 28 Dec 2024 17:54:28 -0500 Subject: [PATCH 3/4] Implementing shopify into next.js --- env.local | 3 + next-env.d.ts | 2 +- src/components/product-card/ProductCard.tsx | 27 +++---- src/data/menu.ts | 2 +- src/pages/shop/index.js | 58 +++++++++++++++ src/utils/shopify.js | 82 +++++++++------------ tsconfig.json | 2 +- 7 files changed, 108 insertions(+), 68 deletions(-) create mode 100644 src/pages/shop/index.js diff --git a/env.local b/env.local index 11827198..baabe636 100644 --- a/env.local +++ b/env.local @@ -1,2 +1,5 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-WSXY307CRR NEXT_PUBLIC_GOOGLE_MAPS_KEY=AIzaSyB3mMuvl8IUlviRZiizBiX7uhsdIqunx94 + +SHOPIFY_STOREFRONT_ACCESS_TOKEN=3b70a0fb51e102381b458316d9fe2c8d +SHOPIFY_STORE_DOMAIN=hashflag.shop \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts index 52e831b4..a4a7b3f5 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/src/components/product-card/ProductCard.tsx b/src/components/product-card/ProductCard.tsx index 5fed6eaa..75d9fe2a 100644 --- a/src/components/product-card/ProductCard.tsx +++ b/src/components/product-card/ProductCard.tsx @@ -1,4 +1,3 @@ -// components/ProductCard.tsx import Link from 'next/link'; type ProductCardProps = { @@ -12,30 +11,24 @@ type ProductCardProps = { const ProductCard = ({ id, title, description, image, price, path }: ProductCardProps) => { return ( -
- -
+
+
{title}
- -
-

{title}

-

{description}

- - -

{price}

+
+

{title}

+

{description}

+

{price}

- +
diff --git a/src/data/menu.ts b/src/data/menu.ts index 2c155513..e5ec8ba2 100644 --- a/src/data/menu.ts +++ b/src/data/menu.ts @@ -102,7 +102,7 @@ const navigation: NavigationItem[] = [ { id: 62, label: "Shop", - path: "https://hashflag.shop/", + path: "/shop", external: true, }, { diff --git a/src/pages/shop/index.js b/src/pages/shop/index.js new file mode 100644 index 00000000..26ea96dc --- /dev/null +++ b/src/pages/shop/index.js @@ -0,0 +1,58 @@ +import ProductCard from '../../components/product-card/ProductCard'; +import { getAllProducts } from '../../utils/shopify'; // Import from your utility file + +export default function Home({ products, error }) { + if (error) { + return
Error: {error}
; + } + + if (!products || products.length === 0) { // Check for null or empty array + return
No products found.
; + } + + return ( +
+

Our Products

+
+ {products.map((product) => ( + // Spread props for cleaner code + ))} +
+
+ ); +} + +export async function getStaticProps() { + try { + const response = await getAllProducts(); + + if (response.status !== 200 || !response.body.data || !response.body.data.products || !response.body.data.products.edges) { + console.error("Invalid response from Shopify API:", response); + throw new Error("Failed to fetch products from Shopify."); // Throw an error + } + + const products = response.body.data.products.edges.map(({ node }) => { + if (!node || !node.id || !node.title) { + console.warn("Incomplete product data:", node); // Warn about incomplete data + return null; // Skip this product + } + return { + id: node.id, + title: node.title, + description: node.description || "", // Provide default values + price: node.priceRange?.minVariantPrice?.amount || "0.00", + image: node.featuredImage?.src || '/default-product-image.jpg', + }; + }).filter(product => product !== null); // Remove null products + + return { + props: { products }, + revalidate: 60, + }; + } catch (error) { + console.error("Error in getStaticProps:", error); + return { + props: { products: [], error: error.message }, // Return an error prop + }; + } +} \ No newline at end of file diff --git a/src/utils/shopify.js b/src/utils/shopify.js index 246e8e0a..0a82803e 100644 --- a/src/utils/shopify.js +++ b/src/utils/shopify.js @@ -1,56 +1,42 @@ export async function shopifyFetch({ query, variables }) { - const endpoint = `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2023-10/graphql.json`; - const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN; + const endpoint = process.env.SHOPIFY_STORE_DOMAIN; + const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN; - if (!endpoint == !key) { - throw new Error("Shopify Store domain or access token is missing."); - } - - const body = JSON.stringify({ query, variables }); - - try { - const result = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Shopify-Storefront-Access-Token': key, - }, - body, - }); - - if (!result.ok) { - const errorDetails = await result.text(); - throw new Error(`Shopify API error: ${result.status} - ${errorDetails}`); - } - - const responseBody = await result.json(); + try { + const result = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Shopify-Storefront-Access-Token': key + }, + body: { query, variables } && JSON.stringify({ query, variables }) + }); - return { - status: result.status, - body: responseBody, - } - } catch (error) { - console.error('Error:', error); - return { - status: 500, - error: error.message || 'Error receiving data', - }; - } - + return { + status: result.status, + body: await result.json() + }; + } catch (error) { + console.error('Error:', error); + return { + status: 500, + error: 'Error receiving data' + }; + } } export async function getAllProducts() { - return shopifyFetch({ - query: `{ - products(sortKey: TITLE, first: 100) { - edges{ - node { - id - title - description - } + return shopifyFetch({ + query: `{ + products(sortKey: TITLE, first: 100) { + edges{ + node { + id + title + description } } - }` - }); - } \ No newline at end of file + } + }` + }); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 0d43c064..30723769 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,6 +45,6 @@ "postcss.config.js", "next-sitemap.config.js", "playwright.config.ts" - ], +, "src/pages/shop/index.js", "src/utils/shopify.js" ], "exclude": ["node_modules"] } \ No newline at end of file From 46dd1ecbe2825612d36c98bde028e69664449f08 Mon Sep 17 00:00:00 2001 From: Sherika Date: Mon, 6 Jan 2025 20:51:37 -0500 Subject: [PATCH 4/4] update 2 --- env.local | 7 +- next-env.d.ts | 2 +- package-lock.json | 32 +++ package.json | 5 +- .../product-card/ProductCard.module.css | 15 ++ src/components/product-card/ProductCard.tsx | 82 ++++--- .../product-details/product-details.js | 51 ++++ src/pages/shop/home.module.css | 16 ++ src/pages/shop/index.js | 75 +++--- src/utils/shopify.js | 220 +++++++++++++++--- 10 files changed, 393 insertions(+), 112 deletions(-) create mode 100644 src/components/product-card/ProductCard.module.css create mode 100644 src/components/product-details/product-details.js create mode 100644 src/pages/shop/home.module.css diff --git a/env.local b/env.local index baabe636..a729d9e1 100644 --- a/env.local +++ b/env.local @@ -1,5 +1,8 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-WSXY307CRR NEXT_PUBLIC_GOOGLE_MAPS_KEY=AIzaSyB3mMuvl8IUlviRZiizBiX7uhsdIqunx94 -SHOPIFY_STOREFRONT_ACCESS_TOKEN=3b70a0fb51e102381b458316d9fe2c8d -SHOPIFY_STORE_DOMAIN=hashflag.shop \ No newline at end of file +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=3b70a0fb51e102381b458316d9fe2c8d +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=https://hashflagswag.myshopify.com/api/2022-10/graphql.json + +SHOPIFY_STORE_DOMAIN=https://hashflagswag.myshopify.com/api/2022-10/graphql.json +SHOPIFY_STOREFRONT_ACCESS_TOKEN=3b70a0fb51e102381b458316d9fe2c8d \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts index a4a7b3f5..52e831b4 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/package-lock.json b/package-lock.json index d75e27b4..7a25c5a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "express": "^4.18.2", "fast-equals": "3.0.3", "framer-motion": "^6.5.1", + "graphql-request": "^7.1.2", "gray-matter": "^4.0.3", "lucide-react": "^0.378.0", "mailchimp-api-v3": "^1.15.0", @@ -2025,6 +2026,15 @@ "resolved": "https://registry.npmjs.org/@googlemaps/typescript-guards/-/typescript-guards-2.0.3.tgz", "integrity": "sha512-3iHuO8H0jPehftsMK0kgyJzPYU/g/oiTRw+wu/yltqSZ7wJPt3vfsJHkPiuRpQjbnnWygX+T3mkRGyK/eyZ/lw==" }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -10013,6 +10023,28 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.1.2.tgz", + "integrity": "sha512-+XE3iuC55C2di5ZUrB4pjgwe+nIQBuXVIK9J98wrVwojzDW3GMdSBZfxUk8l4j9TieIpjpggclxhNEU9ebGF8w==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", diff --git a/package.json b/package.json index d0245836..b96d5f40 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@ai-sdk/azure": "^1.0.7", "@googlemaps/react-wrapper": "^1.1.35", "@googlemaps/typescript-guards": "^2.0.1", + "@playwright/test": "^1.49.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^1.1.0", "@types/express": "^4.17.17", @@ -35,6 +36,7 @@ "express": "^4.18.2", "fast-equals": "3.0.3", "framer-motion": "^6.5.1", + "graphql-request": "^7.1.2", "gray-matter": "^4.0.3", "lucide-react": "^0.378.0", "mailchimp-api-v3": "^1.15.0", @@ -52,8 +54,7 @@ "react-simple-typewriter": "^3.0.1", "swiper": "^8.3.1", "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7", - "@playwright/test": "^1.49.0" + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@fullhuman/postcss-purgecss": "^4.1.3", diff --git a/src/components/product-card/ProductCard.module.css b/src/components/product-card/ProductCard.module.css new file mode 100644 index 00000000..e97b6892 --- /dev/null +++ b/src/components/product-card/ProductCard.module.css @@ -0,0 +1,15 @@ +.card { + padding: 1rem 1.2rem; +} + +.image { + width: 100%; + height: 600px; + position: relative; + cursor: pointer; +} +.content { + display: flex; + justify-content: space-between; + margin-top: 0.5rem; +} \ No newline at end of file diff --git a/src/components/product-card/ProductCard.tsx b/src/components/product-card/ProductCard.tsx index 75d9fe2a..4843619b 100644 --- a/src/components/product-card/ProductCard.tsx +++ b/src/components/product-card/ProductCard.tsx @@ -1,38 +1,62 @@ +import Image from 'next/image'; import Link from 'next/link'; +import styles from './ProductCard.module.css'; -type ProductCardProps = { - id: string; - title: string; - description: string; - image: string; - price: string; - path: string; -}; +interface ProductCardProps { + product: { + node: { + id: string; + title: string; + handle: string; + featuredImage?: { + url: string; + altText?: string; + }; + priceRange: { + minVariantPrice: { + amount: string; + }; + }; + }; + }; +} + +export default function ProductCard({ product }: ProductCardProps) { + + const { node } = product; -const ProductCard = ({ id, title, description, image, price, path }: ProductCardProps) => { return ( -
-
- {title +
+ +
+ {node.featuredImage?.url ? ( + {node.featuredImage.altText + ) : ( +
No Image Available
+ )}
-
-

{title}

-

{description}

-

{price}

- - - - + +
+ + + {node.title} + + + + {new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + }).format(parseFloat(node.priceRange.minVariantPrice.amount))} +
); -}; - -export default ProductCard; +} diff --git a/src/components/product-details/product-details.js b/src/components/product-details/product-details.js new file mode 100644 index 00000000..659a8987 --- /dev/null +++ b/src/components/product-details/product-details.js @@ -0,0 +1,51 @@ +import Image from "next/image"; +import Link from "next/link"; +import { useState } from "react"; +import styles from "./ProductDetails.module.css"; + +export default function ProductDetails({ product }) { + const [quantity, setQuantity] = useState(0); + const [checkout, setCheckout] = useState(false); + const updateQuantity = (e) => { + setQuantity(e.target.value); + if (quantity == 0) setCheckout(false); + }; + + const handleAddToCart = async () => { + let cartId = sessionStorage.getItem("cartId"); + if (quantity > 0) { + if (cartId) { + await updateCart(cartId, product.variants.edges[0].node.id, quantity); + setCheckout(true); + } else { + let data = await addToCart(product.variants.edges[0].node.id, quantity); + cartId = data.cartCreate.cart.id; + sessionStorage.setItem("cartId", cartId); + setCheckout(true); + } + } + }; + return ( + <> +
+
+ {product.featuredImage.altText} +
+
+ +

{product.title}

+

{product.priceRange.minVariantPrice.amount}

+
+ + +
+
+ + ); +} diff --git a/src/pages/shop/home.module.css b/src/pages/shop/home.module.css new file mode 100644 index 00000000..e00d2610 --- /dev/null +++ b/src/pages/shop/home.module.css @@ -0,0 +1,16 @@ +.products { + display: grid; + grid-template-columns: 1fr 1fr 1fr; +} +@media screen and (max-width: 992px) { + .products { + display: grid; + grid-template-columns: 1fr 1fr; + } +} +@media screen and (max-width: 600px) { + .products { + display: grid; + grid-template-columns: 1fr; + } +} \ No newline at end of file diff --git a/src/pages/shop/index.js b/src/pages/shop/index.js index 26ea96dc..048a64b3 100644 --- a/src/pages/shop/index.js +++ b/src/pages/shop/index.js @@ -1,58 +1,49 @@ -import ProductCard from '../../components/product-card/ProductCard'; -import { getAllProducts } from '../../utils/shopify'; // Import from your utility file +import Head from "next/head"; +import { getProducts } from "../../utils/shopify"; +import ProductCard from "../../components/product-card/ProductCard"; +import styles from "./home.module.css"; -export default function Home({ products, error }) { - if (error) { - return
Error: {error}
; - } - - if (!products || products.length === 0) { // Check for null or empty array - return
No products found.
; - } +export default function Home({ data }) { + const products = data.products.edges; return ( -
-

Our Products

-
- {products.map((product) => ( - // Spread props for cleaner code - ))} -
-
+ <> + + Nextjs Shopify + + + + +
+
+ {products.length ? ( + products.map((product) => ( + + )) + ) : ( +

No products available at the moment.

+ )} +
+
+ ); } -export async function getStaticProps() { +export const getServerSideProps = async () => { try { - const response = await getAllProducts(); + const data = await getProducts(); - if (response.status !== 200 || !response.body.data || !response.body.data.products || !response.body.data.products.edges) { - console.error("Invalid response from Shopify API:", response); - throw new Error("Failed to fetch products from Shopify."); // Throw an error + if (!data || !data.products || !data.products.edges) { + throw new Error("Invalid data from Shopify API"); } - const products = response.body.data.products.edges.map(({ node }) => { - if (!node || !node.id || !node.title) { - console.warn("Incomplete product data:", node); // Warn about incomplete data - return null; // Skip this product - } - return { - id: node.id, - title: node.title, - description: node.description || "", // Provide default values - price: node.priceRange?.minVariantPrice?.amount || "0.00", - image: node.featuredImage?.src || '/default-product-image.jpg', - }; - }).filter(product => product !== null); // Remove null products - return { - props: { products }, - revalidate: 60, + props: { data }, }; } catch (error) { - console.error("Error in getStaticProps:", error); + console.error("Error fetching products:", error); return { - props: { products: [], error: error.message }, // Return an error prop + props: { data: { products: { edges: [] } } }, }; } -} \ No newline at end of file +}; diff --git a/src/utils/shopify.js b/src/utils/shopify.js index 0a82803e..8c6a10a9 100644 --- a/src/utils/shopify.js +++ b/src/utils/shopify.js @@ -1,42 +1,190 @@ -export async function shopifyFetch({ query, variables }) { - const endpoint = process.env.SHOPIFY_STORE_DOMAIN; - const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN; - - try { - const result = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Shopify-Storefront-Access-Token': key - }, - body: { query, variables } && JSON.stringify({ query, variables }) - }); - - return { - status: result.status, - body: await result.json() +import { gql, GraphQLClient } from "graphql-request"; + +const storefrontAccessToken = process.env.SHOPIFY_STOREFRONT_ACCESS; +const endpoint = process.env.SHOPIFY_STORE_DOMAIN; + +const graphQLClient = new GraphQLClient(endpoint, { + headers: { + "X-Shopify-Storefront-Access-Token": storefrontAccessToken, + }, +}); + +export async function getProducts() { + const getAllProductsQuery = gql` + { + products(first: 10) { + edges { + node { + id + title + handle + priceRange { + minVariantPrice { + amount + } + } + featuredImage { + altText + url + } + } + } + } + } + `; + try { + return await graphQLClient.request(getAllProductsQuery); + } catch (error) { + throw new Error(error); + } +} + +export async function addToCart(itemId, quantity) { + const createCartMutation = gql` + mutation createCart($cartInput: CartInput) { + cartCreate(input: $cartInput) { + cart { + id + } + } + } + `; + const variables = { + cartInput: { + lines: [ + { + quantity: parseInt(quantity), + merchandiseId: itemId, + }, + ], + }, }; - } catch (error) { - console.error('Error:', error); - return { - status: 500, - error: 'Error receiving data' + try { + return await graphQLClient.request(createCartMutation, variables); + } catch (error) { + throw new Error(error); + } +} + +export async function updateCart(cartId, itemId, quantity) { + const updateCartMutation = gql` + mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) { + cartLinesAdd(cartId: $cartId, lines: $lines) { + cart { + id + } + } + } + `; + const variables = { + cartId: cartId, + lines: [ + { + quantity: parseInt(quantity), + merchandiseId: itemId, + }, + ], }; - } + try { + return await graphQLClient.request(updateCartMutation, variables); + } catch (error) { + throw new Error(error); + } } -export async function getAllProducts() { - return shopifyFetch({ - query: `{ - products(sortKey: TITLE, first: 100) { - edges{ - node { - id - title - description +export async function retrieveCart(cartId) { + const cartQuery = gql` + query cartQuery($cartId: ID!) { + cart(id: $cartId) { + id + createdAt + updatedAt + + lines(first: 10) { + edges { + node { + id + quantity + merchandise { + ... on ProductVariant { + id + } + } + } + } + } + estimatedCost { + totalAmount { + amount + } + } + } + } + `; + const variables = { + cartId, + }; + try { + const data = await graphQLClient.request(cartQuery, variables); + return data.cart; + } catch (error) { + throw new Error(error); + } +} + +export const getProduct = async (id) => { + const productQuery = gql` + query getProduct($id: ID!) { + product(id: $id) { + id + handle + title + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + featuredImage { + url + altText + } + variants(first: 10) { + edges { + node { + id + } + } + } } - } } - }` - }); -} \ No newline at end of file + `; + + const variables = { id }; + + try { + const data = await graphQLClient.request(productQuery, variables); + return data.product; // Return the product object + } catch (error) { + throw new Error(`Error fetching product by ID: ${error.message}`); + } +}; + +export const getCheckoutUrl = async (cartId) => { + const getCheckoutUrlQuery = gql` + query checkoutURL($cartId: ID!) { + cart(id: $cartId) { + checkoutUrl + } + } + `; + const variables = { + cartId: cartId, + }; + try { + return await graphQLClient.request(getCheckoutUrlQuery, variables); + } catch (error) { + throw new Error(error); + } +};