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

Merge pull request #7298 from weseek/feat/questionnaire-settings-for-each-user

feat: Questionnaire settings for each user
Haku Mizuki 3 лет назад
Родитель
Сommit
59ea6d1b4a

+ 1 - 0
packages/app/public/static/locales/en_US/translation.json

@@ -233,6 +233,7 @@
     "link_sharing_is_disabled": "Link sharing is disabled"
   },
   "API Settings": "API settings",
+  "Other Settings": "Other Settings",
   "API Token Settings": "API token settings",
   "Current API Token": "Current API token",
   "Update API Token": "Update API token",

+ 1 - 0
packages/app/public/static/locales/ja_JP/translation.json

@@ -235,6 +235,7 @@
     "link_sharing_is_disabled": "リンクのシェアは無効化されています"
   },
   "API Settings": "API設定",
+  "Other Settings": "その他の設定",
   "API Token Settings": "API Token設定",
   "Current API Token": "現在のAPI Token",
   "Update API Token": "API Tokenを更新",

+ 1 - 0
packages/app/public/static/locales/zh_CN/translation.json

@@ -225,6 +225,7 @@
 		"password_is_not_set": "密码未设置"
 	},
 	"API Settings": "API设置",
+  "Other Settings": "其他设置",
 	"API Token Settings": "API token 设置",
 	"Current API Token": "当前 API token",
 	"Update API Token": "更新 API token",

+ 85 - 0
packages/app/src/components/Me/OtherSettings.tsx

@@ -0,0 +1,85 @@
+import {
+  useState, useEffect, useCallback,
+} from 'react';
+
+import { useTranslation } from 'next-i18next';
+
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { apiv3Put } from '~/client/util/apiv3-client';
+import { useCurrentUser } from '~/stores/context';
+
+const OtherSettings = (): JSX.Element => {
+  const { t } = useTranslation();
+  const { data: currentUser, error: errorCurrentUser } = useCurrentUser();
+
+  const [isEnableQuestionnaire, setIsEnableQuestionnaire] = useState(currentUser?.isEnableQuestionnaire);
+
+  const onChangeIsEnableQuestionnaireHandler = useCallback(async() => {
+    setIsEnableQuestionnaire(prev => !prev);
+  }, []);
+
+  const onClickUpdateIsEnableQuestionnaireHandler = useCallback(async() => {
+    try {
+      await apiv3Put('/personal-setting/questionnaire-settings', {
+        isEnableQuestionnaire,
+      });
+      toastSuccess(t('toaster.update_successed', { target: 'アンケート設定', ns: 'commons' }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [isEnableQuestionnaire, t]);
+
+  // Sync currentUser and state
+  useEffect(() => {
+    setIsEnableQuestionnaire(currentUser?.isEnableQuestionnaire);
+  }, [currentUser?.isEnableQuestionnaire]);
+
+  const isLoadingCurrentUser = currentUser === undefined && errorCurrentUser === undefined;
+
+  return (
+    <>
+      <h2 className="border-bottom my-4">アンケート設定</h2>
+
+      {isLoadingCurrentUser && <div className="text-muted text-center mb-5">
+        <i className="fa fa-2x fa-spinner fa-pulse mr-1" />
+      </div>}
+
+      <div className="form-group row">
+        <div className="offset-md-3 col-md-6 text-left">
+          {!isLoadingCurrentUser && (
+            <div className="custom-control custom-switch custom-checkbox-primary">
+              <input
+                type="checkbox"
+                className="custom-control-input"
+                id="isEnableQuestionnaire"
+                checked={isEnableQuestionnaire}
+                onChange={onChangeIsEnableQuestionnaireHandler}
+              />
+              <label className="custom-control-label" htmlFor="isEnableQuestionnaire">
+                アンケートを有効にする
+              </label>
+              <p className="form-text text-muted small">
+                GROWI 改善のためのアンケートが表示されるようになります。ご意見ご要望はユーザーアイコンのドロップダウンからお願いいたします。
+              </p>
+            </div>
+          )}
+        </div>
+      </div>
+
+      <div className="row my-3">
+        <div className="offset-4 col-5">
+          <button
+            type="button"
+            className="btn btn-primary"
+            onClick={onClickUpdateIsEnableQuestionnaireHandler}
+          >
+            {t('Update')}
+          </button>
+        </div>
+      </div>
+    </>
+  );
+};
+
+export default OtherSettings;

+ 7 - 1
packages/app/src/components/Me/PersonalSettings.jsx

@@ -1,7 +1,6 @@
 
 import React, { useMemo } from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
 
 import CustomNavAndContents from '../CustomNavigation/CustomNavAndContents';
@@ -10,6 +9,7 @@ import ApiSettings from './ApiSettings';
 import { EditorSettings } from './EditorSettings';
 import ExternalAccountLinkedMe from './ExternalAccountLinkedMe';
 import InAppNotificationSettings from './InAppNotificationSettings';
+import OtherSettings from './OtherSettings';
 import PasswordSettings from './PasswordSettings';
 import UserSettings from './UserSettings';
 
@@ -55,6 +55,12 @@ const PersonalSettings = () => {
         i18n: t('in_app_notification_settings.in_app_notification_settings'),
         index: 5,
       },
+      other_settings: {
+        Icon: () => <i className="icon-fw icon-settings"></i>,
+        Content: OtherSettings,
+        i18n: t('Other Settings'),
+        index: 6,
+      },
     };
   }, [t]);
 

+ 6 - 0
packages/app/src/server/models/user.js

@@ -69,6 +69,7 @@ module.exports = function(crowi) {
     lastLoginAt: { type: Date },
     admin: { type: Boolean, default: 0, index: true },
     isInvitationEmailSended: { type: Boolean, default: false },
+    isEnableQuestionnaire: { type: Boolean, default: true },
   }, {
     timestamps: true,
     toObject: {
@@ -730,6 +731,11 @@ module.exports = function(crowi) {
     return { users, totalCount };
   };
 
+  userSchema.methods.updateIsEnableQuestionnaire = async function(value) {
+    this.isEnableQuestionnaire = value;
+    return this.save();
+  };
+
   class UserUpperLimitException {
 
     constructor() {

+ 18 - 0
packages/app/src/server/routes/apiv3/personal-setting.js

@@ -127,6 +127,9 @@ module.exports = (crowi) => {
       body('defaultSubscribeRules.*.name').isString(),
       body('defaultSubscribeRules.*.isEnabled').optional().isBoolean(),
     ],
+    questionnaireSettings: [
+      body('isEnableQuestionnaire').isBoolean(),
+    ],
   };
 
   /**
@@ -681,6 +684,21 @@ module.exports = (crowi) => {
     }
   });
 
+  // eslint-disable-next-line max-len
+  router.put('/questionnaire-settings', accessTokenParser, loginRequiredStrictly, validator.questionnaireSettings, apiV3FormValidator, async(req, res) => {
+    const { isEnableQuestionnaire } = req.body;
+    const { user } = req;
+    try {
+      await user.updateIsEnableQuestionnaire(isEnableQuestionnaire);
+
+      return res.apiv3({ message: 'Successfully updated questionnaire settings.', isEnableQuestionnaire });
+    }
+    catch (err) {
+      logger.error(err);
+      return res.apiv3Err({ error: 'Failed to update questionnaire settings.' });
+    }
+  });
+
 
   return router;
 };

+ 1 - 0
packages/core/src/interfaces/user.ts

@@ -22,6 +22,7 @@ export type IUser = {
   lastLoginAt?: Date,
   introduction: string,
   status: IUserStatus,
+  isEnableQuestionnaire: boolean,
 }
 
 export type IUserGroupRelation = {