Sfoglia il codice sorgente

Merge pull request #3814 from weseek/imprv/refactor-conduction-at-withoutProxy

Imprv/refactor conduction at without proxy
Yuki Takei 4 anni fa
parent
commit
c7383719cf

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

@@ -263,7 +263,6 @@
     "selecting_bot_types": {
       "slack_bot": "Slack bot",
       "detailed_explanation": "Detailed explanation",
-      "selecting_bot_type": "・Select bot type",
       "official_bot": "Official bot",
       "custom_bot": "Custom bot",
       "without_proxy": "without proxy",

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

@@ -261,7 +261,6 @@
     "selecting_bot_types": {
       "slack_bot": "Slack bot",
       "detailed_explanation": "詳しい説明はこちら",
-      "selecting_bot_type": "・Botタイプを選択する",
       "official_bot": "Official bot",
       "custom_bot": "Custom bot",
       "without_proxy": "without proxy",

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

@@ -271,7 +271,6 @@
     "selecting_bot_types": {
       "slack_bot": "Slack bot",
       "detailed_explanation": "详细说明",
-      "selecting_bot_type": "・选择机器人类型",
       "official_bot": "Official bot",
       "custom_bot": "Custom bot",
       "without_proxy": "without proxy",

+ 94 - 0
src/client/js/components/Admin/SlackIntegration/Bridge.jsx

@@ -0,0 +1,94 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import PropTypes from 'prop-types';
+import { UncontrolledTooltip } from 'reactstrap';
+
+const ProxyCircle = () => (
+  <div className="grw-bridge-proxy-circle position-absolute bg-primary border-light">
+    <p className="circle-inner text-light font-weight-bold">Proxy Server</p>
+  </div>
+);
+
+const BridgeCore = (props) => {
+  const {
+    description, iconClass, hrClass, withProxy,
+  } = props;
+
+  return (
+    <>
+      <div id="grw-bridge-container" className={`grw-bridge-container ${withProxy ? 'with-proxy' : ''}`}>
+        <p className="label">
+          <i className={iconClass} />
+          <small
+            className="ml-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>
+    </>
+  );
+};
+
+BridgeCore.propTypes = {
+  description: PropTypes.string.isRequired,
+  iconClass: PropTypes.string.isRequired,
+  hrClass: PropTypes.string.isRequired,
+  withProxy: PropTypes.bool,
+};
+
+
+const Bridge = (props) => {
+  const { t } = useTranslation();
+  const { errorCount, totalCount, withProxy } = props;
+
+  let description;
+  let iconClass;
+  let hrClass;
+
+  // empty or all failed
+  if (totalCount === 0 || errorCount === totalCount) {
+    description = t('admin:slack_integration.integration_sentence.integration_is_not_complete');
+    iconClass = 'icon-info text-danger';
+    hrClass = 'border-danger admin-border-failed';
+  }
+  // all green
+  else if (errorCount === 0) {
+    description = t('admin:slack_integration.integration_sentence.integration_successful');
+    iconClass = 'fa fa-check text-success';
+    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 = 'fa fa-check text-warning';
+    hrClass = 'border-warning admin-border-failed';
+  }
+
+  return (
+    <BridgeCore
+      description={description}
+      iconClass={iconClass}
+      hrClass={hrClass}
+      withProxy={withProxy}
+    />
+  );
+};
+
+Bridge.propTypes = {
+  errorCount: PropTypes.number.isRequired,
+  totalCount: PropTypes.number.isRequired,
+  withProxy: PropTypes.bool,
+};
+
+export default Bridge;

+ 0 - 22
src/client/js/components/Admin/SlackIntegration/ConductionStatusHr.jsx

@@ -1,22 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-
-const ConductionStatusHr = (props) => {
-  const { errorCount, totalCount } = props;
-
-  return (
-    <>
-      {errorCount === 0 && totalCount !== 0 && <hr className="align-self-center border-success admin-border-success"></hr>}
-      {errorCount === totalCount && <hr className="align-self-center border-danger admin-border-danger"></hr>}
-      {errorCount >= 1 && errorCount < totalCount && <hr className="align-self-center border-warning admin-border-danger"></hr>}
-    </>
-  );
-};
-
-ConductionStatusHr.propTypes = {
-  errorCount: PropTypes.number.isRequired,
-  totalCount: PropTypes.number.isRequired,
-};
-
-export default ConductionStatusHr;

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

@@ -1,6 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import IntegrationStatus from './IntegrationStatus';
+import Bridge from './Bridge';
 
 const CustomBotWithProxyIntegrationCard = (props) => {
   const { workspaceNames } = props;
@@ -25,7 +25,7 @@ const CustomBotWithProxyIntegrationCard = (props) => {
       </div>
 
       <div className="text-center w-25 mt-3">
-        <IntegrationStatus workspaceNames={workspaceNames} />
+        <Bridge workspaceNames={workspaceNames} withProxy />
       </div>
 
       <div className="card rounded-lg shadow border-0 w-50 admin-bot-card">

+ 57 - 0
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.jsx

@@ -0,0 +1,57 @@
+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 => connectionStatusValues.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 font-weight-bold mt-3 ml-4">Slack</h5>
+        <div className="card-body p-2 w-50 mx-auto">
+          {totalCount > 0 ? '' : (
+            <div className="card slack-work-space-name-card">
+              <div className="m-2 text-center">
+                <h5 className="font-weight-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-lg shadow border-0 w-50 admin-bot-card mb-0">
+        <h5 className="card-title font-weight-bold mt-3 ml-4">GROWI App</h5>
+        <div className="card-body p-4 mb-5 text-center">
+          <div className="border p-2 bg-primary text-light mx-5">
+            {siteName}
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+CustomBotWithoutProxyConnectionStatus.propTypes = {
+  siteName: PropTypes.string.isRequired,
+  connectionStatuses: PropTypes.object.isRequired,
+};
+
+export default CustomBotWithoutProxyConnectionStatus;

+ 0 - 103
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxyIntegrationCard.jsx

@@ -1,103 +0,0 @@
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import PropTypes from 'prop-types';
-
-import { UncontrolledTooltip } from 'reactstrap';
-
-const IntegrationSuccess = () => {
-  const { t } = useTranslation();
-
-  return (
-    <>
-      <div className="d-none d-lg-block">
-        <p className="text-success small mt-5">
-          <i className="fa fa-check mr-1" />
-          {t('admin:slack_integration.integration_sentence.integration_successful')}
-        </p>
-        <hr className="align-self-center admin-border-success border-success"></hr>
-      </div>
-      <div id="integration-line-for-tooltip" className="d-block d-lg-none mt-5">
-        <i className="fa fa-check mr-1 text-success" />
-        <hr className="align-self-center admin-border-success border-success"></hr>
-      </div>
-      <UncontrolledTooltip placement="top" fade={false} target="integration-line-for-tooltip">
-        <small>
-          {t('admin:slack_integration.integration_sentence.integration_successful')}
-        </small>
-      </UncontrolledTooltip>
-    </>
-  );
-};
-
-const IntegrationFailed = () => {
-  const { t } = useTranslation();
-
-  return (
-    <>
-      <div className="d-none d-lg-block">
-        <p className="mt-4">
-          <small
-            className="text-danger m-0"
-          // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_is_not_complete') }}
-          />
-        </p>
-        <hr className="align-self-center admin-border-danger border-danger"></hr>
-
-      </div>
-      <div id="integration-line-for-tooltip" className="d-block d-lg-none mt-5">
-        <i className="icon-info text-danger" />
-        <hr className="align-self-center admin-border-danger border-danger"></hr>
-      </div>
-      <UncontrolledTooltip placement="top" fade={false} target="integration-line-for-tooltip">
-        <small
-          className="m-0"
-        // eslint-disable-next-line react/no-danger
-          dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_is_not_complete') }}
-        />
-      </UncontrolledTooltip>
-    </>
-  );
-};
-
-const CustomBotWithoutProxyIntegrationCard = (props) => {
-
-  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 font-weight-bold mt-3 ml-4">Slack</h5>
-        <div className="card-body p-2 w-50 mx-auto">
-          {props.isIntegrationSuccess && props.slackWSNameInWithoutProxy != null && (
-            <div className="card slack-work-space-name-card">
-              <div className="m-2 text-center">
-                <h5 className="font-weight-bold">{props.slackWSNameInWithoutProxy}</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">
-        {props.isIntegrationSuccess ? <IntegrationSuccess /> : <IntegrationFailed />}
-      </div>
-
-      <div className="card rounded-lg shadow border-0 w-50 admin-bot-card mb-0">
-        <h5 className="card-title font-weight-bold mt-3 ml-4">GROWI App</h5>
-        <div className="card-body p-4 mb-5 text-center">
-          <div className="border p-2 bg-primary text-light mx-5">
-            {props.siteName}
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-CustomBotWithoutProxyIntegrationCard.propTypes = {
-  siteName: PropTypes.string.isRequired,
-  slackWSNameInWithoutProxy: PropTypes.string,
-  isIntegrationSuccess: PropTypes.bool,
-};
-
-export default CustomBotWithoutProxyIntegrationCard;

+ 8 - 7
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx

@@ -5,11 +5,11 @@ import AppContainer from '../../../services/AppContainer';
 import AdminAppContainer from '../../../services/AdminAppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import CustomBotWithoutProxySettingsAccordion, { botInstallationStep } from './CustomBotWithoutProxySettingsAccordion';
-import CustomBotWithoutProxyIntegrationCard from './CustomBotWithoutProxyIntegrationCard';
+import CustomBotWithoutProxyConnectionStatus from './CustomBotWithoutProxyConnectionStatus';
 import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
 
 const CustomBotWithoutProxySettings = (props) => {
-  const { appContainer, onResetSettings } = props;
+  const { appContainer, onResetSettings, connectionStatuses } = props;
   const { t } = useTranslation();
 
   const [siteName, setSiteName] = useState('');
@@ -54,10 +54,9 @@ const CustomBotWithoutProxySettings = (props) => {
     <>
       <h2 className="admin-setting-header">{t('admin:slack_integration.custom_bot_without_proxy_integration')}</h2>
 
-      <CustomBotWithoutProxyIntegrationCard
+      <CustomBotWithoutProxyConnectionStatus
         siteName={siteName}
-        slackWSNameInWithoutProxy={props.slackWSNameInWithoutProxy}
-        isIntegrationSuccess={isIntegrationSuccess}
+        connectionStatuses={connectionStatuses}
       />
 
       <h2 className="admin-setting-header">{t('admin:slack_integration.integration_procedure')}</h2>
@@ -70,8 +69,8 @@ const CustomBotWithoutProxySettings = (props) => {
       >{t('admin:slack_integration.reset')}
       </button>
       )}
-
       <div className="my-5 mx-3">
+        {/* {isConnectedFailed && (<>Settings #1 <span className="text-danger">{t('admin:slack_integration.integration_failed')}</span></>)} */}
         <CustomBotWithoutProxySettingsAccordion
           {...props}
           activeStep={botInstallationStep.CREATE_BOT}
@@ -81,7 +80,6 @@ const CustomBotWithoutProxySettings = (props) => {
           testChannel={testChannel}
           onTestFormSubmitted={testConnection}
           inputTestChannelHandler={inputTestChannelHandler}
-
         />
       </div>
       <DeleteSlackBotSettingsModal
@@ -99,14 +97,17 @@ const CustomBotWithoutProxySettingsWrapper = withUnstatedContainers(CustomBotWit
 CustomBotWithoutProxySettings.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminAppContainer: PropTypes.instanceOf(AdminAppContainer).isRequired,
+
   slackSigningSecret: PropTypes.string,
   slackSigningSecretEnv: PropTypes.string,
   slackBotToken: PropTypes.string,
   slackBotTokenEnv: PropTypes.string,
+
   isRgisterSlackCredentials: PropTypes.bool,
   isIntegrationSuccess: PropTypes.bool,
   slackWSNameInWithoutProxy: PropTypes.string,
   onResetSettings: PropTypes.func,
+  connectionStatuses: PropTypes.object.isRequired,
 };
 
 export default CustomBotWithoutProxySettingsWrapper;

+ 0 - 176
src/client/js/components/Admin/SlackIntegration/IntegrationStatus.jsx

@@ -1,176 +0,0 @@
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import PropTypes from 'prop-types';
-import { UncontrolledTooltip } from 'reactstrap';
-
-import ConductionStatusHr from './ConductionStatusHr';
-
-const IntegrationSuccess = (props) => {
-  const { t } = useTranslation();
-  const { errorCount, totalCount } = props;
-
-  return (
-    <>
-      <div className="d-none d-lg-block">
-        <p className="text-success small">
-          <i className="fa fa-check mr-1" />
-          {t('admin:slack_integration.integration_sentence.integration_successful')}
-        </p>
-        <div className="pt-2">
-          <div className="position-relative mt-5">
-            <div className="circle position-absolute bg-primary border-light">
-              <p className="circle-inner text-light font-weight-bold">Proxy Server</p>
-            </div>
-          </div>
-        </div>
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <div id="integration-line-for-tooltip" className="d-block d-lg-none mt-5">
-        <i className="fa fa-check mr-1 text-success" />
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <UncontrolledTooltip placement="top" fade={false} target="integration-line-for-tooltip">
-        <small>
-          {t('admin:slack_integration.integration_sentence.integration_successful')}
-        </small>
-      </UncontrolledTooltip>
-    </>
-  );
-};
-
-IntegrationSuccess.propTypes = {
-  errorCount: PropTypes.number.isRequired,
-  totalCount: PropTypes.number.isRequired,
-};
-
-
-const IntegrationFailed = (props) => {
-  const { t } = useTranslation();
-  const { errorCount, totalCount } = props;
-
-  return (
-    <>
-      <div className="d-none d-lg-block">
-        <p className="mt-4">
-          <small
-            className="text-danger m-0"
-          // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_is_not_complete') }}
-          />
-        </p>
-        <div className="pt-2">
-          <div className="position-relative mt-5">
-            <div className="circle position-absolute bg-primary border-light">
-              <p className="circle-inner text-light font-weight-bold">Proxy Server</p>
-            </div>
-          </div>
-        </div>
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <div id="integration-line-for-tooltip" className="d-block d-lg-none mt-5">
-        <i className="icon-info text-danger" />
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <UncontrolledTooltip placement="top" fade={false} target="integration-line-for-tooltip">
-        <small
-          className="m-0"
-        // eslint-disable-next-line react/no-danger
-          dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_is_not_complete') }}
-        />
-      </UncontrolledTooltip>
-    </>
-  );
-};
-
-IntegrationFailed.propTypes = {
-  errorCount: PropTypes.number.isRequired,
-  totalCount: PropTypes.number.isRequired,
-
-};
-
-
-const SomeWorkSpacesNotIntegration = (props) => {
-  const { t } = useTranslation();
-  const { errorCount, totalCount } = props;
-
-  return (
-    <>
-      <div className="d-none d-lg-block">
-        <p className="mt-4">
-          <small
-            className="text-danger m-0"
-          // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_some_ws_is_not_complete') }}
-          />
-        </p>
-        <div className="pt-2">
-          <div className="position-relative mt-5">
-            <div className="circle position-absolute bg-primary border-light">
-              <p className="circle-inner text-light font-weight-bold">Proxy Server</p>
-            </div>
-          </div>
-        </div>
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <div id="integration-line-for-tooltip" className="d-block d-lg-none mt-5">
-        <i className="icon-info text-danger" />
-        <ConductionStatusHr errorCount={errorCount} totalCount={totalCount} />
-      </div>
-      <UncontrolledTooltip placement="top" fade={false} target="integration-line-for-tooltip">
-        <small
-          className="m-0"
-        // eslint-disable-next-line react/no-danger
-          dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.integration_sentence.integration_some_ws_is_not_complete') }}
-        />
-      </UncontrolledTooltip>
-    </>
-  );
-};
-
-SomeWorkSpacesNotIntegration.propTypes = {
-  errorCount: PropTypes.number.isRequired,
-  totalCount: PropTypes.number.isRequired,
-
-};
-
-
-const IntegrationStatus = (props) => {
-  const { workspaceNames } = props;
-
-  let errorCount = 0;
-  workspaceNames.forEach((w) => {
-    if (w == null) {
-      errorCount++;
-    }
-  });
-
-  return (
-    <>
-      {errorCount === 0 && workspaceNames.length !== 0 && (
-      <IntegrationSuccess
-        errorCount={errorCount}
-        totalCount={workspaceNames.length}
-      />
-      )}
-      {errorCount === workspaceNames.length && (
-      <IntegrationFailed
-        errorCount={errorCount}
-        totalCount={workspaceNames.length}
-      />
-      )}
-
-      {errorCount >= 1 && errorCount < workspaceNames.length && (
-      <SomeWorkSpacesNotIntegration
-        errorCount={errorCount}
-        totalCount={workspaceNames.length}
-      />
-      )}
-    </>
-  );
-};
-
-IntegrationStatus.propTypes = {
-  workspaceNames: PropTypes.array.isRequired,
-};
-
-export default IntegrationStatus;

+ 9 - 19
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -41,12 +41,7 @@ const SlackIntegration = (props) => {
       if (data.connectionStatuses == null) {
         data.connectionStatuses = {};
       }
-      // if (data.connectionStatuses != null) {
-      // TODO fix
-      // const { workspaceName } = data.connectionStatuses[slackBotToken];
-      // setSlackWSNameInWithoutProxy(workspaceName);
-      // setConnectionStatuses(data.connectionStatuses);
-      // }
+
       setConnectionStatuses(data.connectionStatuses);
       setCurrentBotType(data.currentBotType);
       setSlackSigningSecret(slackSigningSecret);
@@ -160,6 +155,7 @@ const SlackIntegration = (props) => {
           onSetSlackBotToken={setSlackBotToken}
           onResetSettings={resetWithOutSettings}
           fetchSlackIntegrationData={fetchSlackIntegrationData}
+          connectionStatuses={connectionStatuses}
         />
       );
       break;
@@ -201,19 +197,13 @@ const SlackIntegration = (props) => {
           </a>
         </h2>
 
-        <div className="d-flex justify-content">
-          <div className="mr-auto">
-            {t('admin:slack_integration.selecting_bot_types.selecting_bot_type')}
-          </div>
-
-          {(currentBotType != null) && (
-            <button
-              className="mx-3 btn btn-outline-danger flex-end"
-              type="button"
-              onClick={() => setIsDeleteConfirmModalShown(true)}
-            >{t('admin:slack_integration.reset_all_settings')}
-            </button>
-          )}
+        <div className="d-flex justify-content-end">
+          <button
+            className="btn btn-outline-danger"
+            type="button"
+            onClick={() => setIsDeleteConfirmModalShown(true)}
+          >{t('admin:slack_integration.reset_all_settings')}
+          </button>
         </div>
 
         <div className="row my-5 flex-wrap-reverse justify-content-center">

+ 27 - 11
src/client/styles/scss/_admin.scss

@@ -122,31 +122,47 @@ $slack-work-space-name-card-border: #efc1f6;
     .admin-bot-card {
       border-radius: 8px !important;
     }
-    .admin-border-danger {
+    .admin-border-failed {
       border-style: dashed;
       border-width: 2px;
     }
     .admin-border-success {
       border-width: 3px;
     }
-    .circle {
-      top: 50%;
+
+    .grw-bridge-proxy-circle {
       left: 50%;
       width: 100px;
       height: 100px;
       border: 13px solid;
       border-radius: 50%;
-      -webkit-transform: translate(-50%, -50%);
-      -ms-transform: translate(-50%, -50%);
       transform: translate(-50%, -50%);
+
+      .circle-inner {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+      }
     }
-    .circle-inner {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      -webkit-transform: translate(-50%, -50%);
-      transform: translate(-50%, -50%);
+
+    // switch layout for Bridge component
+    .grw-bridge-container {
+      .label {
+        @extend .mt-5;
+      }
+
+      // with ProxyCircle
+      &.with-proxy {
+        .label {
+          @extend .mt-0;
+        }
+        .hr-container {
+          margin-top: 65px;
+        }
+      }
     }
+
     .slack-work-space-name-card {
       background-color: $slack-work-space-name-card-background;
       border: 1px solid $slack-work-space-name-card-border;