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

Merge pull request #3730 from weseek/feat/5812-5816-implement-api

Feat/5812 5816 implement api
Yuki Takei 4 лет назад
Родитель
Сommit
37c2dcff32

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/default-dark.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/default-light.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/fluid-dark.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/fluid-light.svg


+ 5 - 3
src/client/js/components/Admin/Customize/Customize.jsx

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 
 
 import loggerFactory from '@alias/logger';
 import loggerFactory from '@alias/logger';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
+import AppContainer from '../../../services/AppContainer';
 
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 import { toastError } from '../../../util/apiNotification';
@@ -23,7 +24,7 @@ const logger = loggerFactory('growi:services:AdminCustomizePage');
 
 
 let retrieveErrors = null;
 let retrieveErrors = null;
 function Customize(props) {
 function Customize(props) {
-  const { adminCustomizeContainer } = props;
+  const { appContainer, adminCustomizeContainer } = props;
 
 
   if (adminCustomizeContainer.state.currentTheme === adminCustomizeContainer.dummyCurrentTheme) {
   if (adminCustomizeContainer.state.currentTheme === adminCustomizeContainer.dummyCurrentTheme) {
     throw (async() => {
     throw (async() => {
@@ -47,7 +48,7 @@ function Customize(props) {
   return (
   return (
     <Fragment>
     <Fragment>
       <div className="mb-5">
       <div className="mb-5">
-        <CustomizeLayoutSetting />
+        <CustomizeLayoutSetting appContainer={appContainer} />
       </div>
       </div>
       <div className="mb-5">
       <div className="mb-5">
         <CustomizeThemeSetting />
         <CustomizeThemeSetting />
@@ -74,9 +75,10 @@ function Customize(props) {
   );
   );
 }
 }
 
 
-const CustomizePageWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(Customize), [AdminCustomizeContainer]);
+const CustomizePageWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(Customize), [AppContainer, AdminCustomizeContainer]);
 
 
 Customize.propTypes = {
 Customize.propTypes = {
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminCustomizeContainer: PropTypes.instanceOf(AdminCustomizeContainer).isRequired,
   adminCustomizeContainer: PropTypes.instanceOf(AdminCustomizeContainer).isRequired,
 };
 };
 
 

+ 57 - 10
src/client/js/components/Admin/Customize/CustomizeLayoutSetting.jsx

@@ -1,13 +1,46 @@
-import React from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
-import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
+import AppContainer from '../../../services/AppContainer';
+
+import { toastSuccess, toastError } from '../../../util/apiNotification';
+import { isDarkMode as isDarkModeByUtil } from '../../../util/color-scheme';
+
+const isDarkMode = isDarkModeByUtil();
+const colorText = isDarkMode ? 'dark' : 'light';
 
 
 const CustomizeLayoutSetting = (props) => {
 const CustomizeLayoutSetting = (props) => {
-  const { t } = props;
+  const { t, appContainer } = props;
+
+  const [isContainerFluid, setIsContainerFluid] = useState(false);
+  const [retrieveError, setRetrieveError] = useState();
+
+  const retrieveData = useCallback(async() => {
+    try {
+      const res = await appContainer.apiv3Get('/customize-setting/layout');
+      setIsContainerFluid(res.data.isContainerFluid);
+    }
+    catch (err) {
+      setRetrieveError(err);
+      toastError(err);
+    }
+  }, [appContainer]);
 
 
-  const onClickSubmit = () => {};
+  useEffect(() => {
+    retrieveData();
+  }, [retrieveData]);
+
+  const onClickSubmit = async() => {
+    try {
+      await appContainer.apiv3Put('/customize-setting/layout', { isContainerFluid });
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.layout') }));
+      retrieveData();
+    }
+    catch (err) {
+      toastError(err);
+    }
+  };
 
 
   return (
   return (
     <React.Fragment>
     <React.Fragment>
@@ -16,15 +49,23 @@ const CustomizeLayoutSetting = (props) => {
           <h2 className="admin-setting-header">{t('admin:customize_setting.layout')}</h2>
           <h2 className="admin-setting-header">{t('admin:customize_setting.layout')}</h2>
 
 
           <div className="d-flex justify-content-around mt-5">
           <div className="d-flex justify-content-around mt-5">
-            <div className="card-deck">
-              <div className="card">
-                <img src="https://via.placeholder.com/350x150" />
+            <div id="layoutOptions" className="card-deck">
+              <div
+                className={`card customize-layout-card ${!isContainerFluid ? 'border-active' : ''}`}
+                onClick={() => setIsContainerFluid(false)}
+                role="button"
+              >
+                <img src={`/images/customize-settings/default-${colorText}.svg`} />
                 <div className="card-body text-center">
                 <div className="card-body text-center">
                   {t('admin:customize_setting.layout_options.default')}
                   {t('admin:customize_setting.layout_options.default')}
                 </div>
                 </div>
               </div>
               </div>
-              <div className="card">
-                <img src="https://via.placeholder.com/350x150" />
+              <div
+                className={`card customize-layout-card ${isContainerFluid ? 'border-active' : ''}`}
+                onClick={() => setIsContainerFluid(true)}
+                role="button"
+              >
+                <img src={`/images/customize-settings/fluid-${colorText}.svg`} />
                 <div className="card-body  text-center">
                 <div className="card-body  text-center">
                   {t('admin:customize_setting.layout_options.expanded')}
                   {t('admin:customize_setting.layout_options.expanded')}
                 </div>
                 </div>
@@ -32,7 +73,11 @@ const CustomizeLayoutSetting = (props) => {
             </div>
             </div>
           </div>
           </div>
 
 
-          <AdminUpdateButtonRow onClick={onClickSubmit} />
+          <div className="row my-3">
+            <div className="mx-auto">
+              <button type="button" className="btn btn-primary" onClick={onClickSubmit} disabled={retrieveError != null}>{ t('Update') }</button>
+            </div>
+          </div>
         </div>
         </div>
       </div>
       </div>
     </React.Fragment>
     </React.Fragment>
@@ -41,6 +86,8 @@ const CustomizeLayoutSetting = (props) => {
 
 
 CustomizeLayoutSetting.propTypes = {
 CustomizeLayoutSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
+
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 };
 };
 
 
 export default withTranslation()(CustomizeLayoutSetting);
 export default withTranslation()(CustomizeLayoutSetting);

+ 6 - 0
src/client/styles/scss/_admin.scss

@@ -118,6 +118,12 @@
   //   }
   //   }
   // }
   // }
 
 
+  #layoutOptions {
+    .customize-layout-card {
+      border: 4px solid $border-color;
+    }
+  }
+
   // theme selector
   // theme selector
   #themeOptions {
   #themeOptions {
     // layout
     // layout

+ 9 - 1
src/client/styles/scss/theme/_apply-colors.scss

@@ -553,9 +553,17 @@ mark.rbt-highlight-text {
 }
 }
 
 
 /*
 /*
- * GROWI admin page #themeOptions
+ * GROWI admin page #layoutOptions #themeOptions
  */
  */
 .admin-page {
 .admin-page {
+  #layoutOptions {
+    .customize-layout-card {
+      &.border-active {
+        border-color: $color-theme-color-box;
+      }
+    }
+  }
+
   #themeOptions {
   #themeOptions {
     .theme-option-container.active {
     .theme-option-container.active {
       .theme-option-name {
       .theme-option-name {

+ 81 - 0
src/server/routes/apiv3/customize-setting.js

@@ -21,6 +21,12 @@ const ErrorV3 = require('../../models/vo/error-apiv3');
  *
  *
  *  components:
  *  components:
  *    schemas:
  *    schemas:
+ *      CustomizeLayout:
+ *        description: CustomizeLayout
+ *        type: object
+ *        properties:
+ *          isContainerFluid:
+ *            type: boolean
  *      CustomizeTheme:
  *      CustomizeTheme:
  *        description: CustomizeTheme
  *        description: CustomizeTheme
  *        type: object
  *        type: object
@@ -87,6 +93,9 @@ module.exports = (crowi) => {
   const { customizeService } = crowi;
   const { customizeService } = crowi;
 
 
   const validator = {
   const validator = {
+    layout: [
+      body('isContainerFluid').isBoolean(),
+    ],
     themeAssetPath: [
     themeAssetPath: [
       query('themeName').isString(),
       query('themeName').isString(),
     ],
     ],
@@ -168,6 +177,78 @@ module.exports = (crowi) => {
     return res.apiv3({ customizeParams });
     return res.apiv3({ customizeParams });
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *    /customize-setting/layout:
+   *      get:
+   *        tags: [CustomizeSetting]
+   *        operationId: getLayoutCustomizeSetting
+   *        summary: /customize-setting/layout
+   *        description: Get layout
+   *        responses:
+   *          200:
+   *            description: Succeeded to get layout
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  $ref: '#/components/schemas/CustomizeLayout'
+   */
+  router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
+
+    try {
+      const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
+      return res.apiv3({ isContainerFluid });
+    }
+    catch (err) {
+      const msg = 'Error occurred in getting layout';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
+    }
+  });
+
+  /**
+   * @swagger
+   *
+   *    /customize-setting/layout:
+   *      put:
+   *        tags: [CustomizeSetting]
+   *        operationId: updateLayoutCustomizeSetting
+   *        summary: /customize-setting/layout
+   *        description: Update layout
+   *        requestBody:
+   *          required: true
+   *          content:
+   *            application/json:
+   *              schema:
+   *                $ref: '#/components/schemas/CustomizeLayout'
+   *        responses:
+   *          200:
+   *            description: Succeeded to update layout
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  $ref: '#/components/schemas/CustomizeLayout'
+   */
+  router.put('/layout', loginRequiredStrictly, adminRequired, csrf, validator.layout, apiV3FormValidator, async(req, res) => {
+    const requestParams = {
+      'customize:isContainerFluid': req.body.isContainerFluid,
+    };
+
+    try {
+      await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
+      const customizedParams = {
+        isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
+      };
+      return res.apiv3({ customizedParams });
+    }
+    catch (err) {
+      const msg = 'Error occurred in updating layout';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
+    }
+  });
+
   /**
   /**
    * @swagger
    * @swagger
    *
    *

Некоторые файлы не были показаны из-за большого количества измененных файлов