diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js new file mode 100644 index 0000000000..c985095fd8 --- /dev/null +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js @@ -0,0 +1,126 @@ +import React, { useRef } from 'react'; +import styled from 'styled-components'; +import PropTypes from 'prop-types'; +import { Row } from '@strapi/parts/Row'; +import { Box } from '@strapi/parts/Box'; +import { ButtonText } from '@strapi/parts/Text'; +import { Stack } from '@strapi/parts/Stack'; +import EditIcon from '@strapi/icons/EditIcon'; +import CloseAlertIcon from '@strapi/icons/CloseAlertIcon'; +import Drag from '@strapi/icons/Drag'; + +const ActionButton = styled.button` + display: flex; + align-items: center; + height: ${({ theme }) => theme.spaces[7]}; + + &:last-child { + padding: 0 ${({ theme }) => theme.spaces[3]}; + } +`; + +const DragButton = styled(ActionButton)` + padding: 0 ${({ theme }) => theme.spaces[3]}; + border-right: 1px solid ${({ theme }) => theme.colors.neutral150}; + cursor: all-scroll; + + svg { + width: ${12 / 16}rem; + height: ${12 / 16}rem; + } +`; + +const FieldContainer = styled(Row)` + min-width: ${200 / 16}rem; + cursor: pointer; + + svg { + width: ${10 / 16}rem; + height: ${10 / 16}rem; + + path { + fill: ${({ theme }) => theme.colors.neutral600}; + } + } + + &:hover { + background-color: ${({ theme }) => theme.colors.primary100}; + border-color: ${({ theme }) => theme.colors.primary200}; + + svg { + path { + fill: ${({ theme }) => theme.colors.primary600}; + } + } + + ${ButtonText} { + color: ${({ theme }) => theme.colors.primary600}; + } + + ${DragButton} { + border-right: 1px solid ${({ theme }) => theme.colors.primary200}; + } + } +`; + +const FieldWrapper = styled(Box)` + &:last-child { + padding-right: ${({ theme }) => theme.spaces[3]}; + } +`; + +const DraggableCard = ({ title, onRemoveField }) => { + const editButtonRef = useRef(); + + const rowHandleClick = () => { + if (editButtonRef.current) { + editButtonRef.current.click(); + } + }; + + return ( + + + + + + + {title} + + + { + e.stopPropagation(); + console.log('edit'); + }} + aria-label={`edit ${title}`} + type="button" + > + + + + + + + + + ); +}; + +DraggableCard.defaultProps = { + onRemoveField: () => {}, +}; + +DraggableCard.propTypes = { + onRemoveField: PropTypes.func, + title: PropTypes.string.isRequired, +}; + +export default DraggableCard; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/index.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/index.js index 0806bcd420..b7798467be 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/index.js @@ -1,52 +1,68 @@ -import React, { - memo, - useContext, - // useMemo, - useReducer, - useState, -} from 'react'; +import React, { memo, useContext, useMemo, useReducer, useState } from 'react'; import PropTypes from 'prop-types'; +import styled from 'styled-components'; import { useMutation } from 'react-query'; -import { isEqual, upperFirst, pick } from 'lodash'; +import { isEqual, upperFirst, pick, get } from 'lodash'; import { stringify } from 'qs'; import { useNotification, useTracking, ConfirmDialog } from '@strapi/helper-plugin'; import { useIntl } from 'react-intl'; import { Box } from '@strapi/parts/Box'; +import { Row } from '@strapi/parts/Row'; +import { Stack } from '@strapi/parts/Stack'; import { Divider } from '@strapi/parts/Divider'; import { Layout, HeaderLayout, ContentLayout } from '@strapi/parts/Layout'; import { Link } from '@strapi/parts/Link'; import { Main } from '@strapi/parts/Main'; +import { H3 } from '@strapi/parts/Text'; +import { Select, Option } from '@strapi/parts/Select'; import { Button } from '@strapi/parts/Button'; import CheckIcon from '@strapi/icons/CheckIcon'; import BackIcon from '@strapi/icons/BackIcon'; +// import LayoutDndProvider from '../../components/LayoutDndProvider'; +import { checkIfAttributeIsDisplayable, getTrad } from '../../utils'; import ModelsContext from '../../contexts/ModelsContext'; import { usePluginsQueryParams } from '../../hooks'; import putCMSettingsLV from './utils/api'; import Settings from './components/Settings'; -// import LayoutDndProvider from '../../components/LayoutDndProvider'; +import DraggableCard from './components/DraggableCard'; import init from './init'; import reducer, { initialState } from './reducer'; +const Flex = styled(Box)` + flex: ${({ size }) => size}; +`; + +const ScrollableContainer = styled(Flex)` + overflow-x: scroll; + overflow-y: hidden; +`; + +const SelectContainer = styled(Flex)` + max-width: ${200 / 16}rem; +`; + const ListSettingsView = ({ layout, slug, updateLayout }) => { + const { formatMessage } = useIntl(); + const { trackUsage } = useTracking(); const pluginsQueryParams = usePluginsQueryParams(); const toggleNotification = useNotification(); const { refetchData } = useContext(ModelsContext); + const [showWarningSubmit, setWarningSubmit] = useState(false); + const toggleWarningSubmit = () => setWarningSubmit(prevState => !prevState); const [reducerState, dispatch] = useReducer(reducer, initialState, () => init(initialState, layout) ); // const [isOpen, setIsOpen] = useState(false); // const [isModalFormOpen, setIsModalFormOpen] = useState(false); // const [isDraggingSibling, setIsDraggingSibling] = useState(false); - const { formatMessage } = useIntl(); - const { trackUsage } = useTracking(); // const toggleModalForm = () => setIsModalFormOpen(prevState => !prevState); + const { // labelForm, // labelToEdit, initialData, modifiedData, } = reducerState; - // const metadatas = get(modifiedData, ['metadatas'], {}); // const attributes = useMemo(() => { // return get(modifiedData, ['attributes'], {}); @@ -54,66 +70,12 @@ const ListSettingsView = ({ layout, slug, updateLayout }) => { const { attributes } = layout; - // const displayedFields = useMemo(() => { - // return get(modifiedData, ['layouts', 'list'], []); - // }, [modifiedData]); + const displayedFields = useMemo(() => { + return get(modifiedData, ['layouts', 'list'], []); + }, [modifiedData]); const excludedSortOptions = ['media', 'richtext', 'dynamiczone', 'relation', 'component', 'json']; - const sortOptions = Object.entries(attributes).reduce((acc, cur) => { - const [name, { type }] = cur; - - if (!excludedSortOptions.includes(type)) { - acc.push(name); - } - - return acc; - }, []); - - // const listRemainingFields = useMemo(() => { - // return Object.keys(metadatas) - // .filter(key => { - // return checkIfAttributeIsDisplayable(get(attributes, key, {})); - // }) - // .filter(field => { - // return !displayedFields.includes(field); - // }) - // .sort(); - // }, [displayedFields, attributes, metadatas]); - - // console.log(displayedFields, listRemainingFields); - - // const handleClickEditLabel = labelToEdit => { - // dispatch({ - // type: 'SET_LABEL_TO_EDIT', - // labelToEdit, - // }); - // toggleModalForm(); - // }; - - // const handleClosed = () => { - // dispatch({ - // type: 'UNSET_LABEL_TO_EDIT', - // }); - // }; - - const handleChange = ({ target: { name, value } }) => { - dispatch({ - type: 'ON_CHANGE', - keys: name, - value: name === 'settings.pageSize' ? parseInt(value, 10) : value, - }); - }; - - const [showWarningSubmit, setWarningSubmit] = useState(false); - const toggleWarningSubmit = () => setWarningSubmit(prevState => !prevState); - - const handleSubmit = e => { - e.preventDefault(); - toggleWarningSubmit(); - trackUsage('willSaveContentTypeLayout'); - }; - const goBackUrl = () => { const { settings: { pageSize, defaultSortBy, defaultSortOrder }, @@ -133,19 +95,59 @@ const ListSettingsView = ({ layout, slug, updateLayout }) => { return `/content-manager/${kind}/${uid}?${goBackSearch}`; }; - // const handleChangeEditLabel = ({ target: { name, value } }) => { - // dispatch({ - // type: 'ON_CHANGE_LABEL_METAS', - // name, - // value, - // }); - // }; + const handleChange = ({ target: { name, value } }) => { + dispatch({ + type: 'ON_CHANGE', + keys: name, + value: name === 'settings.pageSize' ? parseInt(value, 10) : value, + }); + console.log('here'); + }; const handleConfirm = async () => { const body = pick(modifiedData, ['layouts', 'settings', 'metadatas']); submitMutation.mutateAsync(body); }; + const handleAddField = item => { + dispatch({ + type: 'ADD_FIELD', + item, + }); + }; + + const handleRemoveField = (e, index) => { + e.stopPropagation(); + + if (displayedFields.length === 1) { + toggleNotification({ + type: 'info', + message: { id: getTrad('notification.info.minimumFields') }, + }); + } else { + dispatch({ + type: 'REMOVE_FIELD', + index, + }); + } + }; + + const handleSubmit = e => { + e.preventDefault(); + toggleWarningSubmit(); + trackUsage('willSaveContentTypeLayout'); + }; + + const sortOptions = Object.entries(attributes).reduce((acc, cur) => { + const [name, { type }] = cur; + + if (!excludedSortOptions.includes(type)) { + acc.push(name); + } + + return acc; + }, []); + const submitMutation = useMutation(body => putCMSettingsLV(body, slug), { onSuccess: async ({ data: { data } }) => { updateLayout(data); @@ -164,9 +166,42 @@ const ListSettingsView = ({ layout, slug, updateLayout }) => { }, refetchActive: true, }); - const { isLoading: isSubmittingForm } = submitMutation; + const metadatas = get(modifiedData, ['metadatas'], {}); + const listRemainingFields = useMemo(() => { + return Object.keys(metadatas) + .filter(key => { + return checkIfAttributeIsDisplayable(get(attributes, key, {})); + }) + .filter(field => { + return !displayedFields.includes(field); + }) + .sort(); + }, [displayedFields, attributes, metadatas]); + + // const handleClickEditLabel = labelToEdit => { + // dispatch({ + // type: 'SET_LABEL_TO_EDIT', + // labelToEdit, + // }); + // toggleModalForm(); + // }; + + // const handleClosed = () => { + // dispatch({ + // type: 'UNSET_LABEL_TO_EDIT', + // }); + // }; + + // const handleChangeEditLabel = ({ target: { name, value } }) => { + // dispatch({ + // type: 'ON_CHANGE_LABEL_METAS', + // name, + // value, + // }); + // }; + // const move = (originalIndex, atIndex) => { // dispatch({ // type: 'MOVE_FIELD', @@ -263,9 +298,47 @@ const ListSettingsView = ({ layout, slug, updateLayout }) => { onChange={handleChange} sortOptions={sortOptions} /> - + + +

+ {formatMessage({ + id: 'content-manager.containers.SettingPage.view', + defaultMessage: 'View', + })} +

+
+ + + + {displayedFields.map((field, index) => ( + handleRemoveField(e, index)} + key={field} + title={field} + /> + ))} + + + + + +
// eslint-disable-next-line consistent-return produce(state, draftState => { - // const layoutFieldListPath = ['modifiedData', 'layouts', 'list']; + const layoutFieldListPath = ['modifiedData', 'layouts', 'list']; switch (action.type) { - // case 'ADD_FIELD': { - // const layoutFieldList = get(state, layoutFieldListPath, []); - // set(draftState, layoutFieldListPath, [...layoutFieldList, action.item]); - // break; - // } + case 'ADD_FIELD': { + const layoutFieldList = get(state, layoutFieldListPath, []); + set(draftState, layoutFieldListPath, [action.item, ...layoutFieldList]); + break; + } // case 'MOVE_FIELD': { // const layoutFieldList = get(state, layoutFieldListPath, []); // const { originalIndex, atIndex } = action; @@ -43,15 +43,15 @@ const reducer = (state = initialState, action) => // draftState.modifiedData = state.initialData; // break; // } - // case 'REMOVE_FIELD': { - // const layoutFieldList = get(state, layoutFieldListPath, []); - // set( - // draftState, - // layoutFieldListPath, - // layoutFieldList.filter((_, index) => action.index !== index) - // ); - // break; - // } + case 'REMOVE_FIELD': { + const layoutFieldList = get(state, layoutFieldListPath, []); + set( + draftState, + layoutFieldListPath, + layoutFieldList.filter((_, index) => action.index !== index) + ); + break; + } // case 'SET_LABEL_TO_EDIT': { // const { labelToEdit } = action; // draftState.labelToEdit = labelToEdit; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/reducer.test.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/reducer.test.js index 90505406e9..8e3d8a1ae4 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/reducer.test.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/reducer.test.js @@ -19,21 +19,21 @@ describe('CONTENT MANAGER | CONTAINERS | ListSettingsView | reducer', () => { expect(reducer(state, {})).toEqual(expected); }); - // describe('ADD_FIELD', () => { - // it('should add a field to the layout correctly', () => { - // const expected = { - // ...state, - // modifiedData: { - // layouts: { - // list: ['title'], - // }, - // }, - // }; - // const action = { type: 'ADD_FIELD', item: 'title' }; + describe('ADD_FIELD', () => { + it('should add a field to the layout correctly', () => { + const expected = { + modifiedData: { + layouts: { + list: ['title'], + }, + }, + ...state, + }; + const action = { type: 'ADD_FIELD', item: 'title' }; - // expect(reducer(state, action)).toEqual(expected); - // }); - // }); + expect(reducer(state, action)).toEqual(expected); + }); + }); // describe('MOVE_FIELD', () => { // it('should replace the title by the description and vice-versa', () => { @@ -103,32 +103,32 @@ describe('CONTENT MANAGER | CONTAINERS | ListSettingsView | reducer', () => { // }); // }); - // describe('REMOVE_FIELD', () => { - // it('should remove the field', () => { - // state.modifiedData = { - // layouts: { - // list: ['id', 'description', 'title'], - // }, - // settings: { - // defaultSortBy: 'id', - // }, - // }; - // const expected = { - // ...state, - // modifiedData: { - // layouts: { - // list: ['id', 'title'], - // }, - // settings: { - // defaultSortBy: 'id', - // }, - // }, - // }; - // const action = { type: 'REMOVE_FIELD', index: 1 }; + describe('REMOVE_FIELD', () => { + it('should remove the field', () => { + state.modifiedData = { + layouts: { + list: ['id', 'description', 'title'], + }, + settings: { + defaultSortBy: 'id', + }, + }; + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['id', 'title'], + }, + settings: { + defaultSortBy: 'id', + }, + }, + }; + const action = { type: 'REMOVE_FIELD', index: 1 }; - // expect(reducer(state, action)).toEqual(expected); - // }); - // }); + expect(reducer(state, action)).toEqual(expected); + }); + }); // describe('SET_LABEL_TO_EDIT', () => { // it('should set the label form data of the field to edit', () => {