Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: fix storefront sections in digital product recipe #10833

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"

// ...

Expand All @@ -2605,81 +2608,99 @@ 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
}

```

### 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.
Expand Down Expand Up @@ -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[]
}
Expand All @@ -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[]
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand All @@ -2935,25 +2979,35 @@ 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
}
```

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 (
Expand Down Expand Up @@ -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.

<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
Expand Down
2 changes: 1 addition & 1 deletion www/apps/resources/generated/edit-dates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading