mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	Merge branch 'migrations/admin-users-filters' of github.com:strapi/strapi into migrations/admin-users-delete
This commit is contained in:
		
						commit
						99ba54cb43
					
				@ -59,6 +59,7 @@ module.exports = {
 | 
			
		||||
    '<rootDir>/test/config/front/test-bundler.js',
 | 
			
		||||
    '<rootDir>/packages/admin-test-utils/lib/mocks/LocalStorageMock.js',
 | 
			
		||||
    '<rootDir>/packages/admin-test-utils/lib/mocks/IntersectionObserver.js',
 | 
			
		||||
    '<rootDir>/packages/admin-test-utils/lib/mocks/ResizeObserver.js',
 | 
			
		||||
  ],
 | 
			
		||||
  testPathIgnorePatterns: [
 | 
			
		||||
    '/node_modules/',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/admin-test-utils/lib/mocks/ResizeObserver.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/admin-test-utils/lib/mocks/ResizeObserver.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
class ResizeObserverMock {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.disconnect = () => null;
 | 
			
		||||
    this.observe = () => null;
 | 
			
		||||
    this.unobserve = () => null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
global.ResizeObserver = ResizeObserverMock;
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Box, Row, TableLabel } from '@strapi/parts';
 | 
			
		||||
import { MultiSelectNested } from '@strapi/parts/Select';
 | 
			
		||||
import { upperFirst } from 'lodash';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Row, TableLabel } from '@strapi/parts';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useIntl } from 'react-intl';
 | 
			
		||||
import styled from 'styled-components';
 | 
			
		||||
 | 
			
		||||
// import ConditionsSelect from '../ConditionsSelect';
 | 
			
		||||
import { rowHeight } from '../../Permissions/utils/constants';
 | 
			
		||||
 | 
			
		||||
const RowWrapper = styled(Row)`
 | 
			
		||||
@ -12,16 +12,46 @@ const RowWrapper = styled(Row)`
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
const ActionRow = ({
 | 
			
		||||
  // arrayOfOptionsGroupedByCategory,
 | 
			
		||||
  // isFormDisabled,
 | 
			
		||||
  arrayOfOptionsGroupedByCategory,
 | 
			
		||||
  isFormDisabled,
 | 
			
		||||
  isGrey,
 | 
			
		||||
  label,
 | 
			
		||||
  // name,
 | 
			
		||||
  // onCategoryChange,
 | 
			
		||||
  // onChange,
 | 
			
		||||
  // value,
 | 
			
		||||
  name,
 | 
			
		||||
  onChange,
 | 
			
		||||
  value,
 | 
			
		||||
}) => {
 | 
			
		||||
  const { formatMessage } = useIntl();
 | 
			
		||||
  const options = arrayOfOptionsGroupedByCategory.reduce((arr, curr) => {
 | 
			
		||||
    const [label, children] = curr;
 | 
			
		||||
    const obj = {
 | 
			
		||||
      label: upperFirst(label),
 | 
			
		||||
      children: children.map(child => ({
 | 
			
		||||
        label: child.displayName,
 | 
			
		||||
        value: child.id,
 | 
			
		||||
      })),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return [...arr, obj];
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  // Output: ['value1', 'value2']
 | 
			
		||||
  const values = Object.values(value)
 | 
			
		||||
    .map(x =>
 | 
			
		||||
      Object.entries(x)
 | 
			
		||||
        .filter(([, value]) => value)
 | 
			
		||||
        .map(([key]) => key)
 | 
			
		||||
    )
 | 
			
		||||
    .flat();
 | 
			
		||||
 | 
			
		||||
  // ! Only expects arrayOfOpt to be [['default', obj]] - might break in future changes
 | 
			
		||||
  const handleChange = val => {
 | 
			
		||||
    const [[, values]] = arrayOfOptionsGroupedByCategory;
 | 
			
		||||
    const formattedValues = values.reduce(
 | 
			
		||||
      (acc, curr) => ({ [curr.id]: val.includes(curr.id), ...acc }),
 | 
			
		||||
      {}
 | 
			
		||||
    );
 | 
			
		||||
    onChange(name, formattedValues);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <RowWrapper as="li" background={isGrey ? 'neutral100' : 'neutral0'}>
 | 
			
		||||
@ -56,26 +86,27 @@ const ActionRow = ({
 | 
			
		||||
          })}
 | 
			
		||||
        </TableLabel>
 | 
			
		||||
      </Row>
 | 
			
		||||
      {/* <ConditionsSelect
 | 
			
		||||
        arrayOfOptionsGroupedByCategory={arrayOfOptionsGroupedByCategory}
 | 
			
		||||
        name={name}
 | 
			
		||||
        isFormDisabled={isFormDisabled}
 | 
			
		||||
        onCategoryChange={onCategoryChange}
 | 
			
		||||
        onChange={onChange}
 | 
			
		||||
        value={value}
 | 
			
		||||
      /> */}
 | 
			
		||||
      <Box style={{ maxWidth: 430, width: '100%' }}>
 | 
			
		||||
        <MultiSelectNested
 | 
			
		||||
          id={name}
 | 
			
		||||
          customizeContent={values => `${values.length} currently selected`}
 | 
			
		||||
          onChange={handleChange}
 | 
			
		||||
          value={values}
 | 
			
		||||
          options={options}
 | 
			
		||||
          disabled={isFormDisabled}
 | 
			
		||||
        />
 | 
			
		||||
      </Box>
 | 
			
		||||
    </RowWrapper>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ActionRow.propTypes = {
 | 
			
		||||
  // arrayOfOptionsGroupedByCategory: PropTypes.array.isRequired,
 | 
			
		||||
  // isFormDisabled: PropTypes.bool.isRequired,
 | 
			
		||||
  arrayOfOptionsGroupedByCategory: PropTypes.array.isRequired,
 | 
			
		||||
  isFormDisabled: PropTypes.bool.isRequired,
 | 
			
		||||
  isGrey: PropTypes.bool.isRequired,
 | 
			
		||||
  label: PropTypes.string.isRequired,
 | 
			
		||||
  // name: PropTypes.string.isRequired,
 | 
			
		||||
  // value: PropTypes.object.isRequired,
 | 
			
		||||
  // onCategoryChange: PropTypes.func.isRequired,
 | 
			
		||||
  // onChange: PropTypes.func.isRequired,
 | 
			
		||||
  name: PropTypes.string.isRequired,
 | 
			
		||||
  value: PropTypes.object.isRequired,
 | 
			
		||||
  onChange: PropTypes.func.isRequired,
 | 
			
		||||
};
 | 
			
		||||
export default ActionRow;
 | 
			
		||||
 | 
			
		||||
@ -3,31 +3,24 @@ import {
 | 
			
		||||
  Breadcrumbs,
 | 
			
		||||
  Button,
 | 
			
		||||
  Crumb,
 | 
			
		||||
  Divider,
 | 
			
		||||
  H2,
 | 
			
		||||
  ModalFooter,
 | 
			
		||||
  ModalHeader,
 | 
			
		||||
  ModalLayout,
 | 
			
		||||
  Stack,
 | 
			
		||||
  Text,
 | 
			
		||||
  Divider,
 | 
			
		||||
} from '@strapi/parts';
 | 
			
		||||
import { cloneDeep, get, groupBy, set, upperFirst } from 'lodash';
 | 
			
		||||
import produce from 'immer';
 | 
			
		||||
import { get, groupBy, upperFirst } from 'lodash';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import React, { useMemo, useState } from 'react';
 | 
			
		||||
import { useIntl } from 'react-intl';
 | 
			
		||||
import { usePermissionsDataManager } from '../../../hooks';
 | 
			
		||||
import updateValues from '../Permissions/utils/updateValues';
 | 
			
		||||
import ActionRow from './ActionRow';
 | 
			
		||||
import createDefaultConditionsForm from './utils/createDefaultConditionsForm';
 | 
			
		||||
 | 
			
		||||
const ConditionsModal = ({
 | 
			
		||||
  actions,
 | 
			
		||||
  headerBreadCrumbs,
 | 
			
		||||
  isOpen,
 | 
			
		||||
  isFormDisabled,
 | 
			
		||||
  onClosed,
 | 
			
		||||
  onToggle,
 | 
			
		||||
}) => {
 | 
			
		||||
const ConditionsModal = ({ actions, headerBreadCrumbs, isFormDisabled, onClosed, onToggle }) => {
 | 
			
		||||
  const { formatMessage } = useIntl();
 | 
			
		||||
  const { availableConditions, modifiedData, onChangeConditions } = usePermissionsDataManager();
 | 
			
		||||
 | 
			
		||||
@ -50,26 +43,20 @@ const ConditionsModal = ({
 | 
			
		||||
 | 
			
		||||
  const [state, setState] = useState(initState);
 | 
			
		||||
 | 
			
		||||
  const handleCategoryChange = ({ keys, value }) => {
 | 
			
		||||
    setState(prevState => {
 | 
			
		||||
      const updatedState = cloneDeep(prevState);
 | 
			
		||||
      const objToUpdate = get(prevState, keys, {});
 | 
			
		||||
      const updatedValues = updateValues(objToUpdate, value);
 | 
			
		||||
  const handleChange = (name, values) => {
 | 
			
		||||
    setState(
 | 
			
		||||
      produce(draft => {
 | 
			
		||||
        if (!draft[name]) {
 | 
			
		||||
          draft[name] = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      set(updatedState, keys, updatedValues);
 | 
			
		||||
        if (!draft[name].default) {
 | 
			
		||||
          draft[name].default = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      return updatedState;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleChange = ({ keys, value }) => {
 | 
			
		||||
    setState(prevState => {
 | 
			
		||||
      const updatedState = cloneDeep(prevState);
 | 
			
		||||
 | 
			
		||||
      set(updatedState, keys, value);
 | 
			
		||||
 | 
			
		||||
      return updatedState;
 | 
			
		||||
    });
 | 
			
		||||
        draft[name].default = values;
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleSubmit = () => {
 | 
			
		||||
@ -89,8 +76,6 @@ const ConditionsModal = ({
 | 
			
		||||
    onToggle();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (!isOpen) return null;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ModalLayout onClose={onClosed}>
 | 
			
		||||
      <ModalHeader>
 | 
			
		||||
@ -140,7 +125,6 @@ const ConditionsModal = ({
 | 
			
		||||
                    isFormDisabled={isFormDisabled}
 | 
			
		||||
                    isGrey={index % 2 === 0}
 | 
			
		||||
                    name={name}
 | 
			
		||||
                    onCategoryChange={handleCategoryChange}
 | 
			
		||||
                    onChange={handleChange}
 | 
			
		||||
                    value={get(state, name, {})}
 | 
			
		||||
                  />
 | 
			
		||||
@ -181,7 +165,6 @@ ConditionsModal.propTypes = {
 | 
			
		||||
    })
 | 
			
		||||
  ).isRequired,
 | 
			
		||||
  headerBreadCrumbs: PropTypes.arrayOf(PropTypes.string).isRequired,
 | 
			
		||||
  isOpen: PropTypes.bool.isRequired,
 | 
			
		||||
  isFormDisabled: PropTypes.bool.isRequired,
 | 
			
		||||
  onClosed: PropTypes.func.isRequired,
 | 
			
		||||
  onToggle: PropTypes.func.isRequired,
 | 
			
		||||
 | 
			
		||||
@ -228,14 +228,15 @@ const Collapse = ({
 | 
			
		||||
          )}
 | 
			
		||||
        </Row>
 | 
			
		||||
        <Box style={{ width: 120 }} />
 | 
			
		||||
        {isModalOpen && (
 | 
			
		||||
          <ConditionsModal
 | 
			
		||||
            headerBreadCrumbs={[label, 'app.components.LeftMenuLinkContainer.settings']}
 | 
			
		||||
            actions={checkboxesActions}
 | 
			
		||||
          isOpen={isModalOpen}
 | 
			
		||||
            isFormDisabled={isFormDisabled}
 | 
			
		||||
            onClosed={handleModalClose}
 | 
			
		||||
            onToggle={handleToggleModalIsOpen}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
      </Wrapper>
 | 
			
		||||
      <AbsoluteBox>
 | 
			
		||||
        <ConditionsButton
 | 
			
		||||
 | 
			
		||||
@ -130,14 +130,15 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p
 | 
			
		||||
          />
 | 
			
		||||
        </Row>
 | 
			
		||||
      </Box>
 | 
			
		||||
      {isModalOpen && (
 | 
			
		||||
        <ConditionsModal
 | 
			
		||||
          headerBreadCrumbs={[categoryName, subCategoryName]}
 | 
			
		||||
          actions={formattedActions}
 | 
			
		||||
        isOpen={isModalOpen}
 | 
			
		||||
          isFormDisabled={isFormDisabled}
 | 
			
		||||
          onClosed={handleModalClose}
 | 
			
		||||
          onToggle={handleToggleModalIsOpen}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
import { useEffect, useReducer } from 'react';
 | 
			
		||||
import { hasPermissions, useRBACProvider, useStrapiApp } from '@strapi/helper-plugin';
 | 
			
		||||
import { hasPermissions, useRBACProvider, useStrapiApp, useAppInfos } from '@strapi/helper-plugin';
 | 
			
		||||
 | 
			
		||||
import reducer, { initialState } from './reducer';
 | 
			
		||||
import init from './init';
 | 
			
		||||
 | 
			
		||||
const useSettingsMenu = (noCheck = false) => {
 | 
			
		||||
  const { allPermissions: permissions } = useRBACProvider();
 | 
			
		||||
 | 
			
		||||
  const { shouldUpdateStrapi } = useAppInfos();
 | 
			
		||||
  const { settings } = useStrapiApp();
 | 
			
		||||
 | 
			
		||||
  const [{ isLoading, menu }, dispatch] = useReducer(reducer, initialState, () =>
 | 
			
		||||
    init(initialState, settings)
 | 
			
		||||
    init(initialState, { settings, shouldUpdateStrapi })
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,15 @@ import adminPermissions from '../../permissions';
 | 
			
		||||
import formatLinks from './utils/formatLinks';
 | 
			
		||||
import globalLinks from './utils/globalLinks';
 | 
			
		||||
 | 
			
		||||
const init = (initialState, settings) => {
 | 
			
		||||
const init = (initialState, { settings, shouldUpdateStrapi }) => {
 | 
			
		||||
  // Retrieve the links that will be injected into the global section
 | 
			
		||||
  const pluginsGlobalLinks = settings.global.links;
 | 
			
		||||
  // Sort the links by name
 | 
			
		||||
  const sortedGlobalLinks = sortLinks([...pluginsGlobalLinks, ...globalLinks]);
 | 
			
		||||
  const sortedGlobalLinks = sortLinks([...pluginsGlobalLinks, ...globalLinks]).map(link => ({
 | 
			
		||||
    ...link,
 | 
			
		||||
    hasNotification: link.id === 'application-infos' && shouldUpdateStrapi,
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  const otherSections = Object.values(omit(settings, 'global'));
 | 
			
		||||
 | 
			
		||||
  const menu = [
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ const SettingsNav = ({ menu }) => {
 | 
			
		||||
        {sections.map(section => (
 | 
			
		||||
          <SubNavSection key={section.id} label={formatMessage(section.intlLabel)}>
 | 
			
		||||
            {section.links.map(link => (
 | 
			
		||||
              <SubNavLink to={link.to} key={link.id}>
 | 
			
		||||
              <SubNavLink withBullet={link.hasNotification} to={link.to} key={link.id}>
 | 
			
		||||
                {formatMessage(link.intlLabel)}
 | 
			
		||||
              </SubNavLink>
 | 
			
		||||
            ))}
 | 
			
		||||
 | 
			
		||||
@ -40,8 +40,8 @@
 | 
			
		||||
    "@fortawesome/react-fontawesome": "^0.1.14",
 | 
			
		||||
    "@strapi/babel-plugin-switch-ee-ce": "1.0.0",
 | 
			
		||||
    "@strapi/helper-plugin": "3.6.7",
 | 
			
		||||
    "@strapi/icons": "0.0.1-alpha.18",
 | 
			
		||||
    "@strapi/parts": "0.0.1-alpha.18",
 | 
			
		||||
    "@strapi/icons": "0.0.1-alpha.19",
 | 
			
		||||
    "@strapi/parts": "0.0.1-alpha.19",
 | 
			
		||||
    "@strapi/utils": "3.6.7",
 | 
			
		||||
    "axios": "^0.21.1",
 | 
			
		||||
    "babel-loader": "8.2.2",
 | 
			
		||||
 | 
			
		||||
@ -53,8 +53,8 @@
 | 
			
		||||
    "@storybook/builder-webpack5": "^6.3.7",
 | 
			
		||||
    "@storybook/manager-webpack5": "^6.3.7",
 | 
			
		||||
    "@storybook/react": "^6.3.7",
 | 
			
		||||
    "@strapi/icons": "0.0.1-alpha.18",
 | 
			
		||||
    "@strapi/parts": "0.0.1-alpha.18",
 | 
			
		||||
    "@strapi/icons": "0.0.1-alpha.19",
 | 
			
		||||
    "@strapi/parts": "0.0.1-alpha.19",
 | 
			
		||||
    "babel-loader": "^8.2.2",
 | 
			
		||||
    "enzyme": "^3.8.0",
 | 
			
		||||
    "enzyme-adapter-react-16": "^1.15.6",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								yarn.lock
									
									
									
									
									
								
							@ -4915,15 +4915,15 @@
 | 
			
		||||
    resolve-from "^5.0.0"
 | 
			
		||||
    store2 "^2.12.0"
 | 
			
		||||
 | 
			
		||||
"@strapi/icons@0.0.1-alpha.18":
 | 
			
		||||
  version "0.0.1-alpha.18"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.18.tgz#c64ca4a896b965b1ea02b2863352391daa5406ff"
 | 
			
		||||
  integrity sha512-QDjCckoI5vGQc6eH/rgFxLMGrBkYqmbNsMAb7T5GOUhJxoSymPM4VPKOjqL36u6MRpi1xqYdw3GBiv48tnxZVw==
 | 
			
		||||
"@strapi/icons@0.0.1-alpha.19":
 | 
			
		||||
  version "0.0.1-alpha.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.19.tgz#88153c77ff02deccfedffb1dc2e7f0f4cc2f830a"
 | 
			
		||||
  integrity sha512-nFpT3Gsp6nQ4+FNd9DkbqhMZrX9KJbWXLQ6XFHhtYdNVA3n5vr2/0MFaqWrvYwxpnxMs3gn1xZOjG4+VzLBwbA==
 | 
			
		||||
 | 
			
		||||
"@strapi/parts@0.0.1-alpha.18":
 | 
			
		||||
  version "0.0.1-alpha.18"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.18.tgz#19306892ba98404f7554360760f1870a2e57e381"
 | 
			
		||||
  integrity sha512-x0LcIounSdgFY7lVsOjinh6ksS9/Nw2/CHNDVW7ZZTuXS5QHODebNlZibrTyPVZNLSJniYqn8P0rxAtc+FcLjQ==
 | 
			
		||||
"@strapi/parts@0.0.1-alpha.19":
 | 
			
		||||
  version "0.0.1-alpha.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.19.tgz#e4c64c1f63256ab21f4ad643f684738a283c0efc"
 | 
			
		||||
  integrity sha512-NzhwmCJnMNlCcRHpXTxILm0rVH+qLl2JoaCxx7XoMRnq4JZHHBaLjEiyTZHYJXRTdro+YUcWeayssL9kqEX8cw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@internationalized/number" "^3.0.2"
 | 
			
		||||
    compute-scroll-into-view "^1.0.17"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user