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:
5
.changeset/sixty-kids-divide.md
Normal file
5
.changeset/sixty-kids-divide.md
Normal 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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" ? "}|" : "||"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}"/>`
|
||||||
}
|
}
|
||||||
|
|||||||
8
docs-util/packages/types/lib/index.d.ts
vendored
8
docs-util/packages/types/lib/index.d.ts
vendored
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=
|
||||||
@@ -7,5 +7,5 @@ module.exports = {
|
|||||||
next: {
|
next: {
|
||||||
rootDir: ".",
|
rootDir: ".",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
25
www/apps/api-reference/components/PageHeading/index.tsx
Normal file
25
www/apps/api-reference/components/PageHeading/index.tsx
Normal 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
|
||||||
@@ -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),
|
||||||
|
|||||||
@@ -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 && (
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
{
|
{
|
||||||
|
|||||||
22
www/apps/api-reference/components/VersionNote/index.tsx
Normal file
22
www/apps/api-reference/components/VersionNote/index.tsx
Normal 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
|
||||||
36
www/apps/api-reference/components/VersionSwitcher/index.tsx
Normal file
36
www/apps/api-reference/components/VersionSwitcher/index.tsx
Normal 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
|
||||||
18
www/apps/api-reference/config/index.ts
Normal file
18
www/apps/api-reference/config/index.ts
Normal 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,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ const withMDX = mdx({
|
|||||||
extension: /\.mdx?$/,
|
extension: /\.mdx?$/,
|
||||||
options: {
|
options: {
|
||||||
rehypePlugins: [],
|
rehypePlugins: [],
|
||||||
|
development: process.env.NODE_ENV === "development",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
74
www/apps/api-reference/providers/version.tsx
Normal file
74
www/apps/api-reference/providers/version.tsx
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function getBaseUrl() {
|
|
||||||
return process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function getLinkWithBasePath(path: string): string {
|
|
||||||
return `${process.env.NEXT_PUBLIC_BASE_PATH || "/api"}${path}`
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ In this guide, you’ll 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 />
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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, you’ll learn the general steps to follow when deploying the Medusa admin separately from the backend. This guide isn’t tailored towards any hosting provider.
|
In this guide, you’ll learn the general steps to follow when deploying the Medusa Admin separately from the backend. This guide isn’t 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 doesn’t follow known framework presets.
|
- If your hosting provider supports choosing a Framework Preset, choose the “Other” option as the Medusa Admin doesn’t 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
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ In this document, you’ll 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
|
||||||
|
|
||||||

|

|
||||||
@@ -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 won’t 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 won’t work without a deployed Medusa backend URL.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ It’s 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
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ You can also create endpoints that don't reside under these two prefixes, simila
|
|||||||
|
|
||||||
## CORS Configuration
|
## CORS Configuration
|
||||||
|
|
||||||
If you’re 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 you’re 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:
|
||||||
|
|
||||||
|
|||||||
@@ -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 you’ve 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 you’ve 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 you’ve 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 you’ve 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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ In this document, you’ll learn about the Redis cache module and how you can in
|
|||||||
|
|
||||||
Medusa’s 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.
|
Medusa’s 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ In this document, you’ll learn about the Redis events module and how you can i
|
|||||||
|
|
||||||
Medusa’s 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.
|
Medusa’s 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 Medusa’s 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 Medusa’s 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ In this document, you’ll 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 Medusa’s 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 Medusa’s core.
|
Modules further increase Medusa’s 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 Medusa’s 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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 Medusa’s 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 Medusa’s 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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Pricing Concepts
|
# Pricing Concepts
|
||||||
|
|
||||||
In this document, you’ll learn about the main concepts in the Pricing module, and how data is stored and related.
|
In this document, you’ll 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 variant’s 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 variant’s 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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import TabItem from '@theme/TabItem';
|
|||||||
|
|
||||||
# Examples of Pricing Module
|
# Examples of Pricing Module
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Pricing module in your application.
|
In this document, you’ll find common examples of how you can use the Pricing Module in your application.
|
||||||
|
|
||||||
## Create a Price Set
|
## Create a Price Set
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Install in Node.js-Based Application
|
# Install in Node.js-Based Application
|
||||||
|
|
||||||
In this document, you’ll learn how to setup and use the Pricing module in a Node.js based application.
|
In this document, you’ll 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 aren’t 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 aren’t 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.
|
||||||
@@ -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.'
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import TabItem from '@theme/TabItem';
|
|||||||
|
|
||||||
# Examples of Product Module
|
# Examples of Product Module
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Product module in your application.
|
In this document, you’ll find common examples of how you can use the Product Module in your application.
|
||||||
|
|
||||||
## Create Product
|
## Create Product
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Install in Node.js-Based Application
|
# Install in Node.js-Based Application
|
||||||
|
|
||||||
In this document, you’ll learn how to setup and use the Product module in a Node.js based application.
|
In this document, you’ll 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 aren’t 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 aren’t 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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]} />
|
]} />
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ The process is implemented as follows:
|
|||||||
1. When the idempotency key’s 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 key’s 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 key’s 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 key’s 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 key’s 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 key’s 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 key’s recovery point is set to `finished`.
|
4. Once the process detailed above is done, the idempotency key’s recovery point is set to `finished`.
|
||||||
|
|
||||||
|
|||||||
@@ -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, it’s recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you don’t have any location logic implemented in place.
|
Furthermore, inventory levels are tied to a location ID. So, it’s recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you don’t 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 item’s quantity in stock.
|
- `stocked_quantity`: A number indicating the item’s 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).
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
---
|
---
|
||||||
description: "In this document, you’ll learn about the inventory module, how it works, and its relation to other processes in your commerce application."
|
description: "In this document, you’ll 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, you’ll learn about the inventory module and how it works.
|
In this document, you’ll 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
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
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`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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 order’s 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 order’s 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.
|
||||||
|
|||||||
@@ -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.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
---
|
---
|
||||||
description: "In this document, you’ll learn about the inventory module, how it works, and its relation to other processes in your commerce application."
|
description: "In this document, you’ll 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, you’ll learn about the Stock Location module and how it works.
|
In this document, you’ll 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 cart’s 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 cart’s 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 cart’s 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 cart’s 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.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ When a fulfillment is created for one or more item, shipments can then be create
|
|||||||
Some of the `Fulfillment` entity’s attributes include:
|
Some of the `Fulfillment` entity’s 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.
|
||||||
|
|||||||
@@ -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.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]} />
|
]} />
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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, they’re 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, they’re 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 Variant’s Price
|
### Displaying the Product Variant’s Price
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ In this document, you’ll 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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 you’re running it locally, it should be `http://localhost:9000`.
|
3. Change `MEDUSA_BACKEND_URL` to the URL of your Medusa backend. If you’re running it locally, it should be `http://localhost:9000`.
|
||||||
4. Change `MEDUSA_BACKEND_ADMIN` to the URL of your Medusa admin. If you’re running it locally, it should be `http://localhost:7001`.
|
4. Change `MEDUSA_BACKEND_ADMIN` to the URL of your Medusa Admin. If you’re 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 user’s email. By default, it’s `support@medusa-commerce.com`.
|
1. `SUPERUSER_EMAIL`: the super user’s email. By default, it’s `support@medusa-commerce.com`.
|
||||||
2. `SUPERUSER_USERNAME`: the super user’s username. By default, it’s `SuperUser`.
|
2. `SUPERUSER_USERNAME`: the super user’s username. By default, it’s `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
|
||||||
|
|
||||||
|
|||||||
@@ -4064,4 +4064,4 @@ You don’t 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.
|
||||||
|
|||||||
@@ -135,4 +135,4 @@ If you’re 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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 don’t 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 don’t 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
|
||||||
|
|
||||||
|
|||||||
@@ -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 don’t 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 don’t 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
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Medusa’s 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. You’ll 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. You’ll 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 you’ve created.
|
After or while you add your products, set the products’ availability to the B2B sales channel you’ve 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 you’ve 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 you’ve 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
Reference in New Issue
Block a user