docs: open AI assistant based on search query (#13063)
* docs: open AI assistant based on search query * use URLSearchParams * fix vale error
This commit is contained in:
1 parent
ea9ed62ee0
commit
7a07319a69
5 files changed
+162
-88
No files matched your search
@@ -1897,7 +1897,7 @@ If your use case is complex and these rules are not enough, you can create a new
|
|||||||
|
|
||||||
Medusa v1 has gift card features out-of-the-box.
|
Medusa v1 has gift card features out-of-the-box.
|
||||||
|
|
||||||
In Medusa v2, gift card features are now only available to [Medusa Cloud](https://medusajs.com/cloud/) users.
|
In Medusa v2, gift card features are now only available to [Cloud](https://medusajs.com/cloud/) users.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from "react"
|
import React, { useEffect, useMemo, useRef } from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { ArrowUpCircleSolid } from "@medusajs/icons"
|
import { ArrowUpCircleSolid } from "@medusajs/icons"
|
||||||
import { useAiAssistant } from "../../../../providers"
|
import { useAiAssistant, useIsBrowser } from "../../../../providers"
|
||||||
import { useChat } from "@kapaai/react-sdk"
|
import { useChat } from "@kapaai/react-sdk"
|
||||||
import { useAiAssistantChatNavigation } from "../../../../hooks"
|
import { useAiAssistantChatNavigation } from "../../../../hooks"
|
||||||
|
|
||||||
@@ -12,8 +12,20 @@ type AiAssistantChatWindowInputProps = {
|
|||||||
export const AiAssistantChatWindowInput = ({
|
export const AiAssistantChatWindowInput = ({
|
||||||
chatWindowRef,
|
chatWindowRef,
|
||||||
}: AiAssistantChatWindowInputProps) => {
|
}: AiAssistantChatWindowInputProps) => {
|
||||||
const { chatOpened, inputRef, loading } = useAiAssistant()
|
const { chatOpened, inputRef, loading, setChatOpened } = useAiAssistant()
|
||||||
const { submitQuery, conversation } = useChat()
|
const { submitQuery, conversation } = useChat()
|
||||||
|
const { isBrowser } = useIsBrowser()
|
||||||
|
const { searchQuery, searchQueryType } = useMemo(() => {
|
||||||
|
if (!isBrowser) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const searchParams = new URLSearchParams(location.search)
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchQuery: searchParams.get("query"),
|
||||||
|
searchQueryType: searchParams.get("queryType"),
|
||||||
|
}
|
||||||
|
}, [isBrowser])
|
||||||
const [question, setQuestion] = React.useState("")
|
const [question, setQuestion] = React.useState("")
|
||||||
const formRef = useRef<HTMLFormElement | null>(null)
|
const formRef = useRef<HTMLFormElement | null>(null)
|
||||||
|
|
||||||
@@ -97,6 +109,21 @@ export const AiAssistantChatWindowInput = ({
|
|||||||
question,
|
question,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (searchQueryType === "submit") {
|
||||||
|
onSubmit()
|
||||||
|
}
|
||||||
|
}, [searchQueryType])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!searchQuery) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuestion(searchQuery)
|
||||||
|
setChatOpened(true)
|
||||||
|
}, [searchQuery])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { Badge, Button, Link, type ButtonProps } from "@/components"
|
import { Badge, Button, Link, type ButtonProps } from "@/components"
|
||||||
import { ThumbDown, ThumbUp } from "@medusajs/icons"
|
import {
|
||||||
import { AiAssistantThreadItem as AiAssistantThreadItemType } from "../../../../providers"
|
ThumbDown,
|
||||||
|
ThumbUp,
|
||||||
|
Link as LinkIcon,
|
||||||
|
CheckCircle,
|
||||||
|
} from "@medusajs/icons"
|
||||||
|
import {
|
||||||
|
AiAssistantThreadItem as AiAssistantThreadItemType,
|
||||||
|
useSiteConfig,
|
||||||
|
} from "../../../../providers"
|
||||||
import { Reaction, useChat } from "@kapaai/react-sdk"
|
import { Reaction, useChat } from "@kapaai/react-sdk"
|
||||||
|
import { useCopy } from "../../../../hooks"
|
||||||
|
|
||||||
export type AiAssistantThreadItemActionsProps = {
|
export type AiAssistantThreadItemActionsProps = {
|
||||||
item: AiAssistantThreadItemType
|
item: AiAssistantThreadItemType
|
||||||
@@ -14,6 +23,12 @@ export const AiAssistantThreadItemActions = ({
|
|||||||
}: AiAssistantThreadItemActionsProps) => {
|
}: AiAssistantThreadItemActionsProps) => {
|
||||||
const [feedback, setFeedback] = useState<Reaction | null>(null)
|
const [feedback, setFeedback] = useState<Reaction | null>(null)
|
||||||
const { addFeedback } = useChat()
|
const { addFeedback } = useChat()
|
||||||
|
const {
|
||||||
|
config: { baseUrl },
|
||||||
|
} = useSiteConfig()
|
||||||
|
const { handleCopy, isCopied } = useCopy(
|
||||||
|
`${baseUrl}?query=${encodeURI(item.content)}`
|
||||||
|
)
|
||||||
|
|
||||||
const handleFeedback = async (
|
const handleFeedback = async (
|
||||||
reaction: Reaction,
|
reaction: Reaction,
|
||||||
@@ -31,36 +46,59 @@ export const AiAssistantThreadItemActions = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx("flex gap-docs_0.75 justify-between items-center")}>
|
<div
|
||||||
{item.sources !== undefined && item.sources.length > 0 && (
|
className={clsx(
|
||||||
<div className="flex gap-[6px] items-center flex-wrap">
|
"flex gap-docs_0.75 items-center",
|
||||||
{item.sources.map((source) => (
|
item.type === "question" && "justify-end",
|
||||||
<Badge key={source.source_url} variant="neutral">
|
item.type === "answer" && "justify-between"
|
||||||
<Link href={source.source_url} className="!text-inherit">
|
)}
|
||||||
{source.title}
|
>
|
||||||
</Link>
|
{item.type === "question" && (
|
||||||
</Badge>
|
<div className="flex gap-docs_0.25 items-center text-medusa-fg-muted">
|
||||||
))}
|
<ActionButton onClick={handleCopy}>
|
||||||
|
{isCopied ? <CheckCircle /> : <LinkIcon />}
|
||||||
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-docs_0.25 items-center text-medusa-fg-muted">
|
{item.type === "answer" && (
|
||||||
{(feedback === null || feedback === "upvote") && (
|
<>
|
||||||
<ActionButton
|
{item.sources !== undefined && item.sources.length > 0 && (
|
||||||
onClick={async () => handleFeedback("upvote", item.question_id)}
|
<div className="flex gap-[6px] items-center flex-wrap">
|
||||||
className={clsx(feedback === "upvote" && "!text-medusa-fg-muted")}
|
{item.sources.map((source) => (
|
||||||
>
|
<Badge key={source.source_url} variant="neutral">
|
||||||
<ThumbUp />
|
<Link href={source.source_url} className="!text-inherit">
|
||||||
</ActionButton>
|
{source.title}
|
||||||
)}
|
</Link>
|
||||||
{(feedback === null || feedback === "downvote") && (
|
</Badge>
|
||||||
<ActionButton
|
))}
|
||||||
onClick={async () => handleFeedback("downvote", item.question_id)}
|
</div>
|
||||||
className={clsx(feedback === "downvote" && "!text-medusa-fg-muted")}
|
)}
|
||||||
>
|
<div className="flex gap-docs_0.25 items-center text-medusa-fg-muted">
|
||||||
<ThumbDown />
|
{(feedback === null || feedback === "upvote") && (
|
||||||
</ActionButton>
|
<ActionButton
|
||||||
)}
|
onClick={async () => handleFeedback("upvote", item.question_id)}
|
||||||
</div>
|
className={clsx(
|
||||||
|
feedback === "upvote" && "!text-medusa-fg-muted"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ThumbUp />
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
|
{(feedback === null || feedback === "downvote") && (
|
||||||
|
<ActionButton
|
||||||
|
onClick={async () =>
|
||||||
|
handleFeedback("downvote", item.question_id)
|
||||||
|
}
|
||||||
|
className={clsx(
|
||||||
|
feedback === "downvote" && "!text-medusa-fg-muted"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ThumbDown />
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,80 +41,89 @@ export const AiAssistantThreadItem = ({ item }: AiAssistantThreadItemProps) => {
|
|||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"txt-small text-medusa-fg-base",
|
"txt-small text-medusa-fg-base",
|
||||||
item.type === "question" && [
|
"flex flex-col gap-docs_0.75",
|
||||||
"rounded-docs_xl bg-medusa-tag-neutral-bg",
|
|
||||||
"px-docs_0.75 py-docs_0.5 max-w-full md:max-w-[400px]",
|
|
||||||
],
|
|
||||||
item.type !== "question" && "flex-1",
|
item.type !== "question" && "flex-1",
|
||||||
item.type === "answer" && "text-pretty flex-1 max-w-[calc(100%-20px)]"
|
item.type === "answer" && "text-pretty flex-1 max-w-[calc(100%-20px)]"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{item.type === "question" && (
|
<div
|
||||||
<MarkdownContent
|
className={clsx(
|
||||||
className="[&>*:last-child]:mb-0"
|
"flex flex-col gap-docs_0.75",
|
||||||
allowedElements={["br", "p", "code", "pre"]}
|
item.type === "question" && [
|
||||||
unwrapDisallowed={true}
|
"rounded-docs_xl bg-medusa-tag-neutral-bg",
|
||||||
components={{
|
"px-docs_0.75 py-docs_0.5 max-w-full md:max-w-[400px]",
|
||||||
...MDXComponents,
|
]
|
||||||
code: (props: CodeMdxProps) => {
|
)}
|
||||||
return (
|
>
|
||||||
<CodeMdx
|
{item.type === "question" && (
|
||||||
{...props}
|
|
||||||
noCopy
|
|
||||||
noReport
|
|
||||||
forceNoTitle
|
|
||||||
noAskAi
|
|
||||||
inlineCodeProps={{
|
|
||||||
...props.inlineCodeProps,
|
|
||||||
className: "!text-wrap !break-words",
|
|
||||||
variant: "grey-bg",
|
|
||||||
}}
|
|
||||||
collapsibleLines="11"
|
|
||||||
codeBlockProps={{
|
|
||||||
className: clsx(
|
|
||||||
"rounded-docs_lg p-[5px]",
|
|
||||||
props.className
|
|
||||||
),
|
|
||||||
wrapperClassName: "rounded-docs_lg",
|
|
||||||
innerClassName: "border rounded-docs_lg",
|
|
||||||
overrideColors: {
|
|
||||||
bg: "bg-medusa-contrast-bg-subtle",
|
|
||||||
innerBg: "bg-medusa-contrast-bg-subtle",
|
|
||||||
innerBorder: "border-medusa-contrast-border-bot",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.content}
|
|
||||||
</MarkdownContent>
|
|
||||||
)}
|
|
||||||
{item.type === "answer" && (
|
|
||||||
<div className="flex flex-col gap-docs_0.75">
|
|
||||||
{showLoading && <DotsLoading />}
|
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
className="[&>*:last-child]:mb-0"
|
className="[&>*:last-child]:mb-0"
|
||||||
|
allowedElements={["br", "p", "code", "pre"]}
|
||||||
|
unwrapDisallowed={true}
|
||||||
components={{
|
components={{
|
||||||
...MDXComponents,
|
...MDXComponents,
|
||||||
code: (props: CodeMdxProps) => {
|
code: (props: CodeMdxProps) => {
|
||||||
return (
|
return (
|
||||||
<CodeMdx
|
<CodeMdx
|
||||||
{...props}
|
{...props}
|
||||||
|
noCopy
|
||||||
noReport
|
noReport
|
||||||
|
forceNoTitle
|
||||||
noAskAi
|
noAskAi
|
||||||
wrapperClassName="mt-docs_1"
|
inlineCodeProps={{
|
||||||
|
...props.inlineCodeProps,
|
||||||
|
className: "!text-wrap !break-words",
|
||||||
|
variant: "grey-bg",
|
||||||
|
}}
|
||||||
|
collapsibleLines="11"
|
||||||
|
codeBlockProps={{
|
||||||
|
className: clsx(
|
||||||
|
"rounded-docs_lg p-[5px]",
|
||||||
|
props.className
|
||||||
|
),
|
||||||
|
wrapperClassName: "rounded-docs_lg",
|
||||||
|
innerClassName: "border rounded-docs_lg",
|
||||||
|
overrideColors: {
|
||||||
|
bg: "bg-medusa-contrast-bg-subtle",
|
||||||
|
innerBg: "bg-medusa-contrast-bg-subtle",
|
||||||
|
innerBorder: "border-medusa-contrast-border-bot",
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
disallowedElements={["h1", "h2", "h3", "h4", "h5", "h6"]}
|
|
||||||
>
|
>
|
||||||
{item.content}
|
{item.content}
|
||||||
</MarkdownContent>
|
</MarkdownContent>
|
||||||
{item.question_id && <AiAssistantThreadItemActions item={item} />}
|
)}
|
||||||
</div>
|
{item.type === "answer" && (
|
||||||
|
<>
|
||||||
|
{showLoading && <DotsLoading />}
|
||||||
|
<MarkdownContent
|
||||||
|
className="[&>*:last-child]:mb-0"
|
||||||
|
components={{
|
||||||
|
...MDXComponents,
|
||||||
|
code: (props: CodeMdxProps) => {
|
||||||
|
return (
|
||||||
|
<CodeMdx
|
||||||
|
{...props}
|
||||||
|
noReport
|
||||||
|
noAskAi
|
||||||
|
wrapperClassName="mt-docs_1"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
disallowedElements={["h1", "h2", "h3", "h4", "h5", "h6"]}
|
||||||
|
>
|
||||||
|
{item.content}
|
||||||
|
</MarkdownContent>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{(item.question_id || item.type === "question") && (
|
||||||
|
<AiAssistantThreadItemActions item={item} />
|
||||||
)}
|
)}
|
||||||
{item.type === "error" && (
|
{item.type === "error" && (
|
||||||
<span className="text-medusa-fg-error">{item.content}</span>
|
<span className="text-medusa-fg-error">{item.content}</span>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const InlineCode = ({
|
|||||||
<code
|
<code
|
||||||
{...props}
|
{...props}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"text-medusa-tag-neutral-text border",
|
"text-medusa-tag-neutral-text border whitespace-break-spaces",
|
||||||
"font-monospace text-code-label rounded-docs_sm py-0 px-[5px]",
|
"font-monospace text-code-label rounded-docs_sm py-0 px-[5px]",
|
||||||
variant === "default" && [
|
variant === "default" && [
|
||||||
"bg-medusa-tag-neutral-bg group-hover:bg-medusa-tag-neutral-bg-hover",
|
"bg-medusa-tag-neutral-bg group-hover:bg-medusa-tag-neutral-bg-hover",
|
||||||
|
|||||||
Reference in new issue
Block a user