CustomBotWithProxySettings.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import PropTypes from 'prop-types';
  4. import { apiv3Delete, apiv3Put } from '~/client/util/apiv3-client';
  5. import { toastError, toastSuccess } from '~/client/util/toastr';
  6. import { useAppTitle } from '~/states/global';
  7. import loggerFactory from '~/utils/logger';
  8. import { CustomBotWithProxyConnectionStatus } from './CustomBotWithProxyConnectionStatus';
  9. import { DeleteSlackBotSettingsModal } from './DeleteSlackBotSettingsModal';
  10. import { SlackAppIntegrationControl } from './SlackAppIntegrationControl';
  11. import WithProxyAccordions from './WithProxyAccordions';
  12. const logger = loggerFactory(
  13. 'growi:cli:SlackIntegration:CustomBotWithProxySettings',
  14. );
  15. const CustomBotWithProxySettings = (props) => {
  16. const {
  17. slackAppIntegrations,
  18. proxyServerUri,
  19. onClickAddSlackWorkspaceBtn,
  20. onPrimaryUpdated,
  21. connectionStatuses,
  22. onUpdateTokens,
  23. onSubmitForm,
  24. } = props;
  25. const [newProxyServerUri, setNewProxyServerUri] = useState();
  26. const [integrationIdToDelete, setIntegrationIdToDelete] = useState(null);
  27. const [siteName, setSiteName] = useState('');
  28. const { t } = useTranslation();
  29. const appTitle = useAppTitle();
  30. // componentDidUpdate
  31. useEffect(() => {
  32. setNewProxyServerUri(proxyServerUri);
  33. }, [proxyServerUri]);
  34. const addSlackAppIntegrationHandler = async () => {
  35. if (onClickAddSlackWorkspaceBtn != null) {
  36. onClickAddSlackWorkspaceBtn();
  37. }
  38. };
  39. const isPrimaryChangedHandler = useCallback(
  40. async (slackIntegrationToChange, newValue) => {
  41. // do nothing when turning off
  42. if (!newValue) {
  43. return;
  44. }
  45. try {
  46. await apiv3Put(
  47. `/slack-integration-settings/slack-app-integrations/${slackIntegrationToChange._id}/make-primary`,
  48. );
  49. if (onPrimaryUpdated != null) {
  50. onPrimaryUpdated();
  51. }
  52. toastSuccess(
  53. t('toaster.update_successed', { target: 'Primary', ns: 'commons' }),
  54. );
  55. } catch (err) {
  56. toastError(err);
  57. logger.error('Failed to change isPrimary', err);
  58. }
  59. },
  60. [t, onPrimaryUpdated],
  61. );
  62. const deleteSlackAppIntegrationHandler = async () => {
  63. try {
  64. await apiv3Delete(
  65. `/slack-integration-settings/slack-app-integrations/${integrationIdToDelete}`,
  66. );
  67. if (props.onDeleteSlackAppIntegration != null) {
  68. props.onDeleteSlackAppIntegration();
  69. }
  70. toastSuccess(
  71. t('admin:slack_integration.toastr.delete_slack_integration_procedure'),
  72. );
  73. } catch (err) {
  74. toastError(err);
  75. logger.error('Failed to delete', err);
  76. }
  77. };
  78. const updateProxyUri = async () => {
  79. try {
  80. await apiv3Put('/slack-integration-settings/proxy-uri', {
  81. proxyUri: newProxyServerUri,
  82. });
  83. toastSuccess(
  84. t('toaster.update_successed', { target: 'Proxy URL', ns: 'commons' }),
  85. );
  86. } catch (err) {
  87. toastError(err);
  88. logger.error('Failed to update', err);
  89. }
  90. };
  91. useEffect(() => {
  92. setSiteName(appTitle);
  93. }, [appTitle]);
  94. return (
  95. <>
  96. <h2 className="admin-setting-header mb-2">
  97. {t('admin:slack_integration.custom_bot_with_proxy_integration')}
  98. <a
  99. href={t('admin:slack_integration.docs_url.custom_bot_with_proxy')}
  100. target="_blank"
  101. rel="noopener noreferrer"
  102. >
  103. <span className="growi-custom-icons btn-link ms-2">
  104. external_link
  105. </span>
  106. </a>
  107. </h2>
  108. {slackAppIntegrations.length !== 0 && (
  109. <>
  110. <CustomBotWithProxyConnectionStatus
  111. siteName={siteName}
  112. connectionStatuses={connectionStatuses}
  113. />
  114. <div className="row my-4">
  115. <label
  116. className="text-start text-md-end col-md-3 col-form-label mt-3"
  117. htmlFor="admin-slack-proxy-url"
  118. >
  119. Proxy URL
  120. </label>
  121. <div className="col-md-6 mt-3">
  122. <input
  123. className="form-control"
  124. type="text"
  125. name="settingForm[proxyUrl]"
  126. id="admin-slack-proxy-url"
  127. value={newProxyServerUri}
  128. onChange={(e) => {
  129. setNewProxyServerUri(e.target.value);
  130. }}
  131. />
  132. </div>
  133. <div className="col-md-2 mt-3 text-center text-md-start">
  134. <button
  135. type="button"
  136. className="btn btn-primary"
  137. onClick={updateProxyUri}
  138. >
  139. {t('Update')}
  140. </button>
  141. </div>
  142. </div>
  143. <h2 className="admin-setting-header">
  144. {t('admin:slack_integration.integration_procedure')}
  145. </h2>
  146. </>
  147. )}
  148. <div className="mx-3">
  149. {slackAppIntegrations.map((slackAppIntegration, i) => {
  150. const {
  151. tokenGtoP,
  152. tokenPtoG,
  153. _id,
  154. permissionsForBroadcastUseCommands,
  155. permissionsForSingleUseCommands,
  156. permissionsForSlackEventActions,
  157. } = slackAppIntegration;
  158. const workspaceName = connectionStatuses[_id]?.workspaceName;
  159. return (
  160. <React.Fragment key={slackAppIntegration._id}>
  161. <div className="my-3 d-flex align-items-center justify-content-between">
  162. <h2 id={_id || `settings-accordions-${i}`}>
  163. {workspaceName != null
  164. ? `${workspaceName} Work Space`
  165. : `Settings #${i}`}
  166. </h2>
  167. <SlackAppIntegrationControl
  168. slackAppIntegration={slackAppIntegration}
  169. onIsPrimaryChanged={isPrimaryChangedHandler}
  170. // set state to open DeleteSlackBotSettingsModal
  171. onDeleteButtonClicked={(saiToDelete) =>
  172. setIntegrationIdToDelete(saiToDelete._id)
  173. }
  174. />
  175. </div>
  176. <WithProxyAccordions
  177. botType="customBotWithProxy"
  178. slackAppIntegrationId={slackAppIntegration._id}
  179. tokenGtoP={tokenGtoP}
  180. tokenPtoG={tokenPtoG}
  181. permissionsForBroadcastUseCommands={
  182. permissionsForBroadcastUseCommands
  183. }
  184. permissionsForSingleUseCommands={
  185. permissionsForSingleUseCommands
  186. }
  187. permissionsForSlackEventActions={
  188. permissionsForSlackEventActions
  189. }
  190. onUpdateTokens={onUpdateTokens}
  191. onSubmitForm={onSubmitForm}
  192. />
  193. </React.Fragment>
  194. );
  195. })}
  196. {slackAppIntegrations.length < 10 && (
  197. <div className="row justify-content-center my-5">
  198. <button
  199. type="button"
  200. className="btn btn-outline-primary"
  201. onClick={addSlackAppIntegrationHandler}
  202. >
  203. {`+ ${t('admin:slack_integration.accordion.add_slack_workspace')}`}
  204. </button>
  205. </div>
  206. )}
  207. </div>
  208. <DeleteSlackBotSettingsModal
  209. isResetAll={false}
  210. isOpen={integrationIdToDelete != null}
  211. onClose={() => setIntegrationIdToDelete(null)}
  212. onClickDeleteButton={deleteSlackAppIntegrationHandler}
  213. />
  214. </>
  215. );
  216. };
  217. CustomBotWithProxySettings.defaultProps = {
  218. slackAppIntegrations: [],
  219. };
  220. CustomBotWithProxySettings.propTypes = {
  221. slackAppIntegrations: PropTypes.array,
  222. proxyServerUri: PropTypes.string,
  223. onClickAddSlackWorkspaceBtn: PropTypes.func,
  224. onPrimaryUpdated: PropTypes.func,
  225. onDeleteSlackAppIntegration: PropTypes.func,
  226. onSubmitForm: PropTypes.func,
  227. connectionStatuses: PropTypes.object.isRequired,
  228. onUpdateTokens: PropTypes.func,
  229. };
  230. export default CustomBotWithProxySettings;