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.
265 lines
6.2 KiB
TypeScript
265 lines
6.2 KiB
TypeScript
"use client"
|
|
|
|
import { useIsBrowser } from "@/hooks"
|
|
import { getLearningPath } from "@/utils/learning-paths"
|
|
import React, { createContext, useContext, useEffect, useState } from "react"
|
|
import { LearningPathFinishType } from "@/components/LearningPath/Finish"
|
|
import { useAnalytics } from "docs-ui"
|
|
import { usePathname, useRouter } from "next/navigation"
|
|
|
|
export type LearningPathType = {
|
|
name: string
|
|
label: string
|
|
description?: string
|
|
steps: LearningPathStepType[]
|
|
finish?: LearningPathFinishType
|
|
notificationId?: string
|
|
}
|
|
|
|
export type LearningPathStepType = {
|
|
title?: string
|
|
description?: string
|
|
descriptionJSX?: JSX.Element
|
|
path?: string
|
|
}
|
|
|
|
export type LearningPathContextType = {
|
|
path: LearningPathType | null
|
|
setPath: (value: LearningPathType) => void
|
|
currentStep: number
|
|
setCurrentStep: (value: number) => void
|
|
startPath: (path: LearningPathType) => void
|
|
updatePath: (data: Pick<LearningPathType, "notificationId">) => void
|
|
endPath: () => void
|
|
nextStep: () => void
|
|
hasNextStep: () => boolean
|
|
previousStep: () => void
|
|
hasPreviousStep: () => boolean
|
|
goToStep: (stepIndex: number) => void
|
|
isCurrentPath: () => boolean
|
|
goToCurrentPath: () => void
|
|
baseUrl?: string
|
|
}
|
|
|
|
type LearningPathProviderProps = {
|
|
children?: React.ReactNode
|
|
baseUrl?: string
|
|
}
|
|
|
|
const LearningPathContext = createContext<LearningPathContextType | null>(null)
|
|
|
|
export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
|
|
children,
|
|
baseUrl,
|
|
}) => {
|
|
const [path, setPath] = useState<LearningPathType | null>(null)
|
|
const [currentStep, setCurrentStep] = useState(-1)
|
|
const isBrowser = useIsBrowser()
|
|
const pathname = usePathname()
|
|
const router = useRouter()
|
|
const { track } = useAnalytics()
|
|
|
|
const startPath = (path: LearningPathType) => {
|
|
setPath(path)
|
|
setCurrentStep(-1)
|
|
if (isBrowser) {
|
|
localStorage.setItem(
|
|
"learning-path",
|
|
JSON.stringify({
|
|
pathName: path.name,
|
|
currentStep: -1,
|
|
})
|
|
)
|
|
}
|
|
|
|
track(`learning_path_${path.name}`, {
|
|
url: pathname,
|
|
state: `start`,
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (path && currentStep === -1) {
|
|
nextStep()
|
|
}
|
|
}, [path])
|
|
|
|
const endPath = () => {
|
|
const didFinish = currentStep === (path?.steps.length || 0) - 1
|
|
const reachedIndex = currentStep === -1 ? 0 : currentStep
|
|
track(`learning_path_${path?.name}`, {
|
|
url: pathname,
|
|
state: !didFinish ? `closed` : `end`,
|
|
reachedStep:
|
|
path?.steps[reachedIndex]?.title ||
|
|
path?.steps[reachedIndex]?.description ||
|
|
path?.steps[reachedIndex]?.descriptionJSX ||
|
|
reachedIndex,
|
|
})
|
|
setPath(null)
|
|
setCurrentStep(-1)
|
|
if (isBrowser) {
|
|
localStorage.removeItem("learning-path")
|
|
}
|
|
}
|
|
|
|
const hasNextStep = () => currentStep !== (path?.steps.length || 0) - 1
|
|
|
|
const nextStep = () => {
|
|
if (!path || !hasNextStep()) {
|
|
return
|
|
}
|
|
const nextStepIndex = currentStep + 1
|
|
setCurrentStep(nextStepIndex)
|
|
const newPath = path.steps[nextStepIndex].path
|
|
if (isBrowser) {
|
|
localStorage.setItem(
|
|
"learning-path",
|
|
JSON.stringify({
|
|
pathName: path.name,
|
|
currentStep: nextStepIndex,
|
|
})
|
|
)
|
|
}
|
|
if (pathname !== newPath && newPath) {
|
|
router.push(newPath)
|
|
}
|
|
}
|
|
|
|
const hasPreviousStep = () => currentStep > 0
|
|
|
|
const previousStep = () => {
|
|
if (!path || !hasPreviousStep()) {
|
|
return
|
|
}
|
|
|
|
const previousStepIndex = currentStep - 1
|
|
setCurrentStep(previousStepIndex)
|
|
const newPath = path.steps[previousStepIndex].path
|
|
if (isBrowser) {
|
|
localStorage.setItem(
|
|
"learning-path",
|
|
JSON.stringify({
|
|
pathName: path.name,
|
|
currentStep: previousStepIndex,
|
|
})
|
|
)
|
|
}
|
|
if (pathname !== newPath && newPath) {
|
|
router.push(newPath)
|
|
}
|
|
}
|
|
|
|
const goToStep = (stepIndex: number) => {
|
|
if (!path || stepIndex >= path.steps.length) {
|
|
return
|
|
}
|
|
|
|
setCurrentStep(stepIndex)
|
|
const newPath = path.steps[stepIndex].path
|
|
if (isBrowser) {
|
|
localStorage.setItem(
|
|
"learning-path",
|
|
JSON.stringify({
|
|
pathName: path.name,
|
|
currentStep: stepIndex,
|
|
})
|
|
)
|
|
}
|
|
if (pathname !== newPath && newPath) {
|
|
router.push(newPath)
|
|
}
|
|
}
|
|
|
|
const isCurrentPath = () => {
|
|
if (!path || currentStep === -1) {
|
|
return false
|
|
}
|
|
|
|
return pathname === path.steps[currentStep].path
|
|
}
|
|
|
|
const goToCurrentPath = () => {
|
|
if (!path || currentStep === -1 || !path.steps[currentStep].path) {
|
|
return
|
|
}
|
|
|
|
router.push(path.steps[currentStep].path!)
|
|
}
|
|
|
|
const updatePath = (data: Pick<LearningPathType, "notificationId">) => {
|
|
if (!path) {
|
|
return
|
|
}
|
|
setPath({
|
|
...path,
|
|
...data,
|
|
})
|
|
}
|
|
|
|
const initPath = () => {
|
|
if (isBrowser) {
|
|
// give query parameters higher precedence over local storage
|
|
const queryPathName = new URLSearchParams(location.search).get("path")
|
|
const queryPath = queryPathName
|
|
? getLearningPath(queryPathName)
|
|
: undefined
|
|
if (queryPath) {
|
|
startPath(queryPath)
|
|
} else {
|
|
const storedPath = localStorage.getItem("learning-path")
|
|
if (storedPath) {
|
|
const storedPathParsed = JSON.parse(storedPath)
|
|
const currentPath = getLearningPath(storedPathParsed?.pathName)
|
|
if (currentPath) {
|
|
setPath(currentPath)
|
|
setCurrentStep(storedPathParsed?.currentStep || 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isBrowser && !path) {
|
|
initPath()
|
|
}
|
|
}, [isBrowser])
|
|
|
|
return (
|
|
<LearningPathContext.Provider
|
|
value={{
|
|
path,
|
|
setPath,
|
|
currentStep,
|
|
setCurrentStep,
|
|
startPath,
|
|
updatePath,
|
|
endPath,
|
|
nextStep,
|
|
hasNextStep,
|
|
previousStep,
|
|
hasPreviousStep,
|
|
goToStep,
|
|
isCurrentPath,
|
|
goToCurrentPath,
|
|
baseUrl,
|
|
}}
|
|
>
|
|
{children}
|
|
</LearningPathContext.Provider>
|
|
)
|
|
}
|
|
|
|
export const useLearningPath = () => {
|
|
const context = useContext(LearningPathContext)
|
|
|
|
if (!context) {
|
|
throw new Error(
|
|
"useLearningPath must be used within a LearningPathProvider"
|
|
)
|
|
}
|
|
|
|
return context
|
|
}
|