docs: redesign search + re-introduce ai assistant in v2 docs (#8678)
* docs: redesign search + re-introduce ai assistant in v2 * change version in ui * show icon in case of error * fixes based on feedback
This commit is contained in:
@@ -1,13 +1,8 @@
|
||||
import React, { useState } from "react"
|
||||
import { ThreadType } from "../.."
|
||||
import clsx from "clsx"
|
||||
import {
|
||||
Button,
|
||||
type ButtonProps,
|
||||
ThumbDownIcon,
|
||||
ThumbUpIcon,
|
||||
} from "@/components"
|
||||
import { Check, SquareTwoStack } from "@medusajs/icons"
|
||||
import { Button, type ButtonProps } from "@/components"
|
||||
import { Check, SquareTwoStackMini, ThumbDown, ThumbUp } from "@medusajs/icons"
|
||||
import { useCopy } from "@/hooks"
|
||||
import { AiAssistantFeedbackType, useAiAssistant } from "@/providers"
|
||||
|
||||
@@ -43,21 +38,17 @@ export const AiAssistantThreadItemActions = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"hidden md:flex gap-docs_0.25",
|
||||
"text-medusa-fg-muted",
|
||||
"sticky top-docs_1"
|
||||
)}
|
||||
className={clsx("hidden md:flex gap-docs_0.25", "text-medusa-fg-muted")}
|
||||
>
|
||||
<ActionButton onClick={handleCopy}>
|
||||
{isCopied ? <Check /> : <SquareTwoStack />}
|
||||
{isCopied ? <Check /> : <SquareTwoStackMini />}
|
||||
</ActionButton>
|
||||
{(feedback === null || feedback === "upvote") && (
|
||||
<ActionButton
|
||||
onClick={async () => handleFeedback("upvote", item.question_id)}
|
||||
className={clsx(feedback === "upvote" && "!text-medusa-fg-subtle")}
|
||||
>
|
||||
<ThumbUpIcon />
|
||||
<ThumbUp />
|
||||
</ActionButton>
|
||||
)}
|
||||
{(feedback === null || feedback === "downvote") && (
|
||||
@@ -65,7 +56,7 @@ export const AiAssistantThreadItemActions = ({
|
||||
onClick={async () => handleFeedback("downvote", item.question_id)}
|
||||
className={clsx(feedback === "downvote" && "!text-medusa-fg-subtle")}
|
||||
>
|
||||
<ThumbDownIcon />
|
||||
<ThumbDown />
|
||||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
@@ -79,7 +70,7 @@ const ActionButton = ({ children, className, ...props }: ButtonProps) => {
|
||||
className={clsx(
|
||||
"text-medusa-fg-muted hover:text-medusa-fg-subtle",
|
||||
"hover:bg-medusa-bg-subtle-hover",
|
||||
"p-docs_0.125 rounded-docs_sm",
|
||||
"!p-[4.5px] rounded-docs_sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { ThreadType } from ".."
|
||||
import { DotsLoading, MarkdownContent, QuestionMarkIcon } from "@/components"
|
||||
import { ExclamationCircle, Sparkles } from "@medusajs/icons"
|
||||
import { AiAssistantIcon, DotsLoading, MarkdownContent } from "@/components"
|
||||
import { AiAssistantThreadItemActions } from "./Actions"
|
||||
|
||||
export type AiAssistantThreadItemProps = {
|
||||
@@ -13,39 +12,37 @@ export const AiAssistantThreadItem = ({ item }: AiAssistantThreadItemProps) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"p-docs_1 flex justify-start gap-docs_1 items-start",
|
||||
"text-medusa-fg-subtle border-solid border-0 border-b",
|
||||
"border-medusa-border-base text-medium relative",
|
||||
item.type === "question" && "bg-medusa-bg-base",
|
||||
item.type === "answer" && "bg-medusa-bg-subtle"
|
||||
"p-docs_0.5 flex gap-docs_0.75 items-start",
|
||||
item.type === "question" && "justify-end",
|
||||
item.type === "answer" && "!pr-[20px]"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
{item.type !== "question" && <AiAssistantIcon />}
|
||||
<div
|
||||
className={clsx(
|
||||
"border border-solid border-medusa-border-base",
|
||||
"rounded-docs_sm p-docs_0.125 bg-medusa-bg-component",
|
||||
"text-medusa-fg-muted flex"
|
||||
"txt-small text-medusa-fg-subtle",
|
||||
item.type === "question" && [
|
||||
"rounded-docs_xl bg-medusa-tag-neutral-bg",
|
||||
"px-docs_0.75 py-docs_0.5",
|
||||
],
|
||||
item.type !== "question" && "flex-1",
|
||||
item.type === "answer" && "text-pretty"
|
||||
)}
|
||||
>
|
||||
{item.type === "question" && <QuestionMarkIcon />}
|
||||
{item.type === "answer" && <Sparkles />}
|
||||
{item.type === "error" && <ExclamationCircle />}
|
||||
</span>
|
||||
<div className="md:max-w-[calc(100%-134px)] md:w-[calc(100%-134px)]">
|
||||
{item.type === "question" && <>{item.content}</>}
|
||||
{item.type === "answer" && (
|
||||
<>
|
||||
<div className="flex flex-col gap-docs_0.75">
|
||||
{!item.question_id && item.content.length === 0 && <DotsLoading />}
|
||||
<MarkdownContent>{item.content}</MarkdownContent>
|
||||
</>
|
||||
<MarkdownContent className="[&>*:last-child]:mb-0">
|
||||
{item.content}
|
||||
</MarkdownContent>
|
||||
{item.question_id && <AiAssistantThreadItemActions item={item} />}
|
||||
</div>
|
||||
)}
|
||||
{item.type === "error" && (
|
||||
<span className="text-medusa-fg-error">{item.content}</span>
|
||||
)}
|
||||
</div>
|
||||
{item.type === "answer" && item.question_id && (
|
||||
<AiAssistantThreadItemActions item={item} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ import {
|
||||
SearchHitGroupName,
|
||||
Tooltip,
|
||||
Link,
|
||||
AiAssistantIcon,
|
||||
} from "@/components"
|
||||
import { useAiAssistant, useSearch } from "@/providers"
|
||||
import { ArrowUturnLeft, XMarkMini } from "@medusajs/icons"
|
||||
import { ArrowUturnLeft } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import { useSearchNavigation } from "@/hooks"
|
||||
import { AiAssistantThreadItem } from "./ThreadItem"
|
||||
@@ -77,26 +76,50 @@ export const AiAssistant = () => {
|
||||
const [answer, setAnswer] = useState("")
|
||||
const [identifiers, setIdentifiers] = useState<IdentifierType | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { getAnswer } = useAiAssistant()
|
||||
const { getAnswer, version } = useAiAssistant()
|
||||
const { setCommand } = useSearch()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const suggestions: SearchSuggestionType[] = [
|
||||
{
|
||||
title: "FAQ",
|
||||
items: [
|
||||
"What is Medusa?",
|
||||
"How can I create an ecommerce store with Medusa?",
|
||||
"How can I build a marketplace with Medusa?",
|
||||
"How can I build subscription-based purchases with Medusa?",
|
||||
"How can I build digital products with Medusa?",
|
||||
"What can I build with Medusa?",
|
||||
"What is Medusa Admin?",
|
||||
"How do I configure the database in Medusa?",
|
||||
],
|
||||
},
|
||||
]
|
||||
const suggestions: SearchSuggestionType[] = useMemo(() => {
|
||||
return version === "v2"
|
||||
? [
|
||||
{
|
||||
title: "FAQ",
|
||||
items: [
|
||||
"What is Medusa?",
|
||||
"How can I create a module?",
|
||||
"How can I create a data model?",
|
||||
"How do I create a workflow?",
|
||||
"How can I extend a data model in the Product Module?",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Recipes",
|
||||
items: [
|
||||
"How do I build a marketplace with Medusa?",
|
||||
"How do I build digital products with Medusa?",
|
||||
"How do I build subscription-based purchases with Medusa?",
|
||||
"What other recipes are available in the Medusa documentation?",
|
||||
],
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: "FAQ",
|
||||
items: [
|
||||
"What is Medusa?",
|
||||
"How can I create an ecommerce store with Medusa?",
|
||||
"How can I build a marketplace with Medusa?",
|
||||
"How can I build subscription-based purchases with Medusa?",
|
||||
"How can I build digital products with Medusa?",
|
||||
"What can I build with Medusa?",
|
||||
"What is Medusa Admin?",
|
||||
"How do I configure the database in Medusa?",
|
||||
],
|
||||
},
|
||||
]
|
||||
}, [version])
|
||||
|
||||
const handleSubmit = (selectedQuestion?: string) => {
|
||||
if (!selectedQuestion?.length && !question.length) {
|
||||
@@ -292,6 +315,22 @@ export const AiAssistant = () => {
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
<div className={clsx("px-docs_1 pt-docs_1")}>
|
||||
<Tooltip
|
||||
tooltipChildren={
|
||||
<>
|
||||
This site is protected by reCAPTCHA and the{" "}
|
||||
<Link href="https://policies.google.com/privacy">
|
||||
Google Privacy Policy
|
||||
</Link>{" "}
|
||||
and <Link href="https://policies.google.com/terms">ToS</Link>{" "}
|
||||
apply
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Badge variant="neutral">AI Assistant</Badge>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex gap-docs_1 px-docs_1 py-docs_0.75",
|
||||
@@ -302,7 +341,7 @@ export const AiAssistant = () => {
|
||||
<Button
|
||||
variant="transparent"
|
||||
onClick={() => setCommand(null)}
|
||||
className="text-medusa-fg-subtle p-[5px]"
|
||||
className="text-medusa-fg-muted p-[6.5px]"
|
||||
>
|
||||
<ArrowUturnLeft />
|
||||
</Button>
|
||||
@@ -311,7 +350,7 @@ export const AiAssistant = () => {
|
||||
onChange={(e) => setQuestion(e.target.value)}
|
||||
className={clsx(
|
||||
"bg-transparent border-0 focus:outline-none hover:!bg-transparent",
|
||||
"shadow-none flex-1 text-medusa-fg-base",
|
||||
"!shadow-none flex-1 text-medusa-fg-base",
|
||||
"disabled:!bg-transparent disabled:cursor-not-allowed"
|
||||
)}
|
||||
placeholder="Ask me a question about Medusa..."
|
||||
@@ -319,23 +358,22 @@ export const AiAssistant = () => {
|
||||
passedRef={inputRef}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
variant="transparent"
|
||||
<span
|
||||
onClick={() => {
|
||||
setQuestion("")
|
||||
inputRef.current?.focus()
|
||||
}}
|
||||
className={clsx(
|
||||
"text-medusa-fg-subtle p-[5px]",
|
||||
"text-medusa-fg-muted hover:text-medusa-fg-subtle",
|
||||
"absolute top-docs_0.75 right-docs_1",
|
||||
"hover:bg-medusa-bg-base-hover rounded-docs_sm",
|
||||
"cursor-pointer",
|
||||
question.length === 0 && "hidden"
|
||||
)}
|
||||
>
|
||||
<XMarkMini />
|
||||
</Button>
|
||||
Clear
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-[calc(100%-120px)] md:h-[calc(100%-114px)] lg:max-h-[calc(100%-114px)] lg:min-h-[calc(100%-114px)] overflow-auto">
|
||||
<div className="h-[calc(100%-95px)] lg:max-h-[calc(100%-140px)] lg:min-h-[calc(100%-140px)] overflow-auto">
|
||||
<div ref={contentRef}>
|
||||
{!thread.length && (
|
||||
<div className="mx-docs_0.5">
|
||||
@@ -372,35 +410,12 @@ export const AiAssistant = () => {
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"py-docs_0.75 flex items-center justify-between px-docs_1",
|
||||
"border-0 border-solid",
|
||||
"py-docs_0.75 hidden md:flex items-center justify-end px-docs_1",
|
||||
"border-medusa-border-base border-t",
|
||||
"bg-medusa-bg-base h-[57px]"
|
||||
"bg-medusa-bg-field-component"
|
||||
)}
|
||||
>
|
||||
<Tooltip
|
||||
tooltipChildren={
|
||||
<>
|
||||
This site is protected by reCAPTCHA and the{" "}
|
||||
<Link href="https://policies.google.com/privacy">
|
||||
Google Privacy Policy
|
||||
</Link>{" "}
|
||||
and <Link href="https://policies.google.com/terms">ToS</Link>{" "}
|
||||
apply
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.75 text-compact-small-plus"
|
||||
)}
|
||||
>
|
||||
<AiAssistantIcon />
|
||||
<span className="text-medusa-fg-subtle">Medusa AI Assistant</span>
|
||||
<Badge variant="purple">Beta</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="hidden items-center gap-docs_1 md:flex">
|
||||
<div className="flex items-center gap-docs_0.75">
|
||||
<div className="flex items-center gap-docs_0.5">
|
||||
{thread.length === 0 && (
|
||||
<>
|
||||
@@ -412,9 +427,23 @@ export const AiAssistant = () => {
|
||||
>
|
||||
Navigate FAQ
|
||||
</span>
|
||||
<span className="gap-docs_0.25 flex">
|
||||
<Kbd>↑</Kbd>
|
||||
<Kbd>↓</Kbd>
|
||||
<span className="gap-[5px] flex">
|
||||
<Kbd
|
||||
className={clsx(
|
||||
"!bg-medusa-bg-field-component !border-medusa-border-strong",
|
||||
"!text-medusa-fg-subtle h-[18px] w-[18px] p-0"
|
||||
)}
|
||||
>
|
||||
↑
|
||||
</Kbd>
|
||||
<Kbd
|
||||
className={clsx(
|
||||
"!bg-medusa-bg-field-component !border-medusa-border-strong",
|
||||
"!text-medusa-fg-subtle h-[18px] w-[18px] p-0"
|
||||
)}
|
||||
>
|
||||
↓
|
||||
</Kbd>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
@@ -426,13 +455,23 @@ export const AiAssistant = () => {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={clsx("h-docs_0.75 w-px bg-medusa-border-strong")}
|
||||
></div>
|
||||
<div className="flex items-center gap-docs_0.5">
|
||||
<span
|
||||
className={clsx("text-medusa-fg-subtle", "text-compact-x-small")}
|
||||
>
|
||||
Ask Question
|
||||
</span>
|
||||
<Kbd>↵</Kbd>
|
||||
<Kbd
|
||||
className={clsx(
|
||||
"!bg-medusa-bg-field-component !border-medusa-border-strong",
|
||||
"!text-medusa-fg-subtle h-[18px] w-[18px] p-0"
|
||||
)}
|
||||
>
|
||||
↵
|
||||
</Kbd>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user