mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
Merge branch 'migrations/admin-users-delete' of github.com:strapi/strapi into migrations/admin-users-search
This commit is contained in:
commit
5b34f91d82
@ -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 }} />
|
||||
<ConditionsModal
|
||||
headerBreadCrumbs={[label, 'app.components.LeftMenuLinkContainer.settings']}
|
||||
actions={checkboxesActions}
|
||||
isOpen={isModalOpen}
|
||||
isFormDisabled={isFormDisabled}
|
||||
onClosed={handleModalClose}
|
||||
onToggle={handleToggleModalIsOpen}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<ConditionsModal
|
||||
headerBreadCrumbs={[label, 'app.components.LeftMenuLinkContainer.settings']}
|
||||
actions={checkboxesActions}
|
||||
isFormDisabled={isFormDisabled}
|
||||
onClosed={handleModalClose}
|
||||
onToggle={handleToggleModalIsOpen}
|
||||
/>
|
||||
)}
|
||||
</Wrapper>
|
||||
<AbsoluteBox>
|
||||
<ConditionsButton
|
||||
|
||||
@ -130,14 +130,15 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p
|
||||
/>
|
||||
</Row>
|
||||
</Box>
|
||||
<ConditionsModal
|
||||
headerBreadCrumbs={[categoryName, subCategoryName]}
|
||||
actions={formattedActions}
|
||||
isOpen={isModalOpen}
|
||||
isFormDisabled={isFormDisabled}
|
||||
onClosed={handleModalClose}
|
||||
onToggle={handleToggleModalIsOpen}
|
||||
/>
|
||||
{isModalOpen && (
|
||||
<ConditionsModal
|
||||
headerBreadCrumbs={[categoryName, subCategoryName]}
|
||||
actions={formattedActions}
|
||||
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>
|
||||
))}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user