OfficialBotSettings.jsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import React, { useState, useEffect, useCallback } from 'react';
  2. import { SlackbotType } from '@growi/slack';
  3. import PropTypes from 'prop-types';
  4. import { useTranslation } from 'react-i18next';
  5. import AppContainer from '~/client/services/AppContainer';
  6. import { toastSuccess, toastError } from '~/client/util/apiNotification';
  7. import { apiv3Delete, apiv3Put } from '~/client/util/apiv3-client';
  8. import loggerFactory from '~/utils/logger';
  9. import { withUnstatedContainers } from '../../UnstatedUtils';
  10. import CustomBotWithProxyConnectionStatus from './CustomBotWithProxyConnectionStatus';
  11. import DeleteSlackBotSettingsModal from './DeleteSlackBotSettingsModal';
  12. import { SlackAppIntegrationControl } from './SlackAppIntegrationControl';
  13. import WithProxyAccordions from './WithProxyAccordions';
  14. const logger = loggerFactory('growi:cli:SlackIntegration:OfficialBotSettings');
  15. const OfficialBotSettings = (props) => {
  16. const {
  17. appContainer, slackAppIntegrations,
  18. onClickAddSlackWorkspaceBtn, onPrimaryUpdated,
  19. connectionStatuses, onUpdateTokens, onSubmitForm,
  20. } = props;
  21. const [siteName, setSiteName] = useState('');
  22. const [integrationIdToDelete, setIntegrationIdToDelete] = useState(null);
  23. const { t } = useTranslation();
  24. const addSlackAppIntegrationHandler = async() => {
  25. if (onClickAddSlackWorkspaceBtn != null) {
  26. onClickAddSlackWorkspaceBtn();
  27. }
  28. };
  29. const isPrimaryChangedHandler = useCallback(async(slackIntegrationToChange, newValue) => {
  30. // do nothing when turning off
  31. if (!newValue) {
  32. return;
  33. }
  34. try {
  35. await apiv3Put(`/slack-integration-settings/slack-app-integrations/${slackIntegrationToChange._id}/make-primary`);
  36. if (onPrimaryUpdated != null) {
  37. onPrimaryUpdated();
  38. }
  39. toastSuccess(t('toaster.update_successed', { target: 'Primary' }));
  40. }
  41. catch (err) {
  42. toastError(err, 'Failed to change isPrimary');
  43. logger.error('Failed to change isPrimary', err);
  44. }
  45. }, [t, onPrimaryUpdated]);
  46. const deleteSlackAppIntegrationHandler = async() => {
  47. await apiv3Delete(`/slack-integration-settings/slack-app-integrations/${integrationIdToDelete}`);
  48. try {
  49. if (props.onDeleteSlackAppIntegration != null) {
  50. props.onDeleteSlackAppIntegration();
  51. }
  52. toastSuccess(t('admin:slack_integration.toastr.delete_slack_integration_procedure'));
  53. }
  54. catch (err) {
  55. toastError('Failed to delete');
  56. logger.error('Failed to delete', err);
  57. }
  58. };
  59. useEffect(() => {
  60. const siteName = appContainer.config.crowi.title;
  61. setSiteName(siteName);
  62. }, [appContainer]);
  63. return (
  64. <>
  65. <h2 className="admin-setting-header">{t('admin:slack_integration.official_bot_integration')}
  66. <a href={t('admin:slack_integration.docs_url.official_bot')} target="_blank" rel="noopener noreferrer">
  67. <i
  68. className="fa fa-external-link btn-link ml-2"
  69. aria-hidden="true"
  70. onClick={() => window.open(`${t('admin:slack_integration.docs_url.official_bot')}`, '_blank')}
  71. />
  72. </a>
  73. </h2>
  74. {slackAppIntegrations.length !== 0 && (
  75. <>
  76. <CustomBotWithProxyConnectionStatus
  77. siteName={siteName}
  78. connectionStatuses={connectionStatuses}
  79. />
  80. <h2 className="admin-setting-header">{t('admin:slack_integration.integration_procedure')}</h2>
  81. </>
  82. )}
  83. <div className="mx-3">
  84. {slackAppIntegrations.map((slackAppIntegration, i) => {
  85. const {
  86. tokenGtoP, tokenPtoG, _id, permissionsForBroadcastUseCommands, permissionsForSingleUseCommands, permissionsForSlackEventActions,
  87. } = slackAppIntegration;
  88. const workspaceName = connectionStatuses[_id]?.workspaceName;
  89. return (
  90. <React.Fragment key={slackAppIntegration._id}>
  91. <div className="my-3 d-flex align-items-center justify-content-between">
  92. <h2 id={_id || `settings-accordions-${i}`}>
  93. {(workspaceName != null) ? `${workspaceName} Work Space` : `Settings #${i}`}
  94. </h2>
  95. <SlackAppIntegrationControl
  96. slackAppIntegration={slackAppIntegration}
  97. onIsPrimaryChanged={isPrimaryChangedHandler}
  98. // set state to open DeleteSlackBotSettingsModal
  99. onDeleteButtonClicked={saiToDelete => setIntegrationIdToDelete(saiToDelete._id)}
  100. />
  101. </div>
  102. <WithProxyAccordions
  103. botType={SlackbotType.OFFICIAL}
  104. slackAppIntegrationId={slackAppIntegration._id}
  105. tokenGtoP={tokenGtoP}
  106. tokenPtoG={tokenPtoG}
  107. permissionsForBroadcastUseCommands={permissionsForBroadcastUseCommands}
  108. permissionsForSingleUseCommands={permissionsForSingleUseCommands}
  109. permissionsForSlackEventActions={permissionsForSlackEventActions}
  110. onUpdateTokens={onUpdateTokens}
  111. onSubmitForm={onSubmitForm}
  112. />
  113. </React.Fragment>
  114. );
  115. })}
  116. <div className="row justify-content-center my-5">
  117. <button
  118. type="button"
  119. className="btn btn-outline-primary"
  120. onClick={addSlackAppIntegrationHandler}
  121. >
  122. {`+ ${t('admin:slack_integration.accordion.add_slack_workspace')}`}
  123. </button>
  124. </div>
  125. </div>
  126. <DeleteSlackBotSettingsModal
  127. isResetAll={false}
  128. isOpen={integrationIdToDelete != null}
  129. onClose={() => setIntegrationIdToDelete(null)}
  130. onClickDeleteButton={deleteSlackAppIntegrationHandler}
  131. />
  132. </>
  133. );
  134. };
  135. const OfficialBotSettingsWrapper = withUnstatedContainers(OfficialBotSettings, [AppContainer]);
  136. OfficialBotSettings.defaultProps = {
  137. slackAppIntegrations: [],
  138. };
  139. OfficialBotSettings.propTypes = {
  140. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  141. slackAppIntegrations: PropTypes.array,
  142. onClickAddSlackWorkspaceBtn: PropTypes.func,
  143. onPrimaryUpdated: PropTypes.func,
  144. onDeleteSlackAppIntegration: PropTypes.func,
  145. connectionStatuses: PropTypes.object.isRequired,
  146. onUpdateTokens: PropTypes.func,
  147. onSubmitForm: PropTypes.func,
  148. };
  149. export default OfficialBotSettingsWrapper;