Просмотр исходного кода

Merge pull request #1512 from weseek/reactify-admin/retrieve-notifications

Reactify admin/retrieve notifications
Yuki Takei 6 лет назад
Родитель
Сommit
2eeb178339

+ 60 - 0
src/client/js/components/Admin/Notification/GlobalNotification.jsx

@@ -0,0 +1,60 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+
+import AppContainer from '../../../services/AppContainer';
+import AdminNotificationContainer from '../../../services/AdminNotificationContainer';
+import GlobalNotificationList from './GlobalNotificationList';
+
+class GlobalNotification extends React.Component {
+
+  render() {
+    const { t, adminNotificationContainer } = this.props;
+    const { globalNotifications } = adminNotificationContainer.state;
+    return (
+      <React.Fragment>
+
+        <a href="/admin/global-notification/new">
+          <p className="btn btn-default">{t('notification_setting.add_notification')}</p>
+        </a>
+
+        <h2 className="border-bottom mb-5">{t('notification_setting.notification_list')}</h2>
+
+        <table className="table table-bordered">
+          <thead>
+            <tr>
+              <th>ON/OFF</th>
+              {/* eslint-disable-next-line react/no-danger */}
+              <th>{t('notification_setting.trigger_path')} <span dangerouslySetInnerHTML={{ __html: t('notification_setting.trigger_path_help') }} /></th>
+              <th>{t('notification_setting.trigger_events')}</th>
+              <th>{t('notification_setting.notify_to')}</th>
+              <th></th>
+            </tr>
+          </thead>
+          {globalNotifications.length !== 0 && (
+            <tbody className="admin-notif-list">
+              <GlobalNotificationList />
+            </tbody>
+          )}
+        </table>
+
+      </React.Fragment>
+    );
+  }
+
+}
+
+const GlobalNotificationWrapper = (props) => {
+  return createSubscribedElement(GlobalNotification, props, [AppContainer, AdminNotificationContainer]);
+};
+
+GlobalNotification.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
+
+};
+
+export default withTranslation()(GlobalNotificationWrapper);

+ 115 - 0
src/client/js/components/Admin/Notification/GlobalNotificationList.jsx

@@ -0,0 +1,115 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+import urljoin from 'url-join';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+
+import AppContainer from '../../../services/AppContainer';
+import AdminNotificationContainer from '../../../services/AdminNotificationContainer';
+
+
+class GlobalNotificationList extends React.Component {
+
+  render() {
+    const { t, adminNotificationContainer } = this.props;
+    const { globalNotifications } = adminNotificationContainer.state;
+
+    return (
+      <React.Fragment>
+        {globalNotifications.map((notification) => {
+          return (
+            <tr key={notification._id}>
+              <td className="align-middle td-abs-center">
+                {/* GW-807 switch enable notification */}
+                <input type="checkbox" className="js-switch" data-size="small" data-id="{{ notification._id.toString() }}" />
+              </td>
+              <td>
+                {notification.triggerPath}
+              </td>
+              <td>
+                {notification.triggerEvents.includes('pageCreate') && (
+                  <span className="label label-success" data-toggle="tooltip" data-placement="top" title="Page Create">
+                    <i className="icon-doc"></i> CREATE
+                  </span>
+                )}
+                {notification.triggerEvents.includes('pageEdit') && (
+                  <span className="label label-warning" data-toggle="tooltip" data-placement="top" title="Page Edit">
+                    <i className="icon-pencil"></i> EDIT
+                  </span>
+                )}
+                {notification.triggerEvents.includes('pageMove') && (
+                  <span className="label label-warning" data-toggle="tooltip" data-placement="top" title="Page Move">
+                    <i className="icon-action-redo"></i> MOVE
+                  </span>
+                )}
+                {notification.triggerEvents.includes('pageDelete') && (
+                  <span className="label label-danger" data-toggle="tooltip" data-placement="top" title="Page Delte">
+                    <i className="icon-fire"></i> DELETE
+                  </span>
+                )}
+                {notification.triggerEvents.includes('pageLike') && (
+                  <span className="label label-info" data-toggle="tooltip" data-placement="top" title="Page Like">
+                    <i className="icon-like"></i> LIKE
+                  </span>
+                )}
+                {notification.triggerEvents.includes('comment') && (
+                  <span className="label label-default" data-toggle="tooltip" data-placement="top" title="New Comment">
+                    <i className="icon-fw icon-bubble"></i> POST
+                  </span>
+                )}
+              </td>
+              <td>
+                {notification.__t === 'mail'
+                  && <span data-toggle="tooltip" data-placement="top" title="Email"><i className="ti-email"></i> {notification.toEmail}</span>}
+                {notification.__t === 'slack'
+                  && <span data-toggle="tooltip" data-placement="top" title="Slack"><i className="fa fa-slack"></i> {notification.slackChannels}</span>}
+              </td>
+              <td className="td-abs-center">
+                <div className="btn-group admin-group-menu">
+                  <button type="button" className="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
+                    <i className="icon-settings"></i> <span className="caret"></span>
+                  </button>
+                  <ul className="dropdown-menu" role="menu">
+                    <li>
+                      <a href={urljoin('/admin/global-notification/', notification._id)}>
+                        <i className="icon-fw icon-note"></i> {t('Edit')}
+                      </a>
+                    </li>
+                    {/*  TODO GW-780 create delete modal  */}
+                    <li className="btn-delete">
+                      <a
+                        href="#"
+                        data-setting-id="{{ notification.id }}"
+                        data-target="#admin-delete-global-notification"
+                        data-toggle="modal"
+                      >
+                        <i className="icon-fw icon-fire text-danger"></i> {t('Delete')}
+                      </a>
+                    </li>
+
+                  </ul>
+                </div>
+              </td>
+            </tr>
+          );
+        })}
+      </React.Fragment>
+    );
+
+  }
+
+}
+
+const GlobalNotificationListWrapper = (props) => {
+  return createSubscribedElement(GlobalNotificationList, props, [AppContainer, AdminNotificationContainer]);
+};
+
+GlobalNotificationList.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
+
+};
+
+export default withTranslation()(GlobalNotificationListWrapper);

+ 0 - 145
src/client/js/components/Admin/Notification/GrobalNotification.jsx

@@ -1,145 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
-import urljoin from 'url-join';
-
-import { createSubscribedElement } from '../../UnstatedUtils';
-
-import AppContainer from '../../../services/AppContainer';
-import AdminNotificationContainer from '../../../services/AdminNotificationContainer';
-
-
-class GrobalNotification extends React.Component {
-
-  renderNotification() {
-    const { t, adminNotificationContainer } = this.props;
-    const { grobalNotifications } = adminNotificationContainer.state;
-
-    grobalNotifications.map((notification) => {
-      return (
-        <tr>
-          <td className="align-middle td-abs-center">
-            {/* GW-807 switch enable notification */}
-            <input type="checkbox" className="js-switch" data-size="small" data-id="{{ notification._id.toString() }}" />
-          </td>
-          <td>
-            {notification.triggerPath}
-          </td>
-          <td>
-            {notification.triggerEvents.includes('pageCreate') && (
-              <span className="label label-success" data-toggle="tooltip" data-placement="top" title="Page Create">
-                <i className="icon-doc"></i> CREATE
-              </span>
-            )}
-            {notification.triggerEvents.includes('pageEdit') && (
-              <span className="label label-warning" data-toggle="tooltip" data-placement="top" title="Page Edit">
-                <i className="icon-pencil"></i> EDIT
-              </span>
-            )}
-            {notification.triggerEvents.includes('pageMove') && (
-              <span className="label label-warning" data-toggle="tooltip" data-placement="top" title="Page Move">
-                <i className="icon-action-redo"></i> MOVE
-              </span>
-            )}
-            {notification.triggerEvents.includes('pageDelete') && (
-              <span className="label label-danger" data-toggle="tooltip" data-placement="top" title="Page Delte">
-                <i className="icon-fire"></i> DELETE
-              </span>
-            )}
-            {notification.triggerEvents.includes('pageLike') && (
-              <span className="label label-info" data-toggle="tooltip" data-placement="top" title="Page Like">
-                <i className="icon-like"></i> LIKE
-              </span>
-            )}
-            {notification.triggerEvents.includes('comment') && (
-              <span className="label label-default" data-toggle="tooltip" data-placement="top" title="New Comment">
-                <i className="icon-fw icon-bubble"></i> POST
-              </span>
-            )}
-          </td>
-          <td>
-            {notification.__t === 'mail'
-              && <span data-toggle="tooltip" data-placement="top" title="Email"><i className="ti-email"></i> {notification.toEmail}</span>}
-            {notification.__t === 'slack'
-              && <span data-toggle="tooltip" data-placement="top" title="Slack"><i className="fa fa-slack"></i> {notification.slackChannels}</span>}
-          </td>
-          <td className="td-abs-center">
-            <div className="btn-group admin-group-menu">
-              <button type="button" className="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
-                <i className="icon-settings"></i> <span className="caret"></span>
-              </button>
-              <ul className="dropdown-menu" role="menu">
-                <li>
-                  <a href={urljoin('/admin/global-notification/', notification.id)}>
-                    <i className="icon-fw icon-note"></i> {t('Edit')}
-                  </a>
-                </li>
-                {/*  TODO GW-780 create delete modal  */}
-                <li className="btn-delete">
-                  <a
-                    href="#"
-                    data-setting-id="{{ notification.id }}"
-                    data-target="#admin-delete-global-notification"
-                    data-toggle="modal"
-                  >
-                    <i className="icon-fw icon-fire text-danger"></i> {t('Delete')}
-                  </a>
-                </li>
-
-              </ul>
-            </div>
-          </td>
-        </tr>
-      );
-    });
-
-  }
-
-  render() {
-    const { t, adminNotificationContainer } = this.props;
-    const { grobalNotifications } = adminNotificationContainer.state;
-    return (
-      <React.Fragment>
-
-        <a href="/admin/global-notification/new">
-          <p className="btn btn-default">{t('notification_setting.add_notification')}</p>
-        </a>
-
-        <h2 className="border-bottom mb-5">{t('notification_setting.notification_list')}</h2>
-
-        <table className="table table-bordered">
-          <thead>
-            <tr>
-              <th>ON/OFF</th>
-              {/* eslint-disable-next-line react/no-danger */}
-              <th>{t('notification_setting.trigger_path')} <span dangerouslySetInnerHTML={{ __html: t('notification_setting.trigger_path_help') }} /></th>
-              <th>{t('notification_setting.trigger_events')}</th>
-              <th>{t('notification_setting.notify_to')}</th>
-              <th></th>
-            </tr>
-          </thead>
-          {grobalNotifications.length !== 0 && (
-            <tbody className="admin-notif-list">
-              {this.renderNotification()}
-            </tbody>
-          )}
-        </table>
-
-      </React.Fragment>
-    );
-  }
-
-}
-
-const GrobalNotificationWrapper = (props) => {
-  return createSubscribedElement(GrobalNotification, props, [AppContainer, AdminNotificationContainer]);
-};
-
-GrobalNotification.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminNotificationContainer: PropTypes.instanceOf(AdminNotificationContainer).isRequired,
-
-};
-
-export default withTranslation()(GrobalNotificationWrapper);

+ 2 - 2
src/client/js/components/Admin/Notification/NotificationSetting.jsx

@@ -12,7 +12,7 @@ import AdminNotificationContainer from '../../../services/AdminNotificationConta
 
 
 import SlackAppConfiguration from './SlackAppConfiguration';
 import SlackAppConfiguration from './SlackAppConfiguration';
 import UserTriggerNotification from './UserTriggerNotification';
 import UserTriggerNotification from './UserTriggerNotification';
-import GrobalNotification from './GrobalNotification';
+import GlobalNotification from './GlobalNotification';
 
 
 const logger = loggerFactory('growi:NotificationSetting');
 const logger = loggerFactory('growi:NotificationSetting');
 
 
@@ -56,7 +56,7 @@ class NotificationSetting extends React.Component {
               <UserTriggerNotification />
               <UserTriggerNotification />
             </div>
             </div>
             <div id="global-notification" className="tab-pane" role="tabpanel">
             <div id="global-notification" className="tab-pane" role="tabpanel">
-              <GrobalNotification />
+              <GlobalNotification />
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>

+ 28 - 4
src/client/js/services/AdminNotificationContainer.js

@@ -1,5 +1,11 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
+import loggerFactory from '@alias/logger';
+
+import { toastError } from '../util/apiNotification';
+
+const logger = loggerFactory('growi:services:AdminNotificationContainer');
+
 /**
 /**
  * Service container for admin Notification setting page (NotificationSetting.jsx)
  * Service container for admin Notification setting page (NotificationSetting.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -18,7 +24,7 @@ export default class AdminNotificationContainer extends Container {
       isIncomingWebhookPrioritized: false,
       isIncomingWebhookPrioritized: false,
       slackToken: '',
       slackToken: '',
       userNotifications: [],
       userNotifications: [],
-      grobalNotifications: [],
+      globalNotifications: [],
     };
     };
 
 
   }
   }
@@ -33,8 +39,24 @@ export default class AdminNotificationContainer extends Container {
   /**
   /**
    * Retrieve notificationData
    * Retrieve notificationData
    */
    */
-  retrieveNotificationData() {
-    // TODO GW-821 retrive data from api
+  async retrieveNotificationData() {
+    try {
+      const response = await this.appContainer.apiv3.get('/notification-setting/');
+      const { notificationParams } = response.data;
+
+      this.setState({
+        webhookUrl: notificationParams.webhookUrl || '',
+        isIncomingWebhookPrioritized: notificationParams.isIncomingWebhookPrioritized || false,
+        slackToken: notificationParams.slackToken || '',
+        userNotifications: notificationParams.userNotifications || [],
+        globalNotifications: notificationParams.globalNotifications || [],
+      });
+
+    }
+    catch (err) {
+      logger.error(err);
+      toastError(new Error('Failed to fetch data'));
+    }
   }
   }
 
 
   /**
   /**
@@ -84,10 +106,12 @@ export default class AdminNotificationContainer extends Container {
    * @memberOf SlackAppConfiguration
    * @memberOf SlackAppConfiguration
    */
    */
   async addNotificationPattern(pathPattern, channel) {
   async addNotificationPattern(pathPattern, channel) {
-    return this.appContainer.apiv3.post('/notification-setting/user-notification', {
+    const response = await this.appContainer.apiv3.post('/notification-setting/user-notification', {
       pathPattern,
       pathPattern,
       channel,
       channel,
     });
     });
+
+    this.setState({ userNotifications: response.data.responseParams.userNotifications });
   }
   }
 
 
 }
 }

+ 47 - 3
src/server/routes/apiv3/notification-setting.js

@@ -61,8 +61,41 @@ module.exports = (crowi) => {
   const adminRequired = require('../../middleware/admin-required')(crowi);
   const adminRequired = require('../../middleware/admin-required')(crowi);
   const csrf = require('../../middleware/csrf')(crowi);
   const csrf = require('../../middleware/csrf')(crowi);
 
 
+  const UpdatePost = crowi.model('UpdatePost');
+  const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
+
   const { ApiV3FormValidator } = crowi.middlewares;
   const { ApiV3FormValidator } = crowi.middlewares;
 
 
+  /**
+   * @swagger
+   *
+   *    /notification-setting/:
+   *      get:
+   *        tags: [NotificationSetting]
+   *        description: Get notification paramators
+   *        responses:
+   *          200:
+   *            description: params of notification
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  properties:
+   *                    notificationParams:
+   *                      type: object
+   *                      description: notification params
+   */
+  router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
+
+    const notificationParams = {
+      webhookUrl: await crowi.configManager.getConfig('notification', 'slack:incomingWebhookUrl'),
+      isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'),
+      slackToken: await crowi.configManager.getConfig('notification', 'slack:token'),
+      userNotifications: await UpdatePost.findAll(),
+      globalNotifications: await GlobalNotificationSetting.findAll(),
+    };
+    return res.apiv3({ notificationParams });
+  });
+
   /**
   /**
    * @swagger
    * @swagger
    *
    *
@@ -129,7 +162,15 @@ module.exports = (crowi) => {
   *            content:
   *            content:
   *              application/json:
   *              application/json:
   *                schema:
   *                schema:
-  *                  $ref: '#/components/schemas/UserNotificationParams'
+  *                  responseParams:
+  *                    type: object
+  *                    properties:
+  *                      createdUser:
+  *                        type: object
+  *                        description: user who set notification
+  *                      userNotifications:
+  *                        type: object
+  *                        description: user trigger notifications for updated
   */
   */
   router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, ApiV3FormValidator, async(req, res) => {
   router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, ApiV3FormValidator, async(req, res) => {
     const { pathPattern, channel } = req.body;
     const { pathPattern, channel } = req.body;
@@ -137,8 +178,11 @@ module.exports = (crowi) => {
 
 
     try {
     try {
       logger.info('notification.add', pathPattern, channel);
       logger.info('notification.add', pathPattern, channel);
-      const params = await UpdatePost.create(pathPattern, channel, req.user);
-      return res.apiv3({ params });
+      const responseParams = {
+        createdUser: await UpdatePost.create(pathPattern, channel, req.user),
+        userNotifications: await UpdatePost.findAll(),
+      };
+      return res.apiv3({ responseParams });
     }
     }
     catch (err) {
     catch (err) {
       const msg = 'Error occurred in updating user notification';
       const msg = 'Error occurred in updating user notification';