docs: added new recipes (#4793)
* added new recipes * added more recipes
This commit is contained in:
@@ -4,6 +4,7 @@ description: 'Learn what entities are in Medusa. There are entities defined in t
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
|
||||
# Entities
|
||||
|
||||
@@ -17,6 +18,8 @@ Aside from the entities in the Medusa core package, you can also create custom e
|
||||
|
||||
Entities are based on [Typeorm’s Entities](https://typeorm.io/entities) and use Typeorm decorators. Each entity also require a [repository](./repositories.md) to be created. A repository provides basic methods to access and manipulate the entity's data.
|
||||
|
||||
<LearningPath pathName="entity-and-api" />
|
||||
|
||||
---
|
||||
|
||||
## Base Entities
|
||||
|
||||
@@ -5,11 +5,16 @@ addHowToData: true
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
|
||||
# How to Create a Plugin
|
||||
|
||||
In this document, you’ll learn how to create a plugin and some tips for develoment. If you’re interested to learn more about what plugins are and where to find available official and community plugins, check out the [overview document](./overview.mdx).
|
||||
|
||||
Alternatively, you can follow this recipe to create a plugin with step-by-step guidance.
|
||||
|
||||
<LearningPath pathName="plugin" />
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You must have an existing Medusa project that you want to create the plugin with.
|
||||
|
||||
@@ -4,6 +4,7 @@ description: 'Learn what Plugins are and how they are used in Medusa. Plugins ar
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
|
||||
# Plugins
|
||||
|
||||
@@ -23,6 +24,8 @@ Plugins run within the same process as the core Medusa backend eliminating the n
|
||||
|
||||
Plugins can contain customizations to the Medusa backend or the admin dashboard.
|
||||
|
||||
<LearningPath pathName="plugin" />
|
||||
|
||||
---
|
||||
|
||||
## Using Existing Plugins
|
||||
|
||||
@@ -3,6 +3,7 @@ import DocCard from '@theme/DocCard';
|
||||
import Icons from '@theme/Icon';
|
||||
import Feedback from '@site/src/components/Feedback';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
import LearningPathList from '@site/src/components/LearningPath/List';
|
||||
import LargeCard from '@site/src/components/LargeCard';
|
||||
import Button from '@site/src/components/Button';
|
||||
|
||||
@@ -60,15 +61,26 @@ Follow this guide if you want to implement subscription-based purhcases with Med
|
||||
}
|
||||
}} />
|
||||
|
||||
---
|
||||
|
||||
## Other Recipes
|
||||
|
||||
This section includes general recipes available to guide you through your development with Medusa.
|
||||
|
||||
<LearningPathList ignore={[
|
||||
"simple-quickstart",
|
||||
"marketplace",
|
||||
"subscriptions"
|
||||
]} />
|
||||
|
||||
<Feedback
|
||||
event="survey_use_cases"
|
||||
question="Did your find your use case?"
|
||||
positiveQuestion="Is there anything that should improved?"
|
||||
negativeQuestion="What was your use case?"
|
||||
showLongForm={false}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
<!-- vale docs.HeadingPunctuation = NO -->
|
||||
|
||||
## Can't find your path?
|
||||
|
||||
@@ -5,6 +5,7 @@ description: 'Learn about the different resources you need and the general steps
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import DocCard from '@theme/DocCard';
|
||||
import Icons from '@theme/Icon';
|
||||
import LearningPath from '@site/src/components/LearningPath';
|
||||
|
||||
# Build Your Own Storefront Roadmap
|
||||
|
||||
@@ -16,6 +17,10 @@ Although Medusa provides a [Next.js Starter Template](../starters/nextjs-medusa-
|
||||
|
||||
This guide provides a roadmap that can guide you into how you can build your own storefront, regardless of what tools you’re using.
|
||||
|
||||
You can instead follow this recipe that will guide you through implementing features in your storefront step-by-step.
|
||||
|
||||
<LearningPath pathName="storefront" />
|
||||
|
||||
---
|
||||
|
||||
## Connect to the Backend
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { getLearningPaths } from "../../../utils/learning-paths"
|
||||
import LearningPath from ".."
|
||||
|
||||
type LearningPathListProps = {
|
||||
ignore?: string[]
|
||||
} & React.AllHTMLAttributes<HTMLDivElement>
|
||||
|
||||
const LearningPathList: React.FC<LearningPathListProps> = ({ ignore = [] }) => {
|
||||
const paths = useMemo(() => {
|
||||
const paths = getLearningPaths()
|
||||
ignore.forEach((pathName) => {
|
||||
const pathIndex = paths.findIndex((path) => path.name === pathName)
|
||||
if (pathIndex !== -1) {
|
||||
paths.splice(pathIndex, 1)
|
||||
}
|
||||
})
|
||||
|
||||
return paths
|
||||
}, [ignore])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-wrap gap-2 mt-1.5">
|
||||
{paths.map((path, index) => (
|
||||
<LearningPath
|
||||
pathName={path.name}
|
||||
key={index}
|
||||
className="!mt-0 !mb-0"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LearningPathList
|
||||
@@ -20,62 +20,64 @@ const LearningPathSteps: React.FC<LearningPathStepsProps> = ({ ...rest }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{path.steps.map((step, index) => (
|
||||
<div
|
||||
className={clsx(
|
||||
"border-0 border-b border-solid border-medusa-border-base dark:border-medusa-border-base-dark",
|
||||
"relative p-1"
|
||||
)}
|
||||
key={index}
|
||||
>
|
||||
<div className={clsx("flex items-center gap-1")}>
|
||||
<div className="w-2 flex-none flex items-center justify-center">
|
||||
{index === currentStep && (
|
||||
<IconCircleDottedLine
|
||||
className="shadow-active dark:shadow-active-dark rounded-full"
|
||||
iconColorClassName="stroke-medusa-fg-interactive dark:stroke-medusa-fg-interactive-dark"
|
||||
/>
|
||||
)}
|
||||
{index < currentStep && (
|
||||
<IconCheckCircleSolid iconColorClassName="fill-medusa-fg-interactive dark:fill-medusa-fg-interactive-dark" />
|
||||
)}
|
||||
{index > currentStep && <IconCircleMiniSolid />}
|
||||
</div>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-compact-medium-plus text-medusa-fg-base dark:text-medusa-fg-base-dark"
|
||||
)}
|
||||
>
|
||||
{step.title}
|
||||
</span>
|
||||
</div>
|
||||
{index === currentStep && (
|
||||
<>
|
||||
<div className="overflow-auto basis-3/4">
|
||||
{path.steps.map((step, index) => (
|
||||
<div
|
||||
className={clsx(
|
||||
"border-0 border-b border-solid border-medusa-border-base dark:border-medusa-border-base-dark",
|
||||
"relative p-1"
|
||||
)}
|
||||
key={index}
|
||||
>
|
||||
<div className={clsx("flex items-center gap-1")}>
|
||||
<div className="w-2 flex-none"></div>
|
||||
<div
|
||||
<div className="w-2 flex-none flex items-center justify-center">
|
||||
{index === currentStep && (
|
||||
<IconCircleDottedLine
|
||||
className="shadow-active dark:shadow-active-dark rounded-full"
|
||||
iconColorClassName="stroke-medusa-fg-interactive dark:stroke-medusa-fg-interactive-dark"
|
||||
/>
|
||||
)}
|
||||
{index < currentStep && (
|
||||
<IconCheckCircleSolid iconColorClassName="fill-medusa-fg-interactive dark:fill-medusa-fg-interactive-dark" />
|
||||
)}
|
||||
{index > currentStep && <IconCircleMiniSolid />}
|
||||
</div>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-medium text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark mt-1"
|
||||
"text-compact-medium-plus text-medusa-fg-base dark:text-medusa-fg-base-dark"
|
||||
)}
|
||||
>
|
||||
{step.descriptionJSX ?? step.description}
|
||||
</div>
|
||||
{step.title}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{index < currentStep && (
|
||||
<Link
|
||||
href={step.path}
|
||||
className={clsx("absolute top-0 left-0 w-full h-full")}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
goToStep(index)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{index === currentStep && (
|
||||
<div className={clsx("flex items-center gap-1")}>
|
||||
<div className="w-2 flex-none"></div>
|
||||
<div
|
||||
className={clsx(
|
||||
"text-medium text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark mt-1"
|
||||
)}
|
||||
>
|
||||
{step.descriptionJSX ?? step.description}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{index < currentStep && (
|
||||
<Link
|
||||
href={step.path}
|
||||
className={clsx("absolute top-0 left-0 w-full h-full")}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
goToStep(index)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<LearningPathStepActions {...rest} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ const LearningPath: React.FC<LearningPathProps> = ({
|
||||
}) => {
|
||||
const path = getLearningPath(pathName)
|
||||
if (!path) {
|
||||
throw new Error("Learning path does not exist.")
|
||||
throw new Error(`Learning path ${pathName} does not exist.`)
|
||||
}
|
||||
const { startPath, path: currentPath } = useLearningPath()
|
||||
const notificationContext = useNotifications()
|
||||
@@ -43,7 +43,7 @@ const LearningPath: React.FC<LearningPathProps> = ({
|
||||
)}
|
||||
>
|
||||
<LearningPathIcon />
|
||||
<div className={clsx("flex-auto")}>
|
||||
<div className={clsx("basis-3/4")}>
|
||||
<span
|
||||
className={clsx(
|
||||
"text-medusa-fg-base dark:text-medusa-fg-base-dark text-compact-large-plus block"
|
||||
@@ -61,7 +61,7 @@ const LearningPath: React.FC<LearningPathProps> = ({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button onClick={handleClick} className={clsx("flex-initial")}>
|
||||
<Button onClick={handleClick} className={clsx("basis-1/4 max-w-fit")}>
|
||||
Start Path
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -48,11 +48,11 @@ const NotificationContainer = () => {
|
||||
<>
|
||||
{renderFilteredNotifications(
|
||||
(notification) => notification.placement === "top",
|
||||
"flex fixed flex-col gap-0.5 md:right-1 right-0 md:top-1 top-0 z-[400] md:w-auto w-full"
|
||||
"flex fixed flex-col gap-0.5 right-0 top-0 z-[400] md:w-auto w-full max-h-[calc(100vh-57px)] overflow-y-auto"
|
||||
)}
|
||||
{renderFilteredNotifications(
|
||||
(notification) => notification.placement !== "top",
|
||||
"flex flex-col gap-0.5 fixed md:right-1 right-0 md:bottom-1 bottom-0 z-[400] md:w-auto w-full"
|
||||
"flex flex-col gap-0.5 fixed right-0 bottom-0 z-[400] md:w-auto w-full max-h-[calc(100vh-57px)] overflow-y-auto"
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -36,8 +36,8 @@ const Notification = ({
|
||||
<div
|
||||
className={clsx(
|
||||
"md:max-w-[320px] md:w-[320px] w-full bg-medusa-bg-base dark:bg-medusa-bg-base-dark rounded",
|
||||
"shadow-flyout dark:shadow-flyout-dark",
|
||||
"fixed md:right-1 left-0 block z-[400]",
|
||||
"shadow-flyout dark:shadow-flyout-dark max-h-[calc(100vh-90px)]",
|
||||
"fixed md:right-1 left-0 z-[400] md:m-1",
|
||||
placement === "bottom" && "md:bottom-1 bottom-0",
|
||||
placement === "top" && "md:top-1 top-0",
|
||||
"opacity-100 transition-opacity duration-200 ease-ease",
|
||||
|
||||
@@ -73,6 +73,7 @@ const useCurrentLearningPath = () => {
|
||||
layout: "empty",
|
||||
id,
|
||||
children: LearningStep(id),
|
||||
className: "flex flex-col",
|
||||
})
|
||||
updatePath({
|
||||
notificationId: id,
|
||||
|
||||
@@ -34,7 +34,7 @@ const paths: LearningPathType[] = [
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on building your store!",
|
||||
description: "Please rate your experience using this learning path.",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_simple-quickstart",
|
||||
},
|
||||
},
|
||||
@@ -120,7 +120,7 @@ const paths: LearningPathType[] = [
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on building your marketplace!",
|
||||
description: "Please rate your experience using this learning path.",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_marketplace",
|
||||
},
|
||||
},
|
||||
@@ -188,14 +188,277 @@ const paths: LearningPathType[] = [
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on implementing subscription-based purchases!",
|
||||
description: "Please rate your experience using this learning path.",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_subscriptions",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "entity-and-api",
|
||||
label: "Create Entity and Expose it with Endpoints",
|
||||
description:
|
||||
"Learn how to create a new table in your database, then create endpoints to expose and manipulate its data.",
|
||||
steps: [
|
||||
{
|
||||
title: "Create entity",
|
||||
path: "/development/entities/create",
|
||||
description: "Create your entity, its migration, and repository.",
|
||||
},
|
||||
{
|
||||
title: "Create service",
|
||||
path: "/development/services/create-service",
|
||||
description:
|
||||
"A service is a class that defines helper methods for your entity. The service will be used by the endpoints to access or modify the entity's data.",
|
||||
},
|
||||
{
|
||||
title: "Create endpoints",
|
||||
path: "/development/endpoints/create",
|
||||
},
|
||||
],
|
||||
finish: {
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on creating your entity and endpoints!",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_entity-and-api",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storefront",
|
||||
label: "Create a Custom Storefront",
|
||||
description:
|
||||
"Learn how to create a custom storefront with your preferred language or framework.",
|
||||
steps: [
|
||||
{
|
||||
title: "Choose your client",
|
||||
path: "/medusa-react/overview",
|
||||
descriptionJSX: (
|
||||
<>
|
||||
As your storefront connect to the Medusa backend, you need a way to
|
||||
interact with the backend's REST APIs. There are three ways to
|
||||
do so, based on your type of project:
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/medusa-react/overview">Medusa React</Link>: Can be
|
||||
used in any React-based project. For example, in a Next.js
|
||||
storefront.
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/js-client/overview">Medusa JS Client</Link>: Can be
|
||||
used in any JavaScript and NPM based project. For example, in a
|
||||
Nuxt storefront.
|
||||
</li>
|
||||
<li>
|
||||
<Link to={`https://docs.medusajs.com/api/store`}>
|
||||
Store REST APIs
|
||||
</Link>
|
||||
: You can send requests directly to the API endpoints without
|
||||
using Medusa's clients.
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Set CORS configurations in Backend",
|
||||
path: "/development/backend/configurations#admin_cors-and-store_cors",
|
||||
description:
|
||||
"To ensure your storefront can connect to the backend, make sure to configure the backend's CORS configuration based on your storefront's local or remote URL.",
|
||||
},
|
||||
{
|
||||
title: "Create a Publishable API Key",
|
||||
path: "/user-guide/settings/publishable-api-keys",
|
||||
descriptionJSX: (
|
||||
<>
|
||||
A publishable API key allows you to associate a key with a sales
|
||||
channel. Then, you can include that key in the headers of all your
|
||||
requests.
|
||||
<br />
|
||||
You can create the publishable API key from the dashboard.
|
||||
Alternatively, you can create it using the{" "}
|
||||
<Link to="/development/publishable-api-keys/admin/manage-publishable-api-keys">
|
||||
Admin APIs
|
||||
</Link>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Use Publishable API Key",
|
||||
path: "/development/publishable-api-keys/storefront/use-in-requests",
|
||||
description:
|
||||
"After creating the publishable API key and associating it with sales channels, you can pass it in the header of your requests to Store API endpoints.",
|
||||
},
|
||||
{
|
||||
title: "Add Region Selection",
|
||||
path: "/modules/regions-and-currencies/storefront/use-regions",
|
||||
description:
|
||||
"In your storefront, you can allow your customers to view available regions and select their current region. This can affect the prices, discounts, and shipping and payment providers available to the customer.",
|
||||
},
|
||||
{
|
||||
title: "Display Products",
|
||||
path: "/modules/products/storefront/show-products",
|
||||
description: "Display products to your customers in the storefront.",
|
||||
},
|
||||
{
|
||||
title: "Implement Cart Functionalities",
|
||||
path: "/modules/carts-and-checkout/storefront/implement-cart",
|
||||
description:
|
||||
"Allow your customers to add items to their cart, update them, and more in preparation for checkout.",
|
||||
},
|
||||
{
|
||||
title: "Implement Checkout Flow",
|
||||
path: "/modules/carts-and-checkout/storefront/implement-checkout-flow",
|
||||
description:
|
||||
"Implement the checkout flow that allows customers to handle shipping and payment, then place their orders.",
|
||||
},
|
||||
{
|
||||
title: "Implement Customer Profiles",
|
||||
path: "/modules/customers/storefront/implement-customer-profiles",
|
||||
description:
|
||||
"Allow customers to register, login, edit their profile information, and more.",
|
||||
},
|
||||
{
|
||||
title: "More Commerce Functionalities",
|
||||
path: "/modules/overview",
|
||||
descriptionJSX: (
|
||||
<>
|
||||
This recipe guided you to create a storefront with basic
|
||||
functionalities. You can add more functionalities to your storefront
|
||||
based on your use case.
|
||||
<ul>
|
||||
<li>
|
||||
The <Link to="/modules/overview">Commerce Modules</Link>{" "}
|
||||
documentation holds various storefront-related how-to guides to
|
||||
help you implement different features.
|
||||
</li>
|
||||
<li>
|
||||
You can also checkout the{" "}
|
||||
<Link to={`https://docs.medusajs.com/api/store`}>
|
||||
Store REST APIs
|
||||
</Link>{" "}
|
||||
for a full list of available REST APIs.
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
finish: {
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on creating your storefront!",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_storefront",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "plugin",
|
||||
label: "Create a Plugin",
|
||||
description:
|
||||
"Learn how to create a plugin that can be re-used across Medusa backends.",
|
||||
steps: [
|
||||
{
|
||||
title: "Setup plugin project",
|
||||
path: "/development/backend/install",
|
||||
description:
|
||||
"A plugin is initially a Medusa backend with customizations. If you don't have a project ready, you can create one using Medusa's CLI tool.",
|
||||
},
|
||||
{
|
||||
title: "Implement Customizations",
|
||||
path: "/development/entities/create",
|
||||
descriptionJSX: (
|
||||
<>
|
||||
Your plugin can hold backend and admin customizations. Those
|
||||
include:
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/development/entities/create">Create Entity</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/services/create-service">
|
||||
Create Service
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/endpoints/create">
|
||||
Create an Endpoint
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/events/create-subscriber">
|
||||
Create Subscriber
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/admin/widgets">Create Admin Widgets</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/admin/routes">Create Admin Routes</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/admin/setting-pages">
|
||||
Create Admin Setting Pages
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/search/create">
|
||||
Create Search Service
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/file-service/create-file-service">
|
||||
Create File Service
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/development/notification-service/create-notification-service">
|
||||
Create Notification Service
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
If you've already made your custom development, you can skip to
|
||||
the next step.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Change your package.json",
|
||||
path: "/development/plugins/create#changes-to-packagejson",
|
||||
descriptionJSX: (
|
||||
<>
|
||||
Once you're done making your customizations and you're
|
||||
ready to publish your plugin, make changes to your{" "}
|
||||
<code>package.json</code> in preparation for publishing.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Optionally test locally",
|
||||
path: "/development/plugins/create#test-your-plugin",
|
||||
description:
|
||||
"If necessary, you can test your plugin in a separate local Medusa backend. It's recommended, however, to do your plugin testing within the plugin project.",
|
||||
},
|
||||
{
|
||||
title: "Publish plugin",
|
||||
path: "/development/plugins/publish",
|
||||
description: "Publish your plugin on NPM.",
|
||||
},
|
||||
],
|
||||
finish: {
|
||||
type: "rating",
|
||||
step: {
|
||||
title: "Congratulations on creating your plugin!",
|
||||
description: "Please rate your experience using this recipe.",
|
||||
eventName: "rating_path_plugin",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const getLearningPaths = () => paths
|
||||
// get a shallow copy
|
||||
export const getLearningPaths = () => [...paths]
|
||||
|
||||
export const getLearningPath = (
|
||||
pathName: string
|
||||
|
||||
Reference in New Issue
Block a user