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

Merge pull request #4081 from weseek/feat/6450-6886-implement-checkbox

Feat/6450 6886 implement checkbox
itizawa 4 лет назад
Родитель
Сommit
c741c728b6

+ 8 - 0
packages/slack/src/index.ts

@@ -8,6 +8,14 @@ export const supportedGrowiCommands: string[] = [
   'help',
 ];
 
+export const defaultSupportedCommandsNameForBroadcastUse: string[] = [
+  'search',
+];
+
+export const defaultSupportedCommandsNameForSingleUse: string[] = [
+  'create',
+];
+
 export * from './interfaces/growi-command';
 export * from './interfaces/request-between-growi-and-proxy';
 export * from './interfaces/request-from-slack';

+ 1 - 0
resource/locales/en_US/admin/admin.json

@@ -326,6 +326,7 @@
       "install_complete_if_checked": "Confirm that \"Install your app\" is checked.",
       "invite_bot_to_channel": "Invite GROWI bot to channel by calling @example.",
       "register_secret_and_token": "Set Signing Secret and Bot Token",
+      "manage_commands": "Manage GROWI commands",
       "test_connection": "Test Connection",
       "test_connection_by_pressing_button": "Press the button to test the connection",
       "error_check_logs_below": "An error has occurred. Please check the logs below.",

+ 1 - 0
resource/locales/ja_JP/admin/admin.json

@@ -323,6 +323,7 @@
       "install_complete_if_checked": "Install your app の右側に緑色のチェックがつけばワークスペースへのインストール完了です。",
       "invite_bot_to_channel": "GROWI bot を使いたいチャンネルに @example を使用して招待します。",
       "register_secret_and_token": "Signing Secret と Bot Token を登録する",
+      "manage_commands": "使用可能なGROWIコマンドを設定する",
       "test_connection": "連携状況のテストをする",
       "test_connection_by_pressing_button": "以下のテストボタンを押して、Slack連携が完了しているかの確認をしましょう",
       "error_check_logs_below": "エラーが発生しました。下記のログを確認してください。",

+ 1 - 0
resource/locales/zh_CN/admin/admin.json

@@ -333,6 +333,7 @@
       "install_complete_if_checked": "确认已选中 \"Install your app\"。",
       "invite_bot_to_channel": "通过调用 @example 邀请 GROWI Bot 进行频道。",
       "register_secret_and_token": "设置签名秘密和BOT令牌",
+      "manage_commands": "管理 GROWI 命令",
       "test_connection": "测试连接",
       "test_connection_by_pressing_button": "按下按钮以测试连接",
       "error_check_logs_below": "发生了错误。请检查以下日志。",

+ 5 - 1
src/client/js/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx

@@ -103,7 +103,9 @@ const CustomBotWithProxySettings = (props) => {
 
       <div className="mx-3">
         {slackAppIntegrations.map((slackAppIntegration, i) => {
-          const { tokenGtoP, tokenPtoG, _id } = slackAppIntegration;
+          const {
+            tokenGtoP, tokenPtoG, _id, supportedCommandsForBroadcastUse, supportedCommandsForSingleUse,
+          } = slackAppIntegration;
           const workspaceName = connectionStatuses[_id]?.workspaceName;
           return (
             <React.Fragment key={slackAppIntegration._id}>
@@ -125,6 +127,8 @@ const CustomBotWithProxySettings = (props) => {
                 slackAppIntegrationId={slackAppIntegration._id}
                 tokenGtoP={tokenGtoP}
                 tokenPtoG={tokenPtoG}
+                supportedCommandsForBroadcastUse={supportedCommandsForBroadcastUse}
+                supportedCommandsForSingleUse={supportedCommandsForSingleUse}
                 onUpdateTokens={onUpdateTokens}
                 onSubmitForm={onSubmitForm}
               />

+ 136 - 0
src/client/js/components/Admin/SlackIntegration/ManageCommandsProcess.jsx

@@ -0,0 +1,136 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { useTranslation } from 'react-i18next';
+import loggerFactory from '@alias/logger';
+
+import { defaultSupportedCommandsNameForBroadcastUse, defaultSupportedCommandsNameForSingleUse } from '@growi/slack';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+const logger = loggerFactory('growi:SlackIntegration:ManageCommandsProcess');
+
+const ManageCommandsProcess = ({
+  apiv3Put, slackAppIntegrationId, supportedCommandsForBroadcastUse, supportedCommandsForSingleUse,
+}) => {
+  const { t } = useTranslation();
+  const [selectedCommandsForBroadcastUse, setSelectedCommandsForBroadcastUse] = useState(new Set(supportedCommandsForBroadcastUse));
+  const [selectedCommandsForSingleUse, setSelectedCommandsForSingleUse] = useState(new Set(supportedCommandsForSingleUse));
+
+  const toggleCheckboxForBroadcast = (e) => {
+    const { target } = e;
+    const { name, checked } = target;
+
+    setSelectedCommandsForBroadcastUse((prevState) => {
+      const selectedCommands = new Set(prevState);
+      if (checked) {
+        selectedCommands.add(name);
+      }
+      else {
+        selectedCommands.delete(name);
+      }
+
+      return selectedCommands;
+    });
+  };
+
+  const toggleCheckboxForSingleUse = (e) => {
+    const { target } = e;
+    const { name, checked } = target;
+
+    setSelectedCommandsForSingleUse((prevState) => {
+      const selectedCommands = new Set(prevState);
+      if (checked) {
+        selectedCommands.add(name);
+      }
+      else {
+        selectedCommands.delete(name);
+      }
+
+      return selectedCommands;
+    });
+  };
+
+  const updateCommandsHandler = async() => {
+    try {
+      await apiv3Put(`/slack-integration-settings/${slackAppIntegrationId}/supported-commands`, {
+        supportedCommandsForBroadcastUse: Array.from(selectedCommandsForBroadcastUse),
+        supportedCommandsForSingleUse: Array.from(selectedCommandsForSingleUse),
+      });
+      toastSuccess(t('toaster.update_successed', { target: 'Token' }));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  };
+
+
+  return (
+    <div className="py-4 px-5">
+      <p className="mb-4">{t('admin:slack_integration.accordion.manage_commands')}</p>
+      <span className="mb-2">Broadcast Use</span>
+      <div className="custom-control custom-checkbox">
+        <div className="row mb-5">
+          {defaultSupportedCommandsNameForBroadcastUse.map((commandName) => {
+            return (
+              <div className="col-sm-6 my-1" key={commandName}>
+                <input
+                  type="checkbox"
+                  className="custom-control-input"
+                  id={commandName}
+                  name={commandName}
+                  value={commandName}
+                  checked={selectedCommandsForBroadcastUse.has(commandName)}
+                  onChange={toggleCheckboxForBroadcast}
+                />
+                <label className="text-capitalize custom-control-label ml-3" htmlFor={commandName}>
+                  {commandName}
+                </label>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+      <span className="mb-2">Single Use</span>
+      <div className="custom-control custom-checkbox">
+        <div className="row mb-5">
+          {defaultSupportedCommandsNameForSingleUse.map((commandName) => {
+            return (
+              <div className="col-sm-6 my-1" key={commandName}>
+                <input
+                  type="checkbox"
+                  className="custom-control-input"
+                  id={commandName}
+                  name={commandName}
+                  value={commandName}
+                  checked={selectedCommandsForSingleUse.has(commandName)}
+                  onChange={toggleCheckboxForSingleUse}
+                />
+                <label className="text-capitalize custom-control-label ml-3" htmlFor={commandName}>
+                  {commandName}
+                </label>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+      <div className="row">
+        <button
+          type="button"
+          className="btn btn-primary mx-auto"
+          onClick={updateCommandsHandler}
+        >
+          { t('Update') }
+        </button>
+      </div>
+    </div>
+  );
+};
+
+ManageCommandsProcess.propTypes = {
+  apiv3Put: PropTypes.func,
+  slackAppIntegrationId: PropTypes.string.isRequired,
+  supportedCommandsForBroadcastUse: PropTypes.arrayOf(PropTypes.string),
+  supportedCommandsForSingleUse: PropTypes.arrayOf(PropTypes.string),
+};
+
+export default ManageCommandsProcess;

+ 5 - 1
src/client/js/components/Admin/SlackIntegration/OfficialBotSettings.jsx

@@ -69,7 +69,9 @@ const OfficialBotSettings = (props) => {
 
       <div className="mx-3">
         {slackAppIntegrations.map((slackAppIntegration, i) => {
-          const { tokenGtoP, tokenPtoG, _id } = slackAppIntegration;
+          const {
+            tokenGtoP, tokenPtoG, _id, supportedCommandsForBroadcastUse, supportedCommandsForSingleUse,
+          } = slackAppIntegration;
           const workspaceName = connectionStatuses[_id]?.workspaceName;
           return (
             <React.Fragment key={slackAppIntegration._id}>
@@ -91,6 +93,8 @@ const OfficialBotSettings = (props) => {
                 slackAppIntegrationId={slackAppIntegration._id}
                 tokenGtoP={tokenGtoP}
                 tokenPtoG={tokenPtoG}
+                supportedCommandsForBroadcastUse={supportedCommandsForBroadcastUse}
+                supportedCommandsForSingleUse={supportedCommandsForSingleUse}
                 onUpdateTokens={onUpdateTokens}
                 onSubmitForm={onSubmitForm}
               />

+ 22 - 2
src/client/js/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -11,6 +11,7 @@ import AppContainer from '../../../services/AppContainer';
 import Accordion from '../Common/Accordion';
 import { addLogs } from './slak-integration-util';
 import MessageBasedOnConnection from './MessageBasedOnConnection';
+import ManageCommandsProcess from './ManageCommandsProcess';
 
 const logger = loggerFactory('growi:SlackIntegration:WithProxyAccordionsWrapper');
 
@@ -287,7 +288,6 @@ const WithProxyAccordions = (props) => {
       props.onSubmitForm();
     }
   };
-
   const submitFormFailed = () => {
     setIsLatestConnectionSuccess(false);
   };
@@ -309,6 +309,15 @@ const WithProxyAccordions = (props) => {
       />,
     },
     '③': {
+      title: 'manage_commands',
+      content: <ManageCommandsProcess
+        apiv3Put={props.appContainer.apiv3.put}
+        slackAppIntegrationId={props.slackAppIntegrationId}
+        supportedCommandsForBroadcastUse={props.supportedCommandsForBroadcastUse}
+        supportedCommandsForSingleUse={props.supportedCommandsForSingleUse}
+      />,
+    },
+    '④': {
       title: 'test_connection',
       content: <TestProcess
         apiv3Post={props.appContainer.apiv3.post}
@@ -344,6 +353,15 @@ const WithProxyAccordions = (props) => {
       content: <RegisteringProxyUrlProcess />,
     },
     '⑤': {
+      title: 'manage_commands',
+      content: <ManageCommandsProcess
+        apiv3Put={props.appContainer.apiv3.put}
+        slackAppIntegrationId={props.slackAppIntegrationId}
+        supportedCommandsForBroadcastUse={props.supportedCommandsForBroadcastUse}
+        supportedCommandsForSingleUse={props.supportedCommandsForSingleUse}
+      />,
+    },
+    '⑥': {
       title: 'test_connection',
       content: <TestProcess
         apiv3Post={props.appContainer.apiv3.post}
@@ -370,7 +388,7 @@ const WithProxyAccordions = (props) => {
                 {t(`admin:slack_integration.accordion.${value.title}`)}
                 {value.title === 'test_connection' && isLatestConnectionSuccess && <i className="ml-3 text-success fa fa-check"></i>}
               </>
-)}
+            )}
             key={key}
           >
             {value.content}
@@ -392,6 +410,8 @@ WithProxyAccordions.propTypes = {
   slackAppIntegrationId: PropTypes.string.isRequired,
   tokenPtoG: PropTypes.string,
   tokenGtoP: PropTypes.string,
+  supportedCommandsForBroadcastUse: PropTypes.arrayOf(PropTypes.string),
+  supportedCommandsForSingleUse: PropTypes.arrayOf(PropTypes.string),
 };
 
 export default WithProxyAccordionsWrapper;

+ 7 - 5
src/server/routes/apiv3/slack-integration-settings.js

@@ -5,7 +5,9 @@ const axios = require('axios');
 const urljoin = require('url-join');
 const loggerFactory = require('@alias/logger');
 
-const { getConnectionStatus, getConnectionStatuses, sendSuccessMessage } = require('@growi/slack');
+const {
+  getConnectionStatus, getConnectionStatuses, sendSuccessMessage, defaultSupportedCommandsNameForBroadcastUse, defaultSupportedCommandsNameForSingleUse,
+} = require('@growi/slack');
 
 const ErrorV3 = require('../../models/vo/error-apiv3');
 
@@ -405,12 +407,12 @@ module.exports = (crowi) => {
     }
 
     const { tokenGtoP, tokenPtoG } = await SlackAppIntegration.generateUniqueAccessTokens();
-    const supportedCommandsForBroadcastUse = ['search'];
-    const supportedCommandsForSingleUse = ['create'];
-
     try {
       const slackAppTokens = await SlackAppIntegration.create({
-        tokenGtoP, tokenPtoG, supportedCommandsForBroadcastUse, supportedCommandsForSingleUse,
+        tokenGtoP,
+        tokenPtoG,
+        supportedCommandsForBroadcastUse: defaultSupportedCommandsNameForBroadcastUse,
+        supportedCommandsForSingleUse: defaultSupportedCommandsNameForSingleUse,
       });
       return res.apiv3(slackAppTokens, 200);
     }