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

Revert "Revert "fix: Styling error in the Slack Integration""

This reverts commit cc5412f6e8d5d3d1f10099e440a26a3acf593229.
reiji-h 2 лет назад
Родитель
Сommit
ca2805d9a9
22 измененных файлов с 316 добавлено и 212 удалено
  1. 0 6
      apps/app/_obsolete/src/styles/theme/apply-colors.scss
  2. 0 1
      apps/app/next-env.d.ts
  3. 40 24
      apps/app/src/components/Admin/SlackIntegration/BotTypeCard.tsx
  4. 0 0
      apps/app/src/components/Admin/SlackIntegration/Bridge.jsx
  5. 100 0
      apps/app/src/components/Admin/SlackIntegration/Bridge.tsx
  6. 0 57
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxyConnectionStatus.jsx
  7. 59 0
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxyConnectionStatus.tsx
  8. 2 2
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx
  9. 0 57
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.jsx
  10. 57 0
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.tsx
  11. 1 1
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx
  12. 1 1
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySettingsAccordion.jsx
  13. 21 19
      apps/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.tsx
  14. 2 2
      apps/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx
  15. 1 2
      apps/app/src/components/Admin/SlackIntegration/SlackAppIntegrationControl.tsx
  16. 12 12
      apps/app/src/components/Admin/SlackIntegration/SlackIntegration.tsx
  17. 1 1
      apps/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx
  18. 4 0
      apps/app/src/components/Admin/SlackIntegration/slack-integration-util.ts
  19. 0 20
      apps/app/src/components/Admin/SlackIntegration/slak-integration-util.js
  20. 9 3
      apps/app/src/components/Layout/Admin.module.scss
  21. 5 4
      apps/app/src/pages/admin/slack-integration.page.tsx
  22. 1 0
      packages/slack/src/interfaces/index.ts

+ 0 - 6
apps/app/_obsolete/src/styles/theme/apply-colors.scss

@@ -402,12 +402,6 @@ ul.pagination {
   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
 }
 
-.admin-bot-card {
-  .grw-botcard-title-active {
-    color: $gray-200;
-  }
-}
-
 /*
  * Form Slider
  */

+ 0 - 1
apps/app/next-env.d.ts

@@ -1,6 +1,5 @@
 /// <reference types="next" />
 /// <reference types="next/image-types/global" />
-/// <reference types="next/navigation-types/compat/navigation" />
 
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/basic-features/typescript for more information.

+ 40 - 24
apps/app/src/components/Admin/SlackIntegration/BotTypeCard.jsx → apps/app/src/components/Admin/SlackIntegration/BotTypeCard.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 
 import { SlackbotType } from '@growi/slack';
 import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
+import Image from 'next/image';
 
 const botDetails = {
   officialBot: {
@@ -30,25 +30,33 @@ const botDetails = {
   },
 };
 
-const BotTypeCard = (props) => {
+type BotTypeCardProps = {
+  isActive: boolean,
+  botType: string,
+  onBotTypeSelectHandler: (botType: SlackbotType) => void,
+};
+
+export const BotTypeCard = (props: BotTypeCardProps): JSX.Element => {
   const { t } = useTranslation();
 
-  const isBotTypeOfficial = props.botType === SlackbotType.OFFICIAL;
+  const { isActive, botType, onBotTypeSelectHandler } = props;
+
+  const isBotTypeOfficial = botType === SlackbotType.OFFICIAL;
 
   return (
     <div
-      className={`card admin-bot-card rounded border-radius-sm shadow ${props.isActive ? 'border-primary' : ''}`}
-      onClick={() => props.onBotTypeSelectHandler(botDetails[props.botType].botType)}
+      className={`card admin-bot-card rounded border-radius-sm shadow ${isActive ? 'border-primary' : ''}`}
+      onClick={() => onBotTypeSelectHandler(botDetails[botType].botType)}
       role="button"
-      key={props.botType}
+      key={botType}
     >
       <div>
         <h3 className={`card-header mb-0 py-3
               ${isBotTypeOfficial ? 'd-flex align-items-center justify-content-center' : 'text-center'}
-              ${props.isActive ? 'bg-primary grw-botcard-title-active' : ''}`}
+              ${isActive ? 'bg-primary grw-botcard-title-active' : ''}`}
         >
           <span className="me-2">
-            {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].botTypeCategory}`)}
+            {t(`admin:slack_integration.selecting_bot_types.${botDetails[botType].botTypeCategory}`)}
           </span>
 
           {/*  A recommended badge is shown on official bot card, supplementary names are shown on Custom bot cards   */}
@@ -59,40 +67,48 @@ const BotTypeCard = (props) => {
               </span>
             ) : (
               <span className="supplementary-bot-name me-2">
-                {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].supplementaryBotName}`)}
+                {t(`admin:slack_integration.selecting_bot_types.${botDetails[botType].supplementaryBotName}`)}
               </span>
             )}
 
-          <i className={props.isActive ? 'grw-botcard-title-active' : ''} aria-hidden="true"></i>
+          <i className={isActive ? 'grw-botcard-title-active' : ''} aria-hidden="true"></i>
         </h3>
       </div>
       <div className="card-body p-4">
         <div className="card-text">
           <div className="my-2">
-            <img
+            <Image
               className="bot-difficulty-icon d-block mx-auto mb-4"
-              src={`/images/slack-integration/slackbot-difficulty-level-${botDetails[props.botType].setUp}.svg`}
+              src={`/images/slack-integration/slackbot-difficulty-level-${botDetails[botType].setUp}.svg`}
+              alt=""
+              width={60}
+              height={60}
             />
-            <div className="d-flex justify-content-between mb-3">
+            <div className="d-flex justify-content-between mb-3 align-items-center">
               <span>{t('admin:slack_integration.selecting_bot_types.multiple_workspaces_integration')}</span>
-              <img className="bot-type-disc" src={`/images/slack-integration/${botDetails[props.botType].multiWSIntegration}.png`} alt="" />
+              <Image
+                className="bot-type-disc"
+                src={`/images/slack-integration/${botDetails[botType].multiWSIntegration}.png`}
+                alt=""
+                width={20}
+                height={20}
+              />
             </div>
-            <div className="d-flex justify-content-between">
+            <div className="d-flex justify-content-between align-items-center">
               <span>{t('admin:slack_integration.selecting_bot_types.security_control')}</span>
-              <img className="bot-type-disc" src={`/images/slack-integration/${botDetails[props.botType].securityControl}.png`} alt="" />
+              <Image
+                className="bot-type-disc"
+                src={`/images/slack-integration/${botDetails[botType].securityControl}.png`}
+                alt=""
+                width={20}
+                height={20}
+              />
             </div>
           </div>
         </div>
       </div>
     </div>
   );
-
-};
-
-BotTypeCard.propTypes = {
-  isActive: PropTypes.bool.isRequired,
-  botType: PropTypes.string.isRequired,
-  onBotTypeSelectHandler: PropTypes.func.isRequired,
 };
 
-export default BotTypeCard;
+BotTypeCard.displayName = 'BotTypeCard';

+ 0 - 0
apps/app/src/components/Admin/SlackIntegration/Bridge.jsx


+ 100 - 0
apps/app/src/components/Admin/SlackIntegration/Bridge.tsx

@@ -0,0 +1,100 @@
+
+import { useTranslation } from 'next-i18next';
+import { UncontrolledTooltip } from 'reactstrap';
+
+const ProxyCircle = () => (
+  <div className="grw-bridge-proxy-circle">
+    <div className="circle bg-primary border-light rounded-circle">
+      <p className="circle-inner text-light fw-bold d-none d-lg-inline">Proxy Server</p>
+      <p className="circle-inner grw-proxy-server-name d-inline d-lg-none">Proxy Server</p>
+    </div>
+  </div>
+);
+
+type BridgeCoreProps = {
+  description: string,
+  iconClass: string,
+  iconName: string,
+  hrClass: string,
+  withProxy?: boolean,
+}
+const BridgeCore = (props: BridgeCoreProps): JSX.Element => {
+  const {
+    description, iconClass, iconName, hrClass, withProxy,
+  } = props;
+
+  return (
+    <>
+      <div id="grw-bridge-container" className={`grw-bridge-container ${withProxy ? 'with-proxy' : ''}`}>
+        <p className={`${withProxy ? 'mt-0' : 'mt-2'}`}>
+          <span className={iconClass}>{iconName}</span>
+          <small
+            className="ms-2 d-none d-lg-inline"
+            // eslint-disable-next-line react/no-danger
+            dangerouslySetInnerHTML={{ __html: description }}
+          />
+        </p>
+        <div className="hr-container">
+          { withProxy && <ProxyCircle /> }
+          <hr className={`align-self-center ${hrClass}`} />
+        </div>
+      </div>
+      <UncontrolledTooltip placement="top" fade={false} target="grw-bridge-container" className="d-block d-lg-none">
+        <small
+          // eslint-disable-next-line react/no-danger
+          dangerouslySetInnerHTML={{ __html: description }}
+        />
+      </UncontrolledTooltip>
+    </>
+  );
+};
+
+
+type BridgeProps = {
+  errorCount: number,
+  totalCount: number,
+  withProxy?: boolean,
+}
+export const Bridge = (props: BridgeProps): JSX.Element => {
+  const { t } = useTranslation();
+  const { errorCount, totalCount, withProxy } = props;
+
+  let description;
+  let iconClass;
+  let iconName;
+  let hrClass;
+
+  // empty or all failed
+  if (totalCount === 0 || errorCount === totalCount) {
+    description = t('admin:slack_integration.integration_sentence.integration_is_not_complete');
+    iconClass = 'material-symbols-outlined text-danger';
+    iconName = 'info';
+    hrClass = 'border-danger admin-border-failed';
+  }
+  // all green
+  else if (errorCount === 0) {
+    description = t('admin:slack_integration.integration_sentence.integration_successful');
+    iconClass = 'material-symbols-outlined text-success';
+    iconName = 'check';
+    hrClass = 'border-success admin-border-success';
+  }
+  // some of them failed
+  else {
+    description = t('admin:slack_integration.integration_sentence.integration_some_ws_is_not_complete');
+    iconClass = 'material-symbols-outlined text-warning';
+    iconName = 'check';
+    hrClass = 'border-warning admin-border-failed';
+  }
+
+  return (
+    <BridgeCore
+      description={description}
+      iconClass={iconClass}
+      iconName={iconName}
+      hrClass={hrClass}
+      withProxy={withProxy}
+    />
+  );
+};
+
+Bridge.displayName = 'Bridge';

+ 0 - 57
apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxyConnectionStatus.jsx

@@ -1,57 +0,0 @@
-import React from 'react';
-
-import PropTypes from 'prop-types';
-
-import Bridge from './Bridge';
-
-const CustomBotWithProxyConnectionStatus = (props) => {
-  const { siteName, connectionStatuses } = props;
-
-  const connectionStatusValues = Object.values(connectionStatuses); // type: ConnectionStatus[]
-
-  const totalCount = connectionStatusValues.length;
-  const errorCount = connectionStatusValues.filter(connectionStatus => connectionStatusValues.error != null).length;
-
-  return (
-    <div className="d-flex justify-content-center my-5 bot-integration">
-
-      <div className="card rounded shadow border-0 w-50 admin-bot-card">
-        <h5 className="card-title fw-bold mt-3 ms-3">Slack</h5>
-        <div className="card-body px-5">
-          {connectionStatusValues.map((connectionStatus, i) => {
-            const workspaceName = connectionStatus.workspaceName || `Settings #${i}`;
-
-            return (
-              <div key={workspaceName} className="card slack-work-space-name-card">
-                <div className="m-2 text-center">
-                  <h5 className="fw-bold">{workspaceName}</h5>
-                  <img width={20} height={20} src="/images/slack-integration/growi-bot-kun-icon.png" />
-                </div>
-              </div>
-            );
-          })}
-        </div>
-      </div>
-
-      <div className="text-center w-25 mt-3">
-        <Bridge errorCount={errorCount} totalCount={totalCount} withProxy />
-      </div>
-
-      <div className="card rounded-3 shadow border-0 w-50 admin-bot-card">
-        <h5 className="card-title fw-bold mt-3 ms-3">GROWI App</h5>
-        <div className="card-body text-center">
-          <div className="mx-md-3 my-4 my-lg-5 p-2 border bg-primary text-light">
-            {siteName}
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-CustomBotWithProxyConnectionStatus.propTypes = {
-  siteName: PropTypes.string.isRequired,
-  connectionStatuses: PropTypes.object.isRequired,
-};
-
-export default CustomBotWithProxyConnectionStatus;

+ 59 - 0
apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxyConnectionStatus.tsx

@@ -0,0 +1,59 @@
+import React from 'react';
+
+import type { ConnectionStatus } from '@growi/slack';
+import Image from 'next/image';
+
+import { Bridge } from './Bridge';
+
+
+type CustomBotWithProxyConnectionStatusProps = {
+  siteName: string,
+  connectionStatuses: any,
+}
+
+export const CustomBotWithProxyConnectionStatus = (props: CustomBotWithProxyConnectionStatusProps): JSX.Element => {
+  const { siteName, connectionStatuses } = props;
+
+  const connectionStatusValues: ConnectionStatus[] = Object.values(connectionStatuses);
+
+  const totalCount = connectionStatusValues.length;
+  const errorCount = connectionStatusValues.filter(connectionStatus => connectionStatus.error != null).length;
+
+  return (
+    <div className="row justify-content-center my-5 bot-integration">
+
+      <div className="card rounded shadow col-4 border-0 admin-bot-card">
+        <h5 className="card-title fw-bold mt-3 text-center">Slack</h5>
+        <div className="card-body px-5">
+          {connectionStatusValues.map((connectionStatus, i) => {
+            const workspaceName = connectionStatus.workspaceName || `Settings #${i}`;
+
+            return (
+              <div key={workspaceName} className="card slack-work-space-name-card">
+                <div className="m-2 text-center">
+                  <h5 className="fw-bold">{workspaceName}</h5>
+                  <Image width={20} height={20} src="/images/slack-integration/growi-bot-kun-icon.png" alt="" />
+                </div>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+
+      <div className="col-3 mt-3 text-center">
+        <Bridge errorCount={errorCount} totalCount={totalCount} withProxy />
+      </div>
+
+      <div className="card rounded-3 shadow col-4 border-0 admin-bot-card">
+        <h5 className="card-title fw-bold mt-3 text-center">GROWI App</h5>
+        <div className="card-body text-center">
+          <div className="mx-md-3 my-4 my-lg-5 p-2 border bg-primary text-light">
+            {siteName}
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+CustomBotWithProxyConnectionStatus.displayName = 'CustomBotWithProxyConnectionStatus';

+ 2 - 2
apps/app/src/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx

@@ -9,8 +9,8 @@ import { useAppTitle } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
 
 
-import CustomBotWithProxyConnectionStatus from './CustomBotWithProxyConnectionStatus';
-import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
+import { CustomBotWithProxyConnectionStatus } from './CustomBotWithProxyConnectionStatus';
+import { DeleteSlackBotSettingsModal } from './DeleteSlackBotSettingsModal';
 import { SlackAppIntegrationControl } from './SlackAppIntegrationControl';
 import WithProxyAccordions from './WithProxyAccordions';
 

+ 0 - 57
apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.jsx

@@ -1,57 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Bridge from './Bridge';
-
-const CustomBotWithoutProxyConnectionStatus = (props) => {
-  const { siteName, connectionStatuses } = props;
-
-  const connectionStatusValues = Object.values(connectionStatuses); // type: ConnectionStatus[]
-
-  const totalCount = connectionStatusValues.length;
-  const errorCount = connectionStatusValues.filter(connectionStatus => connectionStatus.error != null).length;
-
-  let workspaceName;
-  if (totalCount > 0) {
-    workspaceName = connectionStatusValues[0].workspaceName;
-  }
-
-  return (
-    <div className="d-flex justify-content-center my-5 bot-integration">
-      <div className="card rounded shadow border-0 w-50 admin-bot-card mb-0">
-        <h5 className="card-title fw-bold mt-3 ms-4">Slack</h5>
-        <div className="card-body px-4 text-center mx-md-5">
-          {totalCount > 0 ? (
-            <div className="card slack-work-space-name-card">
-              <div className="m-2 text-center">
-                <h5 className="fw-bold">
-                  {workspaceName != null ? workspaceName : 'Settings #1'}
-                </h5>
-                <img width={20} height={20} src="/images/slack-integration/growi-bot-kun-icon.png" />
-              </div>
-            </div>
-          ) : ''}
-        </div>
-      </div>
-
-      <div className="text-center w-25">
-        <Bridge errorCount={errorCount} totalCount={totalCount} />
-      </div>
-
-      <div className="card rounded-3 shadow border-0 w-50 admin-bot-card mb-0">
-        <h5 className="card-title fw-bold mt-3 ms-4">GROWI App</h5>
-        <div className="card-body p-4 text-center">
-          <div className="border p-2 bg-primary text-light mx-md-5">
-            {siteName}
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-CustomBotWithoutProxyConnectionStatus.propTypes = {
-  siteName: PropTypes.string.isRequired,
-  connectionStatuses: PropTypes.object.isRequired,
-};
-
-export default CustomBotWithoutProxyConnectionStatus;

+ 57 - 0
apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.tsx

@@ -0,0 +1,57 @@
+import React from 'react';
+
+import type { ConnectionStatus } from '@growi/slack';
+import Image from 'next/image';
+
+import { Bridge } from './Bridge';
+
+type CustomBotWithoutProxyConnectionStatusProps = {
+  siteName: string,
+  connectionStatuses: any,
+}
+
+export const CustomBotWithoutProxyConnectionStatus = (props: CustomBotWithoutProxyConnectionStatusProps): JSX.Element => {
+  const { siteName, connectionStatuses } = props;
+
+  const connectionStatusValues: ConnectionStatus[] = Object.values(connectionStatuses);
+
+  const totalCount = connectionStatusValues.length;
+  const errorCount = connectionStatusValues.filter(connectionStatus => connectionStatus.error != null).length;
+  const workspaceName = connectionStatusValues[0]?.workspaceName;
+
+  return (
+    <div className="row justify-content-center my-5 bot-integration">
+      <div className="card rounded shadow col-4 border-0 admin-bot-card mb-0">
+        <h5 className="card-title fw-bold mt-3 text-center">Slack</h5>
+        <div className="card-body px-4 text-center mx-md-5">
+          {totalCount > 0 ? (
+            <div className="card slack-work-space-name-card">
+              <div className="m-2 text-center">
+                <h5 className="fw-bold">
+                  {workspaceName != null ? workspaceName : 'Settings #1'}
+                </h5>
+                <Image width={20} height={20} src="/images/slack-integration/growi-bot-kun-icon.png" alt="" />
+              </div>
+            </div>
+          ) : ''}
+        </div>
+      </div>
+
+      <div className="col-3 text-center">
+        <Bridge errorCount={errorCount} totalCount={totalCount} />
+      </div>
+
+      <div className="card rounded-3 shadow col-4 border-0 admin-bot-card mb-0">
+        <h5 className="card-title fw-bold mt-3 text-center">GROWI App</h5>
+        <div className="card-body p-4 text-center">
+          <div className="border p-2 bg-primary text-light mx-md-5">
+            {siteName}
+          </div>
+        </div>
+      </div>
+
+    </div>
+  );
+};
+
+CustomBotWithoutProxyConnectionStatus.displayName = 'CustomBotWithoutProxyConnectionStatus';

+ 1 - 1
apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx

@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
 
 import { useAppTitle } from '~/stores/context';
 
-import CustomBotWithoutProxyConnectionStatus from './CustomBotWithoutProxyConnectionStatus';
+import { CustomBotWithoutProxyConnectionStatus } from './CustomBotWithoutProxyConnectionStatus';
 import CustomBotWithoutProxySettingsAccordion, { botInstallationStep } from './CustomBotWithoutProxySettingsAccordion';
 
 const CustomBotWithoutProxySettings = (props) => {

+ 1 - 1
apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySettingsAccordion.jsx

@@ -10,7 +10,7 @@ import Accordion from '../Common/Accordion';
 import CustomBotWithoutProxySecretTokenSection from './CustomBotWithoutProxySecretTokenSection';
 import ManageCommandsProcessWithoutProxy from './ManageCommandsProcessWithoutProxy';
 import MessageBasedOnConnection from './MessageBasedOnConnection';
-import { addLogs } from './slak-integration-util';
+import { addLogs } from './slack-integration-util';
 
 
 export const botInstallationStep = {

+ 21 - 19
apps/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.jsx → apps/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.tsx

@@ -1,13 +1,24 @@
 import React, { useCallback } from 'react';
 
 import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
 import {
   Button, Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 
-const DeleteSlackBotSettingsModal = React.memo((props) => {
-  const { t, onClickDeleteButton, onClose } = useTranslation();
+type DeleteSlackBotSettingsModalProps = {
+  isResetAll: boolean,
+  isOpen: boolean,
+  onClose?: () => void,
+  onClickDeleteButton?: () => void,
+}
+
+export const DeleteSlackBotSettingsModal = React.memo((props: DeleteSlackBotSettingsModalProps) => {
+
+  const { t } = useTranslation();
+
+  const {
+    isResetAll, isOpen, onClose, onClickDeleteButton,
+  } = props;
 
   const deleteSlackCredentialsHandler = useCallback(() => {
     onClickDeleteButton?.();
@@ -19,16 +30,16 @@ const DeleteSlackBotSettingsModal = React.memo((props) => {
   }, [onClose]);
 
   return (
-    <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">
+    <Modal isOpen={isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">
       <ModalHeader tag="h4" toggle={closeButtonHandler} className="bg-danger text-light">
         <span>
-          {props.isResetAll && (
+          {isResetAll && (
             <>
               <span className="material-symbols-outlined">delete_forever</span>
               {t('admin:slack_integration.reset_all_settings')}
             </>
           )}
-          {!props.isResetAll && (
+          {!isResetAll && (
             <>
               <span className="material-symbols-outlined">delete</span>
               {t('admin:slack_integration.delete_slackbot_settings')}
@@ -37,13 +48,13 @@ const DeleteSlackBotSettingsModal = React.memo((props) => {
         </span>
       </ModalHeader>
       <ModalBody>
-        {props.isResetAll && (
+        {isResetAll && (
           <span
             // eslint-disable-next-line react/no-danger
             dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.all_settings_of_the_bot_will_be_reset') }}
           />
         )}
-        {!props.isResetAll && (
+        {!isResetAll && (
           <span
             // eslint-disable-next-line react/no-danger
             dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.slackbot_settings_notice') }}
@@ -53,13 +64,13 @@ const DeleteSlackBotSettingsModal = React.memo((props) => {
       <ModalFooter>
         <Button onClick={closeButtonHandler}>{t('Cancel')}</Button>
         <Button color="danger" onClick={deleteSlackCredentialsHandler}>
-          {props.isResetAll && (
+          {isResetAll && (
             <>
               <span className="material-symbols-outlined">delete_forever</span>
               {t('admin:slack_integration.reset')}
             </>
           )}
-          {!props.isResetAll && (
+          {!isResetAll && (
             <>
               <span className="material-symbols-outlined">delete</span>
               {t('admin:slack_integration.delete')}
@@ -72,13 +83,4 @@ const DeleteSlackBotSettingsModal = React.memo((props) => {
 
 });
 
-DeleteSlackBotSettingsModal.propTypes = {
-  isResetAll: PropTypes.bool.isRequired,
-  isOpen: PropTypes.bool.isRequired,
-  onClose: PropTypes.func,
-  onClickDeleteButton: PropTypes.func,
-};
-
 DeleteSlackBotSettingsModal.displayName = 'DeleteSlackBotSettingsModal';
-
-export default DeleteSlackBotSettingsModal;

+ 2 - 2
apps/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx

@@ -11,8 +11,8 @@ import { useAppTitle } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
 
 
-import CustomBotWithProxyConnectionStatus from './CustomBotWithProxyConnectionStatus';
-import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
+import { CustomBotWithProxyConnectionStatus } from './CustomBotWithProxyConnectionStatus';
+import { DeleteSlackBotSettingsModal } from './DeleteSlackBotSettingsModal';
 import { SlackAppIntegrationControl } from './SlackAppIntegrationControl';
 import WithProxyAccordions from './WithProxyAccordions';
 

+ 1 - 2
apps/app/src/components/Admin/SlackIntegration/SlackAppIntegrationControl.tsx

@@ -1,4 +1,3 @@
-import React, { FC } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
@@ -11,7 +10,7 @@ type Props = {
   onDeleteButtonClicked?: (slackAppIntegration: unknown) => void,
 }
 
-export const SlackAppIntegrationControl: FC<Props> = (props: Props) => {
+export const SlackAppIntegrationControl = (props: Props): JSX.Element => {
   const { t } = useTranslation();
 
   const { slackAppIntegration, onIsPrimaryChanged, onDeleteButtonClicked } = props;

+ 12 - 12
apps/app/src/components/Admin/SlackIntegration/SlackIntegration.jsx → apps/app/src/components/Admin/SlackIntegration/SlackIntegration.tsx

@@ -10,21 +10,21 @@ import {
 } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 
-import BotTypeCard from './BotTypeCard';
+import { BotTypeCard } from './BotTypeCard';
 import ConfirmBotChangeModal from './ConfirmBotChangeModal';
 import CustomBotWithProxySettings from './CustomBotWithProxySettings';
 import CustomBotWithoutProxySettings from './CustomBotWithoutProxySettings';
-import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
+import { DeleteSlackBotSettingsModal } from './DeleteSlackBotSettingsModal';
 import OfficialBotSettings from './OfficialBotSettings';
 
 
 const botTypes = Object.values(SlackbotType);
 
-const SlackIntegration = () => {
+export const SlackIntegration = (): JSX.Element => {
 
   const { t } = useTranslation();
-  const [currentBotType, setCurrentBotType] = useState(null);
-  const [selectedBotType, setSelectedBotType] = useState(null);
+  const [currentBotType, setCurrentBotType] = useState<SlackbotType | undefined>();
+  const [selectedBotType, setSelectedBotType] = useState<SlackbotType | undefined>();
   const [slackSigningSecret, setSlackSigningSecret] = useState(null);
   const [slackBotToken, setSlackBotToken] = useState(null);
   const [slackSigningSecretEnv, setSlackSigningSecretEnv] = useState('');
@@ -106,12 +106,12 @@ const SlackIntegration = () => {
     fetchSlackIntegrationData();
   }, [fetchSlackIntegrationData]);
 
-  const changeCurrentBotSettings = async(botType) => {
+  const changeCurrentBotSettings = async(botType?: SlackbotType) => {
     try {
       await apiv3Put('/slack-integration-settings/bot-type', {
         currentBotType: botType,
       });
-      setSelectedBotType(null);
+      setSelectedBotType(undefined);
       fetchSlackIntegrationData();
     }
     catch (err) {
@@ -119,7 +119,7 @@ const SlackIntegration = () => {
     }
   };
 
-  const botTypeSelectHandler = async(botType) => {
+  const botTypeSelectHandler = async(botType: SlackbotType) => {
     if (botType === currentBotType) {
       return;
     }
@@ -135,10 +135,10 @@ const SlackIntegration = () => {
   };
 
   const cancelBotChangeHandler = () => {
-    setSelectedBotType(null);
+    setSelectedBotType(undefined);
   };
 
-  let settingsComponent = null;
+  let settingsComponent = <></>;
 
   switch (currentBotType) {
     case SlackbotType.OFFICIAL:
@@ -231,7 +231,7 @@ const SlackIntegration = () => {
           </button>
         </div>
 
-        <div className="row my-5 flex-wrap-reverse justify-content-center">
+        <div className="my-5 d-flex flex-wrap-reverse justify-content-center">
           {botTypes.map((botType) => {
             return (
               <div key={botType} className="m-3">
@@ -251,4 +251,4 @@ const SlackIntegration = () => {
   );
 };
 
-export default SlackIntegration;
+SlackIntegration.displayName = 'SlackIntegration';

+ 1 - 1
apps/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -15,7 +15,7 @@ import Accordion from '../Common/Accordion';
 
 import ManageCommandsProcess from './ManageCommandsProcess';
 import MessageBasedOnConnection from './MessageBasedOnConnection';
-import { addLogs } from './slak-integration-util';
+import { addLogs } from './slack-integration-util';
 
 const logger = loggerFactory('growi:SlackIntegration:WithProxyAccordionsWrapper');
 

+ 4 - 0
apps/app/src/components/Admin/SlackIntegration/slack-integration-util.ts

@@ -0,0 +1,4 @@
+export const addLogs = (log: string, newLogMessage:string, newLogCode?: string): string => {
+  const newLog = `${new Date()} - ${newLogCode ? `${newLogCode}, ` : ''}${newLogMessage}\n\n`;
+  return `${newLog}${log ?? ''}`;
+};

+ 0 - 20
apps/app/src/components/Admin/SlackIntegration/slak-integration-util.js

@@ -1,20 +0,0 @@
-const addLogs = (log, newLogMessage, newLogCode = undefined) => {
-
-  let newLog;
-  if (newLogCode == null) {
-    newLog = `${new Date()} - ${newLogMessage}\n\n`;
-  }
-  else {
-    newLog = `${new Date()} - ${newLogCode}, ${newLogMessage}\n\n`;
-  }
-
-  if (log == null) {
-    return newLog;
-  }
-  return `${newLog}${log}`;
-};
-
-export {
-  // eslint-disable-next-line import/prefer-default-export
-  addLogs,
-};

+ 9 - 3
apps/app/src/components/Layout/Admin.module.scss

@@ -95,9 +95,12 @@ $slack-work-space-name-card-border: #efc1f6;
       font-size: 0.6rem;
     }
     .admin-bot-card {
-      min-width: 280px;
+      min-width: 300px;
       max-width: 500px;
       border-radius: 8px !important;
+      .grw-botcard-title-active {
+        color: $gray-200;
+      }
     }
     .border-primary {
       border-width: 2px;
@@ -134,13 +137,15 @@ $slack-work-space-name-card-border: #efc1f6;
     }
 
     .grw-bridge-proxy-circle {
+      position: relative;
       .circle {
         inset: 0;
         width: 100px;
         height: 100px;
+        margin:auto;
         border: 13px solid;
-        transform: translate(-50%, -50%);
-        @include media-breakpoint-down(md) {
+
+        @include media-breakpoint-down(lg) {
           width: 50px;
           height: 50px;
           border: 8px solid;
@@ -152,6 +157,7 @@ $slack-work-space-name-card-border: #efc1f6;
       }
       .circle-inner.grw-proxy-server-name {
         margin-top: 55px;
+        transform: translate(-50%, -25%);
       }
     }
 

+ 5 - 4
apps/app/src/pages/admin/slack-integration.page.tsx

@@ -1,19 +1,20 @@
-import {
+import type {
   NextPage, GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
-import { CrowiRequest } from '~/interfaces/crowi-request';
-import { CommonProps, generateCustomTitle } from '~/pages/utils/commons';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
+import type { CommonProps } from '~/pages/utils/commons';
+import { generateCustomTitle } from '~/pages/utils/commons';
 import { useCurrentUser, useSiteUrl } from '~/stores/context';
 
 import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
-const SlackIntegration = dynamic(() => import('~/components/Admin/SlackIntegration/SlackIntegration'), { ssr: false });
+const SlackIntegration = dynamic(() => import('~/components/Admin/SlackIntegration/SlackIntegration').then(mod => mod.SlackIntegration), { ssr: false });
 const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 

+ 1 - 0
packages/slack/src/interfaces/index.ts

@@ -1,4 +1,5 @@
 export * from './channel';
+export * from './connection-status';
 export * from './growi-command-processor';
 export * from './growi-interaction-processor';
 export * from './growi-event-processor';