UserTriggerNotification.jsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import React from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import PropTypes from 'prop-types';
  4. import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
  5. import { toastSuccess, toastError } from '~/client/util/toastr';
  6. import loggerFactory from '~/utils/logger';
  7. import { withUnstatedContainers } from '../../UnstatedUtils';
  8. import UserNotificationRow from './UserNotificationRow';
  9. const logger = loggerFactory('growi:slackAppConfiguration');
  10. class UserTriggerNotification extends React.Component {
  11. constructor(props) {
  12. super(props);
  13. this.state = {
  14. pathPattern: '',
  15. channel: '',
  16. };
  17. this.changePathPattern = this.changePathPattern.bind(this);
  18. this.changeChannel = this.changeChannel.bind(this);
  19. this.validateForm = this.validateForm.bind(this);
  20. this.onClickSubmit = this.onClickSubmit.bind(this);
  21. this.onClickDeleteBtn = this.onClickDeleteBtn.bind(this);
  22. }
  23. /**
  24. * Change pathPattern
  25. */
  26. changePathPattern(pathPattern) {
  27. this.setState({ pathPattern });
  28. }
  29. /**
  30. * Change channel
  31. */
  32. changeChannel(channel) {
  33. this.setState({ channel });
  34. }
  35. validateForm() {
  36. return this.state.pathPattern !== '' && this.state.channel !== '';
  37. }
  38. async onClickSubmit() {
  39. const { t, adminNotificationContainer } = this.props;
  40. try {
  41. await adminNotificationContainer.addNotificationPattern(this.state.pathPattern, this.state.channel);
  42. toastSuccess(t('notification_settings.add_notification_pattern'));
  43. this.setState({ pathPattern: '', channel: '' });
  44. }
  45. catch (err) {
  46. toastError(err);
  47. logger.error(err);
  48. }
  49. }
  50. async onClickDeleteBtn(notificationIdForDelete) {
  51. const { t, adminNotificationContainer } = this.props;
  52. try {
  53. const deletedNotificaton = await adminNotificationContainer.deleteUserTriggerNotificationPattern(notificationIdForDelete);
  54. toastSuccess(t('notification_settings.delete_notification_pattern', { path: deletedNotificaton.pathPattern }));
  55. }
  56. catch (err) {
  57. toastError(err);
  58. logger.error(err);
  59. }
  60. }
  61. render() {
  62. const { t, adminNotificationContainer } = this.props;
  63. const userNotifications = adminNotificationContainer.state.userNotifications || [];
  64. return (
  65. <React.Fragment>
  66. <h2 className="border-bottom my-4">{t('notification_settings.user_trigger_notification_header')}</h2>
  67. <table className="table table-bordered">
  68. <thead>
  69. <tr>
  70. <th>{t('notification_settings.pattern')}</th>
  71. <th>{t('notification_settings.channel')}</th>
  72. <th />
  73. </tr>
  74. </thead>
  75. <tbody className="admin-notif-list">
  76. <tr>
  77. <td>
  78. <input
  79. className="form-control"
  80. type="text"
  81. name="pathPattern"
  82. value={this.state.pathPattern}
  83. placeholder="e.g. /projects/xxx/MTG/*"
  84. onChange={(e) => { this.changePathPattern(e.target.value) }}
  85. />
  86. <p className="p-2 mb-0">
  87. {/* eslint-disable-next-line react/no-danger */}
  88. <span dangerouslySetInnerHTML={{ __html: t('notification_settings.pattern_desc') }} />
  89. </p>
  90. </td>
  91. <td>
  92. <div className="input-group notify-to-option" id="slack-input">
  93. <div className="input-group-prepend">
  94. <span className="input-group-text"><i className="fa fa-hashtag" /></span>
  95. </div>
  96. <input
  97. className="form-control form-inline"
  98. type="text"
  99. name="channel"
  100. value={this.state.channel}
  101. placeholder="e.g. project-xxx"
  102. onChange={(e) => { this.changeChannel(e.target.value) }}
  103. />
  104. </div>
  105. <p className="p-2 mb-0">
  106. {/* eslint-disable-next-line react/no-danger */}
  107. <span dangerouslySetInnerHTML={{ __html: t('notification_settings.channel_desc') }} />
  108. </p>
  109. </td>
  110. <td>
  111. <button type="button" className="btn btn-primary" disabled={!this.validateForm()} onClick={this.onClickSubmit}>{t('commons:Add')}</button>
  112. </td>
  113. </tr>
  114. {userNotifications.length > 0 && userNotifications.map((notification) => {
  115. return <UserNotificationRow notification={notification} onClickDeleteBtn={this.onClickDeleteBtn} key={notification._id} />;
  116. })}
  117. </tbody>
  118. </table>
  119. </React.Fragment>
  120. );
  121. }
  122. }
  123. UserTriggerNotification.propTypes = {
  124. t: PropTypes.func.isRequired, // i18next
  125. adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
  126. };
  127. const UserTriggerNotificationWrapperFC = (props) => {
  128. const { t } = useTranslation('admin');
  129. return <UserTriggerNotification t={t} {...props} />;
  130. };
  131. const UserTriggerNotificationWrapper = withUnstatedContainers(UserTriggerNotificationWrapperFC, [AdminNotificationContainer]);
  132. export default UserTriggerNotificationWrapper;