docs: add copy subscriber button (#12405)

* docs: add copy subscriber button

* re-generate

* fixes + update copy button
This commit is contained in:
Shahed Nasser
2025-05-08 12:48:44 +03:00
committed by GitHub
parent f929185021
commit f81eb51b67
38 changed files with 20525 additions and 18707 deletions
@@ -3,18 +3,23 @@
import {
Badge,
DetailsSummary,
DropdownMenu,
Link,
MarkdownContent,
parseEventPayload,
Tabs,
TabsContent,
TabsContentWrapper,
TabsList,
TabsTrigger,
Tooltip,
useCopy,
useGenerateSnippet,
} from "docs-ui"
import { useMemo } from "react"
import type { OpenAPI } from "types"
import TagOperationParameters from "../../Parameters"
import { Brackets, CheckCircle, SquareTwoStack, Tag } from "@medusajs/icons"
export type TagsOperationDescriptionSectionEventsProps = {
events: OpenAPI.OasEvents[]
@@ -68,62 +73,73 @@ const TagsOperationDescriptionSectionEvent = ({
}: {
event: OpenAPI.OasEvents
}) => {
const parsedPayload: OpenAPI.SchemaObject = useMemo(() => {
const payloadParams = event.payload.matchAll(
/([\w_]+),? \/\/ (\(\w*\) )*(.*)/g
)
const payload = Array.from(payloadParams).map((match) => {
return {
name: match[1],
type: match[2]?.replace(/\(|\)/g, "") || "string",
description: match[3],
}
})
return {
type: "object",
required: ["payload"],
properties: {
payload: {
type: "object",
description: "The payload emitted with the event",
required: [...payload.map((param) => param.name)],
properties: payload.reduce(
(acc, curr) => {
acc[curr.name] = {
type: curr.type as OpenAPI.OpenAPIV3.NonArraySchemaObjectType,
description: curr.description,
properties: {},
}
return acc
},
{} as Record<string, OpenAPI.SchemaObject>
),
},
},
}
const {
parsed_payload: parsedPayload,
payload_for_snippet: payloadForSnippet,
} = useMemo(() => {
return parseEventPayload(event.payload)
}, [event.payload])
const { snippet } = useGenerateSnippet({
type: "subscriber",
options: {
event: event.name,
payload: payloadForSnippet,
},
})
const { handleCopy: handleEventNameCopy, isCopied: eventNameCopied } =
useCopy(event.name)
const { handleCopy: handleSnippetCopy, isCopied: snippetCopied } =
useCopy(snippet)
return (
<TabsContent value={event.name}>
<div className="my-1 flex flex-wrap gap-1">
<MarkdownContent
allowedElements={["code", "p", "a"]}
className={"[&_p:last-child]:!mb-1"}
>
{`\`${event.name}\`: ${event.description}`}
</MarkdownContent>
{event.deprecated &&
(event.deprecated_message ? (
<Tooltip text={event.deprecated_message}>
<div className="my-1 flex flex-wrap justify-between items-center mb-1">
<div className="flex flex-wrap items-center gap-0.5">
<MarkdownContent
allowedElements={["code", "p", "a"]}
className={"[&_p:last-child]:!mb-0"}
>
{event.description}
</MarkdownContent>
{event.deprecated &&
(event.deprecated_message ? (
<Tooltip text={event.deprecated_message}>
<Badge variant="orange">Deprecated</Badge>
</Tooltip>
) : (
<Badge variant="orange">Deprecated</Badge>
))}
{event.version && (
<Tooltip text={`This event is emitted since v${event.version}`}>
<Badge variant="blue">v{event.version}</Badge>
</Tooltip>
) : (
<Badge variant="orange">Deprecated</Badge>
))}
{event.version && (
<Tooltip text={`This event is emitted since v${event.version}`}>
<Badge variant="blue">v{event.version}</Badge>
</Tooltip>
)}
)}
</div>
<DropdownMenu
dropdownButtonContent={
<>
{eventNameCopied || snippetCopied ? (
<CheckCircle />
) : (
<SquareTwoStack />
)}
</>
}
menuItems={[
{
type: "action",
title: "Copy event name",
action: () => handleEventNameCopy(),
icon: <Tag />,
},
{
type: "action",
title: "Copy subscriber for event",
action: () => handleSnippetCopy(),
icon: <Brackets />,
},
]}
menuClassName="z-10"
/>
</div>
<TagOperationParameters
schemaObject={parsedPayload}
+2 -2
View File
@@ -16,8 +16,8 @@
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@medusajs/icons": "2.6.0",
"@medusajs/ui": "4.0.6",
"@medusajs/icons": "2.7.1",
"@medusajs/ui": "4.0.9",
"@next/mdx": "15.3.1",
"@react-hook/resize-observer": "^2.0.2",
"@readme/openapi-parser": "^2.5.0",
+1 -1
View File
@@ -16,7 +16,7 @@
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@medusajs/icons": "~2.5.1",
"@medusajs/icons": "~2.7.1",
"@next/mdx": "15.3.1",
"clsx": "^2.1.0",
"docs-ui": "*",
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -66,7 +66,7 @@ async function main() {
"commerce-modules"
),
allowedFilesPatterns: [
/^(?!.*\/(workflows|js-sdk|extend|events|admin-widget-zones)\/).*$/,
/^(?!.*\/(workflows|js-sdk|extend|admin-widget-zones)\/).*$/,
],
},
{
@@ -101,6 +101,16 @@ async function main() {
},
},
},
{
dir: path.join(
process.cwd(),
"..",
"resources",
"references",
"modules",
"events"
),
},
{
dir: path.join(process.cwd(), "..", "resources", "app", "medusa-cli"),
},
@@ -197,7 +197,7 @@ const priceSet = await pricingModuleService.createPriceSets({
amount: 200,
currency_code: "EUR",
min_quantity: 100,
}
},
],
})
```
@@ -101,10 +101,10 @@ const { result } = await createProductsWorkflow(container)
},
],
// ...
}]
}],
}],
// ...
}
},
})
```
@@ -217,8 +217,8 @@ To create the Bundled Product Module's service, create the file `src/modules/bun
```ts title="src/modules/bundled-product/service.ts"
import { MedusaService } from "@medusajs/framework/utils"
import { Bundle } from "./models/bundle";
import { BundleItem } from "./models/bundle-item";
import { Bundle } from "./models/bundle"
import { BundleItem } from "./models/bundle-item"
export default class BundledProductModuleService extends MedusaService({
Bundle,
@@ -338,9 +338,9 @@ You can define links between data models in a TypeScript or JavaScript file unde
So, to define the link between a bundle and a product, create the file `src/links/bundle-product.ts` with the following content:
```ts title="src/links/bundle-product.ts"
import { defineLink } from "@medusajs/framework/utils";
import ProductModule from "@medusajs/medusa/product";
import BundledProductsModule from "../modules/bundled-product";
import { defineLink } from "@medusajs/framework/utils"
import ProductModule from "@medusajs/medusa/product"
import BundledProductsModule from "../modules/bundled-product"
export default defineLink(
BundledProductsModule.linkable.bundle,
@@ -360,9 +360,9 @@ You'll later learn how to query and manage the linked records.
Next, you'll define the link between the `BundleItem` data model and the `Product` data model. Create the file `src/links/bundle-item-product.ts` with the following content:
```ts title="src/links/bundle-item-product.ts"
import { defineLink } from "@medusajs/framework/utils";
import ProductModule from "@medusajs/medusa/product";
import BundledProductsModule from "../modules/bundled-product";
import { defineLink } from "@medusajs/framework/utils"
import ProductModule from "@medusajs/medusa/product"
import BundledProductsModule from "../modules/bundled-product"
export default defineLink(
{
@@ -579,13 +579,13 @@ export const createBundleItemsStep = createStep(
container.resolve(BUNDLED_PRODUCT_MODULE)
const bundleItems = await bundledProductModuleService.createBundleItems(
items.map(item => ({
items.map((item) => ({
bundle_id,
quantity: item.quantity,
}))
)
return new StepResponse(bundleItems, bundleItems.map(item => item.id))
return new StepResponse(bundleItems, bundleItems.map((item) => item.id))
},
async (itemIds, { container }) => {
if (!itemIds?.length) {
@@ -657,7 +657,7 @@ export const createBundledProductWorkflow = createWorkflow(
const bundleProduct = createProductsWorkflow.runAsStep({
input: {
products: [bundleData.product],
}
},
})
createRemoteLinkStep([{
@@ -671,7 +671,7 @@ export const createBundledProductWorkflow = createWorkflow(
const bundleProducttemLinks = transform({
bundleData,
bundleItems
bundleItems,
}, (data) => {
return data.bundleItems.map((item, index) => ({
[BUNDLED_PRODUCT_MODULE]: {
@@ -758,16 +758,16 @@ export const bundledProductsRouteHighlights = [
```ts title="src/api/admin/bundled-products/route.ts" highlights={bundledProductsRouteHighlights}
import {
AuthenticatedMedusaRequest,
MedusaResponse
} from "@medusajs/framework/http";
import { z } from "zod";
MedusaResponse,
} from "@medusajs/framework/http"
import { z } from "zod"
import {
AdminCreateProduct
AdminCreateProduct,
} from "@medusajs/medusa/api/admin/products/validators"
import {
createBundledProductWorkflow,
CreateBundledProductWorkflowInput
} from "../../../workflows/create-bundled-product";
CreateBundledProductWorkflowInput,
} from "../../../workflows/create-bundled-product"
export const PostBundledProductsSchema = z.object({
title: z.string(),
@@ -785,12 +785,12 @@ export async function POST(
res: MedusaResponse
) {
const {
result: bundledProduct
result: bundledProduct,
} = await createBundledProductWorkflow(req.scope)
.run({
input: {
bundle: req.validatedBody,
} as CreateBundledProductWorkflowInput
} as CreateBundledProductWorkflowInput,
})
res.json({
@@ -843,9 +843,9 @@ export const middlewaresHighlights = [
```ts title="src/api/middlewares.ts" highlights={middlewaresHighlights}
import {
defineMiddlewares,
validateAndTransformBody
} from "@medusajs/framework/http";
import { PostBundledProductsSchema } from "./admin/bundled-products/route";
validateAndTransformBody,
} from "@medusajs/framework/http"
import { PostBundledProductsSchema } from "./admin/bundled-products/route"
export default defineMiddlewares({
routes: [
@@ -856,7 +856,7 @@ export default defineMiddlewares({
validateAndTransformBody(PostBundledProductsSchema),
],
},
]
],
})
```
@@ -893,10 +893,10 @@ export async function GET(
const {
data: bundledProducts,
metadata: { count, take, skip } = {}
metadata: { count, take, skip } = {},
} = await query.graph({
entity: "bundle",
...req.queryConfig
...req.queryConfig,
})
res.json({
@@ -932,8 +932,8 @@ export const getBundledProductsMiddlewareHighlights = [
```ts title="src/api/middlewares.ts" highlights={getBundledProductsMiddlewareHighlights}
// other imports...
import { validateAndTransformQuery } from "@medusajs/framework/http";
import { createFindParams } from "@medusajs/medusa/api/utils/validators";
import { validateAndTransformQuery } from "@medusajs/framework/http"
import { createFindParams } from "@medusajs/medusa/api/utils/validators"
export default defineMiddlewares({
routes: [
@@ -955,7 +955,7 @@ export default defineMiddlewares({
}),
],
},
]
],
})
```
@@ -1253,7 +1253,7 @@ import {
Input,
Label,
Select,
toast
toast,
} from "@medusajs/ui"
import { useState, useRef, useCallback, useMemo } from "react"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
@@ -1270,7 +1270,7 @@ const CreateBundledProduct = () => {
{
product_id: undefined,
quantity: 1,
}
},
])
// TODO fetch products
}
@@ -1303,8 +1303,9 @@ const [products, setProducts] = useState<HttpTypes.AdminProduct[]>([])
const productsLimit = 15
const [currnetProductPage, setCurrentProductPage] = useState(0)
const [productsCount, setProductsCount] = useState(0)
const hasNextPage = useMemo(() =>
productsCount ? productsCount > productsLimit : true,
const hasNextPage = useMemo(() => {
return productsCount ? productsCount > productsLimit : true
},
[productsCount, productsLimit])
const queryClient = useQueryClient()
useQuery({
@@ -1353,14 +1354,14 @@ export const createBundledProductComponentHighlights3 = [
```tsx title="src/admin/components/create-bundled-product.tsx" highlights={createBundledProductComponentHighlights3}
const {
mutateAsync: createBundledProduct,
isPending: isCreating
isPending: isCreating,
} = useMutation({
mutationFn: async (data: Record<string, any>) => {
await sdk.client.fetch("/admin/bundled-products", {
method: "POST",
body: data
body: data,
})
}
},
})
const handleCreate = async () => {
@@ -1372,8 +1373,8 @@ const handleCreate = async () => {
options: [
{
title: "Default",
values: ["default"]
}
values: ["default"],
},
],
status: "published",
variants: [
@@ -1382,21 +1383,21 @@ const handleCreate = async () => {
// You can set prices in the product's page
prices: [],
options: {
Default: "default"
Default: "default",
},
manage_inventory: false
}
]
manage_inventory: false,
},
],
},
items: items.map((item) => ({
product_id: item.product_id,
quantity: item.quantity,
}))
})),
})
setOpen(false)
toast.success("Bundled product created successfully")
queryClient.invalidateQueries({
queryKey: ["bundled-products"]
queryKey: ["bundled-products"],
})
setTitle("")
setItems([{ product_id: undefined, quantity: 1 }])
@@ -1452,7 +1453,7 @@ const BundledProductItem = ({
setItems,
products,
fetchMoreProducts,
hasNextPage
hasNextPage,
}: BundledProductItemProps) => {
const observer = useRef(
new IntersectionObserver(
@@ -1491,14 +1492,14 @@ const BundledProductItem = ({
value={item.product_id}
onValueChange={(value) =>
setItems((items) =>
items.map((item, i) =>
i === index
items.map((item, i) => {
return i === index
? {
...item,
product_id: value,
}
: item
)
})
)
}
>
@@ -1530,11 +1531,11 @@ const BundledProductItem = ({
value={item.quantity}
onChange={(e) =>
setItems((items) =>
items.map((item, i) =>
i === index
items.map((item, i) => {
return i === index
? { ...item, quantity: parseInt(e.target.value) }
: item
)
})
)
}
/>
@@ -1838,8 +1839,8 @@ export const prepareBundleCartDataStep = createStep(
quantity: item.quantity * quantity,
metadata: {
bundle_id: bundle.id,
quantity: quantity
}
quantity: quantity,
},
}
})
@@ -1869,8 +1870,8 @@ return {
unit_price: 100,
metadata: {
bundle_id: bundle.id,
quantity: quantity
}
quantity: quantity,
},
}
```
@@ -1895,15 +1896,15 @@ export const addBundleToCartWorkflowHighlights = [
import {
createWorkflow,
transform,
WorkflowResponse
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
addToCartWorkflow,
useQueryGraphStep
useQueryGraphStep,
} from "@medusajs/medusa/core-flows"
import {
prepareBundleCartDataStep,
PrepareBundleCartDataStepInput
PrepareBundleCartDataStepInput,
} from "./steps/prepare-bundle-cart-data"
type AddBundleToCartWorkflowInput = {
@@ -1926,27 +1927,27 @@ export const addBundleToCartWorkflow = createWorkflow(
"id",
"items.*",
"items.product.*",
"items.product.variants.*"
"items.product.variants.*",
],
filters: {
id: bundle_id
id: bundle_id,
},
options: {
throwIfKeyNotFound: true
}
throwIfKeyNotFound: true,
},
})
const itemsToAdd = prepareBundleCartDataStep({
bundle: data[0],
quantity,
items
items,
} as unknown as PrepareBundleCartDataStepInput)
addToCartWorkflow.runAsStep({
input: {
cart_id,
items: itemsToAdd
}
items: itemsToAdd,
},
})
// @ts-ignore
@@ -1979,19 +1980,19 @@ You'll now create the API route that exposes the workflow's functionalities to s
To create the API route, create the file `src/api/store/carts/[id]/line-item-bundles/route.ts` with the following content:
```ts title="src/api/store/carts/[id]/line-item-bundles/route.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { z } from "zod";
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { z } from "zod"
import {
addBundleToCartWorkflow
} from "../../../../../workflows/add-bundle-to-cart";
addBundleToCartWorkflow,
} from "../../../../../workflows/add-bundle-to-cart"
export const PostCartsBundledLineItemsSchema = z.object({
bundle_id: z.string(),
quantity: z.number().default(1),
items: z.array(z.object({
item_id: z.string(),
variant_id: z.string()
}))
variant_id: z.string(),
})),
})
type PostCartsBundledLineItemsSchema = z.infer<
@@ -2008,12 +2009,12 @@ export async function POST(
cart_id: req.params.id,
bundle_id: req.validatedBody.bundle_id,
quantity: req.validatedBody.quantity || 1,
items: req.validatedBody.items
}
items: req.validatedBody.items,
},
})
res.json({
cart
cart,
})
}
```
@@ -2039,8 +2040,8 @@ In `src/api/middlewares.ts`, add a new middleware object to the `routes` array:
```ts title="src/api/middlewares.ts"
// other imports...
import {
PostCartsBundledLineItemsSchema
} from "./store/carts/[id]/line-item-bundles/route";
PostCartsBundledLineItemsSchema,
} from "./store/carts/[id]/line-item-bundles/route"
export default defineMiddlewares({
routes: [
@@ -2049,10 +2050,10 @@ export default defineMiddlewares({
matcher: "/store/carts/:id/line-item-bundles",
methods: ["POST"],
middlewares: [
validateAndTransformBody(PostCartsBundledLineItemsSchema)
validateAndTransformBody(PostCartsBundledLineItemsSchema),
],
}
]
},
],
})
```
@@ -2076,8 +2077,8 @@ export const bundleProductsRouteHighlights = [
]
```ts title="src/api/store/bundle-products/[id]/route.ts" highlights={bundleProductsRouteHighlights}
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { QueryContext } from "@medusajs/framework/utils";
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { QueryContext } from "@medusajs/framework/utils"
export async function GET(
req: MedusaRequest,
@@ -2100,7 +2101,7 @@ export async function GET(
"items.product.variants.options.*",
],
filters: {
id
id,
},
context: {
items: {
@@ -2111,16 +2112,16 @@ export async function GET(
currency_code,
}),
},
}
}
},
},
},
}, {
throwIfKeyNotFound: true
throwIfKeyNotFound: true,
})
res.json({
bundle_product: data[0]
bundle_product: data[0],
})
}
```
@@ -2283,7 +2284,7 @@ const pricedProduct = await listProducts({
countryCode: params.countryCode,
queryParams: {
handle: params.handle,
fields: "*bundle"
fields: "*bundle",
},
}).then(({ response }) => response.products[0])
```
@@ -2313,7 +2314,7 @@ export async function addBundleToCart({
bundleId,
quantity,
countryCode,
items
items,
}: {
bundleId: string
quantity: number
@@ -2344,7 +2345,7 @@ export async function addBundleToCart({
body: {
bundle_id: bundleId,
quantity,
items
items,
},
headers,
})
@@ -2442,7 +2443,7 @@ export const bundleActionsComponentHighlights2 = [
// For each product, if it has only 1 variant, preselect it
useEffect(() => {
const initialOptions: Record<string, Record<string, string>> = {}
bundle.items.forEach(item => {
bundle.items.forEach((item) => {
if (item.product.variants?.length === 1) {
const variantOptions = optionsAsKeymap(item.product.variants[0].options)
initialOptions[item.product.id] = variantOptions ?? {}
@@ -2454,10 +2455,10 @@ useEffect(() => {
}, [bundle.items])
const selectedVariants = useMemo(() => {
return bundle.items.map(item => {
if (!item.product.variants || item.product.variants.length === 0) return undefined
return bundle.items.map((item) => {
if (!item.product.variants || item.product.variants.length === 0) {return undefined}
return item.product.variants.find(v => {
return item.product.variants.find((v) => {
const variantOptions = optionsAsKeymap(v.options)
return isEqual(variantOptions, productOptions[item.product.id])
})
@@ -2465,17 +2466,17 @@ const selectedVariants = useMemo(() => {
}, [bundle.items, productOptions])
const setOptionValue = (productId: string, optionId: string, value: string) => {
setProductOptions(prev => ({
setProductOptions((prev) => ({
...prev,
[productId]: {
...prev[productId],
[optionId]: value,
}
},
}))
}
const allVariantsSelected = useMemo(() => {
return selectedVariants.every(v => v !== undefined)
return selectedVariants.every((v) => v !== undefined)
}, [selectedVariants])
// TODO handle add to cart
@@ -2497,7 +2498,7 @@ Replace the `TODO` in the `BundleActions` component with the following code:
```tsx title="src/modules/products/components/bundle-actions/index.tsx" badgeLabel="Storefront" badgeColor="blue"
const handleAddToCart = async () => {
if (!allVariantsSelected) return
if (!allVariantsSelected) {return}
setIsAdding(true)
await addBundleToCart({
@@ -2541,6 +2542,7 @@ Then, in the `return` statement, pass the `className` prop in the classes of the
className={clx("text-xl-semi", {
"text-ui-fg-interactive": selectedPrice.price_type === "sale",
}, className)}
>
{/* ... */}
</span>
</div>
@@ -2805,11 +2807,11 @@ export const removeBundleFromCartWorkflowHighlights = [
import {
createWorkflow,
transform,
WorkflowResponse
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
deleteLineItemsWorkflow,
useQueryGraphStep
useQueryGraphStep,
} from "@medusajs/medusa/core-flows"
type RemoveBundleFromCartWorkflowInput = {
@@ -2831,7 +2833,7 @@ export const removeBundleFromCartWorkflow = createWorkflow(
},
options: {
throwIfKeyNotFound: true,
}
},
})
const itemsToRemove = transform({
@@ -2847,7 +2849,7 @@ export const removeBundleFromCartWorkflow = createWorkflow(
input: {
cart_id,
ids: itemsToRemove,
}
},
})
// retrieve cart again
@@ -2886,10 +2888,10 @@ Next, you'll create the API route that exposes the workflow's functionality to s
Create the file `src/api/store/carts/[id]/line-item-bundles/[bundle_id]/route.ts` with the following content:
```ts title="src/api/store/carts/[id]/line-item-bundles/[bundle_id]/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import {
removeBundleFromCartWorkflow
} from "../../../../../../workflows/remove-bundle-from-cart";
removeBundleFromCartWorkflow,
} from "../../../../../../workflows/remove-bundle-from-cart"
export async function DELETE(
req: MedusaRequest,
@@ -2900,11 +2902,11 @@ export async function DELETE(
input: {
cart_id: req.params.id,
bundle_id: req.params.bundle_id,
}
},
})
res.json({
cart
cart,
})
}
```
@@ -0,0 +1,78 @@
"use client"
import { Brackets, CheckCircle, SquareTwoStack, Tag } from "@medusajs/icons"
import {
DropdownMenu,
H2,
H2Props,
H3,
H3Props,
useCopy,
useGenerateSnippet,
} from "docs-ui"
type EventHeaderProps = (
| {
headerLvl: "2"
headerProps: H2Props
}
| {
headerLvl: "3"
headerProps: H3Props
}
) & {
eventName: string
payload: string
}
export const EventHeader = ({
headerLvl,
headerProps,
eventName,
payload: payloadStr,
}: EventHeaderProps) => {
const Header = headerLvl === "2" ? H2 : H3
const { snippet } = useGenerateSnippet({
type: "subscriber",
options: {
event: eventName,
payload: payloadStr,
},
})
const { handleCopy: handleEventNameCopy, isCopied: eventNameCopied } =
useCopy(eventName)
const { handleCopy: handleSnippetCopy, isCopied: snippetCopied } =
useCopy(snippet)
return (
<div className="flex items-center justify-between flex-wrap">
<Header {...headerProps}>{eventName}</Header>
<DropdownMenu
dropdownButtonContent={
<>
{eventNameCopied || snippetCopied ? (
<CheckCircle />
) : (
<SquareTwoStack />
)}
</>
}
menuItems={[
{
type: "action",
title: "Copy event name",
action: () => handleEventNameCopy(),
icon: <Tag />,
},
{
type: "action",
title: "Copy subscriber for event",
action: () => handleSnippetCopy(),
icon: <Brackets />,
},
]}
menuClassName="z-10"
/>
</div>
)
}
@@ -10,8 +10,10 @@ import {
Table,
Badge,
Tooltip,
CopyGeneratedSnippetButton,
} from "docs-ui"
import { CommerceModuleSections } from "../CommerceModuleSections"
import { EventHeader } from "../EventHeader"
const MDXComponents: MDXComponentsType = {
...UiMdxComponents,
@@ -27,6 +29,8 @@ const MDXComponents: MDXComponentsType = {
Tooltip: (props) => {
return <Tooltip {...props} />
},
EventHeader,
CopyGeneratedSnippetButton,
}
export default MDXComponents
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -16,7 +16,7 @@
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@medusajs/icons": "~2.5.1",
"@medusajs/icons": "~2.7.1",
"@next/mdx": "15.3.1",
"clsx": "^2.1.0",
"docs-ui": "*",
+3 -3
View File
@@ -16,9 +16,9 @@
"dependencies": {
"@faker-js/faker": "^8.0.2",
"@mdx-js/react": "^3.1.0",
"@medusajs/icons": "2.6.0",
"@medusajs/ui": "4.0.6",
"@medusajs/ui-preset": "2.6.0",
"@medusajs/icons": "2.7.1",
"@medusajs/ui": "4.0.9",
"@medusajs/ui-preset": "2.7.1",
"autoprefixer": "10.4.14",
"build-scripts": "*",
"clsx": "^2.0.0",
+1 -1
View File
@@ -16,7 +16,7 @@
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@medusajs/icons": "~2.5.1",
"@medusajs/icons": "~2.7.1",
"@next/mdx": "15.0.4",
"clsx": "^2.1.0",
"docs-ui": "*",
+2 -2
View File
@@ -57,8 +57,8 @@
},
"dependencies": {
"@emotion/is-prop-valid": "^1.3.1",
"@medusajs/icons": "2.6.0",
"@medusajs/ui": "4.0.6",
"@medusajs/icons": "2.7.1",
"@medusajs/ui": "4.0.9",
"@next/third-parties": "15.3.1",
"@octokit/request": "^8.1.1",
"@react-hook/resize-observer": "^1.2.6",
@@ -5,6 +5,10 @@ import clsx from "clsx"
import { Tooltip } from "@/components"
import { useCopy } from "../../hooks"
export type CopyButtonChildFn = (props: {
isCopied: boolean
}) => React.ReactNode
export type CopyButtonProps = {
text: string
buttonClassName?: string
@@ -17,7 +21,8 @@ export type CopyButtonProps = {
| React.TouchEvent<HTMLSpanElement>
) => void
handleTouch?: boolean
} & Omit<React.HTMLAttributes<HTMLDivElement>, "onCopy">
children?: React.ReactNode | CopyButtonChildFn
} & Omit<React.HTMLAttributes<HTMLDivElement>, "onCopy" | "children">
export const CopyButton = ({
text,
@@ -64,7 +69,7 @@ export const CopyButton = ({
}
}}
>
{children}
{typeof children === "function" ? children({ isCopied }) : children}
</span>
</Tooltip>
)
@@ -0,0 +1,31 @@
"use client"
import React from "react"
import { CopyButton, useGenerateSnippet, UseGenerateSnippet } from "../.."
import { SquareTwoStack, CheckCircle } from "@medusajs/icons"
export type CopyGeneratedSnippetButtonProps = UseGenerateSnippet & {
tooltipText?: string
}
export const CopyGeneratedSnippetButton = ({
tooltipText,
...props
}: CopyGeneratedSnippetButtonProps) => {
const { snippet } = useGenerateSnippet(props)
return (
<CopyButton
text={snippet}
tooltipText={tooltipText}
className="inline-block w-fit"
>
{({ isCopied }) => {
if (isCopied) {
return <CheckCircle />
}
return <SquareTwoStack />
}}
</CopyButton>
)
}
@@ -2,7 +2,7 @@ import clsx from "clsx"
import React from "react"
import { LlmDropdown } from "../../LlmDropdown"
type H1Props = React.HTMLAttributes<HTMLHeadingElement> & {
export type H1Props = React.HTMLAttributes<HTMLHeadingElement> & {
id?: string
hideLlmDropdown?: boolean
}
@@ -5,7 +5,7 @@ import React from "react"
import { CopyButton, Link } from "@/components"
import { useHeadingUrl, useLayout } from "../../.."
type H2Props = React.HTMLAttributes<HTMLHeadingElement> & {
export type H2Props = React.HTMLAttributes<HTMLHeadingElement> & {
id?: string
passRef?: React.RefObject<HTMLHeadingElement | null>
}
@@ -5,7 +5,7 @@ import React from "react"
import { CopyButton, Link } from "@/components"
import { useHeadingUrl, useLayout } from "../../.."
type H3Props = React.HTMLAttributes<HTMLHeadingElement> & {
export type H3Props = React.HTMLAttributes<HTMLHeadingElement> & {
id?: string
}
@@ -7,7 +7,7 @@ export const H4 = ({
}: React.HTMLAttributes<HTMLHeadingElement>) => {
return (
<h4
className={clsx("mb-docs_0.5 text-medusa-fg-base text-h4", className)}
className={clsx("mb-docs_1 text-medusa-fg-base text-h4", className)}
{...props}
/>
)
@@ -3,9 +3,8 @@
import React, { useRef, useState } from "react"
import { useAiAssistant, useSiteConfig } from "../../providers"
import { usePathname } from "next/navigation"
import { Button } from "../Button"
import { AiAssistent, Book } from "@medusajs/icons"
import { Menu } from "../Menu"
import { DropdownMenu, Menu } from "../Menu"
import { MarkdownIcon } from "../Icons/Markdown"
import { useAiAssistantChat } from "../../providers/AiAssistant/Chat"
import clsx from "clsx"
@@ -30,43 +29,41 @@ export const LlmDropdown = () => {
const pageUrl = `${baseUrl}${basePath}${pathname}`
return (
<div className="relative hidden md:block">
<Button
variant="transparent"
onClick={() => setOpen(!open)}
className="!p-[6px] text-medusa-fg-subtle"
buttonRef={ref}
>
<Book />
</Button>
<Menu
items={[
{
type: "link",
title: "View as Markdown",
link: `${pageUrl}/index.html.md`,
icon: <MarkdownIcon width={19} height={19} />,
openInNewTab: true,
},
{
type: "action",
title: "Ask AI Assistant",
action: () => {
if (loading) {
return
}
setQuestion(`Explain the page ${pageUrl}`)
setChatOpened(true)
setOpen(false)
<DropdownMenu
open={open}
setOpen={setOpen}
dropdownButtonContent={<Book />}
menuComponent={
<Menu
items={[
{
type: "link",
title: "View as Markdown",
link: `${pageUrl}/index.html.md`,
icon: <MarkdownIcon width={19} height={19} />,
openInNewTab: true,
},
icon: <AiAssistent />,
},
]}
className={clsx(
"absolute right-0 top-[calc(100%+8px)] w-max",
!open && "hidden"
)}
/>
</div>
{
type: "action",
title: "Ask AI Assistant",
action: () => {
if (loading) {
return
}
setQuestion(`Explain the page ${pageUrl}`)
setChatOpened(true)
setOpen(false)
},
icon: <AiAssistent />,
},
]}
className={clsx(
"absolute right-0 top-[calc(100%+8px)] w-max",
!open && "hidden"
)}
/>
}
className="hidden md:block"
/>
)
}
@@ -0,0 +1,76 @@
"use client"
import React from "react"
import { useRef, useState } from "react"
import { Button, Menu, useClickOutside } from "../../.."
import { MenuItem } from "types"
import clsx from "clsx"
type DropdownMenuProps = {
dropdownButtonContent: React.ReactNode
dropdownButtonClassName?: string
menuComponent?: React.ReactNode
menuItems?: MenuItem[]
menuClassName?: string
className?: string
open?: boolean
setOpen?: (open: boolean) => void
}
export const DropdownMenu = ({
dropdownButtonContent,
dropdownButtonClassName,
menuComponent,
menuItems,
menuClassName,
className,
open: externalOpen = false,
setOpen: externalSetOpen,
}: DropdownMenuProps) => {
const [open, setOpen] = useState(externalOpen)
const ref = useRef<HTMLButtonElement | null>(null)
function changeOpenState(newOpenState: boolean) {
if (externalSetOpen) {
externalSetOpen(newOpenState)
} else {
setOpen(newOpenState)
}
}
useClickOutside({
elmRef: ref,
onClickOutside: () => {
changeOpenState(false)
},
})
if (!menuComponent && !menuItems) {
return null
}
return (
<div className={clsx("relative", className)}>
<Button
variant="transparent"
onClick={() => changeOpenState(!open)}
className={clsx(
"!p-[6px] text-medusa-fg-subtle",
dropdownButtonClassName
)}
buttonRef={ref}
>
{dropdownButtonContent}
</Button>
{menuComponent}
{!menuComponent && menuItems && (
<Menu
items={menuItems}
className={clsx(
"absolute right-0 top-[calc(100%+8px)] w-max",
!open && "hidden",
menuClassName
)}
/>
)}
</div>
)
}
@@ -39,3 +39,5 @@ export const Menu = ({ items, className, itemsOnClick }: MenuProps) => {
</div>
)
}
export * from "./Dropdown"
@@ -15,6 +15,7 @@ export * from "./CodeMdx"
export * from "./CodeTabs"
export * from "./CodeTabs/Item"
export * from "./CopyButton"
export * from "./CopyGeneratedSnippetButton"
export * from "./Details"
export * from "./Details/Summary"
export * from "./DetailsList"
+1
View File
@@ -5,6 +5,7 @@ export * from "./use-click-outside"
export * from "./use-collapsible"
export * from "./use-collapsible-code-lines"
export * from "./use-copy"
export * from "./use-generate-snippet"
export * from "./use-heading-url"
export * from "./use-current-learning-path"
export * from "./use-is-external-link"
@@ -0,0 +1,22 @@
import {
subscriberSnippetGenerator,
SubscriberSnippetGeneratorOptions,
} from "./snippet-generators/subscriber"
export type UseGenerateSnippet = {
type: "subscriber"
options: SubscriberSnippetGeneratorOptions
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const generators: Record<string, (options: any) => string> = {
subscriber: subscriberSnippetGenerator,
}
export const useGenerateSnippet = ({ type, options }: UseGenerateSnippet) => {
const snippet = generators[type](options)
return {
snippet,
}
}
@@ -0,0 +1,65 @@
import { parseEventPayload } from "../../../utils"
export type SubscriberSnippetGeneratorOptions = {
event: string
payload: Record<string, unknown> | string
}
export const subscriberSnippetGenerator = ({
event,
payload: initialPayload,
}: SubscriberSnippetGeneratorOptions) => {
const payload =
typeof initialPayload === "string"
? parseEventPayload(initialPayload).payload_for_snippet
: initialPayload
// format subscriber name
const subscriberName =
event
.split(".")
.map((word) =>
word.replace(/-./g, (match) => match.charAt(1).toUpperCase())
)
.map((word, index) => {
if (index === 0) {
return word.charAt(0).toLowerCase() + word.slice(1)
}
return word.charAt(0).toUpperCase() + word.slice(1)
})
.join("") + "Handler"
// format payload
const payloadType: Record<string, unknown> = {}
Object.keys(payload).forEach((key) => {
const value = payload[key]
if (Array.isArray(value)) {
payloadType[key] = `${typeof value[0]}[]`
} else {
payloadType[key] = typeof value
}
})
const payloadString = JSON.stringify(payloadType, null, 2).replaceAll(
/"/g,
""
)
// return the snippet
return subscriberSnippet
.replace("{{subscriberName}}", subscriberName)
.replace("{{event}}", event)
.replace("{{payload}}", payloadString)
}
const subscriberSnippet = `import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
export default async function {{subscriberName}}({
event: { data },
container,
}: SubscriberArgs<{{payload}}>) {
// TODO handle event
}
export const config: SubscriberConfig = {
event: "{{event}}",
}`
@@ -0,0 +1,54 @@
import { OpenAPI } from "types"
export function parseEventPayload(payloadStr: string) {
const payloadParams = payloadStr.matchAll(/([\w_]+),? \/\/ (\(\w*\) )*(.*)/g)
const payloadForSnippet: Record<string, unknown> = {}
const payload = Array.from(payloadParams).map((match) => {
const name = match[1]
const type = (match[2]?.replace(/\(|\)/g, "") || "string").trim()
const description = match[3]
if (type === "string") {
payloadForSnippet[name] = "test"
} else if (type === "number") {
payloadForSnippet[name] = 1
} else if (type === "boolean") {
payloadForSnippet[name] = true
} else if (type === "object") {
payloadForSnippet[name] = {}
} else if (type === "array") {
payloadForSnippet[name] = [{}]
}
return {
name,
type,
description,
}
})
return {
parsed_payload: {
type: "object",
required: ["payload"],
properties: {
payload: {
type: "object",
description: "The payload emitted with the event",
required: [...payload.map((param) => param.name)],
properties: payload.reduce(
(acc, curr) => {
acc[curr.name] = {
type: curr.type as OpenAPI.OpenAPIV3.NonArraySchemaObjectType,
description: curr.description,
properties: {},
}
return acc
},
{} as Record<string, OpenAPI.SchemaObject>
),
},
},
} as OpenAPI.SchemaObject,
payload_for_snippet: payloadForSnippet,
}
}
+1
View File
@@ -3,6 +3,7 @@ export * from "./capitalize"
export * from "./check-sidebar-item-visibility"
export * from "./decode-str"
export * from "./dom-utils"
export * from "./event-parser"
export * from "./get-link-with-base-path"
export * from "./get-local-search"
export * from "./get-navbar-items"
+20 -2
View File
@@ -5,6 +5,8 @@ import {
Expression,
ExpressionJsVar,
ExpressionJsVarLiteral,
JSXElementExpression,
JSXTextExpression,
LiteralExpression,
ObjectExpression,
} from "types"
@@ -64,7 +66,23 @@ function expressionToJs(
data: (expression as LiteralExpression).value,
} as ExpressionJsVarLiteral
case "JSXElement":
// ignore JSXElements
return
case "JSXFragment":
// Only take text children
let text = ""
;(expression as JSXElementExpression).children.forEach((child) => {
if (child.type !== "JSXText") {
return
}
text += (child as JSXTextExpression).value
})
return {
original: {
type: "Literal",
value: text,
raw: `"${text}"`,
},
data: text,
}
}
}
@@ -14,6 +14,7 @@ import {
parseComponentExample,
parseComponentReference,
parseDetails,
parseEventHeader,
parseHookValues,
parseIconSearch,
parseNote,
@@ -48,6 +49,7 @@ const parsers: Record<string, ComponentParser> = {
HookValues: parseHookValues,
Colors: parseColors,
SplitList: parseSplitList,
EventHeader: parseEventHeader,
}
const isComponentAllowed = (nodeName: string): boolean => {
+51 -1
View File
@@ -951,6 +951,54 @@ export const parseSplitList: ComponentParser = (
return [SKIP, index]
}
export const parseEventHeader: ComponentParser = (
node: UnistNodeWithData,
index: number,
parent: UnistTree
): VisitorResult => {
const headerContent = node.attributes?.find(
(attr) => attr.name === "headerProps"
)
const headerLvl = node.attributes?.find((attr) => attr.name === "headerLvl")
if (
!headerContent ||
typeof headerContent.value === "string" ||
!headerContent.value.data?.estree ||
!headerLvl ||
typeof headerLvl.value !== "string" ||
!headerLvl.value
) {
return
}
const headerPropsJsVar = estreeToJs(headerContent.value.data.estree)
if (
!isExpressionJsVarObj(headerPropsJsVar) ||
!("children" in headerPropsJsVar) ||
!isExpressionJsVarLiteral(headerPropsJsVar.children)
) {
return
}
const headerLevel = parseInt(headerLvl.value, 10)
const headerChildren = headerPropsJsVar.children.data as string
const headerChildrenNode = {
type: "text",
value: headerChildren,
}
const headerNode = {
type: "heading",
depth: headerLevel,
children: [headerChildrenNode],
}
parent?.children.splice(index, 1, headerNode)
return [SKIP, index]
}
/**
* Helpers
*/
@@ -964,11 +1012,13 @@ const getTextNode = (node: UnistNode): UnistNode | undefined => {
textNode = child
} else if (child.type === "paragraph") {
textNode = getTextNode(child)
} else if (child.type === "link") {
textNode = getTextNode(child)
} else if (child.value) {
textNode = child
}
return false
return textNode !== undefined
})
return textNode
+1 -1
View File
@@ -32,7 +32,7 @@
"watch": "tsc --watch"
},
"devDependencies": {
"@medusajs/icons": "2.6.0",
"@medusajs/icons": "2.7.1",
"@types/node": "^20.11.20",
"rimraf": "^5.0.5",
"tsconfig": "*",
+13
View File
@@ -37,6 +37,17 @@ export type LiteralExpression = {
raw: string
}
export type JSXElementExpression = {
type: "JSXElement" | "JSXFragment"
children: Expression[]
}
export type JSXTextExpression = {
type: "JSXText"
value: string
raw: string
}
export type Expression =
| {
type: string
@@ -44,6 +55,8 @@ export type Expression =
| ArrayExpression
| ObjectExpression
| LiteralExpression
| JSXElementExpression
| JSXTextExpression
export interface Estree {
body?: {
@@ -67,7 +67,7 @@ function formatEventsType(
eventVariable.name.replaceAll("WorkflowEvents", "")
)
if (showHeader) {
content.push(`## ${header} Events`)
content.push(`${"#".repeat(subtitleLevel - 1)} ${header} Events`)
}
content.push("")
@@ -93,9 +93,7 @@ function formatEventsType(
.find((tag) => tag.tag === "@eventName")
?.content.map((content) => content.text)
.join("") || ""
eventName = `[${eventName}](#${slugify(eventName.replace(".", ""), {
lower: true,
})})`
eventName = `[${eventName}](#${getEventNameSlug(eventName)})`
const eventDescription = event.comment?.summary
.map((content) => content.text)
.join("")
@@ -149,32 +147,30 @@ function formatEventsType(
const deprecatedTag = event.comment?.blockTags.find(
(tag) => tag.tag === "@deprecated"
)
const deprecatedMessage = deprecatedTag?.content
.map((content) => content.text)
.join("")
.trim()
content.push(`${subHeaderPrefix} \`${eventName}\``)
content.push(
getEventHeading({
titleLevel: subtitleLevel,
eventName: eventName || "",
payload: eventPayload || "",
deprecated: !!deprecatedTag,
deprecatedMessage,
})
)
content.push("")
if (deprecatedTag) {
const deprecationText = deprecatedTag.content
.map((content) => content.text)
.join("")
.trim()
if (deprecationText.length) {
content.push(`<Tooltip text="${deprecationText}">`)
}
content.push(`<Badge variant="orange">Deprecated</Badge>`)
if (deprecationText.length) {
content.push(`</Tooltip>`)
}
content.push("")
}
content.push(eventDescription || "")
content.push("")
content.push(`${subHeaderPrefix}# Payload`)
content.push("")
content.push(eventPayload || "")
content.push("")
content.push(`${subHeaderPrefix}# Workflows Emitting this Event`)
content.push(
`${subHeaderPrefix}# Workflows Emitting this Event\n\nThe following workflows emit this event when they're executed. These workflows are executed by Medusa's API routes. You can also view the events emitted by API routes in the [Store](https://docs.medusajs.com/api/store) and [Admin](https://docs.medusajs.com/api/admin) API references.`
)
content.push("")
workflows?.forEach((workflow) => {
content.push(`- [${workflow}](/references/medusa-workflows/${workflow})`)
@@ -188,3 +184,42 @@ function formatEventsType(
return content.join("\n")
}
function getEventHeading({
titleLevel,
eventName,
payload,
deprecated = false,
deprecatedMessage,
}: {
titleLevel: number
eventName: string
payload: string
deprecated?: boolean
deprecatedMessage?: string
}) {
const heading = [eventName]
if (deprecated) {
if (deprecatedMessage?.length) {
heading.push(`<Tooltip text="${deprecatedMessage}">`)
}
heading.push(`<Badge variant="orange">Deprecated</Badge>`)
if (deprecatedMessage?.length) {
heading.push(`</Tooltip>`)
}
}
return `<EventHeader headerLvl="${titleLevel}" headerProps={{ id: "${getEventNameSlug(
eventName
)}", children: (<>${heading.join(
"\n"
)}</>), className: "flex flex-wrap justify-center gap-docs_0.25" }} eventName="${eventName}" payload={\`${payload.replaceAll(
"`",
"\\`"
)}\`} />`
}
function getEventNameSlug(eventName: string) {
return slugify(eventName.replace(".", ""), { lower: true })
}
@@ -25,6 +25,7 @@ export default function () {
str += ` <Table.HeaderCell>\nEvent\n</Table.HeaderCell>\n`
str += ` <Table.HeaderCell>\nDescription\n</Table.HeaderCell>\n`
str += ` <Table.HeaderCell>\nPayload\n</Table.HeaderCell>\n`
str += ` <Table.HeaderCell>\nAction\n</Table.HeaderCell>\n`
str += ` </Table.Row>\n`
str += ` </Table.Header>\n`
str += ` <Table.Body>\n`
@@ -33,29 +34,35 @@ export default function () {
.map((c) => c.text)
.join(" ")
.split("--")
let eventName = `\`${commentContent[0].trim()}\``
const eventName = commentContent[0].trim()
const eventPayload = commentContent[2]?.trim() || ""
let eventNameFormatted = `\`${eventName}\``
const eventDescription = commentContent[1]?.trim() || ""
const eventPayload = (commentContent[2]?.trim() || "")
const eventPayloadFormatted = eventPayload
.replace("```ts\n", "")
.replace("\n```", "")
const isDeprecated = commentContent.length >= 4
if (isDeprecated) {
const deprecatedText = commentContent[4]?.trim()
eventName += `\n`
eventNameFormatted += `\n`
if (deprecatedText) {
eventName += `<Tooltip text="${deprecatedText}">`
eventNameFormatted += `<Tooltip text="${deprecatedText}">`
}
eventName += `<Badge variant="orange">Deprecated</Badge>`
eventNameFormatted += `<Badge variant="orange">Deprecated</Badge>`
if (deprecatedText) {
eventName += `</Tooltip>`
eventNameFormatted += `</Tooltip>`
}
}
str += ` <Table.Row>\n`
str += ` <Table.Cell>\n${eventName}\n</Table.Cell>\n`
str += ` <Table.Cell>\n${eventNameFormatted}\n</Table.Cell>\n`
str += ` <Table.Cell>\n${eventDescription}\n</Table.Cell>\n`
str += ` <Table.Cell>\n\`\`\`ts blockStyle="inline"\n${eventPayload}\n\`\`\`\n</Table.Cell>\n`
str += ` <Table.Cell>\n\`\`\`ts blockStyle="inline"\n${eventPayloadFormatted}\n\`\`\`\n</Table.Cell>\n`
str += ` <Table.Cell>\n<CopyGeneratedSnippetButton tooltipText="Copy subscriber for event" type="subscriber" options={{
event: "${eventName}",
payload: \`${eventPayload.replaceAll("`", "\\`")}\`
}}/>\n</Table.Cell>\n`
str += ` </Table.Row>\n`
})
str += ` </Table.Body>\n`
+32 -29
View File
@@ -1868,21 +1868,12 @@ __metadata:
languageName: node
linkType: hard
"@medusajs/icons@npm:2.6.0":
version: 2.6.0
resolution: "@medusajs/icons@npm:2.6.0"
"@medusajs/icons@npm:2.7.1, @medusajs/icons@npm:~2.7.1":
version: 2.7.1
resolution: "@medusajs/icons@npm:2.7.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
checksum: cb8628018398aba010bb7b1f653cb7dc91d9bd59a40f1e7ee4cdc7732559e75062db790fb609fd3f748a9b80ccc0f421267a414ed9f5a7a4e079ef643e65333e
languageName: node
linkType: hard
"@medusajs/icons@npm:^2.5.1, @medusajs/icons@npm:~2.5.1":
version: 2.5.1
resolution: "@medusajs/icons@npm:2.5.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
checksum: f7fbe17475d8c39bd584103ec662f90aa5c8f07159f5820648eba8a0adf42a0d48057518da5a22503e5d5eaca552cbb7b26792613e2ec84827be1bbbafb64201
checksum: 6c271671b8ca5d912b14c8f6e91adcd9312e4290b13e9fabe2ad6eb2635051c769fadaa7f2157c90161d8a90f57fd5fb6f8b24b4f86024248414871478f0997c
languageName: node
linkType: hard
@@ -1898,11 +1889,23 @@ __metadata:
languageName: node
linkType: hard
"@medusajs/ui@npm:4.0.6":
version: 4.0.6
resolution: "@medusajs/ui@npm:4.0.6"
"@medusajs/ui-preset@npm:2.7.1":
version: 2.7.1
resolution: "@medusajs/ui-preset@npm:2.7.1"
dependencies:
"@medusajs/icons": ^2.5.1
"@tailwindcss/forms": ^0.5.3
tailwindcss-animate: ^1.0.6
peerDependencies:
tailwindcss: ">=3.0.0"
checksum: 6b67c5a614e1a8f985cac256c7a363292ca3326ee02a067ec310fec4b3b24b36726e5bc34427b75efb3a395cba2c4e7ba7bd97acc2fb2bb25004570b979e63c0
languageName: node
linkType: hard
"@medusajs/ui@npm:4.0.9":
version: 4.0.9
resolution: "@medusajs/ui@npm:4.0.9"
dependencies:
"@medusajs/icons": 2.7.1
"@tanstack/react-table": 8.20.5
clsx: ^1.2.1
copy-to-clipboard: ^3.3.3
@@ -1918,7 +1921,7 @@ __metadata:
peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
checksum: ff45b909b3573fd6b443eee0aac47a2f13dbd145ed161e0485f7e22e1e963345e2e38dba236d1b927715feaa79d3dc79671bdeb2075c6f83682dcf8827e6f962
checksum: 17f265fa8075cd26f54cda0ef96ffc9afe853fdf10bd42d82ce830f64bc0b932e93f43a9863606a482875fc1f6214a8acb43fd2a6c4fc8f593fa6a4521fbae76
languageName: node
linkType: hard
@@ -6237,8 +6240,8 @@ __metadata:
dependencies:
"@mdx-js/loader": ^3.1.0
"@mdx-js/react": ^3.1.0
"@medusajs/icons": 2.6.0
"@medusajs/ui": 4.0.6
"@medusajs/icons": 2.7.1
"@medusajs/ui": 4.0.9
"@next/bundle-analyzer": 15.3.1
"@next/mdx": 15.3.1
"@react-hook/resize-observer": ^2.0.2
@@ -6617,7 +6620,7 @@ __metadata:
dependencies:
"@mdx-js/loader": ^3.1.0
"@mdx-js/react": ^3.1.0
"@medusajs/icons": ~2.5.1
"@medusajs/icons": ~2.7.1
"@next/mdx": 15.3.1
"@types/mdx": ^2.0.13
"@types/node": ^20
@@ -7979,8 +7982,8 @@ __metadata:
resolution: "docs-ui@workspace:packages/docs-ui"
dependencies:
"@emotion/is-prop-valid": ^1.3.1
"@medusajs/icons": 2.6.0
"@medusajs/ui": 4.0.6
"@medusajs/icons": 2.7.1
"@medusajs/ui": 4.0.9
"@next/third-parties": 15.3.1
"@octokit/request": ^8.1.1
"@react-hook/resize-observer": ^1.2.6
@@ -15011,7 +15014,7 @@ __metadata:
dependencies:
"@mdx-js/loader": ^3.1.0
"@mdx-js/react": ^3.1.0
"@medusajs/icons": ~2.5.1
"@medusajs/icons": ~2.7.1
"@next/bundle-analyzer": ^15.1.1
"@next/mdx": 15.3.1
"@types/mdx": ^2.0.13
@@ -16737,7 +16740,7 @@ turbo@latest:
version: 0.0.0-use.local
resolution: "types@workspace:packages/types"
dependencies:
"@medusajs/icons": 2.6.0
"@medusajs/icons": 2.7.1
"@types/node": ^20.11.20
openapi-types: ^12.1.3
rimraf: ^5.0.5
@@ -16826,9 +16829,9 @@ turbo@latest:
dependencies:
"@faker-js/faker": ^8.0.2
"@mdx-js/react": ^3.1.0
"@medusajs/icons": 2.6.0
"@medusajs/ui": 4.0.6
"@medusajs/ui-preset": 2.6.0
"@medusajs/icons": 2.7.1
"@medusajs/ui": 4.0.9
"@medusajs/ui-preset": 2.7.1
"@types/node": 20.4.9
"@types/react": "npm:types-react@rc"
"@types/react-dom": "npm:types-react@rc"
@@ -17220,7 +17223,7 @@ turbo@latest:
dependencies:
"@mdx-js/loader": ^3.1.0
"@mdx-js/react": ^3.1.0
"@medusajs/icons": ~2.5.1
"@medusajs/icons": ~2.7.1
"@next/mdx": 15.0.4
"@types/mdx": ^2.0.13
"@types/node": ^20