From 33811dbd9d5e9337ea49e5b8a2584b1ae8cf5693 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 23 Jan 2025 06:58:50 +0000 Subject: [PATCH 01/20] add styles --- .../Sidebar/AiAssistantSubstance.module.scss | 6 ++++ .../Sidebar/AiAssistantSubstance.tsx | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss new file mode 100644 index 00000000000..b4badc5ddb4 --- /dev/null +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss @@ -0,0 +1,6 @@ + +.grw-ai-assistant-substance :global { + .grw-ai-assistant-substance-header { + font-size: 14px; + } +} diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx index 16fc33de329..f1d17138e4f 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx @@ -2,15 +2,35 @@ import React from 'react'; import { useAiAssistantManegementModal } from '../../../stores/ai-assistant'; +import styles from './AiAssistantSubstance.module.scss'; + +const moduleClass = styles['grw-ai-assistant-substance'] ?? ''; + export const AiAssistantContent = (): JSX.Element => { const { open } = useAiAssistantManegementModal(); return ( -
- + +
+

+ マイアシスタント +

+
+ +
+

+ チームアシスタント +

+
); }; From 908314911b43d99de5406d3659f880ad67efa3a8 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 02:11:48 +0000 Subject: [PATCH 02/20] impl useSWRxAiAssistants --- .../features/openai/client/stores/ai-assistant.tsx | 14 +++++++++++++- .../src/features/openai/interfaces/ai-assistant.ts | 11 ++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/app/src/features/openai/client/stores/ai-assistant.tsx b/apps/app/src/features/openai/client/stores/ai-assistant.tsx index b6ff01bb822..f344386d4d6 100644 --- a/apps/app/src/features/openai/client/stores/ai-assistant.tsx +++ b/apps/app/src/features/openai/client/stores/ai-assistant.tsx @@ -1,7 +1,11 @@ import { useCallback } from 'react'; import { useSWRStatic } from '@growi/core/dist/swr'; -import type { SWRResponse } from 'swr'; +import useSWR, { type SWRResponse } from 'swr'; + +import { apiv3Get } from '~/client/util/apiv3-client'; + +import { type AccessibleAiAssistantsHasId } from '../../interfaces/ai-assistant'; export const AiAssistantManagementModalPageMode = { HOME: 'home', @@ -38,3 +42,11 @@ export const useAiAssistantManagementModal = ( }, [swrResponse]), }; }; + + +export const useSWRxAiAssistants = (): SWRResponse => { + return useSWR( + ['/openai/ai-assistants'], + ([endpoint]) => apiv3Get(endpoint).then(response => response.data.accessibleAiAssistants), + ); +}; diff --git a/apps/app/src/features/openai/interfaces/ai-assistant.ts b/apps/app/src/features/openai/interfaces/ai-assistant.ts index b995e9681fc..5af2e21874c 100644 --- a/apps/app/src/features/openai/interfaces/ai-assistant.ts +++ b/apps/app/src/features/openai/interfaces/ai-assistant.ts @@ -1,4 +1,6 @@ -import type { IGrantedGroup, IUser, Ref } from '@growi/core'; +import type { + IGrantedGroup, IUser, Ref, HasObjectId, +} from '@growi/core'; import type { VectorStore } from '../server/models/vector-store'; @@ -37,9 +39,16 @@ export interface AiAssistant { accessScope: AiAssistantAccessScope } +export type AiAssistantHasId = AiAssistant & HasObjectId + export type IApiv3AiAssistantCreateParams = Omit export type AccessibleAiAssistants = { myAiAssistants: AiAssistant[], teamAiAssistants: AiAssistant[], } + +export type AccessibleAiAssistantsHasId = { + myAiAssistants: AiAssistantHasId[], + teamAiAssistants: AiAssistantHasId[], +} From bc6fff4975cdd7360b61c974b6488ee4d2df792c Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 02:59:04 +0000 Subject: [PATCH 03/20] fix props --- .../AiAssistantManagementModal/AiAssistantManagementHome.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementHome.tsx b/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementHome.tsx index 31d522b27c4..8546b832857 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementHome.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementHome.tsx @@ -144,7 +144,7 @@ export const AiAssistantManagementHome = (props: Props): JSX.Element => { setIsShareScopeWarningModalOpen(false)} - onSubmit={createAiAssistantHandler} + onSubmit={onCreateAiAssistant} /> ); From a1a58787bad307e0ea393bbe2c5096ab88154cf0 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 02:59:29 +0000 Subject: [PATCH 04/20] Impl AiAssistantTree --- .../Sidebar/AiAssistantSubstance.tsx | 33 ++-- .../Sidebar/AiAssistantTree.module.scss | 25 +++ .../AiAssistant/Sidebar/AiAssistantTree.tsx | 142 ++++++++++++++++++ 3 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss create mode 100644 apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx index 3e7f7e17dc4..adc1ffe84d5 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx @@ -1,6 +1,8 @@ import React from 'react'; -import { useAiAssistantManagementModal } from '../../../stores/ai-assistant'; +import { useAiAssistantManagementModal, useSWRxAiAssistants } from '../../../stores/ai-assistant'; + +import { AiAssistantTree } from './AiAssistantTree'; import styles from './AiAssistantSubstance.module.scss'; @@ -8,28 +10,37 @@ const moduleClass = styles['grw-ai-assistant-substance'] ?? ''; export const AiAssistantContent = (): JSX.Element => { const { open } = useAiAssistantManagementModal(); + const { data: aiAssistants } = useSWRxAiAssistants(); return (
-
-

- マイアシスタント -

-
+
+
+

+ マイアシスタント +

+ {aiAssistants?.myAiAssistants != null && aiAssistants.myAiAssistants.length !== 0 && ( + + )} +
-
-

- チームアシスタント -

+
+

+ チームアシスタント +

+ {aiAssistants?.teamAiAssistants != null && aiAssistants.teamAiAssistants.length !== 0 && ( + + )} +
); diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss new file mode 100644 index 00000000000..b18add85a53 --- /dev/null +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss @@ -0,0 +1,25 @@ +.grw-ai-assistant-tree-container :global { + .grw-ai-assistant-item-container { + .list-group-item { + .grw-ai-assistant-triangle-btn { + border: 0; + transition: transform 0.2s ease-out; + transform: rotate(0deg); + + &.grw-ai-assistant-open { + transform: rotate(90deg); + } + } + + .grw-triangle-container { + height: 30px; + } + + .grw-ai-assistant-title-anchor { + width: 100%; + overflow: hidden; + font-size: 14px; + } + } + } +} diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx new file mode 100644 index 00000000000..a6e254ddf99 --- /dev/null +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -0,0 +1,142 @@ +import React, { useCallback, useState } from 'react'; + +import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; + +import styles from './AiAssistantTree.module.scss'; + + +type ThreadItemProps = { + name: string; + onClick?: () => void; +}; + +const ThreadItem: React.FC = ({ + name, + onClick, +}) => { + return ( +
  • +
    + chat +
    +
    +

    {name}

    +
    +
  • + ); +}; + + +type AiAssistantItemProps = { + name: string; + type: AiAssistantShareScope; + threads: { id: string; name: string }[]; // dummy data + onThreadClick?: (threadId: string) => void; +}; + +const AiAssistantItem: React.FC = ({ + name, + type, + threads, + onThreadClick, +}) => { + const [isExpanded, setIsExpanded] = useState(false); + + const handleToggle = useCallback(() => { + setIsExpanded(prev => !prev); + }, []); + + const handleButtonClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + handleToggle(); + }, [handleToggle]); + + const getShareScopeIcon = (shareScope: AiAssistantShareScope): string => { + switch (shareScope) { + case AiAssistantShareScope.OWNER: + return 'lock'; + case AiAssistantShareScope.GROUPS: + return 'polyline'; + case AiAssistantShareScope.PUBLIC_ONLY: + return 'group'; + case AiAssistantShareScope.SAME_AS_ACCESS_SCOPE: + return 'group'; + } + }; + + return ( +
    +
  • +
    + +
    +
    + {getShareScopeIcon(type)} +
    +
    +

    {name}

    +
    +
  • + + {isExpanded && threads.length > 0 && ( +
    + {threads.map(thread => ( + onThreadClick?.(thread.id)} + /> + ))} +
    + )} +
    + ); +}; + + +// hardcoded data +const dummyThreads = [ + { id: '1', name: 'thread1' }, + { id: '2', name: 'thread2' }, + { id: '3', name: 'thread3' }, +]; + +// Tree Component +type AiAssistantTreeProps = { + aiAssistants: AiAssistantHasId[]; + onThreadClick?: (threadId: string) => void; +}; + +export const AiAssistantTree: React.FC = ({ aiAssistants, onThreadClick }) => { + return ( +
    +
      + {aiAssistants.map(assistant => ( + + ))} +
    +
    + ); +}; From 1e88a888fbec52f3a205119725c87049af73cf11 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 03:35:04 +0000 Subject: [PATCH 05/20] adjust styles --- .../Sidebar/AiAssistantSubstance.module.scss | 26 ++++++++++++++++ .../Sidebar/AiAssistantTree.module.scss | 25 --------------- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 31 +++++++------------ 3 files changed, 38 insertions(+), 44 deletions(-) delete mode 100644 apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss index b4badc5ddb4..7e95f90ed0f 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss @@ -3,4 +3,30 @@ .grw-ai-assistant-substance-header { font-size: 14px; } + + .grw-ai-assistant-item-container { + .list-group-item { + padding-left: 4px; + + .grw-ai-assistant-triangle-btn { + border: 0; + transition: transform 0.2s ease-out; + transform: rotate(0deg); + + &.grw-ai-assistant-open { + transform: rotate(90deg); + } + } + + .grw-triangle-container { + height: 30px; + } + + .grw-ai-assistant-title-anchor { + width: 100%; + overflow: hidden; + font-size: 14px; + } + } + } } diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss deleted file mode 100644 index b18add85a53..00000000000 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.module.scss +++ /dev/null @@ -1,25 +0,0 @@ -.grw-ai-assistant-tree-container :global { - .grw-ai-assistant-item-container { - .list-group-item { - .grw-ai-assistant-triangle-btn { - border: 0; - transition: transform 0.2s ease-out; - transform: rotate(0deg); - - &.grw-ai-assistant-open { - transform: rotate(90deg); - } - } - - .grw-triangle-container { - height: 30px; - } - - .grw-ai-assistant-title-anchor { - width: 100%; - overflow: hidden; - font-size: 14px; - } - } - } -} diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index a6e254ddf99..87d8ebc81c6 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -2,9 +2,6 @@ import React, { useCallback, useState } from 'react'; import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; -import styles from './AiAssistantTree.module.scss'; - - type ThreadItemProps = { name: string; onClick?: () => void; @@ -71,7 +68,7 @@ const AiAssistantItem: React.FC = ({ return (
  • @@ -110,14 +107,12 @@ const AiAssistantItem: React.FC = ({ }; -// hardcoded data const dummyThreads = [ { id: '1', name: 'thread1' }, { id: '2', name: 'thread2' }, { id: '3', name: 'thread3' }, ]; -// Tree Component type AiAssistantTreeProps = { aiAssistants: AiAssistantHasId[]; onThreadClick?: (threadId: string) => void; @@ -125,18 +120,16 @@ type AiAssistantTreeProps = { export const AiAssistantTree: React.FC = ({ aiAssistants, onThreadClick }) => { return ( -
    -
      - {aiAssistants.map(assistant => ( - - ))} -
    -
    +
      + {aiAssistants.map(assistant => ( + + ))} +
    ); }; From 4a1ca176b646430ea723114e62f8d8aab18ac000 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 03:48:27 +0000 Subject: [PATCH 06/20] Refactor getShareScopeIcon() --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index 87d8ebc81c6..811feb545da 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useState } from 'react'; +import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant'; import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; type ThreadItemProps = { @@ -28,16 +29,30 @@ const ThreadItem: React.FC = ({ }; +const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAssistantAccessScope): string => { + const targetScope = shareScope === AiAssistantShareScope.SAME_AS_ACCESS_SCOPE ? accessScope : shareScope; + switch (targetScope) { + case AiAssistantShareScope.OWNER: + return 'lock'; + case AiAssistantShareScope.GROUPS: + return 'polyline'; + case AiAssistantShareScope.PUBLIC_ONLY: + return 'group'; + } +}; + type AiAssistantItemProps = { name: string; - type: AiAssistantShareScope; + shareScope: AiAssistantShareScope; + accessScope: AiAssistantAccessScope; threads: { id: string; name: string }[]; // dummy data onThreadClick?: (threadId: string) => void; }; const AiAssistantItem: React.FC = ({ name, - type, + shareScope, + accessScope, threads, onThreadClick, }) => { @@ -52,19 +67,6 @@ const AiAssistantItem: React.FC = ({ handleToggle(); }, [handleToggle]); - const getShareScopeIcon = (shareScope: AiAssistantShareScope): string => { - switch (shareScope) { - case AiAssistantShareScope.OWNER: - return 'lock'; - case AiAssistantShareScope.GROUPS: - return 'polyline'; - case AiAssistantShareScope.PUBLIC_ONLY: - return 'group'; - case AiAssistantShareScope.SAME_AS_ACCESS_SCOPE: - return 'group'; - } - }; - return (
  • = ({
  • - {getShareScopeIcon(type)} + {getShareScopeIcon(shareScope, accessScope)}

    {name}

    @@ -125,7 +127,8 @@ export const AiAssistantTree: React.FC = ({ aiAssistants, From 9d35f9513557227610b8930518ed1c6099e8b145 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 03:52:38 +0000 Subject: [PATCH 07/20] Mutate useSWRxAiAssistants when creating assistants --- .../AiAssistantManagementModal.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementModal.tsx b/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementModal.tsx index cfbd8106d6e..1d06cd1d8c9 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementModal.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManagementModal/AiAssistantManagementModal.tsx @@ -12,7 +12,7 @@ import loggerFactory from '~/utils/logger'; import type { SelectedPage } from '../../../../interfaces/selected-page'; import { createAiAssistant } from '../../../services/ai-assistant'; -import { useAiAssistantManagementModal, AiAssistantManagementModalPageMode } from '../../../stores/ai-assistant'; +import { useAiAssistantManagementModal, AiAssistantManagementModalPageMode, useSWRxAiAssistants } from '../../../stores/ai-assistant'; import { AiAssistantManagementEditInstruction } from './AiAssistantManagementEditInstruction'; import { AiAssistantManagementEditPages } from './AiAssistantManagementEditPages'; @@ -36,6 +36,7 @@ const convertToGrantedGroups = (selectedGroups: PopulatedGrantedGroup[]): IGrant const AiAssistantManagementModalSubstance = (): JSX.Element => { // Hooks const { t } = useTranslation(); + const { mutate: mutateAiAssistants } = useSWRxAiAssistants(); const { data: aiAssistantManagementModalData, close: closeAiAssistantManagementModal } = useAiAssistantManagementModal(); const pageMode = aiAssistantManagementModalData?.pageMode ?? AiAssistantManagementModalPageMode.HOME; @@ -83,6 +84,7 @@ const AiAssistantManagementModalSubstance = (): JSX.Element => { }); toastSuccess('アシスタントを作成しました'); + mutateAiAssistants(); closeAiAssistantManagementModal(); } catch (err) { @@ -90,6 +92,7 @@ const AiAssistantManagementModalSubstance = (): JSX.Element => { logger.error(err); } }, [ + mutateAiAssistants, closeAiAssistantManagementModal, description, instruction, From 11a4f7762fc2b03ccf458593e5ab19ee5f361ba6 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 04:00:41 +0000 Subject: [PATCH 08/20] clean code --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index 811feb545da..bee767fe443 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -5,17 +5,14 @@ import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interf type ThreadItemProps = { name: string; - onClick?: () => void; }; const ThreadItem: React.FC = ({ name, - onClick, }) => { return (
  • @@ -46,7 +43,6 @@ type AiAssistantItemProps = { shareScope: AiAssistantShareScope; accessScope: AiAssistantAccessScope; threads: { id: string; name: string }[]; // dummy data - onThreadClick?: (threadId: string) => void; }; const AiAssistantItem: React.FC = ({ @@ -54,31 +50,24 @@ const AiAssistantItem: React.FC = ({ shareScope, accessScope, threads, - onThreadClick, }) => { const [isExpanded, setIsExpanded] = useState(false); - const handleToggle = useCallback(() => { - setIsExpanded(prev => !prev); + const clickItemHandler = useCallback(() => { + setIsExpanded(toggle => !toggle); }, []); - const handleButtonClick = useCallback((e: React.MouseEvent) => { - e.stopPropagation(); - handleToggle(); - }, [handleToggle]); - return (
  • + +
  • {isExpanded && threads.length > 0 && ( From 9432cb48de906b46695dc9edacef2fe304b1c9d5 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 04:35:41 +0000 Subject: [PATCH 11/20] Refactor AiAssistantItem props --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index e3de546601a..21185375a16 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -39,16 +39,12 @@ const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAss }; type AiAssistantItemProps = { - name: string; - shareScope: AiAssistantShareScope; - accessScope: AiAssistantAccessScope; + aiAssistant: AiAssistantHasId; threads: { id: string; name: string }[]; // dummy data }; const AiAssistantItem: React.FC = ({ - name, - shareScope, - accessScope, + aiAssistant, threads, }) => { const [isExpanded, setIsExpanded] = useState(false); @@ -75,10 +71,10 @@ const AiAssistantItem: React.FC = ({
    - {getShareScopeIcon(shareScope, accessScope)} + {getShareScopeIcon(aiAssistant.shareScope, aiAssistant.accessScope)}
    -

    {name}

    +

    {aiAssistant.name}

    @@ -128,9 +124,7 @@ export const AiAssistantTree: React.FC = ({ aiAssistants } {aiAssistants.map(assistant => ( ))} From 57c96aedeeb43ccc00492bb15aefccea90a5fb78 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 04:41:53 +0000 Subject: [PATCH 12/20] Only assistants created by owner are Operable --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index 21185375a16..f0d34c2f358 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -1,5 +1,9 @@ import React, { useCallback, useState } from 'react'; +import { getIdStringForRef } from '@growi/core'; + +import { useCurrentUser } from '~/stores-universal/context'; + import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant'; import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; @@ -39,11 +43,13 @@ const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAss }; type AiAssistantItemProps = { + currentUserId?: string; aiAssistant: AiAssistantHasId; threads: { id: string; name: string }[]; // dummy data }; const AiAssistantItem: React.FC = ({ + currentUserId, aiAssistant, threads, }) => { @@ -53,6 +59,8 @@ const AiAssistantItem: React.FC = ({ setIsExpanded(toggle => !toggle); }, []); + const isOperable = currentUserId != null && getIdStringForRef(aiAssistant.owner) === currentUserId; + return (
  • = ({

    {aiAssistant.name}

  • -
    - - -
    + { isOperable && ( +
    + + +
    + )} {isExpanded && threads.length > 0 && ( @@ -119,11 +129,13 @@ type AiAssistantTreeProps = { }; export const AiAssistantTree: React.FC = ({ aiAssistants }) => { + const { data: currentUser } = useCurrentUser(); return (
      {aiAssistants.map(assistant => ( From 48773142b4ec72ece87d0637eaacd056860eeb01 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 04:52:53 +0000 Subject: [PATCH 13/20] Added the ability to remove assistants from the UI. --- .../Sidebar/AiAssistantSubstance.tsx | 7 +++++-- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx index adc1ffe84d5..1795c086871 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx @@ -10,7 +10,7 @@ const moduleClass = styles['grw-ai-assistant-substance'] ?? ''; export const AiAssistantContent = (): JSX.Element => { const { open } = useAiAssistantManagementModal(); - const { data: aiAssistants } = useSWRxAiAssistants(); + const { data: aiAssistants, mutate: mutateAiAssistants } = useSWRxAiAssistants(); return (
      @@ -29,7 +29,10 @@ export const AiAssistantContent = (): JSX.Element => { マイアシスタント {aiAssistants?.myAiAssistants != null && aiAssistants.myAiAssistants.length !== 0 && ( - + )}
      diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index f0d34c2f358..ed212d0a201 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -2,10 +2,12 @@ import React, { useCallback, useState } from 'react'; import { getIdStringForRef } from '@growi/core'; +import { toastError, toastSuccess } from '~/client/util/toastr'; import { useCurrentUser } from '~/stores-universal/context'; import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant'; import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; +import { deleteAiAssistant } from '../../../services/ai-assistant'; type ThreadItemProps = { name: string; @@ -46,12 +48,14 @@ type AiAssistantItemProps = { currentUserId?: string; aiAssistant: AiAssistantHasId; threads: { id: string; name: string }[]; // dummy data + onDeleted?: () => void; }; const AiAssistantItem: React.FC = ({ currentUserId, aiAssistant, threads, + onDeleted, }) => { const [isExpanded, setIsExpanded] = useState(false); @@ -59,6 +63,17 @@ const AiAssistantItem: React.FC = ({ setIsExpanded(toggle => !toggle); }, []); + const clickDeleteAiAssistantHandler = useCallback(async() => { + try { + await deleteAiAssistant(aiAssistant._id); + onDeleted?.(); + toastSuccess('アシスタントを削除しました'); + } + catch (err) { + toastError('アシスタントの削除に失敗しました'); + } + }, [aiAssistant._id, onDeleted]); + const isOperable = currentUserId != null && getIdStringForRef(aiAssistant.owner) === currentUserId; return ( @@ -96,6 +111,7 @@ const AiAssistantItem: React.FC = ({ @@ -126,9 +142,10 @@ const dummyThreads = [ type AiAssistantTreeProps = { aiAssistants: AiAssistantHasId[]; + onDeleted?: () => void; }; -export const AiAssistantTree: React.FC = ({ aiAssistants }) => { +export const AiAssistantTree: React.FC = ({ aiAssistants, onDeleted }) => { const { data: currentUser } = useCurrentUser(); return (
        @@ -138,6 +155,7 @@ export const AiAssistantTree: React.FC = ({ aiAssistants } currentUserId={currentUser?._id} aiAssistant={assistant} threads={dummyThreads} + onDeleted={onDeleted} /> ))}
      From 0294677aa59b3b01a8de56244d8d84ea8e898138 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 06:26:47 +0000 Subject: [PATCH 14/20] Change the target for firing clickOpenThreadHandler --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index ed212d0a201..cb80d958889 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -57,10 +57,10 @@ const AiAssistantItem: React.FC = ({ threads, onDeleted, }) => { - const [isExpanded, setIsExpanded] = useState(false); + const [isThreadsOpened, setIsThreadsOpened] = useState(false); - const clickItemHandler = useCallback(() => { - setIsExpanded(toggle => !toggle); + const clickOpenThreadHandler = useCallback(() => { + setIsThreadsOpened(toggle => !toggle); }, []); const clickDeleteAiAssistantHandler = useCallback(async() => { @@ -80,13 +80,12 @@ const AiAssistantItem: React.FC = ({
    • - {isExpanded && threads.length > 0 && ( + {isThreadsOpened && threads.length > 0 && (
      {threads.map(thread => ( Date: Thu, 6 Feb 2025 06:50:39 +0000 Subject: [PATCH 15/20] fix styles --- .../Sidebar/AiAssistantSubstance.module.scss | 3 ++- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss index bde25ffc74b..8a7506c667c 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.module.scss @@ -1,3 +1,4 @@ +@use '@growi/core-styles/scss/bootstrap/init' as bs; .grw-ai-assistant-substance :global { .grw-ai-assistant-substance-header { @@ -31,7 +32,7 @@ .btn-link { &:hover { - color: var(--bs-primary) !important; + color: var(--bs-gray-800) !important; } } } diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index cb80d958889..659006bf9c1 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -18,7 +18,7 @@ const ThreadItem: React.FC = ({ }) => { return (
    • @@ -33,12 +33,12 @@ const ThreadItem: React.FC = ({ const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAssistantAccessScope): string => { - const targetScope = shareScope === AiAssistantShareScope.SAME_AS_ACCESS_SCOPE ? accessScope : shareScope; - switch (targetScope) { + const determinedSharedScope = shareScope === AiAssistantShareScope.SAME_AS_ACCESS_SCOPE ? accessScope : shareScope; + switch (determinedSharedScope) { case AiAssistantShareScope.OWNER: return 'lock'; case AiAssistantShareScope.GROUPS: - return 'polyline'; + return 'account_tree'; case AiAssistantShareScope.PUBLIC_ONLY: return 'group'; } @@ -92,9 +92,11 @@ const AiAssistantItem: React.FC = ({
    • -
      + +
      {getShareScopeIcon(aiAssistant.shareScope, aiAssistant.accessScope)}
      +

      {aiAssistant.name}

      From 2595282192c75ed22f12d20963f08e2956a67ff6 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 06:53:21 +0000 Subject: [PATCH 16/20] add delete thread button --- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index 659006bf9c1..e80c07d0804 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -16,6 +16,11 @@ type ThreadItemProps = { const ThreadItem: React.FC = ({ name, }) => { + + const deleteThreadHandler = useCallback(() => { + // + }, []); + return (
    • = ({
      chat
      +

      {name}

      + +
      + +
    • ); }; From b632d689b978ac0b5c6d49a03dba82fdf525ceb4 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 06:55:22 +0000 Subject: [PATCH 17/20] fix styles --- .../client/components/AiAssistant/Sidebar/AiAssistantTree.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index e80c07d0804..4c6e7617910 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -24,7 +24,6 @@ const ThreadItem: React.FC = ({ return (
    • chat From b554ad75ed7694dd01e9264e31084ce10ffd21b9 Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 07:09:06 +0000 Subject: [PATCH 18/20] clean code & add todo --- .../Sidebar/AiAssistantSubstance.tsx | 4 +- .../AiAssistant/Sidebar/AiAssistantTree.tsx | 53 ++++++++++++------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx index 1795c086871..1263cd0bfa8 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantSubstance.tsx @@ -41,7 +41,9 @@ export const AiAssistantContent = (): JSX.Element => { チームアシスタント {aiAssistants?.teamAiAssistants != null && aiAssistants.teamAiAssistants.length !== 0 && ( - + )}
    • diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx index 4c6e7617910..0b0a8041405 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx @@ -9,28 +9,46 @@ import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant'; import { deleteAiAssistant } from '../../../services/ai-assistant'; -type ThreadItemProps = { + +type Thread = { + _id: string; name: string; +} + +const dummyThreads: Thread[] = [ + { _id: '1', name: 'thread1' }, + { _id: '2', name: 'thread2' }, + { _id: '3', name: 'thread3' }, +]; + +type ThreadItemProps = { + thread: Thread; }; const ThreadItem: React.FC = ({ - name, + thread, }) => { const deleteThreadHandler = useCallback(() => { - // + // TODO: https://redmine.weseek.co.jp/issues/161490 + }, []); + + const openChatHandler = useCallback(() => { + // TODO: https://redmine.weseek.co.jp/issues/159530 }, []); return (
    • chat
      -

      {name}

      +

      {thread.name}

      @@ -62,7 +80,7 @@ const getShareScopeIcon = (shareScope: AiAssistantShareScope, accessScope: AiAss type AiAssistantItemProps = { currentUserId?: string; aiAssistant: AiAssistantHasId; - threads: { id: string; name: string }[]; // dummy data + threads: Thread[]; onDeleted?: () => void; }; @@ -74,11 +92,15 @@ const AiAssistantItem: React.FC = ({ }) => { const [isThreadsOpened, setIsThreadsOpened] = useState(false); - const clickOpenThreadHandler = useCallback(() => { + const openChatHandler = useCallback(() => { + // TODO: https://redmine.weseek.co.jp/issues/159530 + }, []); + + const openThreadsHandler = useCallback(() => { setIsThreadsOpened(toggle => !toggle); }, []); - const clickDeleteAiAssistantHandler = useCallback(async() => { + const deleteAiAssistantHandler = useCallback(async() => { try { await deleteAiAssistant(aiAssistant._id); onDeleted?.(); @@ -94,12 +116,14 @@ const AiAssistantItem: React.FC = ({ return (
    • @@ -139,8 +163,8 @@ const AiAssistantItem: React.FC = ({
      {threads.map(thread => ( ))}
      @@ -149,13 +173,6 @@ const AiAssistantItem: React.FC = ({ ); }; - -const dummyThreads = [ - { id: '1', name: 'thread1' }, - { id: '2', name: 'thread2' }, - { id: '3', name: 'thread3' }, -]; - type AiAssistantTreeProps = { aiAssistants: AiAssistantHasId[]; onDeleted?: () => void; From c677155d5f0b6a622a9e6478aeac33c35b4c93ea Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 07:36:41 +0000 Subject: [PATCH 19/20] Use useSWRImmutable --- apps/app/src/features/openai/client/stores/ai-assistant.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/app/src/features/openai/client/stores/ai-assistant.tsx b/apps/app/src/features/openai/client/stores/ai-assistant.tsx index f344386d4d6..e238d34eb3c 100644 --- a/apps/app/src/features/openai/client/stores/ai-assistant.tsx +++ b/apps/app/src/features/openai/client/stores/ai-assistant.tsx @@ -1,7 +1,8 @@ import { useCallback } from 'react'; import { useSWRStatic } from '@growi/core/dist/swr'; -import useSWR, { type SWRResponse } from 'swr'; +import { type SWRResponse } from 'swr'; +import useSWRImmutable from 'swr/immutable'; import { apiv3Get } from '~/client/util/apiv3-client'; @@ -45,7 +46,7 @@ export const useAiAssistantManagementModal = ( export const useSWRxAiAssistants = (): SWRResponse => { - return useSWR( + return useSWRImmutable( ['/openai/ai-assistants'], ([endpoint]) => apiv3Get(endpoint).then(response => response.data.accessibleAiAssistants), ); From fc98415ea036a06819ac0986cda2a6f2a59b735c Mon Sep 17 00:00:00 2001 From: Shun Miyazawa Date: Thu, 6 Feb 2025 08:47:01 +0000 Subject: [PATCH 20/20] Add guest user check to AiAssistant component --- .../AiAssistant/Sidebar/AiAssistant.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistant.tsx b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistant.tsx index 446c4f00e06..28523169da8 100644 --- a/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistant.tsx +++ b/apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistant.tsx @@ -4,11 +4,13 @@ import dynamic from 'next/dynamic'; import { useTranslation } from 'react-i18next'; import ItemsTreeContentSkeleton from '~/client/components/ItemsTree/ItemsTreeContentSkeleton'; +import { useIsGuestUser } from '~/stores-universal/context'; const AiAssistantContent = dynamic(() => import('./AiAssistantSubstance').then(mod => mod.AiAssistantContent), { ssr: false }); export const AiAssistant = (): JSX.Element => { const { t } = useTranslation(); + const { data: isGuestUser } = useIsGuestUser(); return (
      @@ -17,9 +19,19 @@ export const AiAssistant = (): JSX.Element => { {t('Knowledge Assistant')}
      - }> - - + + { isGuestUser + ? ( +

      + { t('Not available for guest') } +

      + ) + : ( + }> + + + ) + }
      ); };