OfficialBotSettings.jsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import React, { useState, useEffect, useCallback } 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 { toastSuccess, toastError } from '~/client/util/toastr';
  7. import { useAppTitle } from '~/stores/context';
  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, onPrimaryUpdated,
  18. connectionStatuses, onUpdateTokens, onSubmitForm,
  19. } = props;
  20. const [siteName, setSiteName] = useState('');
  21. const [integrationIdToDelete, setIntegrationIdToDelete] = useState(null);
  22. const { t } = useTranslation();
  23. const { data: appTitle } = useAppTitle();
  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', ns: 'commons' }));
  40. }
  41. catch (err) {
  42. toastError(err);
  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. setSiteName(appTitle);
  61. }, [appTitle]);
  62. return (
  63. <>
  64. <h2 className="admin-setting-header">{t('admin:slack_integration.official_bot_integration')}
  65. <a href={t('admin:slack_integration.docs_url.official_bot')} target="_blank" rel="noopener noreferrer">
  66. <i
  67. className="fa fa-external-link btn-link ml-2"
  68. aria-hidden="true"
  69. onClick={() => window.open(`${t('admin:slack_integration.docs_url.official_bot')}`, '_blank')}
  70. />
  71. </a>
  72. </h2>
  73. {slackAppIntegrations.length !== 0 && (
  74. <>
  75. <CustomBotWithProxyConnectionStatus
  76. siteName={siteName}
  77. connectionStatuses={connectionStatuses}
  78. />
  79. <h2 className="admin-setting-header">{t('admin:slack_integration.integration_procedure')}</h2>
  80. </>
  81. )}
  82. <div className="mx-3">
  83. {slackAppIntegrations.map((slackAppIntegration, i) => {
  84. const {
  85. tokenGtoP, tokenPtoG, _id, permissionsForBroadcastUseCommands, permissionsForSingleUseCommands, permissionsForSlackEventActions,
  86. } = slackAppIntegration;
  87. const workspaceName = connectionStatuses[_id]?.workspaceName;
  88. return (
  89. <React.Fragment key={slackAppIntegration._id}>
  90. <div className="my-3 d-flex align-items-center justify-content-between">
  91. <h2 id={_id || `settings-accordions-${i}`}>
  92. {(workspaceName != null) ? `${workspaceName} Work Space` : `Settings #${i}`}
  93. </h2>
  94. <SlackAppIntegrationControl
  95. slackAppIntegration={slackAppIntegration}
  96. onIsPrimaryChanged={isPrimaryChangedHandler}
  97. // set state to open DeleteSlackBotSettingsModal
  98. onDeleteButtonClicked={saiToDelete => setIntegrationIdToDelete(saiToDelete._id)}
  99. />
  100. </div>
  101. <WithProxyAccordions
  102. botType={SlackbotType.OFFICIAL}
  103. slackAppIntegrationId={slackAppIntegration._id}
  104. tokenGtoP={tokenGtoP}
  105. tokenPtoG={tokenPtoG}
  106. permissionsForBroadcastUseCommands={permissionsForBroadcastUseCommands}
  107. permissionsForSingleUseCommands={permissionsForSingleUseCommands}
  108. permissionsForSlackEventActions={permissionsForSlackEventActions}
  109. onUpdateTokens={onUpdateTokens}
  110. onSubmitForm={onSubmitForm}
  111. />
  112. </React.Fragment>
  113. );
  114. })}
  115. <div className="row justify-content-center my-5">
  116. <button
  117. type="button"
  118. className="btn btn-outline-primary"
  119. onClick={addSlackAppIntegrationHandler}
  120. >
  121. {`+ ${t('admin:slack_integration.accordion.add_slack_workspace')}`}
  122. </button>
  123. </div>
  124. </div>
  125. <DeleteSlackBotSettingsModal
  126. isResetAll={false}
  127. isOpen={integrationIdToDelete != null}
  128. onClose={() => setIntegrationIdToDelete(null)}
  129. onClickDeleteButton={deleteSlackAppIntegrationHandler}
  130. />
  131. </>
  132. );
  133. };
  134. OfficialBotSettings.defaultProps = {
  135. slackAppIntegrations: [],
  136. };
  137. OfficialBotSettings.propTypes = {
  138. slackAppIntegrations: PropTypes.array,
  139. onClickAddSlackWorkspaceBtn: PropTypes.func,
  140. onPrimaryUpdated: PropTypes.func,
  141. onDeleteSlackAppIntegration: PropTypes.func,
  142. connectionStatuses: PropTypes.object.isRequired,
  143. onUpdateTokens: PropTypes.func,
  144. onSubmitForm: PropTypes.func,
  145. };
  146. export default OfficialBotSettings;