OfficialBotSettings.jsx 6.2 KB

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