mirror of
https://github.com/vercel/commerce.git
synced 2025-05-31 05:26:59 +00:00
Refactor: Convert layout components to use dummy data for standalone mode
This commit updates the application to operate in a standalone mode by modifying essential data fetching functions used by layout components to return hardcoded dummy data, removing dependencies on a live Shopify backend for initial page rendering and layout. Key changes: - `lib/shopify/index.ts`: - `getMenu()`: Updated to return a hardcoded array of `Menu[]` items, bypassing any calls to `shopifyFetch`. Caching directives were removed as they are not applicable to static dummy data. - `getCart()`: Updated to return a hardcoded `Cart` object (or `undefined`), bypassing `shopifyFetch` and cookie-based cart ID retrieval. - `shopifyFetch()`: The core `fetch` call within this function has been commented out and replaced with a `throw new Error(...)`. This prevents any accidental live API calls and makes it clear that such calls are disabled in this standalone configuration. A `console.warn` is also added if the function is ever invoked. These changes ensure that the main layout, including the navbar and cart components, can render without external Shopify dependencies, allowing the storefront to function with dummy data as per your current project requirements. This should resolve build errors related to fetching non-existent Shopify data (like menus) in an environment not connected to a live Shopify store.
This commit is contained in:
parent
c8e2bb3cfb
commit
0adf19ed8f
@ -1,16 +1,16 @@
|
||||
import {
|
||||
HIDDEN_PRODUCT_TAG,
|
||||
SHOPIFY_GRAPHQL_API_ENDPOINT,
|
||||
TAGS
|
||||
TAGS // Keep TAGS if used by other functions like getCollection, getProduct etc.
|
||||
} from 'lib/constants';
|
||||
import { isShopifyError } from 'lib/type-guards';
|
||||
import { ensureStartsWith } from 'lib/utils';
|
||||
import {
|
||||
revalidateTag,
|
||||
unstable_cacheTag as cacheTag,
|
||||
unstable_cacheLife as cacheLife
|
||||
unstable_cacheTag as cacheTag, // Keep if used by other functions
|
||||
unstable_cacheLife as cacheLife // Keep if used by other functions
|
||||
} from 'next/cache';
|
||||
import { cookies, headers } from 'next/headers';
|
||||
import { cookies, headers } from 'next/headers'; // Keep 'cookies' if other cart mutations use it
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {
|
||||
addToCartMutation,
|
||||
@ -18,13 +18,14 @@ import {
|
||||
editCartItemsMutation,
|
||||
removeFromCartMutation
|
||||
} from './mutations/cart';
|
||||
import { getCartQuery } from './queries/cart';
|
||||
// import { getCartQuery } from './queries/cart'; // No longer needed for dummy getCart
|
||||
import {
|
||||
getCollectionProductsQuery,
|
||||
getCollectionQuery,
|
||||
getCollectionsQuery
|
||||
} from './queries/collection';
|
||||
import { getMenuQuery } from './queries/menu';
|
||||
// getMenuQuery is removed as getMenu is now returning dummy data
|
||||
// import { getMenuQuery } from './queries/menu';
|
||||
import { getPageQuery, getPagesQuery } from './queries/page';
|
||||
import {
|
||||
getProductQuery,
|
||||
@ -32,22 +33,23 @@ import {
|
||||
getProductsQuery
|
||||
} from './queries/product';
|
||||
import {
|
||||
Cart,
|
||||
Cart, // Ensure Cart type is imported
|
||||
Collection,
|
||||
Connection,
|
||||
Image,
|
||||
Menu,
|
||||
Menu, // Menu type is essential
|
||||
Page,
|
||||
Product,
|
||||
ShopifyAddToCartOperation,
|
||||
ShopifyCart,
|
||||
ShopifyCartOperation,
|
||||
ShopifyCart, // Still needed for other cart mutations if they use reshapeCart
|
||||
// ShopifyCartOperation, // No longer needed for dummy getCart
|
||||
ShopifyCollection,
|
||||
ShopifyCollectionOperation,
|
||||
ShopifyCollectionProductsOperation,
|
||||
ShopifyCollectionsOperation,
|
||||
ShopifyCreateCartOperation,
|
||||
ShopifyMenuOperation,
|
||||
// ShopifyMenuOperation is removed as getMenu is now returning dummy data
|
||||
// ShopifyMenuOperation,
|
||||
ShopifyPageOperation,
|
||||
ShopifyPagesOperation,
|
||||
ShopifyProduct,
|
||||
@ -77,6 +79,18 @@ export async function shopifyFetch<T>({
|
||||
query: string;
|
||||
variables?: ExtractVariables<T>;
|
||||
}): Promise<{ status: number; body: T } | never> {
|
||||
console.warn(`shopifyFetch called with query: ${query.substring(0, 100)}... This call is currently disabled for standalone dummy data mode.`);
|
||||
|
||||
// Option 1: Throw an error to make it clear this path shouldn't be taken.
|
||||
throw new Error(`Shopify API calls are disabled in standalone dummy data mode. Query: ${query.substring(0,100)}...`);
|
||||
|
||||
// Option 2: Return a mock error structure similar to what Shopify might send,
|
||||
// which some calling functions might expect or handle.
|
||||
// This is more complex as the exact 'T' for body is generic.
|
||||
// For now, throwing an error is simpler and makes unintended calls obvious.
|
||||
|
||||
/*
|
||||
// Original fetch call - to be commented out or removed:
|
||||
try {
|
||||
const result = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
@ -116,12 +130,14 @@ export async function shopifyFetch<T>({
|
||||
query
|
||||
};
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
const removeEdgesAndNodes = <T>(array: Connection<T>): T[] => {
|
||||
return array.edges.map((edge) => edge?.node);
|
||||
};
|
||||
|
||||
// reshapeCart is kept as it's used by other cart mutation functions (createCart, addToCart, etc.)
|
||||
const reshapeCart = (cart: ShopifyCart): Cart => {
|
||||
if (!cart.cost?.totalTaxAmount) {
|
||||
cart.cost.totalTaxAmount = {
|
||||
@ -264,25 +280,95 @@ export async function updateCart(
|
||||
}
|
||||
|
||||
export async function getCart(): Promise<Cart | undefined> {
|
||||
const cartId = (await cookies()).get('cartId')?.value;
|
||||
console.log('getCart called - returning dummy cart data / undefined.'); // For observability
|
||||
|
||||
if (!cartId) {
|
||||
return undefined;
|
||||
}
|
||||
// Using Option 2 from the example: Return a basic dummy cart structure
|
||||
const dummyCart: Cart = {
|
||||
id: 'dummy-cart-id-123',
|
||||
checkoutUrl: '/cart-checkout', // Or some placeholder
|
||||
cost: {
|
||||
subtotalAmount: { amount: '100.00', currencyCode: 'USD' },
|
||||
totalAmount: { amount: '105.00', currencyCode: 'USD' }, // Including some dummy tax/shipping
|
||||
totalTaxAmount: { amount: '5.00', currencyCode: 'USD' }
|
||||
},
|
||||
lines: [
|
||||
{
|
||||
id: 'dummy-line-item-1',
|
||||
quantity: 2,
|
||||
cost: {
|
||||
totalAmount: { amount: '50.00', currencyCode: 'USD' }
|
||||
},
|
||||
merchandise: {
|
||||
id: 'dummy-merch-id-1',
|
||||
title: 'Dummy Product A',
|
||||
selectedOptions: [{ name: 'Color', value: 'Red' }],
|
||||
product: { // Ensure this matches the Product type expected by CartLine.merchandise.product
|
||||
id: 'dummy-prod-id-A',
|
||||
handle: 'dummy-product-a',
|
||||
title: 'Dummy Product A',
|
||||
// featuredImage, priceRange, etc., might be needed if CartLine.merchandise.product expects a full Product
|
||||
// For this dummy data, keeping it minimal as per example.
|
||||
// Add other Product fields if Cart type expects them from merchandise.product
|
||||
// Based on current 'Product' type, these are the minimum required:
|
||||
availableForSale: true,
|
||||
description: 'A dummy product',
|
||||
descriptionHtml: '<p>A dummy product</p>',
|
||||
images: [], // Assuming empty array is acceptable or provide dummy images
|
||||
options: [],
|
||||
priceRange: {
|
||||
maxVariantPrice: { amount: '25.00', currencyCode: 'USD' },
|
||||
minVariantPrice: { amount: '25.00', currencyCode: 'USD' }
|
||||
},
|
||||
seo: { title: 'Dummy Product A', description: 'Dummy A' },
|
||||
tags: [],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [], // Assuming empty array is acceptable or provide dummy variants
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dummy-line-item-2',
|
||||
quantity: 1,
|
||||
cost: {
|
||||
totalAmount: { amount: '50.00', currencyCode: 'USD' }
|
||||
},
|
||||
merchandise: {
|
||||
id: 'dummy-merch-id-2',
|
||||
title: 'Dummy Product B',
|
||||
selectedOptions: [{ name: 'Size', value: 'M' }],
|
||||
product: { // Ensure this matches the Product type expected by CartLine.merchandise.product
|
||||
id: 'dummy-prod-id-B',
|
||||
handle: 'dummy-product-b',
|
||||
title: 'Dummy Product B',
|
||||
availableForSale: true,
|
||||
description: 'Another dummy product',
|
||||
descriptionHtml: '<p>Another dummy product</p>',
|
||||
images: [],
|
||||
options: [],
|
||||
priceRange: {
|
||||
maxVariantPrice: { amount: '50.00', currencyCode: 'USD' },
|
||||
minVariantPrice: { amount: '50.00', currencyCode: 'USD' }
|
||||
},
|
||||
seo: { title: 'Dummy Product B', description: 'Dummy B' },
|
||||
tags: [],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
totalQuantity: 3
|
||||
};
|
||||
|
||||
const res = await shopifyFetch<ShopifyCartOperation>({
|
||||
query: getCartQuery,
|
||||
variables: { cartId }
|
||||
});
|
||||
|
||||
// Old carts becomes `null` when you checkout.
|
||||
if (!res.body.data.cart) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return reshapeCart(res.body.data.cart);
|
||||
await new Promise(resolve => setTimeout(resolve, 50)); // Simulate delay
|
||||
|
||||
// To test the "empty cart" scenario, you can conditionally return undefined or dummyCart here.
|
||||
// For now, let's return the dummyCart.
|
||||
return dummyCart;
|
||||
// return undefined; // Use this to test how Navbar/CartProvider handles no cart
|
||||
}
|
||||
|
||||
|
||||
export async function getCollection(
|
||||
handle: string
|
||||
): Promise<Collection | undefined> {
|
||||
@ -364,36 +450,24 @@ export async function getCollections(): Promise<Collection[]> {
|
||||
}
|
||||
|
||||
export async function getMenu(handle: string): Promise<Menu[]> {
|
||||
'use cache'; // Next.js specific caching directive
|
||||
cacheTag(TAGS.collections); // Next.js specific cache tagging
|
||||
cacheLife('days'); // Next.js specific cache lifetime
|
||||
console.log(`getMenu called with handle: ${handle} - returning dummy menu data.`); // For observability
|
||||
|
||||
try {
|
||||
const res = await shopifyFetch<ShopifyMenuOperation>({
|
||||
query: getMenuQuery, // getMenuQuery is imported from ./queries/menu
|
||||
variables: {
|
||||
handle
|
||||
}
|
||||
});
|
||||
// Dummy menu structure. Modify as needed to match typical menu items.
|
||||
const dummyMenu: Menu[] = [
|
||||
{ title: 'Home', path: '/' },
|
||||
{ title: 'All Products', path: '/search' }, // Example link to a general product listing
|
||||
{ title: 'T-Shirts', path: '/search/t-shirts' }, // Example link to a specific collection
|
||||
{ title: 'About Us', path: '/content/about-us' },
|
||||
{ title: 'Contact Us', path: '/content/contact-us' },
|
||||
{ title: 'Login', path: '/login' },
|
||||
// { title: 'My Page', path: '/my-page' }, // Potentially conditional
|
||||
// { title: 'Cart', path: '/cart-checkout' } // Link to the dedicated cart page
|
||||
];
|
||||
|
||||
// If res.body.data.menu is null or undefined (e.g., menu not found but no explicit API error),
|
||||
// optional chaining will result in undefined, and the `|| []` will correctly return an empty array.
|
||||
return (
|
||||
res.body?.data?.menu?.items.map((item: { title: string; url: string }) => ({
|
||||
title: item.title,
|
||||
path: item.url
|
||||
.replace(domain, '') // domain is defined at the top of lib/shopify/index.ts
|
||||
.replace('/collections', '/search')
|
||||
.replace('/pages', '')
|
||||
})) || []
|
||||
);
|
||||
} catch (error: any) {
|
||||
// Log the error for server-side observability.
|
||||
// During a build process (like for _not-found), this helps identify issues without failing the build.
|
||||
console.error(`Error fetching menu with handle '${handle}':`, error.message || error);
|
||||
// Return an empty array to allow the page to render without menu data.
|
||||
return [];
|
||||
}
|
||||
// Simulate a slight delay if desired, like other dummy data functions
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
return dummyMenu;
|
||||
}
|
||||
|
||||
export async function getPage(handle: string): Promise<Page> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user