SlackIntegration.jsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import React, { useState, useEffect, useCallback } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { useTranslation } from 'react-i18next';
  4. import AppContainer from '../../../services/AppContainer';
  5. import { withUnstatedContainers } from '../../UnstatedUtils';
  6. import { toastSuccess, toastError } from '../../../util/apiNotification';
  7. import OfficialBotSettings from './OfficialBotSettings';
  8. import CustomBotWithoutProxySettings from './CustomBotWithoutProxySettings';
  9. import CustomBotWithProxySettings from './CustomBotWithProxySettings';
  10. import ConfirmBotChangeModal from './ConfirmBotChangeModal';
  11. import BotTypeCard from './BotTypeCard';
  12. import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
  13. const botTypes = ['officialBot', 'customBotWithoutProxy', 'customBotWithProxy'];
  14. const SlackIntegration = (props) => {
  15. const { appContainer } = props;
  16. const { t } = useTranslation();
  17. const [currentBotType, setCurrentBotType] = useState(null);
  18. const [selectedBotType, setSelectedBotType] = useState(null);
  19. const [slackSigningSecret, setSlackSigningSecret] = useState(null);
  20. const [slackBotToken, setSlackBotToken] = useState(null);
  21. const [slackSigningSecretEnv, setSlackSigningSecretEnv] = useState('');
  22. const [slackBotTokenEnv, setSlackBotTokenEnv] = useState('');
  23. const [isDeleteConfirmModalShown, setIsDeleteConfirmModalShown] = useState(false);
  24. const [slackAppIntegrations, setSlackAppIntegrations] = useState();
  25. const [proxyServerUri, setProxyServerUri] = useState();
  26. const [connectionStatuses, setConnectionStatuses] = useState({});
  27. const fetchSlackIntegrationData = useCallback(async() => {
  28. try {
  29. const { data } = await appContainer.apiv3.get('/slack-integration-settings');
  30. const {
  31. slackSigningSecret, slackBotToken, slackSigningSecretEnvVars, slackBotTokenEnvVars, slackAppIntegrations, proxyServerUri,
  32. } = data.settings;
  33. setConnectionStatuses(data.connectionStatuses);
  34. setCurrentBotType(data.currentBotType);
  35. setSlackSigningSecret(slackSigningSecret);
  36. setSlackBotToken(slackBotToken);
  37. setSlackSigningSecretEnv(slackSigningSecretEnvVars);
  38. setSlackBotTokenEnv(slackBotTokenEnvVars);
  39. setSlackAppIntegrations(slackAppIntegrations);
  40. setProxyServerUri(proxyServerUri);
  41. }
  42. catch (err) {
  43. toastError(err);
  44. }
  45. }, [appContainer.apiv3]);
  46. const resetAllSettings = async() => {
  47. try {
  48. await appContainer.apiv3.delete('/slack-integration-settings/bot-type');
  49. fetchSlackIntegrationData();
  50. toastSuccess(t('admin:slack_integration.bot_all_reset_successful'));
  51. }
  52. catch (error) {
  53. toastError(error);
  54. }
  55. };
  56. const createSlackIntegrationData = async() => {
  57. try {
  58. await appContainer.apiv3.put('/slack-integration-settings/slack-app-integrations');
  59. fetchSlackIntegrationData();
  60. toastSuccess(t('admin:slack_integration.adding_slack_ws_integration_settings_successful'));
  61. }
  62. catch (error) {
  63. toastError(error);
  64. }
  65. };
  66. const changeSecretAndToken = (secret, token) => {
  67. setSlackSigningSecret(secret);
  68. setSlackBotToken(token);
  69. };
  70. useEffect(() => {
  71. fetchSlackIntegrationData();
  72. }, [fetchSlackIntegrationData]);
  73. const changeCurrentBotSettings = async(botType) => {
  74. try {
  75. await appContainer.apiv3.put('/slack-integration-settings/bot-type', {
  76. currentBotType: botType,
  77. });
  78. setSelectedBotType(null);
  79. fetchSlackIntegrationData();
  80. }
  81. catch (err) {
  82. toastError(err);
  83. }
  84. };
  85. const botTypeSelectHandler = async(botType) => {
  86. if (botType === currentBotType) {
  87. return;
  88. }
  89. if (currentBotType == null) {
  90. return changeCurrentBotSettings(botType);
  91. }
  92. setSelectedBotType(botType);
  93. };
  94. const changeCurrentBotSettingsHandler = async() => {
  95. changeCurrentBotSettings(selectedBotType);
  96. toastSuccess(t('admin:slack_integration.bot_reset_successful'));
  97. };
  98. const cancelBotChangeHandler = () => {
  99. setSelectedBotType(null);
  100. };
  101. let settingsComponent = null;
  102. switch (currentBotType) {
  103. case 'officialBot':
  104. settingsComponent = (
  105. <OfficialBotSettings
  106. slackAppIntegrations={slackAppIntegrations}
  107. proxyServerUri={proxyServerUri}
  108. onClickAddSlackWorkspaceBtn={createSlackIntegrationData}
  109. onDeleteSlackAppIntegration={fetchSlackIntegrationData}
  110. connectionStatuses={connectionStatuses}
  111. onUpdateTokens={fetchSlackIntegrationData}
  112. onSubmitForm={fetchSlackIntegrationData}
  113. />
  114. );
  115. break;
  116. case 'customBotWithoutProxy':
  117. settingsComponent = (
  118. <CustomBotWithoutProxySettings
  119. slackBotTokenEnv={slackBotTokenEnv}
  120. slackBotToken={slackBotToken}
  121. slackSigningSecretEnv={slackSigningSecretEnv}
  122. slackSigningSecret={slackSigningSecret}
  123. onTestConnectionInvoked={fetchSlackIntegrationData}
  124. onUpdatedSecretToken={changeSecretAndToken}
  125. connectionStatuses={connectionStatuses}
  126. />
  127. );
  128. break;
  129. case 'customBotWithProxy':
  130. settingsComponent = (
  131. <CustomBotWithProxySettings
  132. slackAppIntegrations={slackAppIntegrations}
  133. proxyServerUri={proxyServerUri}
  134. onClickAddSlackWorkspaceBtn={createSlackIntegrationData}
  135. onDeleteSlackAppIntegration={fetchSlackIntegrationData}
  136. connectionStatuses={connectionStatuses}
  137. onUpdateTokens={fetchSlackIntegrationData}
  138. onSubmitForm={fetchSlackIntegrationData}
  139. />
  140. );
  141. break;
  142. }
  143. return (
  144. <>
  145. <ConfirmBotChangeModal
  146. isOpen={selectedBotType != null}
  147. onConfirmClick={changeCurrentBotSettingsHandler}
  148. onCancelClick={cancelBotChangeHandler}
  149. />
  150. <DeleteSlackBotSettingsModal
  151. isResetAll
  152. isOpen={isDeleteConfirmModalShown}
  153. onClose={() => setIsDeleteConfirmModalShown(false)}
  154. onClickDeleteButton={resetAllSettings}
  155. />
  156. <div className="selecting-bot-type mb-5">
  157. <h2 className="admin-setting-header mb-4">
  158. {t('admin:slack_integration.selecting_bot_types.slack_bot')}
  159. {/* TODO: add an appropriate link by GW-5614 */}
  160. <a className="ml-2 btn-link" href="#">
  161. {t('admin:slack_integration.selecting_bot_types.detailed_explanation')}
  162. <i className="fa fa-external-link ml-1" aria-hidden="true"></i>
  163. </a>
  164. </h2>
  165. <div className="d-flex justify-content-end">
  166. <button
  167. className="btn btn-outline-danger"
  168. type="button"
  169. onClick={() => setIsDeleteConfirmModalShown(true)}
  170. >{t('admin:slack_integration.reset_all_settings')}
  171. </button>
  172. </div>
  173. <div className="row my-5 flex-wrap-reverse justify-content-center">
  174. {botTypes.map((botType) => {
  175. return (
  176. <div key={botType} className="m-3">
  177. <BotTypeCard
  178. botType={botType}
  179. isActive={currentBotType === botType}
  180. onBotTypeSelectHandler={botTypeSelectHandler}
  181. />
  182. </div>
  183. );
  184. })}
  185. </div>
  186. </div>
  187. {settingsComponent}
  188. </>
  189. );
  190. };
  191. const SlackIntegrationWrapper = withUnstatedContainers(SlackIntegration, [AppContainer]);
  192. SlackIntegration.propTypes = {
  193. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  194. };
  195. export default SlackIntegrationWrapper;