docs: prep for v2 documentation (#6710)

This PR includes documentation that preps for v2 docs (but doesn't introduce new docs).

_Note: The number of file changes in the PR is due to find-and-replace within the `references` which is unavoidable. Let me know if I should move it to another PR._

## Changes

- Change Medusa version in base OAS used for v2.
- Fix to docblock generator related to not catching all path parameters.
- Added typedoc plugin that generates ER Diagrams, which will be used specifically for data model references in commerce modules.
- Changed OAS tool to output references in `www/apps/api-reference/specs-v2` directory when the `--v2` option is used.
- Added a version switcher to the API reference to switch between V1 and V2. This switcher is enabled by an environment variable, so it won't be visible/usable at the moment.
- Upgraded docusaurus to v3.0.1
- Added new Vale rules to ensure correct spelling of Medusa Admin and module names.
- Added new components to the `docs-ui` package that will be used in future documentation changes.
This commit is contained in:
Shahed Nasser
2024-03-18 09:47:35 +02:00
committed by GitHub
parent 56a6ec0227
commit bb87db8342
2008 changed files with 15716 additions and 10536 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/oas-github-ci": patch
---
feat(oas-github-ci): output specs into `specs-v2` directory when the `--v2` option is passed

View File

@@ -12,6 +12,7 @@ write-good.E-Prime=No
write-good.So=No write-good.So=No
write-good.ThereIs=No write-good.ThereIs=No
Vale.Terms=No Vale.Terms=No
BlockIgnores={/\* [\s\S]+ \*/}
[formats] [formats]
mdx = md mdx = md

View File

@@ -1,6 +1,6 @@
openapi: 3.0.0 openapi: 3.0.0
info: info:
version: 1.0.0 version: 2.0.0
title: Medusa Admin API title: Medusa Admin API
license: license:
name: MIT name: MIT

View File

@@ -1,6 +1,6 @@
openapi: 3.0.0 openapi: 3.0.0
info: info:
version: 1.0.0 version: 2.0.0
title: Medusa Storefront API title: Medusa Storefront API
license: license:
name: MIT name: MIT

View File

@@ -28,7 +28,7 @@ import OasSchemaHelper from "../helpers/oas-schema.js"
import formatOas from "../../utils/format-oas.js" import formatOas from "../../utils/format-oas.js"
import { DEFAULT_OAS_RESPONSES } from "../../constants.js" import { DEFAULT_OAS_RESPONSES } from "../../constants.js"
export const API_ROUTE_PARAM_REGEX = /\[(.+)\]/g export const API_ROUTE_PARAM_REGEX = /\[(.+?)\]/g
const RES_STATUS_REGEX = /^res[\s\S]*\.status\((\d+)\)/ const RES_STATUS_REGEX = /^res[\s\S]*\.status\((\d+)\)/
type SchemaDescriptionOptions = { type SchemaDescriptionOptions = {
@@ -112,9 +112,12 @@ class OasKindGenerator extends FunctionKindGenerator {
// and the second of type `MedusaResponse` // and the second of type `MedusaResponse`
return ( return (
(functionNode.parameters.length === 2 && (functionNode.parameters.length === 2 &&
functionNode.parameters[0].type (functionNode.parameters[0].type
?.getText() ?.getText()
.startsWith("MedusaRequest") && .startsWith("MedusaRequest") ||
functionNode.parameters[0].type
?.getText()
.startsWith("AuthenticatedMedusaRequest")) &&
functionNode.parameters[1].type functionNode.parameters[1].type
?.getText() ?.getText()
.startsWith("MedusaResponse")) || .startsWith("MedusaResponse")) ||
@@ -988,26 +991,32 @@ class OasKindGenerator extends FunctionKindGenerator {
*/ */
tagName?: string tagName?: string
}): OpenAPIV3.ParameterObject[] { }): OpenAPIV3.ParameterObject[] {
const pathParameters = API_ROUTE_PARAM_REGEX.exec(oasPath)?.slice(1) // reset regex manually
API_ROUTE_PARAM_REGEX.lastIndex = 0
let pathParameters: string[] | undefined
const parameters: OpenAPIV3.ParameterObject[] = [] const parameters: OpenAPIV3.ParameterObject[] = []
while (
if (pathParameters?.length) { (pathParameters = API_ROUTE_PARAM_REGEX.exec(oasPath)?.slice(1)) !==
pathParameters.forEach((parameter) => undefined
parameters.push( ) {
this.getParameterObject({ if (pathParameters.length) {
type: "path", pathParameters.forEach((parameter) =>
name: parameter, parameters.push(
description: this.getSchemaDescription({ this.getParameterObject({
typeStr: parameter, type: "path",
parentName: tagName, name: parameter,
}), description: this.getSchemaDescription({
required: true, typeStr: parameter,
schema: { parentName: tagName,
type: "string", }),
}, required: true,
}) schema: {
type: "string",
},
})
)
) )
) }
} }
return parameters return parameters

View File

@@ -71,10 +71,8 @@ module.exports = {
showCommentsAsHeader: true, showCommentsAsHeader: true,
sections: baseSectionsOptions, sections: baseSectionsOptions,
parameterStyle: "component", parameterStyle: "component",
parameterComponent: "ParameterTypes", parameterComponent: "TypeList",
mdxImports: [ mdxImports: [`import TypeList from "@site/src/components/TypeList"`],
`import ParameterTypes from "@site/src/components/ParameterTypes"`,
],
}, },
internal: { internal: {
maxLevel: 1, maxLevel: 1,
@@ -231,7 +229,7 @@ npx medusa develop
reflectionTitle: { reflectionTitle: {
kind: false, kind: false,
typeParameters: false, typeParameters: false,
suffix: " Reference", suffix: "Reference",
}, },
}, },
@@ -638,7 +636,7 @@ npx medusa develop
reflectionTitle: { reflectionTitle: {
kind: false, kind: false,
typeParameters: false, typeParameters: false,
suffix: " Reference", suffix: "Reference",
}, },
}, },
@@ -681,7 +679,7 @@ npx medusa develop
reflectionTitle: { reflectionTitle: {
kind: false, kind: false,
typeParameters: false, typeParameters: false,
suffix: " Reference", suffix: "Reference",
}, },
}, },
@@ -780,7 +778,7 @@ npx medusa develop
reflectionTitle: { reflectionTitle: {
kind: false, kind: false,
typeParameters: false, typeParameters: false,
suffix: " Reference", suffix: "Reference",
}, },
}, },

View File

@@ -5,6 +5,7 @@ import { load as parseOasSchemaPlugin } from "./parse-oas-schema-plugin"
import { load as apiIgnorePlugin } from "./api-ignore" import { load as apiIgnorePlugin } from "./api-ignore"
import { load as eslintExamplePlugin } from "./eslint-example" import { load as eslintExamplePlugin } from "./eslint-example"
import { load as signatureModifierPlugin } from "./signature-modifier" import { load as signatureModifierPlugin } from "./signature-modifier"
import { MermaidDiagramGenerator } from "./mermaid-diagram-generator"
import { load as parentIgnorePlugin } from "./parent-ignore" import { load as parentIgnorePlugin } from "./parent-ignore"
import { GenerateNamespacePlugin } from "./generate-namespace" import { GenerateNamespacePlugin } from "./generate-namespace"
@@ -18,4 +19,5 @@ export function load(app: Application) {
parentIgnorePlugin(app) parentIgnorePlugin(app)
new GenerateNamespacePlugin(app) new GenerateNamespacePlugin(app)
new MermaidDiagramGenerator(app)
} }

View File

@@ -0,0 +1,302 @@
import path from "path"
import {
Application,
Comment,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReferenceType,
Reflection,
ReflectionKind,
TypeDocOptionMap,
} from "typedoc"
import ts from "typescript"
type RelationType =
| "one-to-one"
| "one-to-many"
| "many-to-one"
| "many-to-many"
type Relations = Map<
string,
{
target: string
left: RelationType
right?: RelationType
name: string
}[]
>
type PluginOptions = Pick<
TypeDocOptionMap,
"generateModelsDiagram" | "diagramAddToFile"
>
export class MermaidDiagramGenerator {
private app: Application
private options?: PluginOptions
private mainFileReflection?: Reflection
constructor(app: Application) {
this.app = app
this.app.options.addDeclaration({
name: "generateModelsDiagram",
help: "Whether to generate a Mermaid.js class diagram for data models in the reference.",
type: ParameterType.Boolean,
defaultValue: false,
})
this.app.options.addDeclaration({
name: "diagramAddToFile",
help: "The file to add the mermaid diagram to. The diagram is added as a package comment.",
type: ParameterType.String,
})
app.converter.on(
Converter.EVENT_CREATE_DECLARATION,
this.setMainFile.bind(this)
)
app.converter.on(
Converter.EVENT_RESOLVE_BEGIN,
this.findRelations.bind(this)
)
}
getPluginOptions(): PluginOptions {
if (this.options) {
return this.options
}
this.options = {
generateModelsDiagram: this.app.options.getValue("generateModelsDiagram"),
diagramAddToFile: this.app.options.getValue("diagramAddToFile"),
}
return this.options
}
setMainFile(context: Context) {
const options = this.getPluginOptions()
if (
this.mainFileReflection ||
!options.generateModelsDiagram ||
!options.diagramAddToFile
) {
return
}
const mainFilePath = options.diagramAddToFile.startsWith("packages")
? path.resolve("..", "..", "..", options.diagramAddToFile)
: options.diagramAddToFile
const mainFileSource = context.program.getSourceFile(mainFilePath)
if (!mainFileSource) {
// console.error(
// `Couldn't fine the main source file ${options.diagramAddToFile}`
// )
return
}
const mainFileSymbol = context.checker.getSymbolAtLocation(mainFileSource)
if (!mainFileSymbol) {
// console.error(`Couldn't fine the main file's symbol`)
return
}
this.mainFileReflection =
context.project.getReflectionFromSymbol(mainFileSymbol)
if (!this.mainFileReflection) {
// console.error(`Couldn't fine the main file's reflection`)
}
}
findRelations(context: Context) {
const options = this.getPluginOptions()
if (
!this.mainFileReflection ||
!options.generateModelsDiagram ||
!options.diagramAddToFile
) {
return
}
const relations: Relations = new Map()
for (const reflection of context.project.getReflectionsByKind(
ReflectionKind.Class
)) {
if (!(reflection instanceof DeclarationReflection)) {
return
}
// find relations of that reflection
reflection.children?.forEach((child) => {
let referenceType: ReferenceType | undefined
// check that the child field references another reflection
if (child.type?.type === "reference") {
referenceType = child.type
} else if (child.type?.type === "union") {
referenceType = child.type.types.find(
(unionType) => unionType.type === "reference"
) as ReferenceType
}
if (!referenceType) {
return
}
// If type is Collection from mikro-orm, get the type argument's reflection
// otherwise, use the reflection as-is
const targetReflection =
this.isMikroOrmCollection(referenceType) &&
referenceType.typeArguments?.length &&
referenceType.typeArguments[0].type === "reference" &&
referenceType.typeArguments[0].reflection
? referenceType.typeArguments[0].reflection
: referenceType.reflection
if (!targetReflection) {
return
}
// if entry already exists in relation, don't add anything
const exists =
relations
.get(reflection.name)
?.some((relation) => relation.target === targetReflection.name) ||
relations
.get(targetReflection.name)
?.some((relation) => relation.target === reflection.name)
if (exists) {
return
}
// figure out relation type from decorators
const relationType = this.getRelationFromDecorators(
this.getReflectionDecorators(context, child)
)
if (!relationType) {
return
}
if (!relations.has(reflection.name)) {
relations.set(reflection.name, [])
}
relations.get(reflection.name)?.push({
target: targetReflection.name,
left: relationType,
right: this.getReverseRelationType(relationType),
name: child.name,
})
})
}
if (!relations.size) {
return
}
this.mainFileReflection.comment = new Comment([
{
text: "## Relations Overview\n\n",
kind: "text",
},
{
text: this.buildMermaidDiagram(relations),
kind: "code",
},
])
}
isMikroOrmCollection(referenceType: ReferenceType) {
return (
referenceType.name === "Collection" &&
referenceType.package?.includes("@mikro-orm")
)
}
getReflectionDecorators(context: Context, reflection: Reflection): string[] {
const symbol = context.project.getSymbolFromReflection(reflection)
const decorators: string[] = []
symbol?.declarations?.forEach((declaration) => {
const modifiers =
"modifiers" in declaration && declaration.modifiers
? (declaration.modifiers as ts.NodeArray<ts.Modifier>)
: []
modifiers.forEach((modifier) => {
if (!ts.isDecorator(modifier)) {
return
}
;(modifier as ts.Decorator).forEachChild((childNode) => {
if (!ts.isCallExpression(childNode)) {
return
}
const childNodeExpression = (childNode as ts.CallExpression)
.expression
if (!ts.isIdentifier(childNodeExpression)) {
return
}
decorators.push(childNodeExpression.escapedText.toString())
})
})
})
return decorators
}
getRelationFromDecorators(decorators: string[]): RelationType | undefined {
switch (true) {
case decorators.includes("OneToOne"):
return "one-to-one"
case decorators.includes("OneToMany"):
return "one-to-many"
case decorators.includes("ManyToOne"):
return "many-to-one"
case decorators.includes("ManyToMany"):
return "many-to-many"
}
}
getReverseRelationType(relationType: RelationType): RelationType {
return relationType.split("-").reverse().join("-") as RelationType
}
buildMermaidDiagram(relations: Relations): string {
const linePrefix = `\t`
const lineSuffix = `\n`
let diagram = `erDiagram${lineSuffix}`
relations.forEach((itemRelations, itemName) => {
itemRelations.forEach((itemRelation) => {
diagram += `${linePrefix}${itemName} ${this.getRelationTypeSymbol(
itemRelation.left,
"left"
)}--${this.getRelationTypeSymbol(itemRelation.right!, "right")} ${
itemRelation.target
} : ${itemRelation.name}${lineSuffix}`
})
})
return "```mermaid\n" + diagram + "\n```"
}
getRelationTypeSymbol(
relationType: RelationType,
direction: "left" | "right"
): string {
switch (relationType) {
case "one-to-one":
return "||"
case "one-to-many":
return direction === "left" ? "||" : "|{"
case "many-to-many":
return direction === "left" ? "}|" : "|{"
case "many-to-one":
return direction === "left" ? "}|" : "||"
}
}
}

View File

@@ -54,7 +54,7 @@ export function formatParameterComponent({
} }
// reorder component items to show required items first // reorder component items to show required items first
componentItems = sortComponentItems(componentItems) componentItems = sortComponentItems(componentItems)
return `<${parameterComponent} parameters={${JSON.stringify( return `<${parameterComponent} types={${JSON.stringify(
componentItems componentItems
)}} ${extraPropsArr.join(" ")} sectionTitle="${sectionTitle}"/>` )}} ${extraPropsArr.join(" ")} sectionTitle="${sectionTitle}"/>`
} }

View File

@@ -217,5 +217,13 @@ export declare module "typedoc" {
* @defaultValue false * @defaultValue false
*/ */
checkVariables: boolean checkVariables: boolean
/**
* Whether to generate a Mermaid.js class diagram for data models in the reference.
*/
generateModelsDiagram: boolean
/**
* The file to add the mermaid diagram to. The diagram is added as a package comment.
*/
diagramAddToFile: string
} }
} }

View File

@@ -10,7 +10,8 @@ const withFullFile = process.argv.indexOf("--with-full-file") !== -1
const v2 = process.argv.indexOf("--v2") !== -1 const v2 = process.argv.indexOf("--v2") !== -1
const basePath = path.resolve(__dirname, `../`) const basePath = path.resolve(__dirname, `../`)
const repoRootPath = path.resolve(basePath, `../../../`) const repoRootPath = path.resolve(basePath, `../../../`)
const docsApiPath = path.resolve(repoRootPath, "www/apps/api-reference/specs") const docsApiPath = v2 ? path.resolve(repoRootPath, "www/apps/api-reference/specs-v2") :
path.resolve(repoRootPath, "www/apps/api-reference/specs")
const run = async () => { const run = async () => {
const oasOutDir = isDryRun ? await getTmpDirectory() : docsApiPath const oasOutDir = isDryRun ? await getTmpDirectory() : docsApiPath

View File

@@ -11,4 +11,5 @@ NEXT_PUBLIC_UI_URL=
ALGOLIA_WRITE_API_KEY= ALGOLIA_WRITE_API_KEY=
NEXT_PUBLIC_AI_ASSISTANT_URL= NEXT_PUBLIC_AI_ASSISTANT_URL=
NEXT_PUBLIC_AI_WEBSITE_ID= NEXT_PUBLIC_AI_WEBSITE_ID=
NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY=
NEXT_PUBLIC_VERSIONING=

View File

@@ -7,5 +7,5 @@ module.exports = {
next: { next: {
rootDir: ".", rootDir: ".",
}, },
}, }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import { CodeTabs } from "docs-ui" import { CodeTabs, CodeTab } from "docs-ui"
import Space from "@/components/Space" import Space from "@/components/Space"
import DownloadFull from "@/components/DownloadFull" import DownloadFull from "@/components/DownloadFull"
@@ -10,26 +10,22 @@ Check out the [quickstart guide](https://docs.medusajs.com/create-medusa-app).
### Client Libraries ### Client Libraries
<CodeTabs <CodeTabs group="clients">
tabs={[ <CodeTab label="Medusa JS Client" value="js-client">
{
label: 'Medusa JS Client', ```bash
value: 'js-client', npm install @medusajs/medusa-js
code: { ```
source: `npm install @medusajs/medusa-js`,
lang: `bash`, </CodeTab>
} <CodeTab label="Medusa React" value="medusa-react">
},
{ ```bash
label: 'Medusa React', npm install medusa-react @tanstack/react-query @medusajs/medusa
value: 'medusa-react', ```
code: {
source: `npm install medusa-react @tanstack/react-query @medusajs/medusa`, </CodeTab>
lang: `bash`, </CodeTabs>
}
}
]}
/>
### Download Full Reference ### Download Full Reference

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,9 @@
import clsx from "clsx" import "../../globals.css"
import "../../../css/globals.css"
import Navbar from "@/components/Navbar" import Navbar from "@/components/Navbar"
import { Inter } from "next/font/google"
import { Roboto_Mono } from "next/font/google"
import Providers from "../../../providers" import Providers from "../../../providers"
import { Sidebar } from "docs-ui" import { WideLayout } from "docs-ui"
import { Inter, Roboto_Mono } from "next/font/google"
import clsx from "clsx"
export const metadata = { export const metadata = {
title: "Medusa API Reference", title: "Medusa API Reference",
@@ -28,31 +27,12 @@ export default function RootLayout({
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<html lang="en" className={clsx("h-full w-full")}> <WideLayout
<body ProvidersComponent={Providers}
className={clsx( NavbarComponent={Navbar}
inter.variable, bodyClassName={clsx(inter.variable, robotoMono.variable)}
robotoMono.variable, >
"bg-docs-bg font-base text-medium w-full", {children}
"text-medusa-fg-subtle", </WideLayout>
"h-screen overflow-hidden"
)}
>
<Providers>
<Navbar />
<div
className="w-full h-[calc(100%-57px)] overflow-y-scroll overflow-x-hidden"
id="main"
>
<div className="max-w-xxl mx-auto flex w-full px-1.5">
<Sidebar />
<main className="lg:w-ref-main relative mt-4 w-full flex-1 lg:mt-7">
{children}
</main>
</div>
</div>
</Providers>
</body>
</html>
) )
} }

View File

@@ -8,6 +8,7 @@ import type { Area } from "@/types/openapi"
import DividedLayout from "@/layouts/Divided" import DividedLayout from "@/layouts/Divided"
import { capitalize } from "docs-ui" import { capitalize } from "docs-ui"
import PageTitleProvider from "../../../providers/page-title" import PageTitleProvider from "../../../providers/page-title"
import PageHeading from "../../../components/PageHeading"
type ReferencePageProps = { type ReferencePageProps = {
params: { params: {
@@ -19,15 +20,11 @@ const ReferencePage = async ({ params: { area } }: ReferencePageProps) => {
return ( return (
<AreaProvider area={area}> <AreaProvider area={area}>
<PageTitleProvider> <PageTitleProvider>
<h1 className="!text-h2 block lg:hidden"> <PageHeading className="!text-h2 block lg:hidden" />
Medusa {capitalize(area)} API Reference
</h1>
<DividedLayout <DividedLayout
mainContent={ mainContent={
<Section> <Section>
<h1 className="!text-h2 hidden lg:block"> <PageHeading className="!text-h2 hidden lg:block" />
Medusa {capitalize(area)} API Reference
</h1>
{area.includes("admin") && <AdminDescription />} {area.includes("admin") && <AdminDescription />}
{area.includes("store") && <StoreDescription />} {area.includes("store") && <StoreDescription />}
</Section> </Section>

View File

@@ -2,11 +2,15 @@ import { NextResponse } from "next/server"
import path from "path" import path from "path"
import OpenAPIParser from "@readme/openapi-parser" import OpenAPIParser from "@readme/openapi-parser"
import getPathsOfTag from "@/utils/get-paths-of-tag" import getPathsOfTag from "@/utils/get-paths-of-tag"
import type { ExpandedDocument } from "@/types/openapi" import type { ExpandedDocument, Version } from "@/types/openapi"
export async function GET(request: Request) { export async function GET(request: Request) {
const { searchParams } = new URL(request.url) const { searchParams } = new URL(request.url)
const area = searchParams.get("area") const area = searchParams.get("area")
const version =
process.env.NEXT_PUBLIC_VERSIONING === "true"
? (searchParams.get("version") as Version) || "1"
: "1"
const expand = searchParams.get("expand") const expand = searchParams.get("expand")
if (area !== "admin" && area !== "store") { if (area !== "admin" && area !== "store") {
return NextResponse.json( return NextResponse.json(
@@ -20,7 +24,11 @@ export async function GET(request: Request) {
) )
} }
const baseSpecs = (await OpenAPIParser.parse( const baseSpecs = (await OpenAPIParser.parse(
path.join(process.cwd(), `specs/${area}/openapi.yaml`) path.join(
process.cwd(),
version === "1" ? "specs" : "specs-v2",
`${area}/openapi.yaml`
)
)) as ExpandedDocument )) as ExpandedDocument
if (expand) { if (expand) {

View File

@@ -1,11 +1,16 @@
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import path from "path" import path from "path"
import getPathsOfTag from "@/utils/get-paths-of-tag" import getPathsOfTag from "@/utils/get-paths-of-tag"
import { Version } from "../../../types/openapi"
export async function GET(request: Request) { export async function GET(request: Request) {
const { searchParams } = new URL(request.url) const { searchParams } = new URL(request.url)
const tagName = searchParams.get("tagName") || "" const tagName = searchParams.get("tagName") || ""
const area = searchParams.get("area") const area = searchParams.get("area")
const version =
process.env.NEXT_PUBLIC_VERSIONING === "true"
? (searchParams.get("version") as Version) || "1"
: "1"
if (area !== "admin" && area !== "store") { if (area !== "admin" && area !== "store") {
return NextResponse.json( return NextResponse.json(
@@ -26,9 +31,15 @@ export async function GET(request: Request) {
path.join(process.cwd(), "specs/store/code_samples") path.join(process.cwd(), "specs/store/code_samples")
path.join(process.cwd(), "specs/store/components") path.join(process.cwd(), "specs/store/components")
path.join(process.cwd(), "specs/store/paths") path.join(process.cwd(), "specs/store/paths")
path.join(process.cwd(), "specs-v2/admin/code_samples")
path.join(process.cwd(), "specs-v2/admin/components")
path.join(process.cwd(), "specs-v2/admin/paths")
path.join(process.cwd(), "specs-v2/store/code_samples")
path.join(process.cwd(), "specs-v2/store/components")
path.join(process.cwd(), "specs-v2/store/paths")
// get path files // get path files
const paths = await getPathsOfTag(tagName, area) const paths = await getPathsOfTag(tagName, area, version)
return NextResponse.json( return NextResponse.json(
{ {

View File

@@ -1,14 +1,14 @@
import { MetadataRoute } from "next" import { MetadataRoute } from "next"
import OpenAPIParser from "@readme/openapi-parser" import OpenAPIParser from "@readme/openapi-parser"
import path from "path" import path from "path"
import getBaseUrl from "../../utils/get-base-url"
import type { ExpandedDocument, Operation } from "../../types/openapi" import type { ExpandedDocument, Operation } from "../../types/openapi"
import getUrl from "../../utils/get-url" import getUrl from "../../utils/get-url"
import getSectionId from "../../utils/get-section-id" import getSectionId from "../../utils/get-section-id"
import getPathsOfTag from "../../utils/get-paths-of-tag" import getPathsOfTag from "../../utils/get-paths-of-tag"
import { config } from "../../config"
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = getBaseUrl() const baseUrl = config.baseUrl
const results = [ const results = [
{ {

View File

@@ -2,7 +2,7 @@ import type { MDXComponents } from "mdx/types"
import Security from "./Security" import Security from "./Security"
import type { OpenAPIV3 } from "openapi-types" import type { OpenAPIV3 } from "openapi-types"
import H2 from "./H2" import H2 from "./H2"
import { CodeMdx, Kbd, NextLink } from "docs-ui" import { Link, MDXComponents as UiMDXComponents } from "docs-ui"
export type ScopeType = { export type ScopeType = {
specs?: OpenAPIV3.Document specs?: OpenAPIV3.Document
@@ -11,11 +11,12 @@ export type ScopeType = {
const getCustomComponents = (scope?: ScopeType): MDXComponents => { const getCustomComponents = (scope?: ScopeType): MDXComponents => {
return { return {
...UiMDXComponents,
Security: () => <Security specs={scope?.specs} />, Security: () => <Security specs={scope?.specs} />,
code: CodeMdx, a: Link,
a: NextLink, h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
h2: (props) => <H2 addToSidebar={scope?.addToSidebar} {...props} />, <H2 addToSidebar={scope?.addToSidebar} {...props} />
kbd: Kbd, ),
} }
} }

View File

@@ -20,6 +20,9 @@ const MDXContentServer = ({ content, ...props }: MDXContentServerProps) => {
components={getCustomComponents((props.scope as ScopeType) || {})} components={getCustomComponents((props.scope as ScopeType) || {})}
options={{ options={{
scope: props.scope, scope: props.scope,
mdxOptions: {
development: process.env.NEXT_PUBLIC_ENV === "development",
},
}} }}
{...props} {...props}
/> />

View File

@@ -1,47 +1,48 @@
"use client" "use client"
import { Navbar as UiNavbar, usePageLoading } from "docs-ui" import {
import getLinkWithBasePath from "../../utils/get-link-with-base-path" Navbar as UiNavbar,
import { useSidebar } from "docs-ui" getNavbarItems,
usePageLoading,
useSidebar,
} from "docs-ui"
import FeedbackModal from "./FeedbackModal" import FeedbackModal from "./FeedbackModal"
import { useMemo } from "react"
import { config } from "../../config"
import { usePathname } from "next/navigation"
import VersionSwitcher from "../VersionSwitcher"
const Navbar = () => { const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar() const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
const pathname = usePathname()
const { isLoading } = usePageLoading() const { isLoading } = usePageLoading()
const navbarItems = useMemo(
() =>
getNavbarItems({
basePath: config.baseUrl,
activePath: pathname,
}),
[pathname]
)
return ( return (
<UiNavbar <UiNavbar
logo={{ logo={{
light: "/images/logo-icon.png", light: "/images/logo-icon.png",
dark: "/images/logo-icon-dark.png", dark: "/images/logo-icon-dark.png",
}} }}
items={[ items={navbarItems}
{
href: `/`,
label: "Docs",
},
{
href: `/user-guide`,
label: "User Guide",
},
{
href: `${getLinkWithBasePath("/store")}`,
label: "Store API",
},
{
href: `${getLinkWithBasePath("/admin")}`,
label: "Admin API",
},
{
href: `/ui`,
label: "UI",
},
]}
mobileMenuButton={{ mobileMenuButton={{
setMobileSidebarOpen, setMobileSidebarOpen,
mobileSidebarOpen, mobileSidebarOpen,
}} }}
additionalActions={<FeedbackModal />} additionalActionsBefore={
<>
{process.env.NEXT_PUBLIC_VERSIONING === "true" && <VersionSwitcher />}
</>
}
additionalActionsAfter={<FeedbackModal />}
isLoading={isLoading} isLoading={isLoading}
/> />
) )

View File

@@ -0,0 +1,25 @@
"use client"
import { capitalize } from "docs-ui"
import { useArea } from "../../providers/area"
import { useVersion } from "../../providers/version"
type PageHeadingProps = {
className?: string
}
const PageHeading = ({ className }: PageHeadingProps) => {
const { area } = useArea()
const { version } = useVersion()
const versionText =
process.env.NEXT_PUBLIC_VERSIONING === "true" ? ` V${version}` : ""
return (
<h1 className={className}>
Medusa{versionText} {capitalize(area)} API Reference
</h1>
)
}
export default PageHeading

View File

@@ -1,5 +1,5 @@
import type { Code } from "@/types/openapi" import type { Code } from "@/types/openapi"
import { CodeTabs } from "docs-ui" import { LegacyCodeTabs } from "docs-ui"
import slugify from "slugify" import slugify from "slugify"
export type TagOperationCodeSectionRequestSamplesProps = { export type TagOperationCodeSectionRequestSamplesProps = {
@@ -12,7 +12,7 @@ const TagOperationCodeSectionRequestSamples = ({
return ( return (
<div> <div>
<h3>Request samples</h3> <h3>Request samples</h3>
<CodeTabs <LegacyCodeTabs
tabs={codeSamples.map((codeSample) => ({ tabs={codeSamples.map((codeSample) => ({
label: codeSample.label, label: codeSample.label,
value: slugify(codeSample.label), value: slugify(codeSample.label),

View File

@@ -8,7 +8,7 @@ import dynamic from "next/dynamic"
import TagsOperationDescriptionSectionParameters from "./Parameters" import TagsOperationDescriptionSectionParameters from "./Parameters"
import MDXContentClient from "@/components/MDXContent/Client" import MDXContentClient from "@/components/MDXContent/Client"
import { useArea } from "../../../../providers/area" import { useArea } from "../../../../providers/area"
import { Feedback, Badge, NextLink, FeatureFlagNotice } from "docs-ui" import { Feedback, Badge, Link, FeatureFlagNotice } from "docs-ui"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import formatReportLink from "../../../../utils/format-report-link" import formatReportLink from "../../../../utils/format-report-link"
@@ -70,9 +70,9 @@ const TagsOperationDescriptionSection = ({
{operation.externalDocs && ( {operation.externalDocs && (
<> <>
Related guide:{" "} Related guide:{" "}
<NextLink href={operation.externalDocs.url} target="_blank"> <Link href={operation.externalDocs.url} target="_blank">
{operation.externalDocs.description || "Read More"} {operation.externalDocs.description || "Read More"}
</NextLink> </Link>
</> </>
)} )}
{operation.security && ( {operation.security && (

View File

@@ -1,4 +1,4 @@
import { Badge, NextLink, Tooltip } from "docs-ui" import { Badge, Link, Tooltip } from "docs-ui"
export type TagsOperationFeatureFlagNoticeProps = { export type TagsOperationFeatureFlagNoticeProps = {
featureFlag: string featureFlag: string
@@ -19,12 +19,12 @@ const TagsOperationFeatureFlagNotice = ({
<span className={tooltipTextClassName}> <span className={tooltipTextClassName}>
To use this {type}, make sure to To use this {type}, make sure to
<br /> <br />
<NextLink <Link
href="https://docs.medusajs.com/development/feature-flags/toggle" href="https://docs.medusajs.com/development/feature-flags/toggle"
target="__blank" target="__blank"
> >
enable its feature flag: <code>{featureFlag}</code> enable its feature flag: <code>{featureFlag}</code>
</NextLink> </Link>
</span> </span>
} }
clickable clickable

View File

@@ -3,7 +3,7 @@ import type { SchemaObject } from "@/types/openapi"
import clsx from "clsx" import clsx from "clsx"
import dynamic from "next/dynamic" import dynamic from "next/dynamic"
import { Fragment } from "react" import { Fragment } from "react"
import { NextLink, type InlineCodeProps, capitalize } from "docs-ui" import { Link, type InlineCodeProps, capitalize } from "docs-ui"
const InlineCode = dynamic<InlineCodeProps>( const InlineCode = dynamic<InlineCodeProps>(
async () => (await import("docs-ui")).InlineCode async () => (await import("docs-ui")).InlineCode
@@ -122,9 +122,9 @@ const TagOperationParametersDescription = ({
{schema.externalDocs && ( {schema.externalDocs && (
<> <>
Related guide:{" "} Related guide:{" "}
<NextLink href={schema.externalDocs.url} target="_blank"> <Link href={schema.externalDocs.url} target="_blank">
{schema.externalDocs.description || "Read More"} {schema.externalDocs.description || "Read More"}
</NextLink> </Link>
</> </>
)} )}
</div> </div>

View File

@@ -1,11 +1,5 @@
import type { SchemaObject } from "@/types/openapi" import type { SchemaObject } from "@/types/openapi"
import dynamic from "next/dynamic" import { Badge, ExpandableNotice, FeatureFlagNotice } from "docs-ui"
import type { TooltipProps } from "docs-ui"
import { Badge, ExpandableNotice, FeatureFlagNotice, NextLink } from "docs-ui"
const Tooltip = dynamic<TooltipProps>(
async () => (await import("docs-ui")).Tooltip
) as React.FC<TooltipProps>
export type TagOperationParametersNameProps = { export type TagOperationParametersNameProps = {
name: string name: string
@@ -37,7 +31,7 @@ const TagOperationParametersName = ({
<br /> <br />
<FeatureFlagNotice <FeatureFlagNotice
featureFlag={schema["x-featureFlag"]} featureFlag={schema["x-featureFlag"]}
type="parameter" type="type"
/> />
</> </>
)} )}

View File

@@ -4,22 +4,18 @@ import getSectionId from "@/utils/get-section-id"
import type { OpenAPIV3 } from "openapi-types" import type { OpenAPIV3 } from "openapi-types"
import useSWR from "swr" import useSWR from "swr"
import type { Operation, PathsObject } from "@/types/openapi" import type { Operation, PathsObject } from "@/types/openapi"
import { import { useSidebar, swrFetcher, getLinkWithBasePath } from "docs-ui"
SidebarItemSections,
useSidebar,
type SidebarItemType,
swrFetcher,
} from "docs-ui"
import { Fragment, useEffect, useMemo } from "react" import { Fragment, useEffect, useMemo } from "react"
import dynamic from "next/dynamic" import dynamic from "next/dynamic"
import type { TagOperationProps } from "../Operation" import type { TagOperationProps } from "../Operation"
import { useArea } from "@/providers/area" import { useArea } from "@/providers/area"
import getLinkWithBasePath from "@/utils/get-link-with-base-path"
import clsx from "clsx" import clsx from "clsx"
import { useBaseSpecs } from "@/providers/base-specs" import { useBaseSpecs } from "@/providers/base-specs"
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items" import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
import { useLoading } from "@/providers/loading" import { useLoading } from "@/providers/loading"
import DividedLoading from "@/components/DividedLoading" import DividedLoading from "@/components/DividedLoading"
import { SidebarItemSections, SidebarItemType } from "types"
import { useVersion } from "../../../providers/version"
const TagOperation = dynamic<TagOperationProps>( const TagOperation = dynamic<TagOperationProps>(
async () => import("../Operation") async () => import("../Operation")
@@ -32,6 +28,7 @@ export type TagPathsProps = {
const TagPaths = ({ tag, className }: TagPathsProps) => { const TagPaths = ({ tag, className }: TagPathsProps) => {
const tagSlugName = useMemo(() => getSectionId([tag.name]), [tag]) const tagSlugName = useMemo(() => getSectionId([tag.name]), [tag])
const { area } = useArea() const { area } = useArea()
const { version } = useVersion()
const { items, addItems, findItemInSection } = useSidebar() const { items, addItems, findItemInSection } = useSidebar()
const { baseSpecs } = useBaseSpecs() const { baseSpecs } = useBaseSpecs()
const { loading } = useLoading() const { loading } = useLoading()
@@ -47,7 +44,10 @@ const TagPaths = ({ tag, className }: TagPathsProps) => {
paths: PathsObject paths: PathsObject
}>( }>(
!Object.keys(paths).length !Object.keys(paths).length
? getLinkWithBasePath(`/tag?tagName=${tagSlugName}&area=${area}`) ? getLinkWithBasePath(
`/tag?tagName=${tagSlugName}&area=${area}&version=${version}`,
process.env.NEXT_PUBLIC_BASE_PATH
)
: null, : null,
swrFetcher, swrFetcher,
{ {

View File

@@ -15,7 +15,7 @@ import SectionContainer from "../../Section/Container"
import { useArea } from "../../../providers/area" import { useArea } from "../../../providers/area"
import SectionDivider from "../../Section/Divider" import SectionDivider from "../../Section/Divider"
import clsx from "clsx" import clsx from "clsx"
import { Feedback, Loading, NextLink } from "docs-ui" import { Feedback, Loading, Link } from "docs-ui"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import formatReportLink from "../../../utils/format-report-link" import formatReportLink from "../../../utils/format-report-link"
@@ -100,9 +100,9 @@ const TagSection = ({ tag }: TagSectionProps) => {
{tag.externalDocs && ( {tag.externalDocs && (
<> <>
Related guide:{" "} Related guide:{" "}
<NextLink href={tag.externalDocs.url} target="_blank"> <Link href={tag.externalDocs.url} target="_blank">
{tag.externalDocs.description || "Read More"} {tag.externalDocs.description || "Read More"}
</NextLink> </Link>
</> </>
)} )}
<Feedback <Feedback

View File

@@ -7,11 +7,12 @@ import { useBaseSpecs } from "@/providers/base-specs"
import dynamic from "next/dynamic" import dynamic from "next/dynamic"
import type { TagSectionProps } from "./Section" import type { TagSectionProps } from "./Section"
import { useArea } from "@/providers/area" import { useArea } from "@/providers/area"
import getLinkWithBasePath from "@/utils/get-link-with-base-path" import { swrFetcher, useSidebar, getLinkWithBasePath } from "docs-ui"
import { SidebarItemSections, swrFetcher, useSidebar } from "docs-ui"
import getSectionId from "@/utils/get-section-id" import getSectionId from "@/utils/get-section-id"
import { ExpandedDocument } from "@/types/openapi" import { ExpandedDocument } from "@/types/openapi"
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items" import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
import { SidebarItemSections } from "types"
import { useVersion } from "../../providers/version"
const TagSection = dynamic<TagSectionProps>( const TagSection = dynamic<TagSectionProps>(
async () => import("./Section") async () => import("./Section")
@@ -32,10 +33,14 @@ const Tags = () => {
const { baseSpecs, setBaseSpecs } = useBaseSpecs() const { baseSpecs, setBaseSpecs } = useBaseSpecs()
const { addItems } = useSidebar() const { addItems } = useSidebar()
const { area } = useArea() const { area } = useArea()
const { version } = useVersion()
const { data } = useSWR<ExpandedDocument>( const { data } = useSWR<ExpandedDocument>(
loadData && !baseSpecs loadData && !baseSpecs
? getLinkWithBasePath(`/base-specs?area=${area}&expand=${expand}`) ? getLinkWithBasePath(
`/base-specs?area=${area}&expand=${expand}&version=${version}`,
process.env.NEXT_PUBLIC_BASE_PATH
)
: null, : null,
swrFetcher, swrFetcher,
{ {

View File

@@ -0,0 +1,22 @@
"use client"
import { Note } from "docs-ui"
import { useVersion } from "../../providers/version"
const VersionNote = () => {
const { version } = useVersion()
return (
<>
{version === "2" && (
<Note type="warning" title="Production Warning">
Medusa v2.0 is in development and not suitable for production
environments. As such, the API reference is incomplete and subject to
change, so please use it with caution.
</Note>
)}
</>
)
}
export default VersionNote

View File

@@ -0,0 +1,36 @@
"use client"
import { Toggle } from "docs-ui"
import { useVersion } from "../../providers/version"
import clsx from "clsx"
const VersionSwitcher = () => {
const { version, changeVersion } = useVersion()
return (
<div className="flex gap-0.5 justify-center items-center">
<span
className={clsx(
version === "1" && "text-medusa-fg-subtle",
version === "2" && "text-medusa-fg-disabled"
)}
>
V1
</span>
<Toggle
checked={version === "2"}
onCheckedChange={(checked) => changeVersion(checked ? "2" : "1")}
/>
<span
className={clsx(
version === "1" && "text-medusa-fg-disabled",
version === "2" && "text-medusa-fg-subtle"
)}
>
V2
</span>
</div>
)
}
export default VersionSwitcher

View File

@@ -0,0 +1,18 @@
import { DocsConfig } from "types"
import { mobileSidebarItems } from "docs-ui"
export const config: DocsConfig = {
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
// sidebar is auto generated
sidebar: {
top: [
{
title: "Introduction",
path: "",
loaded: true,
},
],
bottom: [],
mobile: mobileSidebarItems,
},
}

View File

@@ -33,6 +33,7 @@ const withMDX = mdx({
extension: /\.mdx?$/, extension: /\.mdx?$/,
options: { options: {
rehypePlugins: [], rehypePlugins: [],
development: process.env.NODE_ENV === "development",
}, },
}) })

View File

@@ -7,21 +7,23 @@
"dev:monorepo": "yarn dev -p 3000", "dev:monorepo": "yarn dev -p 3000",
"build": "next build", "build": "next build",
"build:dev": "NODE_ENV=test next build", "build:dev": "NODE_ENV=test next build",
"build:prod": "NEXT_PUBLIC_ENV=production next build",
"start": "next start", "start": "next start",
"start:monorepo": "yarn start -p 3000", "start:monorepo": "yarn start -p 3000",
"lint": "next lint --fix" "lint": "next lint --fix"
}, },
"dependencies": { "dependencies": {
"@mdx-js/loader": "^2.3.0", "@mdx-js/loader": "^3.0.0",
"@mdx-js/react": "^2.3.0", "@mdx-js/react": "^3.0.0",
"@medusajs/icons": "^1.2.0", "@medusajs/icons": "^1.2.0",
"@next/mdx": "13.4.19", "@medusajs/ui": "^2.4.1",
"@next/mdx": "14.1.3",
"@readme/openapi-parser": "^2.5.0", "@readme/openapi-parser": "^2.5.0",
"@types/mapbox__rehype-prism": "^0.8.0", "@types/mapbox__rehype-prism": "^0.8.0",
"@types/mdx": "^2.0.5", "@types/mdx": "^2.0.5",
"@types/node": "20.4.5", "@types/node": "20.4.5",
"@types/react": "18.2.17", "@types/react": "^18.2.0",
"@types/react-dom": "18.2.7", "@types/react-dom": "^18.2.0",
"@types/react-transition-group": "^4.4.6", "@types/react-transition-group": "^4.4.6",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"clsx": "^2.0.0", "clsx": "^2.0.0",
@@ -30,14 +32,14 @@
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"json-schema": "^0.4.0", "json-schema": "^0.4.0",
"json-stringify-pretty-compact": "^4.0.0", "json-stringify-pretty-compact": "^4.0.0",
"next": "^14", "next": "^14.1.3",
"next-mdx-remote": "^4.4.1", "next-mdx-remote": "^4.4.1",
"openapi-sampler": "^1.3.1", "openapi-sampler": "^1.3.1",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"postcss": "8.4.27", "postcss": "8.4.27",
"prism-react-renderer": "2.3.1", "prism-react-renderer": "2.3.1",
"react": "latest", "react": "^18.2.0",
"react-dom": "latest", "react-dom": "^18.2.0",
"react-instantsearch": "^7.0.1", "react-instantsearch": "^7.0.1",
"react-intersection-observer": "^9.5.3", "react-intersection-observer": "^9.5.3",
"react-tooltip": "^5.19.0", "react-tooltip": "^5.19.0",
@@ -50,8 +52,9 @@
"yaml": "^2.3.1" "yaml": "^2.3.1"
}, },
"devDependencies": { "devDependencies": {
"@next/bundle-analyzer": "^13.4.19", "@next/bundle-analyzer": "^14.1.3",
"@types/jsdom": "^21.1.1" "@types/jsdom": "^21.1.1",
"types": "*"
}, },
"engines": { "engines": {
"node": ">=18.17.0" "node": ">=18.17.0"

View File

@@ -1,74 +0,0 @@
"use client"
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react"
type ColorMode = "light" | "dark"
type ColorModeContextType = {
colorMode: ColorMode
setColorMode: (value: ColorMode) => void
toggleColorMode: () => void
}
const ColorModeContext = createContext<ColorModeContextType | null>(null)
type ColorModeProviderProps = {
children: React.ReactNode
}
const ColorModeProvider = ({ children }: ColorModeProviderProps) => {
const [colorMode, setColorMode] = useState<ColorMode>("light")
const toggleColorMode = () =>
setColorMode(colorMode === "light" ? "dark" : "light")
const init = () => {
const theme = localStorage.getItem("theme")
if (theme && (theme === "light" || theme === "dark")) {
setColorMode(theme)
}
}
useEffect(() => {
init()
}, [])
useEffect(() => {
const theme = localStorage.getItem("theme")
if (theme !== colorMode) {
localStorage.setItem("theme", colorMode)
}
document.querySelector("html")?.setAttribute("data-theme", colorMode)
}, [colorMode])
return (
<ColorModeContext.Provider
value={{
colorMode,
setColorMode,
toggleColorMode,
}}
>
{children}
</ColorModeContext.Provider>
)
}
export default ColorModeProvider
export const useColorMode = (): ColorModeContextType => {
const context = useContext(ColorModeContext)
if (!context) {
throw new Error("useColorMode must be used inside a ColorModeProvider")
}
return context
}

View File

@@ -1,18 +1,17 @@
"use client" "use client"
import { import {
AiAssistantProvider,
AnalyticsProvider, AnalyticsProvider,
ColorModeProvider, ColorModeProvider,
MobileProvider, MobileProvider,
ModalProvider, ModalProvider,
NavbarProvider,
PageLoadingProvider, PageLoadingProvider,
ScrollControllerProvider, ScrollControllerProvider,
} from "docs-ui" } from "docs-ui"
import BaseSpecsProvider from "./base-specs" import BaseSpecsProvider from "./base-specs"
import SidebarProvider from "./sidebar" import SidebarProvider from "./sidebar"
import SearchProvider from "./search" import SearchProvider from "./search"
import VersionProvider from "./version"
type ProvidersProps = { type ProvidersProps = {
children?: React.ReactNode children?: React.ReactNode
@@ -27,11 +26,11 @@ const Providers = ({ children }: ProvidersProps) => {
<BaseSpecsProvider> <BaseSpecsProvider>
<ScrollControllerProvider scrollableSelector="#main"> <ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider> <SidebarProvider>
<NavbarProvider> <SearchProvider>
<SearchProvider> <MobileProvider>
<MobileProvider>{children}</MobileProvider> <VersionProvider>{children}</VersionProvider>
</SearchProvider> </MobileProvider>
</NavbarProvider> </SearchProvider>
</SidebarProvider> </SidebarProvider>
</ScrollControllerProvider> </ScrollControllerProvider>
</BaseSpecsProvider> </BaseSpecsProvider>

View File

@@ -5,8 +5,9 @@ import {
SearchProvider as UiSearchProvider, SearchProvider as UiSearchProvider,
AiAssistantCommandIcon, AiAssistantCommandIcon,
AiAssistantProvider, AiAssistantProvider,
searchFilters,
} from "docs-ui" } from "docs-ui"
import getBaseUrl from "../utils/get-base-url" import { config } from "../config"
type SearchProviderProps = { type SearchProviderProps = {
children: React.ReactNode children: React.ReactNode
@@ -29,47 +30,30 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
isLoading, isLoading,
suggestions: [ suggestions: [
{ {
title: "Search Suggestions", title: "Getting started? Try one of the following terms.",
items: [ items: [
"Authentication", "Install Medusa with create-medusa-app",
"Expanding fields", "Next.js quickstart",
"Selecting fields", "Admin dashboard quickstart",
"Pagination", "Commerce modules",
"Query parameter types", "Medusa architecture",
],
},
{
title: "Developing with Medusa",
items: [
"Recipes",
"How to create API routes",
"How to create an entity",
"How to create a plugin",
"How to create an admin widget",
], ],
}, },
], ],
checkInternalPattern: new RegExp(`^${getBaseUrl()}/api/(admin|store)`), checkInternalPattern: new RegExp(
filterOptions: [ `^${config.baseUrl}/api/(admin|store)`
{ ),
value: "admin", filterOptions: searchFilters,
label: "Admin API",
},
{
value: "store",
label: "Store API",
},
{
value: "docs",
label: "Docs",
},
{
value: "user-guide",
label: "User Guide",
},
{
value: "plugins",
label: "Plugins",
},
{
value: "reference",
label: "References",
},
{
value: "ui",
label: "UI",
},
],
}} }}
commands={[ commands={[
{ {

View File

@@ -1,9 +1,11 @@
"use client" "use client"
import { import {
SidebarProvider as UiSidebarProvider, SidebarProvider as UiSidebarProvider,
mobileSidebarItems,
usePageLoading, usePageLoading,
useScrollController, useScrollController,
} from "docs-ui" } from "docs-ui"
import { config } from "../config"
type SidebarProviderProps = { type SidebarProviderProps = {
children?: React.ReactNode children?: React.ReactNode
@@ -19,42 +21,7 @@ const SidebarProvider = ({ children }: SidebarProviderProps) => {
setIsLoading={setIsLoading} setIsLoading={setIsLoading}
shouldHandleHashChange={true} shouldHandleHashChange={true}
scrollableElement={scrollableElement} scrollableElement={scrollableElement}
initialItems={{ initialItems={config.sidebar}
top: [
{
title: "Introduction",
path: "",
loaded: true,
},
],
bottom: [],
mobile: [
{
title: "Docs",
path: "https://docs.medusajs.com/",
loaded: true,
isPathHref: true,
},
{
title: "User Guide",
path: "https://docs.medusajs.com/user-guide",
loaded: true,
isPathHref: true,
},
{
title: "Store API",
path: "/api/store",
loaded: true,
isPathHref: true,
},
{
title: "Admin API",
path: "/api/admin",
loaded: true,
isPathHref: true,
},
],
}}
> >
{children} {children}
</UiSidebarProvider> </UiSidebarProvider>

View File

@@ -0,0 +1,74 @@
"use client"
import { createContext, useContext, useEffect, useState } from "react"
import { Version } from "../types/openapi"
import { usePathname } from "next/navigation"
import { useIsBrowser } from "docs-ui"
type VersionContextType = {
version: Version
changeVersion: (value: Version) => void
}
const VersionContext = createContext<VersionContextType | null>(null)
type VersionProviderProps = {
children: React.ReactNode
}
const VersionProvider = ({ children }: VersionProviderProps) => {
const pathname = usePathname()
const [version, setVersion] = useState<Version>("1")
const isBrowser = useIsBrowser()
const changeVersion = (version: Version) => {
if (!isBrowser || process.env.NEXT_PUBLIC_VERSIONING !== "true") {
return
}
localStorage.setItem("api-version", version)
location.href = `${location.href.substring(
0,
location.href.indexOf(location.pathname)
)}${pathname}`
}
useEffect(() => {
if (!isBrowser || process.env.NEXT_PUBLIC_VERSIONING !== "true") {
return
}
// try to load from localstorage
const versionInLocalStorage = localStorage.getItem("api-version") as Version
if (versionInLocalStorage) {
setVersion(versionInLocalStorage)
}
}, [isBrowser])
useEffect(() => {
if (!isBrowser || process.env.NEXT_PUBLIC_VERSIONING !== "true") {
return
}
const versionInLocalStorage = localStorage.getItem("api-version") as Version
if (version !== versionInLocalStorage) {
localStorage.setItem("api-version", version)
}
}, [version, isBrowser])
return (
<VersionContext.Provider value={{ version, changeVersion }}>
{children}
</VersionContext.Provider>
)
}
export default VersionProvider
export const useVersion = (): VersionContextType => {
const context = useContext(VersionContext)
if (!context) {
throw new Error("useVersion must be used inside an VersionProvider")
}
return context
}

View File

@@ -1,6 +1,7 @@
import type { OpenAPIV3 } from "openapi-types" import type { OpenAPIV3 } from "openapi-types"
export type Area = "admin" | "store" export type Area = "admin" | "store"
export type Version = "1" | "2"
export type Code = { export type Code = {
lang: string lang: string

View File

@@ -1,3 +0,0 @@
export default function getBaseUrl() {
return process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"
}

View File

@@ -1,3 +0,0 @@
export default function getLinkWithBasePath(path: string): string {
return `${process.env.NEXT_PUBLIC_BASE_PATH || "/api"}${path}`
}

View File

@@ -1,7 +1,7 @@
import path from "path" import path from "path"
import { promises as fs } from "fs" import { promises as fs } from "fs"
import type { OpenAPIV3 } from "openapi-types" import type { OpenAPIV3 } from "openapi-types"
import type { Operation, Document } from "@/types/openapi" import type { Operation, Document, Version } from "@/types/openapi"
import readSpecDocument from "./read-spec-document" import readSpecDocument from "./read-spec-document"
import getSectionId from "./get-section-id" import getSectionId from "./get-section-id"
import OpenAPIParser from "@readme/openapi-parser" import OpenAPIParser from "@readme/openapi-parser"
@@ -12,10 +12,15 @@ type ParsedPathItemObject = OpenAPIV3.PathItemObject<Operation> & {
export default async function getPathsOfTag( export default async function getPathsOfTag(
tagName: string, tagName: string,
area: string area: string,
version: Version = "1"
): Promise<Document> { ): Promise<Document> {
// get path files // get path files
const basePath = path.join(process.cwd(), `specs/${area}/paths`) const basePath = path.join(
process.cwd(),
version === "1" ? "specs" : "specs-v2",
`${area}/paths`
)
const files = await fs.readdir(basePath) const files = await fs.readdir(basePath)

View File

@@ -1,4 +1,4 @@
import type { SidebarItemType } from "docs-ui" import type { SidebarItemType } from "types"
import type { Operation, PathsObject } from "@/types/openapi" import type { Operation, PathsObject } from "@/types/openapi"
import type { OpenAPIV3 } from "openapi-types" import type { OpenAPIV3 } from "openapi-types"
import dynamic from "next/dynamic" import dynamic from "next/dynamic"

View File

@@ -1,5 +1,5 @@
import getBaseUrl from "./get-base-url" import { config } from "../config"
export default function getUrl(area: string, tagName?: string): string { export default function getUrl(area: string, tagName?: string): string {
return `${getBaseUrl()}/api/${area}#${tagName}` return `${config.baseUrl}/api/${area}#${tagName}`
} }

View File

@@ -2,7 +2,7 @@
description: "In this document, you'll learn about the different ways you can configure the admin dashboard." description: "In this document, you'll learn about the different ways you can configure the admin dashboard."
--- ---
import ParameterTypes from "@site/src/components/ParameterTypes" import { TypeList } from "docs-ui"
# Admin Configuration # Admin Configuration
@@ -37,7 +37,7 @@ const plugins = [
] ]
``` ```
<ParameterTypes parameters={[ <TypeList types={[
{ {
name: "serve", name: "serve",
type: "boolean", type: "boolean",

View File

@@ -8,7 +8,7 @@ In this guide, youll learn how to build the onboarding widget available in th
:::note :::note
The onboarding widget is already implemented within the codebase of your Medusa backend. This guide is helpful if you want to understand how it was implemented or you want an example of customizing the Medusa admin and backend. The onboarding widget is already implemented within the codebase of your Medusa backend. This guide is helpful if you want to understand how it was implemented or you want an example of customizing the Medusa Admin and backend.
::: :::
@@ -1284,7 +1284,7 @@ Notice that as there are two types of flows, you'll be creating the components f
{ {
label: "@medusajs/product", label: "@medusajs/product",
language: "tsx", language: "tsx",
code: `// Install the Product module in a serverless project, such as a Next.js storefront: @medusajs/product\n\nimport {\ninitialize as initializeProductModule,\n} from "@medusajs/product"\n\n// in an async function, or you can use promises\nasync () => {\n // ...\n const productService = await initializeProductModule()\n const products = await productService.list({\n id: "${data?.product_id}",\n })\n\n console.log(products[0])\n}`, code: `// Install the Pricing Module in a serverless project, such as a Next.js storefront: @medusajs/product\n\nimport {\ninitialize as initializeProductModule,\n} from "@medusajs/product"\n\n// in an async function, or you can use promises\nasync () => {\n // ...\n const productService = await initializeProductModule()\n const products = await productService.list({\n id: "${data?.product_id}",\n })\n\n console.log(products[0])\n}`,
}, },
]} className="my-6"> ]} className="my-6">
<CodeBlock.Header /> <CodeBlock.Header />

View File

@@ -149,7 +149,7 @@ You can learn more about the admin dashboard and its features in the [User Guide
Medusa supports multiple languages and translations. Check out available languages [here](../user-guide/tips/languages.md). Medusa supports multiple languages and translations. Check out available languages [here](../user-guide/tips/languages.md).
Refer to [this user guide](../user-guide/users/profile.md#change-admin-language) to learn how to switch the language of the Medusa admin. Refer to [this user guide](../user-guide/users/profile.md#change-admin-language) to learn how to switch the language of the Medusa Admin.
--- ---

View File

@@ -5,11 +5,11 @@ sidebar_position: 2
# Contribute by Translating Admin # Contribute by Translating Admin
In this document, you'll learn how you can contribute to Medusa by translating the Medusa admin. In this document, you'll learn how you can contribute to Medusa by translating the Medusa Admin.
## Overview ## Overview
The Medusa admin supports multiple languages, with the default being English. To ensure a wide support for different languages, your contribution by translating to other languages you're fluent in is highly appreciated. The Medusa Admin supports multiple languages, with the default being English. To ensure a wide support for different languages, your contribution by translating to other languages you're fluent in is highly appreciated.
This type of contribution is a no-code contribution, meaning you don't need advanced technical skills to contribute. This type of contribution is a no-code contribution, meaning you don't need advanced technical skills to contribute.

View File

@@ -8,7 +8,7 @@ import CorsErrorSection from '../../troubleshooting/cors-issues.md'
# General Deployment Guide for Medusa Admin # General Deployment Guide for Medusa Admin
In this guide, youll learn the general steps to follow when deploying the Medusa admin separately from the backend. This guide isnt tailored towards any hosting provider. In this guide, youll learn the general steps to follow when deploying the Medusa Admin separately from the backend. This guide isnt tailored towards any hosting provider.
## Prerequisites ## Prerequisites
@@ -69,7 +69,7 @@ When using the `--deployment` option, the backend's URL is loaded from the `MEDU
The steps to deploy the admin can be different based on the hosting provider you use. The following points cover common configurations across hosting providers: The steps to deploy the admin can be different based on the hosting provider you use. The following points cover common configurations across hosting providers:
- If your hosting provider supports choosing a Framework Preset, choose the “Other” option as the Medusa admin doesnt follow known framework presets. - If your hosting provider supports choosing a Framework Preset, choose the “Other” option as the Medusa Admin doesnt follow known framework presets.
- Set the build command of your deployed project to use the `build:admin` command: - Set the build command of your deployed project to use the `build:admin` command:
```bash npm2yarn ```bash npm2yarn

View File

@@ -14,7 +14,7 @@ In this document, youll learn about the different ways you can deploy your Me
A standard Medusa project is made up of the following: A standard Medusa project is made up of the following:
- Medusa backend - Medusa backend
- Medusa admin - Medusa Admin
- One or more storefronts - One or more storefronts
![Diagram showcasing the connection between the three deployed components](https://res.cloudinary.com/dza7lstvk/image/upload/v1705999238/Medusa%20Docs/Diagrams/Social_Media_Graphics_2024_options_uxzmlx.jpg) ![Diagram showcasing the connection between the three deployed components](https://res.cloudinary.com/dza7lstvk/image/upload/v1705999238/Medusa%20Docs/Diagrams/Social_Media_Graphics_2024_options_uxzmlx.jpg)
@@ -27,7 +27,7 @@ This guide details options to consider when deploying each of these components i
You must deploy the Medusa backend before the admin or storefront, as both of them connect to the backend and wont work without a deployed Medusa backend URL. You must deploy the Medusa backend before the admin or storefront, as both of them connect to the backend and wont work without a deployed Medusa backend URL.
![Diagram showcasing how the Medusa admin and its associated services would be deployed](https://res.cloudinary.com/dza7lstvk/image/upload/v1705999287/Medusa%20Docs/Diagrams/Social_Media_Graphics_2024_backend_deployment__1_twbdsd.jpg) ![Diagram showcasing how the Medusa Admin and its associated services would be deployed](https://res.cloudinary.com/dza7lstvk/image/upload/v1705999287/Medusa%20Docs/Diagrams/Social_Media_Graphics_2024_backend_deployment__1_twbdsd.jpg)
The Medusa backend is a Node.js server. So, it must be deployed to a hosting provider that supports deploying servers, such as Railway, DigitalOcean, AWS, Heroku, etc… The Medusa backend is a Node.js server. So, it must be deployed to a hosting provider that supports deploying servers, such as Railway, DigitalOcean, AWS, Heroku, etc…
@@ -49,11 +49,11 @@ Your backend connects to PostgreSQL and Redis databases. Most hosting providers
## Deploying the Medusa Admin ## Deploying the Medusa Admin
There are two options to deploy the Medusa admin: There are two options to deploy the Medusa Admin:
### Deploy Admin with Backend ### Deploy Admin with Backend
Since the Medusa admin is a plugin installed in the backend, you may choose to host it along with the backend. Since the Medusa Admin is a plugin installed in the backend, you may choose to host it along with the backend.
In this scenario, make sure the hosting provider and plan of your choice provide at least 2GB of RAM, as the admin build requires high RAM usage. In this scenario, make sure the hosting provider and plan of your choice provide at least 2GB of RAM, as the admin build requires high RAM usage.

View File

@@ -366,7 +366,7 @@ For example, to create an admin user you can run the following command:
heroku run -a <APP_NAME> -- npx medusa user -e "<EMAIL>" -p "<PASSWORD>" heroku run -a <APP_NAME> -- npx medusa user -e "<EMAIL>" -p "<PASSWORD>"
``` ```
Where `<APP_NAME>` is the name of your Heroku app, and `<EMAIL>` and `<PASSWORD>` are the credentials you want to use to log in to the Medusa admin dashboard. Where `<APP_NAME>` is the name of your Heroku app, and `<EMAIL>` and `<PASSWORD>` are the credentials you want to use to log in to the Medusa Admin dashboard.
--- ---
@@ -386,5 +386,5 @@ Where `<APP_NAME>` is the name of your Heroku app, `<ENV_NAME>` is the name of t
## See Also ## See Also
- [Deploy your Medusa admin](../admin/index.mdx) - [Deploy your Medusa Admin](../admin/index.mdx)
- [Deploy your storefront](../storefront/index.mdx) - [Deploy your storefront](../storefront/index.mdx)

View File

@@ -234,7 +234,7 @@ Its highly recommended to use strong, randomly generated secrets for `JWT_SE
::: :::
Make sure to add any other environment variables that are relevant for you here. For example, you can add environment variables related to Medusa admin or your modules. Make sure to add any other environment variables that are relevant for you here. For example, you can add environment variables related to Medusa Admin or your modules.
### Change Start Command ### Change Start Command

View File

@@ -111,7 +111,7 @@ You can also create endpoints that don't reside under these two prefixes, simila
## CORS Configuration ## CORS Configuration
If youre adding a storefront or admin endpoint and you want to access these endpoints from the storefront or Medusa admin, you need to pass your endpoints Cross-Origin Resource Origin (CORS) options using the `cors` package. If youre adding a storefront or admin endpoint and you want to access these endpoints from the storefront or Medusa Admin, you need to pass your endpoints Cross-Origin Resource Origin (CORS) options using the `cors` package.
First, import the necessary utility functions and types from Medusa's packages with the `cors` package: First, import the necessary utility functions and types from Medusa's packages with the `cors` package:

View File

@@ -85,11 +85,11 @@ These are the directories present at the root of your Medusa backend.
### .cache ### .cache
This directory will only be available if you have the Medusa admin installed and youve already started your Medusa backend at least once before. It holds all cached files related to building the Medusa admin assets. This directory will only be available if you have the Medusa Admin installed and youve already started your Medusa backend at least once before. It holds all cached files related to building the Medusa Admin assets.
### build ### build
This directory will only be available if you have the Medusa admin installed and youve either built your admin files or ran the Medusa backend at least once before. It holds the built files that are used to serve the admin in your browser. This directory will only be available if you have the Medusa Admin installed and youve either built your admin files or ran the Medusa backend at least once before. It holds the built files that are used to serve the admin in your browser.
### data ### data
@@ -129,10 +129,10 @@ If any of these directories are not available, you can create them yourself.
### admin ### admin
This directory holds all Medusa admin customizations. The main subdirectories of this directory are: This directory holds all Medusa Admin customizations. The main subdirectories of this directory are:
- `widgets`: Holds all [Medusa admin widgets](../../admin/widgets.md). - `widgets`: Holds all [Medusa Admin widgets](../../admin/widgets.md).
- `routes`: Holds all [Medusa admin UI routes](../../admin/routes.md). - `routes`: Holds all [Medusa Admin UI routes](../../admin/routes.md).
### api ### api

View File

@@ -10,7 +10,7 @@ In this document, you will learn how to build your own Medusa cache module.
Medusa provides ready-made modules for cache, including in-memory and Redis modules. If you prefer another technology used for caching in your commerce application, you can build a module locally and use it in your Medusa backend. You can also publish to NPM and reuse it across multiple Medusa backend instances. Medusa provides ready-made modules for cache, including in-memory and Redis modules. If you prefer another technology used for caching in your commerce application, you can build a module locally and use it in your Medusa backend. You can also publish to NPM and reuse it across multiple Medusa backend instances.
In this document, you will learn how to build your own Medusa cache module based on Memcached as an example. This gives you a real-life example of creating the cache module. You can follow the general steps with any other caching system or service. In this document, you will learn how to build your own Medusa cache module based on Memcached as an example. This gives you a real-life example of creating the Cache Module. You can follow the general steps with any other caching system or service.
--- ---
@@ -250,7 +250,7 @@ module.exports = {
Make sure to replace the `path/to/custom-module` with a relative path from your Medusa backend to your module. You can learn more about module reference in the [Create Module documentation](../modules/create.mdx#module-reference). Make sure to replace the `path/to/custom-module` with a relative path from your Medusa backend to your module. You can learn more about module reference in the [Create Module documentation](../modules/create.mdx#module-reference).
You can also add any necessary options to the module. The options added in the example above are relevant to the memcached module and you can replace them with your own options. You can also add any necessary options to the module. The options added in the example above are relevant to the Memcached Module and you can replace them with your own options.
Then, to test the module, run the Medusa backend which also runs your module: Then, to test the module, run the Medusa backend which also runs your module:

View File

@@ -10,7 +10,7 @@ In this document, youll learn about the Redis cache module and how you can in
Medusas modular architecture allows developers to extend or replace the logic used for [caching](../overview.mdx). You can create a custom module, or you can use the modules Medusa provides. Medusas modular architecture allows developers to extend or replace the logic used for [caching](../overview.mdx). You can create a custom module, or you can use the modules Medusa provides.
One of these modules is the Redis module. This module allows you to utilize Redis for the caching functionality. This document will you guide you through installing the Redis module. One of these modules is the Redis Module. This module allows you to utilize Redis for the caching functionality. This document will you guide you through installing the Redis Module.
--- ---

View File

@@ -11,9 +11,9 @@ In this document, youll learn about the Redis events module and how you can i
Medusas modular architecture allows developers to extend or completely replace the logic used for events. You can create a custom module, or you can use the modules Medusa provides. Medusas modular architecture allows developers to extend or completely replace the logic used for events. You can create a custom module, or you can use the modules Medusa provides.
One of these modules is the Redis module. This module allows you to utilize Redis for the event bus functionality. When installed, the Medusas events system is powered by BullMQ and `io-redis`. BullMQ is responsible for the message queue and worker, and `io-redis` is the underlying Redis client that BullMQ connects to for events storage. One of these modules is the Redis Module. This module allows you to utilize Redis for the event bus functionality. When installed, the Medusas events system is powered by BullMQ and `io-redis`. BullMQ is responsible for the message queue and worker, and `io-redis` is the underlying Redis client that BullMQ connects to for events storage.
This document will you guide you through installing the Redis module. This document will you guide you through installing the Redis Module.
--- ---

View File

@@ -13,7 +13,7 @@ In this document, youll learn what Modules are and how can you use them durin
Modules are self-contained, reusable pieces of code that encapsulate specific functionality or features within an ecommerce application. They foster separation of concerns, maintainability, and reusability by organizing code into smaller, independent units that can be easily managed, tested, and integrated with other modules. Modules are self-contained, reusable pieces of code that encapsulate specific functionality or features within an ecommerce application. They foster separation of concerns, maintainability, and reusability by organizing code into smaller, independent units that can be easily managed, tested, and integrated with other modules.
Modules further increase Medusas extensibility. commerce modules, such as the cart engine, can be extended or entirely replaced with your own custom logic. They can also run independently of the core Medusa package, allowing you to utilize the commerce module within a larger commerce ecosystem. For example, you can use the Order module as an Order Management System (OMS) without using Medusas core. Modules further increase Medusas extensibility. commerce modules, such as the cart engine, can be extended or entirely replaced with your own custom logic. They can also run independently of the core Medusa package, allowing you to utilize the commerce module within a larger commerce ecosystem. For example, you can use the Order Module as an Order Management System (OMS) without using Medusas core.
This also applies to core logic such as caching or events systems. You can use modules to integrate any logic or third-party service to handle this logic. This gives you greater flexibility in how you choose your tech stack. This also applies to core logic such as caching or events systems. You can use modules to integrate any logic or third-party service to handle this logic. This gives you greater flexibility in how you choose your tech stack.

View File

@@ -199,7 +199,7 @@ By installing a Module in your project and expose its APIs based on the framewor
### Full-Fledged Ecommerce System ### Full-Fledged Ecommerce System
Developers can use Medusas toolkit to create their ecommerce system. With the use of the [create-medusa-app](../create-medusa-app.mdx) command, developers can set up a Medusa Backend, Medusa admin, and a storefront. Developers can use Medusas toolkit to create their ecommerce system. With the use of the [create-medusa-app](../create-medusa-app.mdx) command, developers can set up a Medusa Backend, Medusa Admin, and a storefront.
![Full-Fledged Ecommerce System](https://res.cloudinary.com/dza7lstvk/image/upload/v1697707368/Medusa%20Docs/Diagrams/Social_Media_Graphics_vosikk.jpg) ![Full-Fledged Ecommerce System](https://res.cloudinary.com/dza7lstvk/image/upload/v1697707368/Medusa%20Docs/Diagrams/Social_Media_Graphics_vosikk.jpg)

View File

@@ -1,6 +1,6 @@
# Pricing Concepts # Pricing Concepts
In this document, youll learn about the main concepts in the Pricing module, and how data is stored and related. In this document, youll learn about the main concepts in the Pricing Module, and how data is stored and related.
## Money Amount ## Money Amount
@@ -74,10 +74,10 @@ Each rule of a price list can have more than one value, representing its values
## Use Case Example: Pricing and Product Modules ## Use Case Example: Pricing and Product Modules
In a real use case, you would use the pricing module with your custom logic or other Medusa Commerce Modules, such as the Product Module. In a real use case, you would use the Pricing Module with your custom logic or other Medusa Commerce Modules, such as the Product Module.
When used with the Product Module, a product variants prices are stored as money amounts belonging to a price set. A relation is formed between the `ProductVariant` and the `PriceSet` when the modules are linked. When used with the Product Module, a product variants prices are stored as money amounts belonging to a price set. A relation is formed between the `ProductVariant` and the `PriceSet` when the modules are linked.
![A diagram showcasing an example of how resources from the Pricing and Product module are linked. The PriceSet is linked to the ProductVariant of the Product module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1700574189/Medusa%20Docs/Diagrams/pricing-product_jcsjt0.jpg) ![A diagram showcasing an example of how resources from the Pricing and Product module are linked. The PriceSet is linked to the ProductVariant of the Pricing Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1700574189/Medusa%20Docs/Diagrams/pricing-product_jcsjt0.jpg)
So, when you want to add prices for a product variant, you create a price set and add the prices as money amounts to it. You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context. So, when you want to add prices for a product variant, you create a price set and add the prices as money amounts to it. You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context.

View File

@@ -4,7 +4,7 @@ import TabItem from '@theme/TabItem';
# Examples of Pricing Module # Examples of Pricing Module
In this document, youll find common examples of how you can use the Pricing module in your application. In this document, youll find common examples of how you can use the Pricing Module in your application.
## Create a Price Set ## Create a Price Set

View File

@@ -5,11 +5,11 @@ import TabItem from '@theme/TabItem';
# Install Pricing Module in Medusa # Install Pricing Module in Medusa
In this document, you'll learn how to install the Pricing module using NPM in the Medusa backend. In this document, you'll learn how to install the Pricing Module using NPM in the Medusa backend.
## Step 1: Install Module ## Step 1: Install Module
To install the Pricing module, run the following command in the root directory of the Medusa backend: To install the Pricing Module, run the following command in the root directory of the Medusa backend:
```bash npm2yarn ```bash npm2yarn
npm install @medusajs/pricing npm install @medusajs/pricing
@@ -19,7 +19,7 @@ npm install @medusajs/pricing
## Step 2: Add Module to Configurations ## Step 2: Add Module to Configurations
In `medusa-config.js`, add the pricing module to the exported object under the `modules` property: In `medusa-config.js`, add the Pricing Module to the exported object under the `modules` property:
```js title=medusa-config.js ```js title=medusa-config.js
module.exports = { module.exports = {
@@ -153,6 +153,6 @@ For example:
## Start Development ## Start Development
You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Pricing module. You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Pricing Module.
You can also refer to the [Module Interface Reference](../../references/pricing/interfaces/pricing.IPricingModuleService.mdx) for a detailed reference on all available methods. You can also refer to the [Module Interface Reference](../../references/pricing/interfaces/pricing.IPricingModuleService.mdx) for a detailed reference on all available methods.

View File

@@ -1,10 +1,10 @@
# Install in Node.js-Based Application # Install in Node.js-Based Application
In this document, youll learn how to setup and use the Pricing module in a Node.js based application. In this document, youll learn how to setup and use the Pricing Module in a Node.js based application.
## Prerequisites ## Prerequisites
Before installing the Pricing module in your application, make sure you have the following prerequisites: Before installing the Pricing Module in your application, make sure you have the following prerequisites:
- Node.js v16 or greater - Node.js v16 or greater
- PostgreSQL database. You can use an existing Medusa database, or set up a new PostgreSQL database. - PostgreSQL database. You can use an existing Medusa database, or set up a new PostgreSQL database.
@@ -13,7 +13,7 @@ Before installing the Pricing module in your application, make sure you have the
## Install Package ## Install Package
In your Node.js-based applications, such as a Next.js application, you can install the Pricing module with the following command: In your Node.js-based applications, such as a Next.js application, you can install the Pricing Module with the following command:
```bash npm2yarn ```bash npm2yarn
npm install @medusajs/pricing npm install @medusajs/pricing
@@ -161,7 +161,7 @@ npm run price:seed
## Next.js Application: Adjust Configurations ## Next.js Application: Adjust Configurations
The Pricing module uses dependencies that arent Webpack optimized. Since Next.js uses Webpack for compilation, you need to add the Pricing module as an external dependency. The Pricing Module uses dependencies that arent Webpack optimized. Since Next.js uses Webpack for compilation, you need to add the Pricing Module as an external dependency.
To do that, add the `serverComponentsExternalPackages` option in `next.config.js`: To do that, add the `serverComponentsExternalPackages` option in `next.config.js`:
@@ -183,6 +183,6 @@ module.exports = nextConfig
## Start Development ## Start Development
You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Pricing module. You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Pricing Module.
You can also refer to the [Module Interface Reference](../../references/pricing/interfaces/pricing.IPricingModuleService.mdx) for a detailed reference on all available methods. You can also refer to the [Module Interface Reference](../../references/pricing/interfaces/pricing.IPricingModuleService.mdx) for a detailed reference on all available methods.

View File

@@ -3,13 +3,13 @@ import Icons from '@theme/Icon'
# Pricing Module Overview # Pricing Module Overview
The Pricing module is the `@medusajs/pricing` NPM package that provides advanced pricing features in your Medusa and Node.js applications. It can be used to add prices to any resource, such as products or shipping options. The Pricing Module is the `@medusajs/pricing` NPM package that provides advanced pricing features in your Medusa and Node.js applications. It can be used to add prices to any resource, such as products or shipping options.
## Features ## Features
### Price Management ### Price Management
With the Product module, you can store the prices of a resource and manage them through the main interface method. Prices are grouped in a price set, allowing you to add more than one price for a resource based on different conditions, such as currency code. With the Pricing Module, you can store the prices of a resource and manage them through the main interface method. Prices are grouped in a price set, allowing you to add more than one price for a resource based on different conditions, such as currency code.
```ts ```ts
const priceSet = await pricingService.create({ const priceSet = await pricingService.create({
@@ -102,7 +102,7 @@ const price = await pricingService.calculatePrices(
The Pricing Module can be used in many use cases, including: The Pricing Module can be used in many use cases, including:
- Medusa Backend: The Medusa backend uses the pricing module to implement some features. However, it's guarded by the [experimental feature flag](../index.md#enabling-experimental-features). If you want to use the pricing module in your backend's customizations, follow [this installation guide](./install-medusa.mdx). - Medusa Backend: The Medusa backend uses the Pricing Module to implement some features. However, it's guarded by the [experimental feature flag](../index.md#enabling-experimental-features). If you want to use the Pricing Module in your backend's customizations, follow [this installation guide](./install-medusa.mdx).
- Serverless Application: Use the Pricing Module in a serverless application, such as a Next.js application, without having to manage a fully-fledged ecommerce system. You can use it by [installing it in your Node.js project as an NPM package](./install-nodejs.md). - Serverless Application: Use the Pricing Module in a serverless application, such as a Next.js application, without having to manage a fully-fledged ecommerce system. You can use it by [installing it in your Node.js project as an NPM package](./install-nodejs.md).
- Node.js Application: Use the Pricing Module in any Node.js application. Follow [this guide](./install-nodejs.md) to learn how to install it. - Node.js Application: Use the Pricing Module in any Node.js application. Follow [this guide](./install-nodejs.md) to learn how to install it.
@@ -116,7 +116,7 @@ The Pricing Module can be used in many use cases, including:
label: 'Pricing Concepts', label: 'Pricing Concepts',
customProps: { customProps: {
icon: Icons['academic-cap-solid'], icon: Icons['academic-cap-solid'],
description: 'Learn about the main concepts in the Pricing module.' description: 'Learn about the main concepts in the Pricing Module.'
} }
}} }}
/> />

View File

@@ -3,7 +3,7 @@ import TabItem from "@theme/TabItem"
# Prices Calculation # Prices Calculation
In this document, you'll learn how prices are calculated under the hood when you use the `calculatePrices` method of the Pricing module interface. In this document, you'll learn how prices are calculated under the hood when you use the `calculatePrices` method of the Pricing Module interface.
## Overview ## Overview

View File

@@ -3,7 +3,7 @@ import TabItem from '@theme/TabItem';
# Examples of Product Module # Examples of Product Module
In this document, youll find common examples of how you can use the Product module in your application. In this document, youll find common examples of how you can use the Product Module in your application.
## Create Product ## Create Product

View File

@@ -5,11 +5,11 @@ import TabItem from '@theme/TabItem';
# Install Product Module in Medusa # Install Product Module in Medusa
In this document, you'll learn how to install the Product module using NPM in the Medusa backend. In this document, you'll learn how to install the Product Module using NPM in the Medusa backend.
## Step 1: Install Module ## Step 1: Install Module
To install the Product module, run the following command in the root directory of the Medusa backend: To install the Product Module, run the following command in the root directory of the Medusa backend:
```bash npm2yarn ```bash npm2yarn
npm install @medusajs/product npm install @medusajs/product
@@ -19,7 +19,7 @@ npm install @medusajs/product
## Step 2: Add Module to Configurations ## Step 2: Add Module to Configurations
In `medusa-config.js`, add the product module to the exported object under the `modules` property: In `medusa-config.js`, add the Product Module to the exported object under the `modules` property:
```js title=medusa-config.js ```js title=medusa-config.js
module.exports = { module.exports = {
@@ -148,6 +148,6 @@ For example:
## Start Development ## Start Development
You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Product module. You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Product Module.
You can also refer to the [Module Interface Reference](../../references/product/interfaces/product.IProductModuleService.mdx) for a detailed reference on all available methods. You can also refer to the [Module Interface Reference](../../references/product/interfaces/product.IProductModuleService.mdx) for a detailed reference on all available methods.

View File

@@ -1,10 +1,10 @@
# Install in Node.js-Based Application # Install in Node.js-Based Application
In this document, youll learn how to setup and use the Product module in a Node.js based application. In this document, youll learn how to setup and use the Product Module in a Node.js based application.
## Prerequisites ## Prerequisites
Before installing the Product module in your application, make sure you have the following prerequisites: Before installing the Product Module in your application, make sure you have the following prerequisites:
- Node.js v16 or greater - Node.js v16 or greater
- PostgreSQL database. You can use an existing Medusa database, or set up a new PostgreSQL database. - PostgreSQL database. You can use an existing Medusa database, or set up a new PostgreSQL database.
@@ -13,7 +13,7 @@ Before installing the Product module in your application, make sure you have the
## Install Package ## Install Package
In your Node.js-based applications, such as a Next.js application, you can install the Product module with the following command: In your Node.js-based applications, such as a Next.js application, you can install the Product Module with the following command:
```bash npm2yarn ```bash npm2yarn
npm install @medusajs/product npm install @medusajs/product
@@ -172,7 +172,7 @@ npm run product:seed
## Next.js Application: Adjust Configurations ## Next.js Application: Adjust Configurations
The Product module uses dependencies that arent Webpack optimized. Since Next.js uses Webpack for compilation, you need to add the Product module as an external dependency. the Product Module uses dependencies that arent Webpack optimized. Since Next.js uses Webpack for compilation, you need to add the Product Module as an external dependency.
To do that, add the `serverComponentsExternalPackages` option in `next.config.js`: To do that, add the `serverComponentsExternalPackages` option in `next.config.js`:
@@ -193,6 +193,6 @@ module.exports = nextConfig
## Start Development ## Start Development
You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Product module. You can refer to the [Example Usages documentation page](./examples.mdx) for examples of using the Product Module.
You can also refer to the [Module Interface Reference](../../references/product/interfaces/product.IProductModuleService.mdx) for a detailed reference on all available methods. You can also refer to the [Module Interface Reference](../../references/product/interfaces/product.IProductModuleService.mdx) for a detailed reference on all available methods.

View File

@@ -3,13 +3,13 @@ import Icons from '@theme/Icon'
# Product Module Overview # Product Module Overview
The Product module is the `@medusajs/product` NPM package that provides product-related features in your Medusa and Node.js applications. It can be used to store products with variants, organize them into categories and collections, and more. the Product Module is the `@medusajs/product` NPM package that provides product-related features in your Medusa and Node.js applications. It can be used to store products with variants, organize them into categories and collections, and more.
## Features ## Features
### Products Management ### Products Management
With the Product module, you can store products and manage them through the main interface methods. Products can have custom options, such as color or size, and each variant in the product sets the value for these options. With the Product Module, you can store products and manage them through the main interface methods. Products can have custom options, such as color or size, and each variant in the product sets the value for these options.
```ts ```ts
const products = await productService.create([ const products = await productService.create([
@@ -36,7 +36,7 @@ const products = await productService.create([
### Product Organizations ### Product Organizations
The Product module provides different entities that can be used to organize products, including categories, collections, tags, and more. the Product Module provides different entities that can be used to organize products, including categories, collections, tags, and more.
```ts ```ts
const category = await productService.createCategory({ const category = await productService.createCategory({
@@ -61,7 +61,7 @@ const products = await productService.update([
The Product Module can be used in many use cases, including: The Product Module can be used in many use cases, including:
- Medusa Backend: The Medusa backend uses the product module to implement some product features. However, it's guarded by the [experimental feature flag](../index.md#enabling-experimental-features). If you want to use the product module in your backend's customizations, follow [this installation guide](./install-medusa.mdx). - Medusa Backend: The Medusa backend uses the Product Module to implement some product features. However, it's guarded by the [experimental feature flag](../index.md#enabling-experimental-features). If you want to use the Product Module in your backend's customizations, follow [this installation guide](./install-medusa.mdx).
- Serverless Application: Use the Product Module in a serverless application, such as a Next.js application, without having to manage a fully-fledged ecommerce system. You can use it by [installing it in your Node.js project as an NPM package](./install-nodejs.md). - Serverless Application: Use the Product Module in a serverless application, such as a Next.js application, without having to manage a fully-fledged ecommerce system. You can use it by [installing it in your Node.js project as an NPM package](./install-nodejs.md).
- Node.js Application: Use the Product Module in any Node.js application. Follow [this guide](./install-nodejs.md) to learn how to install it. - Node.js Application: Use the Product Module in any Node.js application. Follow [this guide](./install-nodejs.md) to learn how to install it.

View File

@@ -55,7 +55,7 @@ Medusa provides the essential building blocks that developers can put together t
label: 'User Guide', label: 'User Guide',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'No-code guides to help users manage their ecommerce stores using the Medusa admin.' description: 'No-code guides to help users manage their ecommerce stores using the Medusa Admin.'
} }
} }
]} /> ]} />

View File

@@ -98,7 +98,7 @@ The client accepts the following options on initialization:
| `maxRetries` | `0` | The amount of times a request is retried. | | `maxRetries` | `0` | The amount of times a request is retried. |
| `baseUrl` | `'http://localhost:9000'` | The url to which requests are made to. | | `baseUrl` | `'http://localhost:9000'` | The url to which requests are made to. |
| `apiKey` | `''` | Optional API key used for authenticating admin requests. | | `apiKey` | `''` | Optional API key used for authenticating admin requests. |
| `publishableApiKey` | `''` | Optional publishable API key used for storefront requests. You can create a publishable API key either using the [admin APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx) or the [Medusa admin](../user-guide/settings/publishable-api-keys.mdx). | | `publishableApiKey` | `''` | Optional publishable API key used for storefront requests. You can create a publishable API key either using the [admin APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx) or the [Medusa Admin](../user-guide/settings/publishable-api-keys.mdx). |
| `customHeaders` | `{}` | Optional headers to attach to every request. | | `customHeaders` | `{}` | Optional headers to attach to every request. |
@@ -268,7 +268,7 @@ You can learn more about publishable API keys and how to use them in [this docum
### Create a Publishable API Key ### Create a Publishable API Key
You can create a publishable API key either using the [admin REST APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx), or using the [Medusa admin dashboard](../user-guide/settings/publishable-api-keys.mdx). You can create a publishable API key either using the [admin REST APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx), or using the [Medusa Admin dashboard](../user-guide/settings/publishable-api-keys.mdx).
### Use a Publishable API Key ### Use a Publishable API Key

View File

@@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem';
[Medusa React](https://www.npmjs.com/package/medusa-react) is a React library that provides a set of utilities and hooks for interacting seamlessly with the Medusa backend. [Medusa React](https://www.npmjs.com/package/medusa-react) is a React library that provides a set of utilities and hooks for interacting seamlessly with the Medusa backend.
For example, if you're creating a storefront with frameworks like Nuxt, you can send requests to the backend using this client. You can also use it in your Medusa admin customizations. For example, if you're creating a storefront with frameworks like Nuxt, you can send requests to the backend using this client. You can also use it in your Medusa Admin customizations.
This reference provides details on the available hooks, providers, and utilities, including examples of each. This reference provides details on the available hooks, providers, and utilities, including examples of each.
@@ -326,7 +326,7 @@ You can learn more about publishable API keys and how to use them in [this docum
### Create a Publishable API Key ### Create a Publishable API Key
You can create a publishable API key either using the [admin REST APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx), or using the [Medusa admin dashboard](../user-guide/settings/publishable-api-keys.mdx). You can create a publishable API key either using the [admin REST APIs](../development/publishable-api-keys/admin/manage-publishable-api-keys.mdx), or using the [Medusa Admin dashboard](../user-guide/settings/publishable-api-keys.mdx).
### Use a Publishable API Key ### Use a Publishable API Key

View File

@@ -86,7 +86,7 @@ The process is implemented as follows:
1. When the idempotency keys recovery point is set to `started`, the tax lines are created for the items in the cart. This is done using the `CartService`'s [createTaxLines method](../../references/services/classes/services.CartService.mdx#createtaxlines). If that is completed with no errors, the recovery point is set to `tax_lines_created` and the process continues. 1. When the idempotency keys recovery point is set to `started`, the tax lines are created for the items in the cart. This is done using the `CartService`'s [createTaxLines method](../../references/services/classes/services.CartService.mdx#createtaxlines). If that is completed with no errors, the recovery point is set to `tax_lines_created` and the process continues.
2. When the idempotency keys recovery point is set to `tax_lines_created`, the payment is authorized using the `CartService`'s method [authorizePayment](../../references/services/classes/services.CartService.mdx#authorizepayment). If the payment requires more action or is pending authorization, then the tax lines that were created in the previous steps are deleted and the cart completion process is terminated. Once the payment is authorized, the process can be restarted. 2. When the idempotency keys recovery point is set to `tax_lines_created`, the payment is authorized using the `CartService`'s method [authorizePayment](../../references/services/classes/services.CartService.mdx#authorizepayment). If the payment requires more action or is pending authorization, then the tax lines that were created in the previous steps are deleted and the cart completion process is terminated. Once the payment is authorized, the process can be restarted.
3. When the idempotency keys recovery point is set to `payment_authorized`, tax lines are created again the same way as the first step. Then, the inventory of each of the items in the cart is confirmed using the `ProductVariantInventoryService`'s method [confirmInventory](../../references/services/classes/services.ProductVariantInventoryService.mdx#confirminventory). If an item is in stock, the quantity is reserved using the `ProductVariantInventoryService`'s method [reserveQuantity](../../references/services/classes/services.ProductVariantInventoryService.mdx#reservequantity). If an item is out of stock, any item reservations that were created are deleted, the payment is canceled, and an error is thrown, terminating the cart completion process. If all item quantities are confirmed to be available: 3. When the idempotency keys recovery point is set to `payment_authorized`, tax lines are created again the same way as the first step. Then, the inventory of each of the items in the cart is confirmed using the `ProductVariantInventoryService`'s method [confirmInventory](../../references/services/classes/services.ProductVariantInventoryService.mdx#confirminventory). If an item is in stock, the quantity is reserved using the `ProductVariantInventoryService`'s method [reserveQuantity](../../references/services/classes/services.ProductVariantInventoryService.mdx#reservequantity). If an item is out of stock, any item reservations that were created are deleted, the payment is canceled, and an error is thrown, terminating the cart completion process. If all item quantities are confirmed to be available:
1. If the cart belongs to a swap (the `type` attribute is set to `swap`), the swap is registered as completed using the `SwapService`'s [registerCartCompletion method](../../references/services/classes/services.SwapService.mdx#registercartcompletion) and the inventory item reservations are removed using the Inventory module. The process ends successfully here for a swap. 1. If the cart belongs to a swap (the `type` attribute is set to `swap`), the swap is registered as completed using the `SwapService`'s [registerCartCompletion method](../../references/services/classes/services.SwapService.mdx#registercartcompletion) and the inventory item reservations are removed using the Inventory Module. The process ends successfully here for a swap.
2. If the cart belongs to an order, the order is created using the `OrderService`'s method [createFromCart](../../references/services/classes/services.OrderService.mdx#createfromcart). The order is then retrieved and sent in the response. 2. If the cart belongs to an order, the order is created using the `OrderService`'s method [createFromCart](../../references/services/classes/services.OrderService.mdx#createfromcart). The order is then retrieved and sent in the response.
4. Once the process detailed above is done, the idempotency keys recovery point is set to `finished`. 4. Once the process detailed above is done, the idempotency keys recovery point is set to `finished`.

View File

@@ -31,7 +31,7 @@ It is assumed that you already have a Medusa backend installed and set up. If no
### Required Module ### Required Module
This guide assumes you have the Inventory module installed on your Medusa backend. If not, you can learn how to install it using [this guide](../install-modules.md#inventory-module). This guide assumes you have the Inventory Module installed on your Medusa backend. If not, you can learn how to install it using [this guide](../install-modules.md#inventory-module).
Furthermore, inventory levels are tied to a location ID. So, its recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you dont have any location logic implemented in place. Furthermore, inventory levels are tied to a location ID. So, its recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you dont have any location logic implemented in place.
@@ -568,7 +568,7 @@ You can create a location level by sending a request to the [Create Inventory Le
This API Route requires the inventory item ID as a path parameter. In the request body, it requires the following parameters: This API Route requires the inventory item ID as a path parameter. In the request body, it requires the following parameters:
- `location_id`: The ID of the location associated with this location level. This ID is typically available through using the stock location module. - `location_id`: The ID of the location associated with this location level. This ID is typically available through using the Stock Location Module.
- `stocked_quantity`: A number indicating the items quantity in stock. - `stocked_quantity`: A number indicating the items quantity in stock.
You can also pass other optional request body parameters, as explained in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitemlocationlevels). You can also pass other optional request body parameters, as explained in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitemlocationlevels).

View File

@@ -19,7 +19,7 @@ This guide will only explain what is required to create in your custom inventory
:::note :::note
It should be noted that the Medusa backend expects the inventory module to have entities for an inventory item, an inventory level, and a reservation item, as it uses the IDs of those entities when orchestrating between different modules and the in the API Routes it exposes. You can learn more about this in the [Inventory Module Architecture documentation](../inventory-module.md). It should be noted that the Medusa backend expects the Inventory Module to have entities for an inventory item, an inventory level, and a reservation item, as it uses the IDs of those entities when orchestrating between different modules and the in the API Routes it exposes. You can learn more about this in the [Inventory Module Architecture documentation](../inventory-module.md).
::: :::

View File

@@ -19,7 +19,7 @@ This guide will only explain what is required to create in your custom stock loc
:::note :::note
It should be noted that the Medusa backend expects the stock location module to have entities for a location and a location address, as it uses the IDs of those entities when orchestrating between different modules and the in the API Routes it exposes. You can learn more about this in the [Stock Location Module Architecture documentation](../stock-location-module.md). It should be noted that the Medusa backend expects the Stock Location Module to have entities for a location and a location address, as it uses the IDs of those entities when orchestrating between different modules and the in the API Routes it exposes. You can learn more about this in the [Stock Location Module Architecture documentation](../stock-location-module.md).
::: :::

View File

@@ -24,7 +24,7 @@ npm install @medusajs/inventory
### Step 2: Add Inventory Module to Configurations ### Step 2: Add Inventory Module to Configurations
In `medusa-config.js`, add the inventory module to the exported object under the `modules` property: In `medusa-config.js`, add the Inventory Module to the exported object under the `modules` property:
```js ```js
module.exports = { module.exports = {
@@ -48,7 +48,7 @@ npx medusa migrations run
### Step 4: Run Migration Script ### Step 4: Run Migration Script
After installing the Stock Location module, make sure to [run the migration script](#run-migration-script) After installing the Stock Location Module, make sure to [run the migration script](#run-migration-script)
--- ---
@@ -64,7 +64,7 @@ npm install @medusajs/stock-location
### Step 2: Add Stock Location Module to Configurations ### Step 2: Add Stock Location Module to Configurations
In `medusa-config.js`, add the stock location module to the exported object under the `modules` property: In `medusa-config.js`, add the Stock Location Module to the exported object under the `modules` property:
```js ```js
module.exports = { module.exports = {
@@ -100,4 +100,4 @@ After installing both modules, run the following command to migrate current prod
node ./node_modules/@medusajs/medusa/dist/scripts/migrate-inventory-items.js node ./node_modules/@medusajs/medusa/dist/scripts/migrate-inventory-items.js
``` ```
You can now start the Medusa backend and use the stock location module in your commerce application. You can now start the Medusa backend and use the Stock Location Module in your commerce application.

View File

@@ -1,16 +1,16 @@
--- ---
description: "In this document, youll learn about the inventory module, how it works, and its relation to other processes in your commerce application." description: "In this document, youll learn about the Inventory Module, how it works, and its relation to other processes in your commerce application."
--- ---
# Inventory Module # Inventory Module
In this document, youll learn about the inventory module and how it works. In this document, youll learn about the Inventory Module and how it works.
## Overview ## Overview
The inventory module includes all functionalities related to product inventory. It implements inventory management for a product, confirming whether a product is available across inventory levels, and updating the inventory availability of a product variant at different points in the order lifecycle. The Inventory Module includes all functionalities related to product inventory. It implements inventory management for a product, confirming whether a product is available across inventory levels, and updating the inventory availability of a product variant at different points in the order lifecycle.
Medusa's Inventory module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the inventory module is designed, then explains how the Medusa core orchestrates relations and processes around this module when it's used with the Medusa backend. Medusa's Inventory module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the Inventory Module is designed, then explains how the Medusa core orchestrates relations and processes around this module when it's used with the Medusa backend.
--- ---
@@ -51,7 +51,7 @@ The `ReservationItem` entity has the following notable attributes, among others:
## How the Module Integrates into Medusa ## How the Module Integrates into Medusa
This section explains how the Medusa backend uses the inventory module along with its entities and other modules, and in its processes. This section explains how the Medusa backend uses the Inventory Module along with its entities and other modules, and in its processes.
### Entities Relation Overview ### Entities Relation Overview
@@ -61,23 +61,23 @@ When you use Medusa's Inventory Module, the Medusa backend uses the `ProductVari
![Inventory Item's Relation to Product Variants in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185070/Medusa%20Docs/Diagrams/inventory-item-medusa-diagram_i21ht8.jpg) ![Inventory Item's Relation to Product Variants in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185070/Medusa%20Docs/Diagrams/inventory-item-medusa-diagram_i21ht8.jpg)
The Medusa backend also orchestrates between the installed inventory and stock location modules. The association between an Inventory Level and a location is handled by passing the ID of a location from the stock location module to the inventory module when an Inventory Level is being created. When using Medusa's [Stock Location module](./stock-location-module.md), the entity representing the location is `StockLocation`. The Medusa backend also orchestrates between the installed inventory and stock location modules. The association between an Inventory Level and a location is handled by passing the ID of a location from the Stock Location Module to the Inventory Module when an Inventory Level is being created. When using Medusa's [Stock Location module](./stock-location-module.md), the entity representing the location is `StockLocation`.
![Inventory Level's relation to Stock Location Module in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185151/Medusa%20Docs/Diagrams/inventory-medusa-diagram_ltojt9.jpg) ![Inventory Level's relation to Stock Location Module in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185151/Medusa%20Docs/Diagrams/inventory-medusa-diagram_ltojt9.jpg)
Similarly, the Medusa backend associates the `ReservationItem` entity with a line item and a location by passing the IDs of each to the inventory module when a reservation item is created. Similarly, the Medusa backend associates the `ReservationItem` entity with a line item and a location by passing the IDs of each to the Inventory Module when a reservation item is created.
### Product Variant Creation Process ### Product Variant Creation Process
In the Medusa backend, when a product variant that has an enabled `manage_inventory` attribute is created, the backend uses the inventory module to automatically create an inventory item along with the product variant. When the inventory item is created, the Medusa backend attaches it to the product variant using the `ProductVariantInventoryItem` entity as explained earlier. In the Medusa backend, when a product variant that has an enabled `manage_inventory` attribute is created, the backend uses the Inventory Module to automatically create an inventory item along with the product variant. When the inventory item is created, the Medusa backend attaches it to the product variant using the `ProductVariantInventoryItem` entity as explained earlier.
The Medusa backend uses the inventory module to create Inventory Levels when the admin sets the available quantity of a product variant in a stock location. The Medusa backend uses the Inventory Module to create Inventory Levels when the admin sets the available quantity of a product variant in a stock location.
### Cart and Checkout ### Cart and Checkout
During the cart and checkout workflow, for example when a product variant is added to the cart or during cart validation, the Medusa backend uses the inventory module to confirm that items in the cart have sufficient stock to be purchased in the desired quantity. If a product variant doesn't have an inventory item, which is the case when the `manage_inventory` attribute of the variant is disabled, the variant is assumed to be available in stock. During the cart and checkout workflow, for example when a product variant is added to the cart or during cart validation, the Medusa backend uses the Inventory Module to confirm that items in the cart have sufficient stock to be purchased in the desired quantity. If a product variant doesn't have an inventory item, which is the case when the `manage_inventory` attribute of the variant is disabled, the variant is assumed to be available in stock.
As an inventory item can exist in multiple locations, the inventory module checks across those locations. The Medusa backend retrieves the locations based on the sales channel of the cart, as each location is associated with a sales channel, and passes them along to the inventory module to perform the checking. As an inventory item can exist in multiple locations, the Inventory Module checks across those locations. The Medusa backend retrieves the locations based on the sales channel of the cart, as each location is associated with a sales channel, and passes them along to the Inventory Module to perform the checking.
:::tip :::tip
@@ -85,18 +85,18 @@ You can learn more about the relation between Stock Locations and Sales Channels
::: :::
Then, the inventory module confirms that the product variant has sufficient quantity across these locations by summing all the `stocked_quantity` of the inventory levels associated with these locations. When retrieving the `stocked_quantity` of each of the inventory levels, the `reserved_quantity` is subtracted from it to ensure accurate availability. Then, the Inventory Module confirms that the product variant has sufficient quantity across these locations by summing all the `stocked_quantity` of the inventory levels associated with these locations. When retrieving the `stocked_quantity` of each of the inventory levels, the `reserved_quantity` is subtracted from it to ensure accurate availability.
### Order Placement ### Order Placement
When an order is placed, the Medusa backend uses the inventory module to reserve the ordered quantity of line items that are associated with product variants having an enabled `manage_inventory` attribute. The reserved quantity is indicated by creating a reservation item for each line item, associating it with its inventory item and a stock location. When an order is placed, the Medusa backend uses the Inventory Module to reserve the ordered quantity of line items that are associated with product variants having an enabled `manage_inventory` attribute. The reserved quantity is indicated by creating a reservation item for each line item, associating it with its inventory item and a stock location.
The Medusa backend chooses the stock location randomly from the available stock locations associated with the orders sales channel. The admin can later change which stock location the item will be fulfilled from. The Medusa backend chooses the stock location randomly from the available stock locations associated with the orders sales channel. The admin can later change which stock location the item will be fulfilled from.
### Order Fulfillment ### Order Fulfillment
When an item in the order is fulfilled, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the inventory module to subtract the inventory level's `reserved_quantity` from the `stocked_quantity`. The inventory module also resets the `reserved_quantity` to `0`. When an item in the order is fulfilled, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the Inventory Module to subtract the inventory level's `reserved_quantity` from the `stocked_quantity`. The Inventory Module also resets the `reserved_quantity` to `0`.
### Order Return ### Order Return
When an item in the order is returned, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the inventory module to increment the inventory level's `stocked_quantity` with the returned amount. When an item in the order is returned, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the Inventory Module to increment the inventory level's `stocked_quantity` with the returned amount.

View File

@@ -60,7 +60,7 @@ Admins can manage the stock locations, which are the places they store their pro
label: 'User Guide: Manage Stock Locations', label: 'User Guide: Manage Stock Locations',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Learn how to manage stock locations in Medusa admin.' description: 'Learn how to manage stock locations in Medusa Admin.'
} }
}, },
{ {
@@ -85,7 +85,7 @@ Admins can manage the inventory of product variants across the different stock l
label: 'User Guide: Manage Inventory', label: 'User Guide: Manage Inventory',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Learn how to manage inventory using the Medusa admin.' description: 'Learn how to manage inventory using the Medusa Admin.'
} }
}, },
{ {
@@ -110,7 +110,7 @@ Admins can manage item allocations to choose which stock location to fulfill ite
label: 'User Guide: Manage Allocations in Orders', label: 'User Guide: Manage Allocations in Orders',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Learn how to manage allocations of items in an order using the Medusa admin.' description: 'Learn how to manage allocations of items in an order using the Medusa Admin.'
} }
}, },
{ {

View File

@@ -1,16 +1,16 @@
--- ---
description: "In this document, youll learn about the inventory module, how it works, and its relation to other processes in your commerce application." description: "In this document, youll learn about the Inventory Module, how it works, and its relation to other processes in your commerce application."
--- ---
# Stock Location Module # Stock Location Module
In this document, youll learn about the Stock Location module and how it works. In this document, youll learn about the Stock Location Module and how it works.
## Overview ## Overview
A stock location indicates a physical address that stock-kept items can be stored in. The Stock Location module handles functionalities related to managing stock locations and their addresses. A stock location indicates a physical address that stock-kept items can be stored in. The Stock Location Module handles functionalities related to managing stock locations and their addresses.
Medusa's Stock Location module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the stock location module is designed, and highlights how the Medusa core orchestrates relations around this module when it's used with the Medusa backend. Medusa's Stock Location module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the Stock Location Module is designed, and highlights how the Medusa core orchestrates relations around this module when it's used with the Medusa backend.
--- ---
@@ -30,7 +30,7 @@ The `StockLocationAddress` entity belongs to the `StockLocation` entity. It is u
## How the Module Integrates into Medusa ## How the Module Integrates into Medusa
This section explains how the Medusa backend uses the stock location module along with its entities and other modules. This section explains how the Medusa backend uses the Stock Location Module along with its entities and other modules.
### Entities Relation Overview ### Entities Relation Overview
@@ -48,7 +48,7 @@ The Medusa backend also orchestrates between different modules. The [Inventory M
- `InventoryLevel`: This entity is used to indicate the stocked quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#inventorylevel), the `InventoryLevel` entity has an attribute `location_id`. - `InventoryLevel`: This entity is used to indicate the stocked quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#inventorylevel), the `InventoryLevel` entity has an attribute `location_id`.
- `ReservationItem`: This entity is used to indicate the reserved quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#reservationitem), the `ReservationItem` entity has an attribute `location_id`. - `ReservationItem`: This entity is used to indicate the reserved quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#reservationitem), the `ReservationItem` entity has an attribute `location_id`.
When both modules are used within the Medusa backend, the Medusa backend bridges between these modules by passing the ID of a `StockLocation` from the stock location module to the inventory module, and the inventory module uses the ID in its entities. When both modules are used within the Medusa backend, the Medusa backend bridges between these modules by passing the ID of a `StockLocation` from the Stock Location Module to the Inventory Module, and the Inventory Module uses the ID in its entities.
### Relation to SalesChannel ### Relation to SalesChannel
@@ -58,6 +58,6 @@ As the `StockLocation` and `SalesChannel` entities are available in separate mod
This relation is used across the Medusa backend and within checkout and order workflows: This relation is used across the Medusa backend and within checkout and order workflows:
- When checking the availability of an inventory item during checkout, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation`, then passes it along to the inventory module to perform the quantity check. - When checking the availability of an inventory item during checkout, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation`, then passes it along to the Inventory Module to perform the quantity check.
- When an order is placed, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation` entity, and passes it to the inventory module that reserves the ordered quantity of the inventory item from that location. The admin can later change the stock location if necessary. - When an order is placed, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation` entity, and passes it to the Inventory Module that reserves the ordered quantity of the inventory item from that location. The admin can later change the stock location if necessary.
- When an item in an order is fulfilled, the admin chooses a stock location to fulfill the item from. Similarly, when an item in an order is returned, the admin can choose a stock location to return the item to. The Medusa backend then passes the ID of the location from the stock module to the inventory module to perform inventory management functionalities. - When an item in an order is fulfilled, the admin chooses a stock location to fulfill the item from. Similarly, when an item in an order is returned, the admin can choose a stock location to return the item to. The Medusa backend then passes the ID of the location from the Stock Location Module to the Inventory Module to perform inventory management functionalities.

View File

@@ -21,7 +21,7 @@ When a fulfillment is created for one or more item, shipments can then be create
Some of the `Fulfillment` entitys attributes include: Some of the `Fulfillment` entitys attributes include:
- `provider_id`: a string indicating the ID of the fulfillment provider that processes this fulfillment. You can also access the provider by expanding the `provider` relation and accessing `fulfillment.provider`. - `provider_id`: a string indicating the ID of the fulfillment provider that processes this fulfillment. You can also access the provider by expanding the `provider` relation and accessing `fulfillment.provider`.
- `location_id`: a string indicating where the fulfillment is being made from. When paired with the Stock Location module in the Medusa backend, this would be the ID of a `StockLocation`. - `location_id`: a string indicating where the fulfillment is being made from. When paired with the Stock Location Module in the Medusa backend, this would be the ID of a `StockLocation`.
- `no_notification`: a boolean value indicating whether the customer should receive notifications for fulfillment updates. - `no_notification`: a boolean value indicating whether the customer should receive notifications for fulfillment updates.
- `data`: an object that can hold any data relevant for the fulfillment provider. - `data`: an object that can hold any data relevant for the fulfillment provider.
- `shipped_at`: a date indicating when the fulfillment was shipped. - `shipped_at`: a date indicating when the fulfillment was shipped.

View File

@@ -48,7 +48,7 @@ Customers can view their previous orders.
label: 'User Guide: Export Orders', label: 'User Guide: Export Orders',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Export orders into CSV files in Medusa admin.' description: 'Export orders into CSV files in Medusa Admin.'
} }
}, },
]} /> ]} />

View File

@@ -200,7 +200,7 @@ In case the customer wants to confirm the order edit, you must check whether a r
### Refund Amount ### Refund Amount
If `difference_due` is less than 0, then the amount will be refunded to the customer by the merchant from the Medusa admin. No additional actions are required here before [completing the order edit](#complete-the-order-edit). If `difference_due` is less than 0, then the amount will be refunded to the customer by the merchant from the Medusa Admin. No additional actions are required here before [completing the order edit](#complete-the-order-edit).
### Make Additional Payments ### Make Additional Payments

View File

@@ -135,7 +135,7 @@ For zero-decimal currencies, the amount is still stored as an integer without mu
This logic of formatting the price is not handled in the backend. So, when you add a product variant using the Admin APIs, you must format the price as explained earlier. The backend stores the price as received from API requests. This logic of formatting the price is not handled in the backend. So, when you add a product variant using the Admin APIs, you must format the price as explained earlier. The backend stores the price as received from API requests.
In addition, the Medusa admin dashboard and the Next.js Starter Template expect the price to be of that format, so when prices are displayed for currencies that are stored multiplied by a `100`, such as USD, theyre divided by a hundred. Also, when you add or update a product variant, its price is sent to the Medusa backend as the price multiplied by a hundred. In addition, the Medusa Admin dashboard and the Next.js Starter Template expect the price to be of that format, so when prices are displayed for currencies that are stored multiplied by a `100`, such as USD, theyre divided by a hundred. Also, when you add or update a product variant, its price is sent to the Medusa backend as the price multiplied by a hundred.
### Displaying the Product Variants Price ### Displaying the Product Variants Price

View File

@@ -8,7 +8,7 @@ In this document, youll learn how to manually calculate taxes during checkout
## Overview ## Overview
By default, taxes are automatically calculated by Medusa during checkout. This behavior can be disabled for a region using the Admin APIs or the Medusa admin to limit the requests being sent to a tax provider. By default, taxes are automatically calculated by Medusa during checkout. This behavior can be disabled for a region using the Admin APIs or the Medusa Admin to limit the requests being sent to a tax provider.
If you disable this behavior, you must manually trigger taxes calculation. When taxes are calculated, this means that requests will be sent to the tax provider to retrieve the tax rates. If you disable this behavior, you must manually trigger taxes calculation. When taxes are calculated, this means that requests will be sent to the tax provider to retrieve the tax rates.

View File

@@ -81,7 +81,7 @@ DATABASE_SCHEMA=public
1. Change `APP_KEYS`, `API_TOKEN_SALT`, `JWT_SECRET`, and `ADMIN_JWT_SECRET` to a random and unique string. These keys are used by Strapi to sign session cookies, generate API tokens, and more. 1. Change `APP_KEYS`, `API_TOKEN_SALT`, `JWT_SECRET`, and `ADMIN_JWT_SECRET` to a random and unique string. These keys are used by Strapi to sign session cookies, generate API tokens, and more.
2. Change `MEDUSA_STRAPI_SECRET` to a random unique string. The value of this environment variable is used later in your Medusa configurations. 2. Change `MEDUSA_STRAPI_SECRET` to a random unique string. The value of this environment variable is used later in your Medusa configurations.
3. Change `MEDUSA_BACKEND_URL` to the URL of your Medusa backend. If youre running it locally, it should be `http://localhost:9000`. 3. Change `MEDUSA_BACKEND_URL` to the URL of your Medusa backend. If youre running it locally, it should be `http://localhost:9000`.
4. Change `MEDUSA_BACKEND_ADMIN` to the URL of your Medusa admin. If youre running it locally, it should be `http://localhost:7001`. 4. Change `MEDUSA_BACKEND_ADMIN` to the URL of your Medusa Admin. If youre running it locally, it should be `http://localhost:7001`.
5. Change the following environment variables to define the Strapi super user: 5. Change the following environment variables to define the Strapi super user:
1. `SUPERUSER_EMAIL`: the super users email. By default, its `support@medusa-commerce.com`. 1. `SUPERUSER_EMAIL`: the super users email. By default, its `support@medusa-commerce.com`.
2. `SUPERUSER_USERNAME`: the super users username. By default, its `SuperUser`. 2. `SUPERUSER_USERNAME`: the super users username. By default, its `SuperUser`.
@@ -209,7 +209,7 @@ info: Strapi Subscriber Initialized
### Two-Way Syncing ### Two-Way Syncing
To test syncing data from Medusa to Strapi, try creating or updating a product either using the Medusa admin or the [REST APIs](https://docs.medusajs.com/api/admin#products_postproducts). This triggers the associated event in Medusa, which makes the updates in Strapi. To test syncing data from Medusa to Strapi, try creating or updating a product either using the Medusa Admin or the [REST APIs](https://docs.medusajs.com/api/admin#products_postproducts). This triggers the associated event in Medusa, which makes the updates in Strapi.
:::tip :::tip

View File

@@ -4064,4 +4064,4 @@ You dont have to create a template for every type in the reference. You can s
## See Also ## See Also
- [Notifications Overview](../../development/notification/overview.mdx) - [Notifications Overview](../../development/notification/overview.mdx)
- Install the [Medusa admin](../../admin/quickstart.mdx) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more. - Install the [Medusa Admin](../../admin/quickstart.mdx) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more.

View File

@@ -135,4 +135,4 @@ If youre on a Twilio trial make sure that the phone number you entered on che
## See Also ## See Also
- [Notifications Overview](../../development/notification/overview.mdx). - [Notifications Overview](../../development/notification/overview.mdx).
- Install the [Medusa admin](../../admin/quickstart.mdx) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more. - Install the [Medusa Admin](../../admin/quickstart.mdx) for functionalities like Gift Cards creation, swaps, claims, order return requests, and more.

View File

@@ -53,7 +53,7 @@ So, to test out the API Route, run the following command in the root of your pro
npx medusa develop npx medusa develop
``` ```
Then, create a dynamic discount. You can do that either using the [Medusa admin](../../user-guide/discounts/create.mdx) which is available (if installed) at `http://localhost:7001` after starting the backend, or using the [Admin REST APIs](../../modules/discounts/admin/manage-discounts.mdx). Then, create a dynamic discount. You can do that either using the [Medusa Admin](../../user-guide/discounts/create.mdx) which is available (if installed) at `http://localhost:7001` after starting the backend, or using the [Admin REST APIs](../../modules/discounts/admin/manage-discounts.mdx).
After that, send a `POST` request to the `/discount-code` API Route, passing the `discount_code` parameter in the request body with the value being the code of the dynamic discount you just created. A new discount will be created with the same attributes as the dynamic discount code and returned in the response. After that, send a `POST` request to the `/discount-code` API Route, passing the `discount_code` parameter in the request body with the value being the code of the dynamic discount you just created. A new discount will be created with the same attributes as the dynamic discount code and returned in the response.

View File

@@ -28,9 +28,9 @@ Before you follow this guide, you must have a Medusa backend installed. If not,
### Event-Bus Module ### Event-Bus Module
To trigger events to the subscribed handler functions, you must have an event-bus module installed. For development purposes, you can use the [Local module](../../development/events/modules/local.md) which should be enabled by default in your Medusa backend. To trigger events to the subscribed handler functions, you must have an event-bus module installed. For development purposes, you can use the [Local Module](../../development/events/modules/local.md) which should be enabled by default in your Medusa backend.
For production, it's recommended to use the [Redis module](../../development/events/modules/redis.md). For production, it's recommended to use the [Redis Module](../../development/events/modules/redis.md).
--- ---
@@ -100,7 +100,7 @@ The API Route accepts the following request body parameters:
After subscribing to the out-of-stock variant, change its stock quantity to the minimum inventory required to test out the event trigger. The new stock quantity should be any value above `0` if you didn't set the `inventory_required` option. After subscribing to the out-of-stock variant, change its stock quantity to the minimum inventory required to test out the event trigger. The new stock quantity should be any value above `0` if you didn't set the `inventory_required` option.
You can use the [Medusa admin](../../user-guide/products/manage.mdx#manage-product-variants) or the [Admin REST API Routes](https://docs.medusajs.com/api/admin#products_postproductsproductvariantsvariant) to update the quantity. You can use the [Medusa Admin](../../user-guide/products/manage.mdx#manage-product-variants) or the [Admin REST API Routes](https://docs.medusajs.com/api/admin#products_postproductsproductvariantsvariant) to update the quantity.
After you update the quantity, you can see the `restock-notification.restocked` triggered and logged in the Medusa backend logs. If you've implemented the notification sending, this is where it'll be triggered and a notification will be sent. After you update the quantity, you can see the `restock-notification.restocked` triggered and logged in the Medusa backend logs. If you've implemented the notification sending, this is where it'll be triggered and a notification will be sent.

View File

@@ -87,13 +87,13 @@ The PayPal plugin also accepts the following optional configurations:
## Admin Setup ## Admin Setup
This section will guide you through adding PayPal as a payment processor in a region using your Medusa admin dashboard. This section will guide you through adding PayPal as a payment processor in a region using your Medusa Admin dashboard.
This step is required for you to be able to use PayPal as a payment processor in your storefront. This step is required for you to be able to use PayPal as a payment processor in your storefront.
### Admin Prerequisites ### Admin Prerequisites
If you dont have a Medusa admin installed, make sure to follow along with [the guide on how to install it](../../admin/quickstart.mdx) before continuing with this section. If you dont have a Medusa Admin installed, make sure to follow along with [the guide on how to install it](../../admin/quickstart.mdx) before continuing with this section.
### Add PayPal to Regions ### Add PayPal to Regions

View File

@@ -119,13 +119,13 @@ STRIPE_WEBHOOK_SECRET=whsec_...
## Admin Setup ## Admin Setup
This section will guide you through adding Stripe as a payment processor in a region using your Medusa admin dashboard. This section will guide you through adding Stripe as a payment processor in a region using your Medusa Admin dashboard.
This step is required for you to be able to use Stripe as a payment processor in your storefront. This step is required for you to be able to use Stripe as a payment processor in your storefront.
### Admin Prerequisites ### Admin Prerequisites
If you dont have a Medusa admin installed, make sure to follow along with [the guide on how to install it](https://github.com/medusajs/admin#-quickstart) before continuing with this section. If you dont have a Medusa Admin installed, make sure to follow along with [the guide on how to install it](https://github.com/medusajs/admin#-quickstart) before continuing with this section.
### Add Stripe to Regions ### Add Stripe to Regions

View File

@@ -25,7 +25,7 @@ Medusas commerce features, including sales channels, customer groups, and pri
In Medusa, a sales channel allows you to set product availability per channel. In this case, you can create a B2B sales channel that will include only B2B products. In Medusa, a sales channel allows you to set product availability per channel. In this case, you can create a B2B sales channel that will include only B2B products.
You can create a sales channel through the Medusa admin or Admin REST APIs. You can create a sales channel through the Medusa Admin or Admin REST APIs.
<DocCardList colSize={6} items={[ <DocCardList colSize={6} items={[
{ {
@@ -34,7 +34,7 @@ You can create a sales channel through the Medusa admin or Admin REST APIs.
label: 'Option 1: Use Medusa Admin', label: 'Option 1: Use Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the sales channel using the Medusa admin.', description: 'Create the sales channel using the Medusa Admin.',
} }
}, },
{ {
@@ -54,7 +54,7 @@ You can create a sales channel through the Medusa admin or Admin REST APIs.
Publishable API keys can be associated with one or more sales channels. Then, in a client such as a storefront, you can pass the publishable API key in the header of your requests to ensure all products retrieved belong to the associated sales channel(s). Publishable API keys can be associated with one or more sales channels. Then, in a client such as a storefront, you can pass the publishable API key in the header of your requests to ensure all products retrieved belong to the associated sales channel(s).
You can create a publishable API key through the Medusa admin or the Admin REST APIs, then associate it with the B2B sales channel. Youll later use this key when developing your B2B storefront. You can create a publishable API key through the Medusa Admin or the Admin REST APIs, then associate it with the B2B sales channel. Youll later use this key when developing your B2B storefront.
<DocCardList colSize={6} items={[ <DocCardList colSize={6} items={[
{ {
@@ -63,7 +63,7 @@ You can create a publishable API key through the Medusa admin or the Admin REST
label: 'Option 1: Use Medusa Admin', label: 'Option 1: Use Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the publishable API key using the Medusa admin.', description: 'Create the publishable API key using the Medusa Admin.',
} }
}, },
{ {
@@ -81,7 +81,7 @@ You can create a publishable API key through the Medusa admin or the Admin REST
## Add Products ## Add Products
Using the Medusa admin or the Admin REST APIs, you can add your B2B products to your store. You can also import your products from an existing CSV file using the Product Import feature. Using the Medusa Admin or the Admin REST APIs, you can add your B2B products to your store. You can also import your products from an existing CSV file using the Product Import feature.
After or while you add your products, set the products availability to the B2B sales channel youve created. After or while you add your products, set the products availability to the B2B sales channel youve created.
@@ -92,7 +92,7 @@ After or while you add your products, set the products availability to the B2
label: 'Add Products Using Medusa Admin', label: 'Add Products Using Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the product using the Medusa admin.', description: 'Create the product using the Medusa Admin.',
} }
}, },
{ {
@@ -110,7 +110,7 @@ After or while you add your products, set the products availability to the B2
label: 'Import Products Using Medusa Admin', label: 'Import Products Using Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Import the products using the Medusa admin.', description: 'Import the products using the Medusa Admin.',
} }
}, },
{ {
@@ -143,7 +143,7 @@ For more advanced customer modeling, you may create custom entities. This is cov
label: 'Option 1: Use Medusa Admin', label: 'Option 1: Use Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the customer group using the Medusa admin.', description: 'Create the customer group using the Medusa Admin.',
} }
}, },
{ {
@@ -163,7 +163,7 @@ For more advanced customer modeling, you may create custom entities. This is cov
After adding your B2B customer group, you can add B2B customers and assign them to the B2B customer group. Alternatively, if you want to allow B2B customers to register themselves, then that will be covered in a later step. After adding your B2B customer group, you can add B2B customers and assign them to the B2B customer group. Alternatively, if you want to allow B2B customers to register themselves, then that will be covered in a later step.
You can create a customer through the Medusa admin or Admin REST APIs. You can create a customer through the Medusa Admin or Admin REST APIs.
<DocCardList colSize={6} items={[ <DocCardList colSize={6} items={[
{ {
@@ -172,7 +172,7 @@ You can create a customer through the Medusa admin or Admin REST APIs.
label: 'Option 1: Use Medusa Admin', label: 'Option 1: Use Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the customers using the Medusa admin.', description: 'Create the customers using the Medusa Admin.',
} }
}, },
{ {
@@ -194,7 +194,7 @@ A price list allows you to set different prices on a set of products for various
You can use price lists to set the same price for all B2B customers or different prices for different B2B customers if youve organized them into separate customer groups. You can use price lists to set the same price for all B2B customers or different prices for different B2B customers if youve organized them into separate customer groups.
You can create a price list through the Medusa admin or Admin REST APIs. You can create a price list through the Medusa Admin or Admin REST APIs.
<DocCardList colSize={6} items={[ <DocCardList colSize={6} items={[
{ {
@@ -203,7 +203,7 @@ You can create a price list through the Medusa admin or Admin REST APIs.
label: 'Add Price List Using Medusa Admin', label: 'Add Price List Using Medusa Admin',
customProps: { customProps: {
icon: Icons['users-solid'], icon: Icons['users-solid'],
description: 'Create the price list using the Medusa admin.', description: 'Create the price list using the Medusa Admin.',
} }
}, },
{ {
@@ -325,9 +325,9 @@ Medusa allows you to create custom API Routes exposed as REST APIs.
## Customize Admin ## Customize Admin
Based on your use case, you may need to customize the Medusa admin to add new widgets or pages. Based on your use case, you may need to customize the Medusa Admin to add new widgets or pages.
The Medusa admin plugin can be extended to add widgets, UI routes, and setting pages. The Medusa Admin plugin can be extended to add widgets, UI routes, and setting pages.
<DocCardList colSize={4} items={[ <DocCardList colSize={4} items={[
{ {
@@ -345,7 +345,7 @@ The Medusa admin plugin can be extended to add widgets, UI routes, and setting p
label: 'Create Admin UI Routes', label: 'Create Admin UI Routes',
customProps: { customProps: {
icon: Icons['academic-cap-solid'], icon: Icons['academic-cap-solid'],
description: 'Learn how to add new pages to your Medusa admin.', description: 'Learn how to add new pages to your Medusa Admin.',
} }
}, },
{ {
@@ -354,7 +354,7 @@ The Medusa admin plugin can be extended to add widgets, UI routes, and setting p
label: 'Create Admin Setting Page', label: 'Create Admin Setting Page',
customProps: { customProps: {
icon: Icons['academic-cap-solid'], icon: Icons['academic-cap-solid'],
description: 'Learn how to add new page to the Medusa admin settings.', description: 'Learn how to add new page to the Medusa Admin settings.',
} }
}, },
]} /> ]} />

Some files were not shown because too many files have changed in this diff Show More