Shun Miyazawa 1 год назад
Родитель
Сommit
3e93a53cd2

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

@@ -512,6 +512,7 @@
     "show_error_detail": "Show error details",
     "show_error_detail": "Show error details",
     "discard": "Discard",
     "discard": "Discard",
     "accept": "Accept",
     "accept": "Accept",
+    "use_assistant": "Use Assistant",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "Summarize this article",
         "title": "Summarize this article",

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

@@ -506,6 +506,7 @@
     "show_error_detail": "Détails de l'exposition",
     "show_error_detail": "Détails de l'exposition",
     "discard": "Annuler",
     "discard": "Annuler",
     "accept": "Accepter",
     "accept": "Accepter",
+    "use_assistant": "Utiliser l'assistant",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "Résumer cet article'",
         "title": "Résumer cet article'",

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

@@ -544,6 +544,7 @@
     "show_error_detail": "詳細を表示",
     "show_error_detail": "詳細を表示",
     "discard": "破棄",
     "discard": "破棄",
     "accept": "採用",
     "accept": "採用",
+    "use_assistant": "アシスタントを使用する",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "この記事の要約をつくる",
         "title": "この記事の要約をつくる",

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

@@ -501,6 +501,7 @@
     "show_error_detail": "显示详情",
     "show_error_detail": "显示详情",
     "discard": "丢弃",
     "discard": "丢弃",
     "accept": "接受",
     "accept": "接受",
+    "use_assistant": "使用助手",
     "preset_menu": {
     "preset_menu": {
       "summarize": {
       "summarize": {
         "title": "为此文章创建摘要",
         "title": "为此文章创建摘要",

+ 69 - 0
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantDropdown.tsx

@@ -0,0 +1,69 @@
+
+import React, { useState, useMemo, useCallback } from 'react';
+
+import { useTranslation } from 'react-i18next';
+import {
+  UncontrolledDropdown,
+  DropdownToggle,
+  DropdownMenu,
+  DropdownItem,
+} from 'reactstrap';
+import 'bootstrap/dist/css/bootstrap.min.css';
+
+import type { AiAssistantHasId } from '../../../../interfaces/ai-assistant';
+import { useSWRxAiAssistants } from '../../../stores/ai-assistant';
+import { getShareScopeIcon } from '../../../utils/get-share-scope-Icon';
+
+
+type Props = {
+  //
+}
+
+export const AiAssistantDropdown = (props: Props): JSX.Element => {
+  const [selectedAiAssistant, setSelectedAiAssistant] = useState<AiAssistantHasId>();
+
+  const { t } = useTranslation();
+  const { data: aiAssistantData } = useSWRxAiAssistants();
+
+  const allAiAssistants = useMemo(() => {
+    if (aiAssistantData == null) {
+      return [];
+    }
+    return [...aiAssistantData.myAiAssistants, ...aiAssistantData.teamAiAssistants];
+  }, [aiAssistantData]);
+
+  const getAiAssistantLabel = useCallback((aiAssistant: AiAssistantHasId) => {
+    return (
+      <>
+        <span className="material-symbols-outlined fs-5 me-1">
+          {getShareScopeIcon(aiAssistant.shareScope, aiAssistant.accessScope)}
+        </span>
+        {aiAssistant.name}
+      </>
+    );
+  }, []);
+
+  return (
+    <UncontrolledDropdown>
+      <DropdownToggle className="btn btn-outline-secondary">
+        {selectedAiAssistant != null
+          ? getAiAssistantLabel(selectedAiAssistant)
+          : <><span className="material-symbols-outlined fs-5">Add</span>{t('sidebar_ai_assistant.use_assistant')}</>
+        }
+      </DropdownToggle>
+      <DropdownMenu>
+        {allAiAssistants.map((aiAssistant) => {
+          return (
+            <DropdownItem
+              key={aiAssistant._id}
+              active={selectedAiAssistant?._id === aiAssistant._id}
+              onClick={() => setSelectedAiAssistant(aiAssistant)}
+            >
+              {getAiAssistantLabel(aiAssistant)}
+            </DropdownItem>
+          );
+        })}
+      </DropdownMenu>
+    </UncontrolledDropdown>
+  );
+};

+ 8 - 3
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantSidebar.tsx

@@ -23,6 +23,7 @@ import { useSWRMUTxMessages } from '../../../stores/message';
 import { useSWRMUTxThreads } from '../../../stores/thread';
 import { useSWRMUTxThreads } from '../../../stores/thread';
 
 
 import { AiAssistantChatInitialView } from './AiAssistantChatInitialView';
 import { AiAssistantChatInitialView } from './AiAssistantChatInitialView';
+import { AiAssistantDropdown } from './AiAssistantDropdown';
 import { MessageCard } from './MessageCard';
 import { MessageCard } from './MessageCard';
 import { QuickMenuList } from './QuickMenuList';
 import { QuickMenuList } from './QuickMenuList';
 import { ResizableTextarea } from './ResizableTextArea';
 import { ResizableTextarea } from './ResizableTextArea';
@@ -358,9 +359,13 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
             : (
             : (
               <>{isEditorAssistant
               <>{isEditorAssistant
                 ? (
                 ? (
-                  <QuickMenuList
-                    onClick={clickQuickMenuHandler}
-                  />
+                  <>
+                    <AiAssistantDropdown />
+                    <QuickMenuList
+                      onClick={clickQuickMenuHandler}
+                    />
+                  </>
+
                 )
                 )
                 : (
                 : (
                   <AiAssistantChatInitialView
                   <AiAssistantChatInitialView

+ 1 - 15
apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx

@@ -9,13 +9,13 @@ import type { IThreadRelationHasId } from '~/features/openai/interfaces/thread-r
 import { useCurrentUser } from '~/stores-universal/context';
 import { useCurrentUser } from '~/stores-universal/context';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant';
 import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant';
 import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant';
 import { determineShareScope } from '../../../../utils/determine-share-scope';
 import { determineShareScope } from '../../../../utils/determine-share-scope';
 import { deleteAiAssistant, setDefaultAiAssistant } from '../../../services/ai-assistant';
 import { deleteAiAssistant, setDefaultAiAssistant } from '../../../services/ai-assistant';
 import { deleteThread } from '../../../services/thread';
 import { deleteThread } from '../../../services/thread';
 import { useAiAssistantSidebar, useAiAssistantManagementModal } from '../../../stores/ai-assistant';
 import { useAiAssistantSidebar, useAiAssistantManagementModal } from '../../../stores/ai-assistant';
 import { useSWRMUTxThreads, useSWRxThreads } from '../../../stores/thread';
 import { useSWRMUTxThreads, useSWRxThreads } from '../../../stores/thread';
+import { getShareScopeIcon } from '../../../utils/get-share-scope-Icon';
 
 
 import styles from './AiAssistantTree.module.scss';
 import styles from './AiAssistantTree.module.scss';
 
 
@@ -125,20 +125,6 @@ const ThreadItems: React.FC<ThreadItemsProps> = ({ aiAssistantData, onThreadClic
 /*
 /*
 *  AiAssistantItem
 *  AiAssistantItem
 */
 */
-const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAssistantAccessScope): string => {
-  const determinedSharedScope = determineShareScope(shareScope, accessScope);
-  switch (determinedSharedScope) {
-    case AiAssistantShareScope.OWNER:
-      return 'lock';
-    case AiAssistantShareScope.GROUPS:
-      return 'account_tree';
-    case AiAssistantShareScope.PUBLIC_ONLY:
-      return 'group';
-    case AiAssistantShareScope.SAME_AS_ACCESS_SCOPE:
-      return '';
-  }
-};
-
 type AiAssistantItemProps = {
 type AiAssistantItemProps = {
   currentUser?: IUserHasId | null;
   currentUser?: IUserHasId | null;
   aiAssistant: AiAssistantHasId;
   aiAssistant: AiAssistantHasId;

+ 17 - 0
apps/app/src/features/openai/client/utils/get-share-scope-Icon.ts

@@ -0,0 +1,17 @@
+import type { AiAssistantAccessScope } from '../../interfaces/ai-assistant';
+import { AiAssistantShareScope } from '../../interfaces/ai-assistant';
+import { determineShareScope } from '../../utils/determine-share-scope';
+
+export const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAssistantAccessScope): string => {
+  const determinedSharedScope = determineShareScope(shareScope, accessScope);
+  switch (determinedSharedScope) {
+    case AiAssistantShareScope.OWNER:
+      return 'lock';
+    case AiAssistantShareScope.GROUPS:
+      return 'account_tree';
+    case AiAssistantShareScope.PUBLIC_ONLY:
+      return 'group';
+    case AiAssistantShareScope.SAME_AS_ACCESS_SCOPE:
+      return '';
+  }
+};