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

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

No files matched your search

@@ -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: In `src/types/global.ts`, add the following types that you’ll use in your customizations:
```ts title="src/types/global.ts" ```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 fileId: string
type: "preview" | "main" type: "preview" | "main"
mimeType: string mimeType: string
digitalProduct?: DigitalProduct[]
} }
export type VariantWithDigitalProduct = BaseProductVariant & { export type DigitalProductPreview = DigitalProductMedia & {
url: string
}
export type VariantWithDigitalProduct = StoreProductVariant & {
digital_product?: DigitalProduct digital_product?: DigitalProduct
} }
@@ -2615,71 +2623,84 @@ export type VariantWithDigitalProduct = BaseProductVariant & {
### Retrieve Digital Products with Variants ### 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 = [ export const fieldHighlights = [
["12"], ["27"] ["24"]
] ]
```ts title="src/lib/data/products.ts" highlights={fieldHighlights} ```ts title="src/lib/data/products.ts" highlights={fieldHighlights}
export const getProductsById = cache(async function ({ export const listProducts = async ({
ids, pageParam = 1,
queryParams,
countryCode,
regionId, regionId,
}: { }: {
ids: string[] pageParam?: number
regionId: string queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams
}) { countryCode?: string
return sdk.store.product regionId?: string
.list( }): 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 product’s details page, digital products linked to variants are also retrieved. When a customer views a product’s details page, digital products linked to variants are also retrieved.
### Get Digital Product Preview Links ### 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" ```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,
}: { }: {
id: string id: string
}) { }) {
const { previews } = await fetch( const headers = {
`${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/digital-products/${id}/preview`, { ...(await getAuthHeaders()),
credentials: "include", }
headers: {
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, const next = {
}, ...(await getCacheOptions("products")),
}).then((res) => res.json()) }
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 // for simplicity, return only the first preview url
// instead you can show all the preview media to the customer // instead you can show all the preview media to the customer
return previews.length ? previews[0].url : "" 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. 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" "use server"
import { DigitalProduct } from "../../types/global" import { DigitalProduct } from "../../types/global"
import { getAuthHeaders } from "./cookies" import { sdk } from "../config"
import { getAuthHeaders, getCacheOptions } from "./cookies"
export const getCustomerDigitalProducts = async () => { export const getCustomerDigitalProducts = async () => {
const { digital_products } = await fetch( const headers = {
`${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/customers/me/digital-products`, { ...(await getAuthHeaders()),
credentials: "include", }
headers: {
...getAuthHeaders(), const next = {
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, ...(await getCacheOptions("products")),
}, }
}).then((res) => res.json()) 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[] 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 { Table } from "@medusajs/ui"
import { DigitalProduct } from "../../../../types/global" import { DigitalProduct } from "../../../../types/global"
import { getDigitalMediaDownloadLink } from "../../../../lib/data/digital-products"
type Props = { type Props = {
digitalProducts: DigitalProduct[] 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. 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.
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: 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 ( return (
<div> <div>
{/* Add before log out */} <div className="small:hidden">
<li> {/* ... */}
<LocalizedClientLink {/* Add before log out */}
href="/account/digital-products" <li>
className="flex items-center justify-between py-4 border-b border-gray-200 px-8" <LocalizedClientLink
data-testid="digital-products-link" href="/account/digital-products"
> className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
<div className="flex items-center gap-x-2"> data-testid="digital-products-link"
<Photo /> >
<span>Digital Products</span> <div className="flex items-center gap-x-2">
</div> <Photo />
<ChevronDown className="transform -rotate-90" /> <span>Digital Products</span>
</LocalizedClientLink> </div>
</li> <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> </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 ### 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" ```ts title="src/lib/data/digital-products.ts"
export const getDigitalMediaDownloadLink = async (mediaId: string) => { export const getDigitalMediaDownloadLink = async (mediaId: string) => {
const { url } = await fetch( const headers = {
`${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}/store/customers/me/digital-products/${ ...(await getAuthHeaders()),
mediaId }
}/download`, {
credentials: "include", const next = {
...(await getCacheOptions("products")),
}
const { url } = await sdk.client.fetch<{
url: string
}>(`/store/customers/me/digital-products/${mediaId}/download`, {
method: "POST", method: "POST",
headers: { headers,
...getAuthHeaders(), next,
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY, cache: "force-cache",
}, })
}).then((res) => res.json())
return url 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. 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" ```tsx title="src/modules/account/components/digital-products-list/index.tsx"
const handleDownload = async ( 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. 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.
<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 ## Next Steps
+1 -1
View File
@@ -112,7 +112,7 @@ export const generatedEditDates = {
"app/nextjs-starter/page.mdx": "2024-12-12T12:31:16.661Z", "app/nextjs-starter/page.mdx": "2024-12-12T12:31:16.661Z",
"app/recipes/b2b/page.mdx": "2024-10-03T13:07:44.153Z", "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/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/digital-products/page.mdx": "2024-10-03T13:07:44.147Z",
"app/recipes/ecommerce/page.mdx": "2024-10-22T11:01:01.218Z", "app/recipes/ecommerce/page.mdx": "2024-10-22T11:01:01.218Z",
"app/recipes/integrate-ecommerce-stack/page.mdx": "2024-12-09T13:03:35.846Z", "app/recipes/integrate-ecommerce-stack/page.mdx": "2024-12-09T13:03:35.846Z",