From 8224466dd745a8d5d34b404f155192fcaa8f1200 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Mon, 6 Jan 2025 13:12:12 +0200 Subject: [PATCH] docs: fix storefront sections in digital product recipe (#10833) --- .../examples/standard/page.mdx | 216 +++++++++++------- www/apps/resources/generated/edit-dates.mjs | 2 +- 2 files changed, 133 insertions(+), 85 deletions(-) diff --git a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx index 3bf0f5ada58c0..634355fff7440 100644 --- a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx +++ b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx @@ -2590,7 +2590,10 @@ If you haven't installed the Next.js Starter storefront in the first step, refer In `src/types/global.ts`, add the following types that you’ll use in your customizations: ```ts title="src/types/global.ts" -import { BaseProductVariant } from "@medusajs/framework/types/http/product/common" +import { + // other imports... + StoreProductVariant +} from "@medusajs/types" // ... @@ -2605,9 +2608,14 @@ export type DigitalProductMedia = { fileId: string type: "preview" | "main" mimeType: string + digitalProduct?: DigitalProduct[] +} + +export type DigitalProductPreview = DigitalProductMedia & { + url: string } -export type VariantWithDigitalProduct = BaseProductVariant & { +export type VariantWithDigitalProduct = StoreProductVariant & { digital_product?: DigitalProduct } @@ -2615,71 +2623,84 @@ export type VariantWithDigitalProduct = BaseProductVariant & { ### Retrieve Digital Products with Variants -To retrieve the digital products details when retrieving a product and its variants, in the `src/lib/data/products.ts` file, change the `getProductsById` and `getProductByHandle` functions to pass the digital products in the `fields` property passed to the `sdk.store.product.list` method: +To retrieve the digital products details when retrieving a product and its variants, in the `src/lib/data/products.ts` file, change the `listProducts` function to pass the digital products in the `fields` property passed to the `sdk.store.product.list` method: export const fieldHighlights = [ - ["12"], ["27"] + ["24"] ] ```ts title="src/lib/data/products.ts" highlights={fieldHighlights} -export const getProductsById = cache(async function ({ - ids, +export const listProducts = async ({ + pageParam = 1, + queryParams, + countryCode, regionId, }: { - ids: string[] - regionId: string -}) { - return sdk.store.product - .list( - { - // ... - fields: "*variants.calculated_price,*variants.digital_product", - } - // ... - ) - // ... -}) - -export const getProductByHandle = cache(async function ( - handle: string, - regionId: string -) { - return sdk.store.product - .list( + pageParam?: number + queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams + countryCode?: string + regionId?: string +}): Promise<{ + response: { products: HttpTypes.StoreProduct[]; count: number } + nextPage: number | null + queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams +}> => { + // ... + return sdk.client + .fetch<{ products: HttpTypes.StoreProduct[]; count: number }>( + `/store/products`, { // ... - fields: "*variants.calculated_price,*variants.digital_product", + query: { + // ... + fields: "*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags,*variants.calculated_price,*variants.digital_product", + } } - // ... ) // ... -}) +} ``` When a customer views a product’s details page, digital products linked to variants are also retrieved. ### Get Digital Product Preview Links -To retrieve the links of a digital product’s preview media, add in `src/lib/data/products.ts` the following function: +To retrieve the links of a digital product’s preview media, first, add the following import at the top of `src/lib/data/products.ts`: ```ts title="src/lib/data/products.ts" -export const getDigitalProductPreview = cache(async function ({ +import { DigitalProductPreview } from "../../types/global" +``` + +Then, add the following function at the end of the file: + +```ts title="src/lib/data/products.ts" +export const getDigitalProductPreview = async function ({ id, }: { id: string }) { - const { previews } = await fetch( - `${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/digital-products/${id}/preview`, { - credentials: "include", - headers: { - "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, - }, - }).then((res) => res.json()) + const headers = { + ...(await getAuthHeaders()), + } + + const next = { + ...(await getCacheOptions("products")), + } + const { previews } = await sdk.client.fetch<{ + previews: DigitalProductPreview[] + }>( + `/store/digital-products/${id}/preview`, + { + headers, + next, + cache: "force-cache" + } + ) // for simplicity, return only the first preview url // instead you can show all the preview media to the customer return previews.length ? previews[0].url : "" -}) +} ``` This function uses the API route you created in the previous section to get the preview links and return the first preview link. @@ -2776,17 +2797,25 @@ Start by creating the file `src/lib/data/digital-products.ts` with the following "use server" import { DigitalProduct } from "../../types/global" -import { getAuthHeaders } from "./cookies" +import { sdk } from "../config" +import { getAuthHeaders, getCacheOptions } from "./cookies" export const getCustomerDigitalProducts = async () => { - const { digital_products } = await fetch( - `${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/customers/me/digital-products`, { - credentials: "include", - headers: { - ...getAuthHeaders(), - "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, - }, - }).then((res) => res.json()) + const headers = { + ...(await getAuthHeaders()), + } + + const next = { + ...(await getCacheOptions("products")), + } + const { digital_products } = await sdk.client.fetch<{ + digital_products: DigitalProduct[] + }>(`/store/customers/me/digital-products`, { + + headers, + next, + cache: "force-cache", + }) return digital_products as DigitalProduct[] } @@ -2801,7 +2830,6 @@ Then, create the file `src/modules/account/components/digital-products-list/inde import { Table } from "@medusajs/ui" import { DigitalProduct } from "../../../../types/global" -import { getDigitalMediaDownloadLink } from "../../../../lib/data/digital-products" type Props = { digitalProducts: DigitalProduct[] @@ -2847,9 +2875,7 @@ export const DigitalProductsList = ({ } ``` -This adds a `DigitalProductsList` component that receives a list of digital products and shows them in a table. - -Each digital product’s media has a download link. You’ll implement its functionality afterwards. +This adds a `DigitalProductsList` component that receives a list of digital products and shows them in a table. Each digital product’s media has a download link. You’ll implement its functionality afterwards. Next, create the file `src/app/[countryCode]/(main)/account/@dashboard/digital-products/page.tsx` with the following content: @@ -2902,26 +2928,44 @@ const AccountNav = ({ return (
- {/* Add before log out */} -
  • - -
    - - Digital Products -
    - -
    -
  • +
    + {/* ... */} + {/* Add before log out */} +
  • + +
    + + Digital Products +
    + +
    +
  • + {/* ... */} +
    +
    + {/* ... */} + {/* Add before log out */} +
  • + + Digital Products + +
  • + {/* ... */} +
    ) } ``` -You add a link to the new route before the log out tab. +You add a link to the new route before the log out tab both for small and large devices. ### Test Purchased Digital Products Page @@ -2935,17 +2979,21 @@ To add a download link for the purchased digital products’ medias, first, add ```ts title="src/lib/data/digital-products.ts" export const getDigitalMediaDownloadLink = async (mediaId: string) => { - const { url } = await fetch( - `${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/customers/me/digital-products/${ - mediaId - }/download`, { - credentials: "include", + const headers = { + ...(await getAuthHeaders()), + } + + const next = { + ...(await getCacheOptions("products")), + } + const { url } = await sdk.client.fetch<{ + url: string + }>(`/store/customers/me/digital-products/${mediaId}/download`, { method: "POST", - headers: { - ...getAuthHeaders(), - "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, - }, - }).then((res) => res.json()) + headers, + next, + cache: "force-cache", + }) return url } @@ -2953,7 +3001,13 @@ export const getDigitalMediaDownloadLink = async (mediaId: string) => { In this function, you send a request to the download API route you created earlier to retrieve the download URL of a purchased digital product media. -Then, in `src/modules/account/components/digital-products-list/index.tsx`, add a `handleDownload` function in the `DigitalProductsList` component: +Then, in `src/modules/account/components/digital-products-list/index.tsx`, import the `getDigitalMediaDownloadLink` at the top of the file: + +```tsx title="src/modules/account/components/digital-products-list/index.tsx" +import { getDigitalMediaDownloadLink } from "../../../../lib/data/digital-products" +``` + +And add a `handleDownload` function in the `DigitalProductsList` component: ```tsx title="src/modules/account/components/digital-products-list/index.tsx" const handleDownload = async ( @@ -2982,12 +3036,6 @@ Finally, add an `onClick` handler to the digital product medias’ link in the r To test the latest changes out, open the purchased digital products page and click on the Download link of any media in the table. The media’s download link will open in a new page. - - -The local file module provider doesn't support private uploads, so the download link won't actually be useful. Instead, use the [S3 module provider](../../../../architectural-modules/file/s3/page.mdx) in production. - - - --- ## Next Steps diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index 41e463f3eae4e..ad8fe33c73a28 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -112,7 +112,7 @@ export const generatedEditDates = { "app/nextjs-starter/page.mdx": "2024-12-12T12:31:16.661Z", "app/recipes/b2b/page.mdx": "2024-10-03T13:07:44.153Z", "app/recipes/commerce-automation/page.mdx": "2024-10-16T08:52:01.585Z", - "app/recipes/digital-products/examples/standard/page.mdx": "2025-01-03T14:38:04.333Z", + "app/recipes/digital-products/examples/standard/page.mdx": "2025-01-06T09:03:35.202Z", "app/recipes/digital-products/page.mdx": "2024-10-03T13:07:44.147Z", "app/recipes/ecommerce/page.mdx": "2024-10-22T11:01:01.218Z", "app/recipes/integrate-ecommerce-stack/page.mdx": "2024-12-09T13:03:35.846Z",