docs: fix storefront sections in digital product recipe (#10833)

This commit is contained in:
Shahed Nasser
2025-01-06 13:12:12 +02:00
committed by GitHub
parent f7ffa3540f
commit 8224466dd7
2 changed files with 133 additions and 85 deletions

View File

@@ -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 youll 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 VariantWithDigitalProduct = BaseProductVariant & {
export type DigitalProductPreview = DigitalProductMedia & {
url: string
}
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(
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",
}
}
// ...
)
// ...
})
export const getProductByHandle = cache(async function (
handle: string,
regionId: string
) {
return sdk.store.product
.list(
{
// ...
fields: "*variants.calculated_price,*variants.digital_product",
}
// ...
)
// ...
})
}
```
When a customer views a products details page, digital products linked to variants are also retrieved.
### Get Digital Product Preview Links
To retrieve the links of a digital products preview media, add in `src/lib/data/products.ts` the following function:
To retrieve the links of a digital products 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 products media has a download link. Youll implement its functionality afterwards.
This adds a `DigitalProductsList` component that receives a list of digital products and shows them in a table. Each digital products media has a download link. Youll 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 (
<div>
{/* Add before log out */}
<li>
<LocalizedClientLink
href="/account/digital-products"
className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
data-testid="digital-products-link"
>
<div className="flex items-center gap-x-2">
<Photo />
<span>Digital Products</span>
</div>
<ChevronDown className="transform -rotate-90" />
</LocalizedClientLink>
</li>
<div className="small:hidden">
{/* ... */}
{/* Add before log out */}
<li>
<LocalizedClientLink
href="/account/digital-products"
className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
data-testid="digital-products-link"
>
<div className="flex items-center gap-x-2">
<Photo />
<span>Digital Products</span>
</div>
<ChevronDown className="transform -rotate-90" />
</LocalizedClientLink>
</li>
{/* ... */}
</div>
<div className="hidden small:block">
{/* ... */}
{/* Add before log out */}
<li>
<AccountNavLink
href="/account/digital-products"
route={route!}
data-testid="digital-products-link"
>
Digital Products
</AccountNavLink>
</li>
{/* ... */}
</div>
</div>
)
}
```
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 medias download link will open in a new page.
<Note>
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.
</Note>
---
## Next Steps

View File

@@ -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",