Просмотр исходного кода

Implementing UserGroupSelector Logic

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

+ 20 - 4
apps/app/src/features/openai/client/components/AiAssistant/AccessScopeDropdown.tsx

@@ -5,18 +5,29 @@ import {
   UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 
+import type { PopulatedGrantedGroup } from '~/interfaces/page-grant';
 import { useCurrentUser } from '~/stores-universal/context';
 
 import { AiAssistantAccessScope, AiAssistantShareScope } from '../../../interfaces/ai-assistant';
 
 import { UserGroupSelector } from './UserGroupSelector';
 
-export const AccessScopeDropdown: React.FC = () => {
+type Props = {
+  selectedAccessScope: AiAssistantAccessScope,
+  selectedUserGroup: PopulatedGrantedGroup[];
+  onSelectAccessScope: (accessScope: AiAssistantAccessScope) => void,
+  onSelectUserGroup: (userGroup: PopulatedGrantedGroup[]) => void,
+}
+
+export const AccessScopeDropdown: React.FC<Props> = (props: Props) => {
   const { t } = useTranslation();
   const { data: currentUser } = useCurrentUser();
 
   const [isUserGroupSelectorOpen, setIsUserGroupSelectorOpen] = useState(false);
-  const [selectedAccessScope, setSelectedAccessScope] = useState<AiAssistantAccessScope>(AiAssistantAccessScope.OWNER);
+
+  const {
+    selectedAccessScope, selectedUserGroup, onSelectAccessScope, onSelectUserGroup,
+  } = props;
 
   const getAccessScopeLabel = useCallback((accessScope: AiAssistantAccessScope) => {
     const baseLabel = `modal_ai_assistant.access_scope.${accessScope}`;
@@ -26,11 +37,14 @@ export const AccessScopeDropdown: React.FC = () => {
   }, [currentUser?.username, t]);
 
   const selectAccessScopeHandler = useCallback((accessScope: AiAssistantAccessScope) => {
-    setSelectedAccessScope(accessScope);
+    onSelectAccessScope(accessScope);
     if (accessScope === AiAssistantAccessScope.GROUPS) {
       setIsUserGroupSelectorOpen(true);
     }
-  }, []);
+    else {
+      onSelectUserGroup([]);
+    }
+  }, [onSelectAccessScope, onSelectUserGroup]);
 
   return (
     <>
@@ -53,6 +67,8 @@ export const AccessScopeDropdown: React.FC = () => {
       <UserGroupSelector
         isOpen={isUserGroupSelectorOpen}
         closeModal={() => setIsUserGroupSelectorOpen(false)}
+        selectedUserGroup={selectedUserGroup}
+        onSelect={onSelectUserGroup}
       />
     </>
   );

+ 19 - 2
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantManegementModal.tsx

@@ -6,7 +6,9 @@ import {
 } from 'reactstrap';
 
 import { toastError, toastSuccess } from '~/client/util/toastr';
+import { AiAssistantAccessScope } from '~/features/openai/interfaces/ai-assistant';
 import type { IPageForItem } from '~/interfaces/page';
+import type { PopulatedGrantedGroup } from '~/interfaces/page-grant';
 import { usePageSelectModal } from '~/stores/modal';
 import loggerFactory from '~/utils/logger';
 
@@ -17,7 +19,6 @@ import { SelectedPageList } from '../Common/SelectedPageList';
 
 import { AccessScopeDropdown } from './AccessScopeDropdown';
 
-
 import styles from './AiAssistantManegementModal.module.scss';
 
 const moduleClass = styles['grw-ai-assistant-manegement'] ?? '';
@@ -28,6 +29,17 @@ const AiAssistantManegementModalSubstance = (): JSX.Element => {
   const { open: openPageSelectModal } = usePageSelectModal();
 
   const [selectedPages, setSelectedPages] = useState<SelectedPage[]>([]);
+  const [selectedAccessScope, setSelectedAccessScope] = useState<AiAssistantAccessScope>(AiAssistantAccessScope.OWNER);
+  const [selectedUserGroupsForShareScope, setSelectedUserGroupsForShareScope] = useState<PopulatedGrantedGroup[]>([]);
+
+  const clickAccessScopeItemHandler = useCallback((accessScope: AiAssistantAccessScope) => {
+    setSelectedAccessScope(accessScope);
+  }, []);
+
+  const selectUserGroupsForShareScopeHandler = useCallback((userGroups: PopulatedGrantedGroup[]) => {
+    console.log('userGroups', userGroups);
+    setSelectedUserGroupsForShareScope(userGroups);
+  }, []);
 
   const clickOpenPageSelectModalHandler = useCallback(() => {
     const onSelected = (page: IPageForItem, isIncludeSubPage: boolean) => {
@@ -106,7 +118,12 @@ const AiAssistantManegementModalSubstance = (): JSX.Element => {
               <Label className="mb-0">共有範囲</Label>
               <span className="ms-1 fs-5 material-symbols-outlined text-secondary">help</span>
             </div>
-            <AccessScopeDropdown />
+            <AccessScopeDropdown
+              selectedAccessScope={selectedAccessScope}
+              selectedUserGroup={selectedUserGroupsForShareScope}
+              onSelectAccessScope={clickAccessScopeItemHandler}
+              onSelectUserGroup={selectUserGroupsForShareScopeHandler}
+            />
           </FormGroup>
 
           <FormGroup className="mb-4">

+ 50 - 3
apps/app/src/features/openai/client/components/AiAssistant/UserGroupSelector.tsx

@@ -1,22 +1,69 @@
-import React from 'react';
+import React, { useState, useCallback } from 'react';
 
+import { GroupType } from '@growi/core';
 import { useTranslation } from 'react-i18next';
 import {
   Modal, ModalHeader, ModalBody,
 } from 'reactstrap';
 
+import type { PopulatedGrantedGroup } from '~/interfaces/page-grant';
 import { useSWRxUserRelatedGroups } from '~/stores/user';
 
 type Props = {
   isOpen: boolean,
   closeModal: () => void,
+  selectedUserGroup: PopulatedGrantedGroup[],
+  onSelect: (userGroup: PopulatedGrantedGroup[]) => void,
 }
 
-const UserGroupSelectorSubstance: React.FC<Props> = () => {
+const UserGroupSelectorSubstance: React.FC<Props> = (props: Props) => {
+  const { t } = useTranslation();
   const { data: userRelatedGroups } = useSWRxUserRelatedGroups();
 
+  const [selectedUserGroup_, setSelectedUserGroup_] = useState<PopulatedGrantedGroup[]>(props.selectedUserGroup);
+
+  const groupListItemClickHandler = useCallback((targetUserGroup: PopulatedGrantedGroup) => {
+    const selectedUserGroupIds = selectedUserGroup_.map(userGroup => userGroup.item._id);
+    if (selectedUserGroupIds.includes(targetUserGroup.item._id)) {
+      // if selected, remove it
+      setSelectedUserGroup_(selectedUserGroup_.filter(userGroup => userGroup.item._id !== targetUserGroup.item._id));
+    }
+    else {
+      // if not selected, add it
+      setSelectedUserGroup_([...selectedUserGroup_, targetUserGroup]);
+    }
+  }, [selectedUserGroup_]);
+
+  const checked = useCallback((targetUserGroup: PopulatedGrantedGroup) => {
+    const selectedUserGroupIds = selectedUserGroup_.map(userGroup => userGroup.item._id);
+    return selectedUserGroupIds.includes(targetUserGroup.item._id);
+  }, [selectedUserGroup_]);
+
   return (
-    <ModalBody>body</ModalBody>
+    <ModalBody className="d-flex flex-column">
+
+      {userRelatedGroups != null && userRelatedGroups.relatedGroups.map(userGroup => (
+        <button
+          className="btn btn-outline-primary d-flex justify-content-start mb-3 mx-4 align-items-center p-3"
+          type="button"
+          key={userGroup.item.id}
+          onClick={() => groupListItemClickHandler(userGroup)}
+        >
+          <input type="checkbox" checked={checked(userGroup)} />
+          <p className="ms-3 mb-0">{userGroup.item.name}</p>
+          {userGroup.type === GroupType.externalUserGroup && <span className="ms-2 badge badge-pill badge-info">{userGroup.item.provider}</span>}
+          {/* TODO: Replace <div className="small">(TBD) List group members</div> */}
+        </button>
+      ))}
+      <button
+        type="button"
+        className="btn btn-primary mt-2 mx-auto"
+        onClick={() => props.onSelect(selectedUserGroup_)}
+      >
+        {t('Done')}
+      </button>
+
+    </ModalBody>
   );
 };