Przeglądaj źródła

Merge pull request #9754 from weseek/feat/163080-implement-accept-and-discard-buttons

feat: Implement accept and discard buttons
Shun Miyazawa 1 rok temu
rodzic
commit
39a796a3f0

+ 2 - 0
apps/app/public/static/locales/en_US/translation.json

@@ -510,6 +510,8 @@
     "budget_exceeded_for_growi_cloud": "You have reached your OpenAI API usage limit. To use the Knowledge Assistant again, please add credits from the GROWI.cloud admin page for Hosted users or from the OpenAI billing page for Owned users.",
     "budget_exceeded_for_growi_cloud": "You have reached your OpenAI API usage limit. To use the Knowledge Assistant again, please add credits from the GROWI.cloud admin page for Hosted users or from the OpenAI billing page for Owned users.",
     "error_message": "An error has occurred",
     "error_message": "An error has occurred",
     "show_error_detail": "Show error details",
     "show_error_detail": "Show error details",
+    "discard": "Discard",
+    "accept": "Accept",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "Summarize this article",
         "title": "Summarize this article",

+ 2 - 0
apps/app/public/static/locales/fr_FR/translation.json

@@ -504,6 +504,8 @@
     "budget_exceeded_for_growi_cloud": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page d'administration de GROWI.cloud pour les utilisateurs hébergés ou à partir de la page de facturation de l'OpenAI pour les utilisateurs propriétaires.",
     "budget_exceeded_for_growi_cloud": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page d'administration de GROWI.cloud pour les utilisateurs hébergés ou à partir de la page de facturation de l'OpenAI pour les utilisateurs propriétaires.",
     "error_message": "Erreur",
     "error_message": "Erreur",
     "show_error_detail": "Détails de l'exposition",
     "show_error_detail": "Détails de l'exposition",
+    "discard": "Annuler",
+    "accept": "Accepter",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "Résumer cet article'",
         "title": "Résumer cet article'",

+ 2 - 0
apps/app/public/static/locales/ja_JP/translation.json

@@ -542,6 +542,8 @@
     "budget_exceeded_for_growi_cloud": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには Hosted の場合は GROWI.cloud の管理画面から Owned の場合は OpenAI の請求ページからクレジットを追加してください。",
     "budget_exceeded_for_growi_cloud": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには Hosted の場合は GROWI.cloud の管理画面から Owned の場合は OpenAI の請求ページからクレジットを追加してください。",
     "error_message": "エラーが発生しました",
     "error_message": "エラーが発生しました",
     "show_error_detail": "詳細を表示",
     "show_error_detail": "詳細を表示",
+    "discard": "破棄",
+    "accept": "採用",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "この記事の要約をつくる",
         "title": "この記事の要約をつくる",

+ 2 - 0
apps/app/public/static/locales/zh_CN/translation.json

@@ -499,6 +499,8 @@
     "budget_exceeded_for_growi_cloud": "您已达到 OpenAI API 使用上限。如需再次使用知识助手,请从GROWI.cloud管理页面为托管用户添加点数,或从OpenAI计费页面为自有用户添加点数。",
     "budget_exceeded_for_growi_cloud": "您已达到 OpenAI API 使用上限。如需再次使用知识助手,请从GROWI.cloud管理页面为托管用户添加点数,或从OpenAI计费页面为自有用户添加点数。",
     "error_message": "错误",
     "error_message": "错误",
     "show_error_detail": "显示详情",
     "show_error_detail": "显示详情",
+    "discard": "丢弃",
+    "accept": "接受",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "为此文章创建摘要",
         "title": "为此文章创建摘要",

+ 17 - 1
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantSidebar.tsx

@@ -303,6 +303,14 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
     await submit({ input: quickMenu });
     await submit({ input: quickMenu });
   }, [submit]);
   }, [submit]);
 
 
+  const clickAcceptHandler = useCallback(() => {
+    // todo: implement
+  }, []);
+
+  const clickDiscardHandler = useCallback(() => {
+    // todo: implement
+  }, []);
+
   return (
   return (
     <>
     <>
       <div className="d-flex flex-column vh-100">
       <div className="d-flex flex-column vh-100">
@@ -325,7 +333,15 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
             ? (
             ? (
               <div className="vstack gap-4 pb-2">
               <div className="vstack gap-4 pb-2">
                 { messageLogs.map(message => (
                 { messageLogs.map(message => (
-                  <MessageCard key={message.id} role={message.isUserMessage ? 'user' : 'assistant'}>{message.content}</MessageCard>
+                  <MessageCard
+                    key={message.id}
+                    role={message.isUserMessage ? 'user' : 'assistant'}
+                    showActionButtons={isEditorAssistant}
+                    onAccept={clickAcceptHandler}
+                    onDiscard={clickDiscardHandler}
+                  >
+                    {message.content}
+                  </MessageCard>
                 )) }
                 )) }
                 { generatingAnswerMessage != null && (
                 { generatingAnswerMessage != null && (
                   <MessageCard role="assistant">{generatingAnswerMessage.content}</MessageCard>
                   <MessageCard role="assistant">{generatingAnswerMessage.content}</MessageCard>

+ 45 - 4
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/MessageCard.tsx

@@ -39,7 +39,15 @@ const NextLinkWrapper = (props: LinkProps & {children: string, href: string}): J
     </NextLink>
     </NextLink>
   );
   );
 };
 };
-const AssistantMessageCard = ({ children }: { children: string }): JSX.Element => {
+
+const AssistantMessageCard = ({
+  children, showActionButtons, onAccept, onDiscard,
+}: {
+  children: string,
+  showActionButtons?: boolean
+  onAccept?: () => void,
+  onDiscard?: () => void,
+}): JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
   return (
   return (
@@ -51,7 +59,28 @@ const AssistantMessageCard = ({ children }: { children: string }): JSX.Element =
         <div>
         <div>
           { children.length > 0
           { children.length > 0
             ? (
             ? (
-              <ReactMarkdown components={{ a: NextLinkWrapper }}>{children}</ReactMarkdown>
+              <>
+                <ReactMarkdown components={{ a: NextLinkWrapper }}>{children}</ReactMarkdown>
+
+                {showActionButtons && (
+                  <div className="d-flex mt-2 justify-content-start">
+                    <button
+                      type="button"
+                      className="btn btn-outline-secondary me-2"
+                      onClick={onDiscard}
+                    >
+                      {t('sidebar_ai_assistant.discard')}
+                    </button>
+                    <button
+                      type="button"
+                      className="btn btn-outline-success"
+                      onClick={onAccept}
+                    >
+                      {t('sidebar_ai_assistant.accept')}
+                    </button>
+                  </div>
+                )}
+              </>
             )
             )
             : (
             : (
               <span className="text-thinking">
               <span className="text-thinking">
@@ -68,12 +97,24 @@ const AssistantMessageCard = ({ children }: { children: string }): JSX.Element =
 type Props = {
 type Props = {
   role: 'user' | 'assistant',
   role: 'user' | 'assistant',
   children: string,
   children: string,
+  showActionButtons?: boolean,
+  onDiscard?: () => void,
+  onAccept?: () => void,
 }
 }
 
 
 export const MessageCard = (props: Props): JSX.Element => {
 export const MessageCard = (props: Props): JSX.Element => {
-  const { role, children } = props;
+  const {
+    role, children, showActionButtons, onAccept, onDiscard,
+  } = props;
 
 
   return role === 'user'
   return role === 'user'
     ? <UserMessageCard>{children}</UserMessageCard>
     ? <UserMessageCard>{children}</UserMessageCard>
-    : <AssistantMessageCard>{children}</AssistantMessageCard>;
+    : (
+      <AssistantMessageCard
+        showActionButtons={showActionButtons}
+        onAccept={onAccept}
+        onDiscard={onDiscard}
+      >{children}
+      </AssistantMessageCard>
+    );
 };
 };