diff --git a/package.json b/package.json index 34b7282f5a..e93100da5b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@babel/preset-react": "^7.8.3", "@babel/runtime": "^7.8.3", "@hapi/joi": "^15.1.1", + "@trbl/react-modal": "0.1.0", "accept-language-parser": "^1.5.0", "async-some": "^1.0.2", "babel-jest": "^24.9.0", diff --git a/src/client/components/Routes.js b/src/client/components/Routes.js index 5f738e6e96..4f90d12d8d 100644 --- a/src/client/components/Routes.js +++ b/src/client/components/Routes.js @@ -15,6 +15,7 @@ import Edit from './views/collections/Edit'; import EditGlobal from './views/globals/Edit'; import { requests } from '../api'; import customComponents from './custom-components'; +import RedirectToLogin from './utilities/RedirectToLogin'; const Routes = () => { const [initialized, setInitialized] = useState(null); @@ -151,7 +152,7 @@ const Routes = () => { ); } - return ; + return ; }} /> diff --git a/src/client/components/data/User.js b/src/client/components/data/User.js index 5ce7a3f27d..c50a556a76 100644 --- a/src/client/components/data/User.js +++ b/src/client/components/data/User.js @@ -9,14 +9,26 @@ const cookies = new Cookies(); const Context = createContext({}); const UserProvider = ({ children }) => { - const cookieToken = cookies.get('token'); const [token, setToken] = useState(''); - const [user, setUser] = useState(cookieToken ? jwtDecode(cookieToken) : null); + const [user, setUser] = useState(null); + + useEffect(() => { + const cookieToken = cookies.get('token'); + if (cookieToken) { + const decoded = jwtDecode(cookieToken); + if (decoded.exp > Date.now() / 1000) { + setUser(decoded); + } + } + }, []); useEffect(() => { if (token) { - setUser(jwtDecode(token)); - cookies.set('token', token, { path: '/' }); + const decoded = jwtDecode(token); + if (decoded.exp > Date.now() / 1000) { + setUser(decoded); + cookies.set('token', token, { path: '/' }); + } } }, [token]); diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js index bb56f5f74a..3d7c3ac873 100644 --- a/src/client/components/forms/Form/index.js +++ b/src/client/components/forms/Form/index.js @@ -123,8 +123,6 @@ const Form = (props) => { baseClass, ].filter(Boolean).join(' '); - // console.log(fields); - return (
{ + const { + addRow, + blocks, + rowIndexBeingAdded, + closeAllModals, + } = props; + + const handleAddRow = (blockType) => { + addRow(rowIndexBeingAdded, blockType); + closeAllModals(); + }; + + return ( +
+
    + {blocks.map((block, i) => { + return ( +
  • + +
  • + ); + })} +
+
+ ); +}; + +AddRowModal.defaultProps = { + rowIndexBeingAdded: null, +}; + +AddRowModal.propTypes = { + addRow: PropTypes.func.isRequired, + closeAllModals: PropTypes.func.isRequired, + blocks: PropTypes.arrayOf( + PropTypes.shape({ + labels: PropTypes.shape({ + singular: PropTypes.string, + }), + previewImage: PropTypes.string, + slug: PropTypes.string, + }), + ).isRequired, + rowIndexBeingAdded: PropTypes.number, +}; + +export default asModal(AddRowModal); diff --git a/src/client/components/forms/field-types/Flexible/FlexibleRow/index.js b/src/client/components/forms/field-types/Flexible/FlexibleRow/index.js new file mode 100644 index 0000000000..e7b01b5412 --- /dev/null +++ b/src/client/components/forms/field-types/Flexible/FlexibleRow/index.js @@ -0,0 +1,125 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import AnimateHeight from 'react-animate-height'; +import { Draggable } from 'react-beautiful-dnd'; + +// eslint-disable-next-line import/no-cycle +import RenderFields from '../../../RenderFields'; +import IconButton from '../../../../controls/IconButton'; + +import './index.scss'; + +const baseClass = 'flexible-row'; + +const FlexibleRow = (props) => { + const { + addRow, + removeRow, + rowIndex, + parentName, + block, + defaultValue, + dispatchCollapsibleStates, + collapsibleStates, + } = props; + + const handleCollapseClick = () => { + dispatchCollapsibleStates({ + type: 'UPDATE_COLLAPSIBLE_STATUS', + collapsibleIndex: rowIndex, + }); + }; + + return ( + + {(providedDrag) => { + return ( +
+
+
+ +
+ {`${block.labels.singular} ${rowIndex + 1 > 9 ? rowIndex + 1 : `0${rowIndex + 1}`}`} +
+ +
+ + + + + + +
+
+ + + { + const fieldName = `${parentName}.${rowIndex}.${field.name}`; + return ({ + ...field, + name: fieldName, + defaultValue: defaultValue?.[field.name], + }); + })} + /> + +
+ ); + }} + + ); +}; + +FlexibleRow.defaultProps = { + defaultValue: null, + collapsibleStates: [], +}; + +FlexibleRow.propTypes = { + block: PropTypes.shape({ + labels: PropTypes.shape({ + singular: PropTypes.string, + }), + fields: PropTypes.arrayOf(PropTypes.shape({})).isRequired, + slug: PropTypes.string, + }).isRequired, + addRow: PropTypes.func.isRequired, + removeRow: PropTypes.func.isRequired, + rowIndex: PropTypes.number.isRequired, + parentName: PropTypes.string.isRequired, + fieldState: PropTypes.shape({}).isRequired, + defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.shape({})]), + dispatchCollapsibleStates: PropTypes.func.isRequired, + collapsibleStates: PropTypes.arrayOf(PropTypes.bool), +}; + +export default FlexibleRow; diff --git a/src/client/components/forms/field-types/Flexible/FlexibleRow/index.scss b/src/client/components/forms/field-types/Flexible/FlexibleRow/index.scss new file mode 100644 index 0000000000..dbaa9483be --- /dev/null +++ b/src/client/components/forms/field-types/Flexible/FlexibleRow/index.scss @@ -0,0 +1,90 @@ +@import '../../../../../scss/styles.scss'; + +.flexible-row { + background: $light-gray; + flex-direction: column; + position: relative; + margin-top: base(.5); + + &:hover { + @include shadow-sm; + } + + &__collapse__icon { + &--open { + svg { + transform: rotate(90deg); + } + } + + &--closed { + svg { + transform: rotate(-90deg); + } + } + } + + &__header { + padding: base(.75) base(1); + display: flex; + align-items: center; + position: relative; + + &__drag-handle { + width: 100%; + height: 100%; + position: absolute; + left: 0; + z-index: 1; + } + + // elements above the drag handle + &__controls, + &__header__row-index { + position: relative; + z-index: 2; + } + + &__row-index { + font-family: $font-body; + font-size: base(.5); + } + + &__heading { + font-family: $font-body; + margin: 0; + font-size: base(.65); + } + + &__controls { + margin-left: auto; + + .btn { + margin-top: 0; + margin-bottom: 0; + } + + .icon-button--crossOut, + .icon-button--crosshair { + margin-right: base(.25); + } + + .icon-button--crosshair { + border-color: $primary; + @include color-svg($primary); + + &:hover { + background: lighten($primary, 50%); + } + } + } + } + + &__content { + box-shadow: inset 0px 1px 0px white; + + > div { + padding: base(.75) base(1); + } + } +} diff --git a/src/client/components/forms/field-types/Flexible/index.js b/src/client/components/forms/field-types/Flexible/index.js new file mode 100644 index 0000000000..7abd4a11e7 --- /dev/null +++ b/src/client/components/forms/field-types/Flexible/index.js @@ -0,0 +1,165 @@ +import React, { + useContext, useEffect, useReducer, useState, Fragment, +} from 'react'; +import PropTypes from 'prop-types'; +import { DragDropContext, Droppable } from 'react-beautiful-dnd'; +import { ModalContext } from '@trbl/react-modal'; + +import FormContext from '../../Form/Context'; +import Section from '../../../layout/Section'; +import FlexibleRow from './FlexibleRow'; // eslint-disable-line import/no-cycle +import AddRowModal from './AddRowModal'; +import collapsibleReducer from './reducer'; + +import './index.scss'; + +const baseClass = 'field-type flexible'; + +const Flexible = (props) => { + const { + label, + name, + blocks, + defaultValue, + } = props; + + const { toggle: toggleModal, closeAll: closeAllModals } = useContext(ModalContext); + const [rowIndexBeingAdded, setRowIndexBeingAdded] = useState(null); + const [rowCount, setRowCount] = useState(0); + const [collapsibleStates, dispatchCollapsibleStates] = useReducer(collapsibleReducer, []); + const formContext = useContext(FormContext); + const modalSlug = `flexible-${name}`; + + const { fields: fieldState, dispatchFields } = formContext; + + const addRow = (rowIndex, blockType) => { + const blockToAdd = blocks.find(block => block.slug === blockType); + + dispatchFields({ + type: 'ADD_ROW', rowIndex, name, fields: blockToAdd.fields, blockType, + }); + + dispatchCollapsibleStates({ + type: 'ADD_COLLAPSIBLE', collapsibleIndex: rowIndex, + }); + + setRowCount(rowCount + 1); + }; + + const removeRow = (rowIndex) => { + dispatchFields({ + type: 'REMOVE_ROW', rowIndex, name, + }); + + dispatchCollapsibleStates({ + type: 'REMOVE_COLLAPSIBLE', + collapsibleIndex: rowIndex, + }); + + setRowCount(rowCount - 1); + }; + + const moveRow = (moveFromIndex, moveToIndex) => { + dispatchFields({ + type: 'MOVE_ROW', moveFromIndex, moveToIndex, name, + }); + + dispatchCollapsibleStates({ + type: 'MOVE_COLLAPSIBLE', collapsibleIndex: moveFromIndex, moveToIndex, + }); + }; + + useEffect(() => { + setRowCount(defaultValue.length); + + dispatchCollapsibleStates({ + type: 'SET_ALL_COLLAPSIBLES', + payload: Array.from(Array(defaultValue.length).keys()).reduce(acc => ([...acc, true]), []), // sets all collapsibles to open on first load + }); + }, [defaultValue]); + + const openAddRowModal = (rowIndex) => { + setRowIndexBeingAdded(rowIndex); + toggleModal(modalSlug); + }; + + const onDragEnd = (result) => { + if (!result.destination) return; + const sourceIndex = result.source.index; + const destinationIndex = result.destination.index; + moveRow(sourceIndex, destinationIndex); + }; + + return ( + + +
+
openAddRowModal(0)} + useAddRowButton + > + + {provided => ( +
+ {rowCount !== 0 + && Array.from(Array(rowCount).keys()).map((_, rowIndex) => { + const blockType = fieldState[`${name}.${rowIndex}.blockType`]; + const blockToRender = blocks.find(block => block.slug === blockType); + + return ( + openAddRowModal(rowIndex)} + removeRow={() => removeRow(rowIndex)} + rowIndex={rowIndex} + fieldState={fieldState} + block={blockToRender} + defaultValue={defaultValue[rowIndex]} + dispatchCollapsibleStates={dispatchCollapsibleStates} + collapsibleStates={collapsibleStates} + /> + ); + }) + } + {provided.placeholder} +
+ )} +
+
+
+
+ +
+ ); +}; + +Flexible.defaultProps = { + label: '', + defaultValue: [], +}; + +Flexible.propTypes = { + defaultValue: PropTypes.arrayOf( + PropTypes.shape({}), + ), + blocks: PropTypes.arrayOf( + PropTypes.shape({}), + ).isRequired, + label: PropTypes.string, + name: PropTypes.string.isRequired, +}; + +export default Flexible; diff --git a/src/client/components/forms/field-types/Flexible/index.scss b/src/client/components/forms/field-types/Flexible/index.scss new file mode 100644 index 0000000000..617a36d1ad --- /dev/null +++ b/src/client/components/forms/field-types/Flexible/index.scss @@ -0,0 +1,3 @@ +.field-type.flexible { + background: white; +} diff --git a/src/client/components/forms/field-types/Flexible/reducer.js b/src/client/components/forms/field-types/Flexible/reducer.js new file mode 100644 index 0000000000..4ab7f3bc82 --- /dev/null +++ b/src/client/components/forms/field-types/Flexible/reducer.js @@ -0,0 +1,35 @@ +const collapsibleReducer = (currentState, action) => { + const { + type, collapsibleIndex, moveToIndex, payload, + } = action; + + const stateCopy = [...currentState]; + const movingCollapsibleState = stateCopy[collapsibleIndex]; + + switch (type) { + case 'SET_ALL_COLLAPSIBLES': + return payload; + + case 'ADD_COLLAPSIBLE': + stateCopy.splice(collapsibleIndex + 1, 0, true); + return stateCopy; + + case 'REMOVE_COLLAPSIBLE': + stateCopy.splice(collapsibleIndex, 1); + return stateCopy; + + case 'UPDATE_COLLAPSIBLE_STATUS': + stateCopy[collapsibleIndex] = !movingCollapsibleState; + return stateCopy; + + case 'MOVE_COLLAPSIBLE': + stateCopy.splice(collapsibleIndex, 1); + stateCopy.splice(moveToIndex, 0, movingCollapsibleState); + return stateCopy; + + default: + return currentState; + } +}; + +export default collapsibleReducer; diff --git a/src/client/components/forms/field-types/Repeater/RepeaterRow/index.js b/src/client/components/forms/field-types/Repeater/RepeaterRow/index.js index 8b60e83c1a..9b82743c5f 100644 --- a/src/client/components/forms/field-types/Repeater/RepeaterRow/index.js +++ b/src/client/components/forms/field-types/Repeater/RepeaterRow/index.js @@ -101,7 +101,6 @@ RepeaterRow.propTypes = { rowIndex: PropTypes.number.isRequired, parentName: PropTypes.string.isRequired, fields: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - fieldState: PropTypes.shape({}).isRequired, rowCount: PropTypes.number, defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.shape({})]), dispatchCollapsibleStates: PropTypes.func.isRequired, diff --git a/src/client/components/forms/field-types/Repeater/index.js b/src/client/components/forms/field-types/Repeater/index.js index b197fc7ef9..a9d8ed1881 100644 --- a/src/client/components/forms/field-types/Repeater/index.js +++ b/src/client/components/forms/field-types/Repeater/index.js @@ -94,23 +94,22 @@ const Repeater = (props) => { {...provided.droppableProps} > {rowCount !== 0 - && Array.from(Array(rowCount).keys()).map((_, rowIndex) => { - return ( - addRow(rowIndex)} - removeRow={() => removeRow(rowIndex)} - rowIndex={rowIndex} - fieldState={fieldState} - fields={fields} - rowCount={rowCount} - defaultValue={defaultValue[rowIndex]} - dispatchCollapsibleStates={dispatchCollapsibleStates} - collapsibleStates={collapsibleStates} - /> - ); - }) + && Array.from(Array(rowCount).keys()).map((_, rowIndex) => { + return ( + addRow(rowIndex)} + removeRow={() => removeRow(rowIndex)} + rowIndex={rowIndex} + fields={fields} + rowCount={rowCount} + defaultValue={defaultValue[rowIndex]} + dispatchCollapsibleStates={dispatchCollapsibleStates} + collapsibleStates={collapsibleStates} + /> + ); + }) } {provided.placeholder}
diff --git a/src/client/components/forms/field-types/index.js b/src/client/components/forms/field-types/index.js index 88c0403b4b..9c5707b156 100644 --- a/src/client/components/forms/field-types/index.js +++ b/src/client/components/forms/field-types/index.js @@ -6,6 +6,7 @@ import text from './Text'; import relationship from './Relationship'; import password from './Password'; import repeater from './Repeater'; +import flexible from './Flexible'; import textarea from './Textarea'; import select from './Select'; import number from './Number'; @@ -18,6 +19,7 @@ export default { text, relationship, // upload, + flexible, number, password, repeater, diff --git a/src/client/components/forms/useFieldType/index.js b/src/client/components/forms/useFieldType/index.js index 8160bd657c..4242ee5676 100644 --- a/src/client/components/forms/useFieldType/index.js +++ b/src/client/components/forms/useFieldType/index.js @@ -26,7 +26,7 @@ const useFieldType = (options) => { }, [name, required, dispatchFields, validate]); useEffect(() => { - sendField(defaultValue); + if (defaultValue != null) sendField(defaultValue); }, [defaultValue, sendField]); const valid = formContext.fields[name] ? formContext.fields[name].valid : true; diff --git a/src/client/components/index.js b/src/client/components/index.js index f11481675c..61b3d745ff 100644 --- a/src/client/components/index.js +++ b/src/client/components/index.js @@ -1,6 +1,7 @@ import React, { Suspense } from 'react'; import { render } from 'react-dom'; import { BrowserRouter as Router } from 'react-router-dom'; +import { ModalProvider, ModalContainer } from '@trbl/react-modal'; import Loading from './views/Loading'; import { SearchParamsProvider } from './utilities/SearchParams'; import { LocaleProvider } from './utilities/Locale'; @@ -14,15 +15,21 @@ const Index = () => { return ( - - - - }> - - - - - + + + + + }> + + + + + + + ); diff --git a/src/client/components/modals/asModal/index.js b/src/client/components/modals/asModal/index.js deleted file mode 100644 index e860cd63dc..0000000000 --- a/src/client/components/modals/asModal/index.js +++ /dev/null @@ -1,102 +0,0 @@ -/////////////////////////////////////////////////////// -// Takes a modal component and -// a slug to match against a 'modal' URL param -/////////////////////////////////////////////////////// - -import React, { Component } from 'react'; -import { createPortal } from 'react-dom'; -import { withRouter } from 'react-router'; -import queryString from 'qs'; -import Close from '../../graphics/Close'; -import Button from '../../controls/Button'; - -import './index.scss'; - -const asModal = (PassedComponent, modalSlug) => { - - class AsModal extends Component { - - constructor(props) { - super(props); - - this.state = { - open: false, - el: null - } - } - - bindEsc = event => { - if (event.keyCode === 27) { - const params = { ...this.props.searchParams }; - delete params.modal; - - this.props.history.push({ - search: queryString.stringify(params) - }) - } - } - - isOpen = () => { - - // Slug can come from either a HOC or from a prop - const slug = this.props.modalSlug ? this.props.modalSlug : modalSlug; - - if (this.props.searchParams.modal === slug) { - return true; - } - - return false; - } - - componentDidMount() { - document.addEventListener('keydown', this.bindEsc, false); - - if (this.isOpen()) { - this.setState({ open: true }) - } - - // Slug can come from either a HOC or from a prop - const slug = this.props.modalSlug ? this.props.modalSlug : modalSlug; - - this.setState({ - el: document.querySelector(`#${slug}`) - }) - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.bindEsc, false); - } - - componentDidUpdate(prevProps, prevState) { - - let open = this.isOpen(); - - if (open !== prevState.open && open) { - this.setState({ open: true }) - } else if (open !== prevState.open) { - this.setState({ open: false }) - } - } - - render() { - - // Slug can come from either a HOC or from a prop - const slug = this.props.modalSlug ? this.props.modalSlug : modalSlug; - const modalDomNode = document.getElementById('portal'); - - return createPortal( -
- - -
, - modalDomNode - ); - } - } - - return withRouter(connect(mapStateToProps)(AsModal)); -} - -export default asModal; diff --git a/src/client/components/modals/asModal/index.scss b/src/client/components/modals/asModal/index.scss deleted file mode 100644 index c2dda34f77..0000000000 --- a/src/client/components/modals/asModal/index.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import '../../../scss/styles.scss'; - -.modal { - transform: translateZ(0); - opacity: 0; - visibility: hidden; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; - height: 100vh; - background-color: rgba(white, .96); - - &.open { - opacity: 1; - visibility: visible; - z-index: 100; - } -} diff --git a/src/client/components/utilities/RedirectToLogin/index.js b/src/client/components/utilities/RedirectToLogin/index.js new file mode 100644 index 0000000000..1340cccba1 --- /dev/null +++ b/src/client/components/utilities/RedirectToLogin/index.js @@ -0,0 +1,23 @@ +import React, { useEffect } from 'react'; +import { + Redirect, +} from 'react-router-dom'; +import { useStatusList } from '../../modules/Status'; +import config from '../../../config/sanitizedClientConfig'; + +const RedirectToLogin = () => { + const { addStatus } = useStatusList(); + + useEffect(() => { + addStatus({ + message: 'You need to log in to be able to do that.', + type: 'error', + }); + }, [addStatus]); + + return ( + + ); +}; + +export default RedirectToLogin; diff --git a/src/mongoose/buildQuery.js b/src/mongoose/buildQuery.js index 6b866c1524..1e37cd4c74 100644 --- a/src/mongoose/buildQuery.js +++ b/src/mongoose/buildQuery.js @@ -1,4 +1,5 @@ const mongoose = require('mongoose'); + const validOperators = ['like', 'in', 'all', 'nin', 'gte', 'gt', 'lte', 'lt', 'ne']; // This plugin asynchronously builds a list of Mongoose query constraints @@ -12,6 +13,7 @@ function buildQueryPlugin(schema) { if (cb) { model .find(params.searchParams) + .sort(params.sort) .exec(cb); } @@ -36,21 +38,25 @@ class ParamParser { // Entry point to the ParamParser class async parse() { - for (let key of Object.keys(this.rawParams)) { - + for (const key of Object.keys(this.rawParams)) { // If rawParams[key] is an object, that means there are operators present. // Need to loop through keys on rawParams[key] to call addSearchParam on each operator found if (typeof this.rawParams[key] === 'object') { - Object.keys(this.rawParams[key]).forEach(async (operator) => { - const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, key, this.rawParams[key][operator], operator); - this.query.searchParams = this.addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema); - }) + Object.keys(this.rawParams[key]) + .forEach(async (operator) => { + const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, key, this.rawParams[key][operator], operator); + this.query.searchParams = this.addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema); + }); // Otherwise there are no operators present } else { const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, key, this.rawParams[key]); - this.query.searchParams = this.addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema); + if (searchParamKey === 'sort') { + this.query.sort = searchParamValue; + } else { + this.query.searchParams = this.addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema); + } } - }; + } return this.query; } @@ -72,7 +78,7 @@ class ParamParser { if (path) { // If the path is an ObjectId with a direct ref, // Grab it - let ref = path.options.ref; + let { ref } = path.options; // If the path is an Array, grab the ref of the first index type if (path.instance === 'Array') { @@ -89,7 +95,7 @@ class ParamParser { Object.keys(val).forEach(async (operator) => { const [searchParamKey, searchParamValue] = await this.buildSearchParam(subModel.schema, localizedSubKey, val[operator], operator); subQuery = this.addSearchParam(searchParamKey, searchParamValue, subQuery, subModel.schema); - }) + }); } else { const [searchParamKey, searchParamValue] = await this.buildSearchParam(subModel.schema, localizedSubKey, val); subQuery = this.addSearchParam(searchParamKey, searchParamValue, subQuery, subModel.schema); @@ -98,7 +104,7 @@ class ParamParser { const matchingSubDocuments = await subModel.find(subQuery); return [localizedPath, { - $in: matchingSubDocuments.map(subDoc => subDoc.id) + $in: matchingSubDocuments.map(subDoc => subDoc.id), }]; } } @@ -129,8 +135,8 @@ class ParamParser { case 'like': formattedValue = { - '$regex': val, - '$options': '-i', + $regex: val, + $options: '-i', }; break; @@ -148,8 +154,8 @@ class ParamParser { [key]: { ...searchParams[key], ...value, - } - } + }, + }; } return { diff --git a/src/mongoose/requestHandlers/query.js b/src/mongoose/requestHandlers/query.js index f6249c5565..e00c36223e 100644 --- a/src/mongoose/requestHandlers/query.js +++ b/src/mongoose/requestHandlers/query.js @@ -9,6 +9,7 @@ const query = (req, res) => { if (req.query.page) paginateQuery.page = req.query.page; if (req.query.limit) paginateQuery.limit = req.query.limit; + if (req.query.sort) paginateQuery.sort = req.query.sort; if (req.query.depth) { paginateQuery.options.autopopulate = { diff --git a/src/mongoose/schema/buildSchema.js b/src/mongoose/schema/buildSchema.js index ffe913b3f5..59b8b1c5e8 100644 --- a/src/mongoose/schema/buildSchema.js +++ b/src/mongoose/schema/buildSchema.js @@ -31,7 +31,7 @@ const buildSchema = (configFields, config, options = {}, additionalBaseFields = }); const blockSchema = new Schema(blockSchemaFields, { _id: false }); - schema.path(field.name).discriminator(block.labels.singular, blockSchema); + schema.path(field.name).discriminator(block.slug, blockSchema); }); } }); diff --git a/yarn.lock b/yarn.lock index 7811adf328..cccffb25f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1187,6 +1187,28 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@trbl/react-html-element@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@trbl/react-html-element/-/react-html-element-1.0.1.tgz#d8936ad47f64b91257a331789eaf73c324182045" + integrity sha512-I6qU3n9fxLS1TRgF0lQuYA+c6GEcOFNFzyTxy1Voz0B9JsIIFB/1Ky+H/rADfDfRgKSVxoZ0fLAN0zqoE/YwKA== + dependencies: + prop-types "^15.7.2" + react "^16.12.0" + react-dom "^16.12.0" + +"@trbl/react-modal@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@trbl/react-modal/-/react-modal-0.1.0.tgz#23e9f24aeed4dea2a79b4ed23a3d5e50ee71ab26" + integrity sha512-Hxmz0ML5s0F+obGm17mNOD4fDgfTOayDqk/kT9W1roMYVa2OQ5zM5On9DMteQNtwz4N3K3eviLEr61OycjEpfA== + dependencies: + "@trbl/react-html-element" "^1.0.1" + minify-css-string "^1.0.0" + prop-types "^15.7.2" + qs "^6.9.1" + react "^16.12.0" + react-dom "^16.12.0" + react-transition-group "^4.3.0" + "@types/babel__core@^7.1.0": version "7.1.6" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610" @@ -3842,9 +3864,9 @@ ejs@^2.6.1: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.380: - version "1.3.382" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.382.tgz#cad02da655c33f7a3d6ca7525bd35c17e90f3a8f" - integrity sha512-gJfxOcgnBlXhfnUUObsq3n3ReU8CT6S8je97HndYRkKsNZMJJ38zO/pI5aqO7L3Myfq+E3pqPyKK/ynyLEQfBA== + version "1.3.383" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.383.tgz#8bbef55963529bfbf8344ac3620e1bcb455cffc3" + integrity sha512-EHYVJl6Ox1kFy/SzGVbijHu8ksQotJnqHCFFfaVhXiC+erOSplwhCtOTSocu1jRwirlNsSn/aZ9Kf84Z6s5qrg== elliptic@^6.0.0: version "6.5.2" @@ -4505,9 +4527,9 @@ fb-watchman@^2.0.0: bser "2.1.1" figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figures@^3.0.0: version "3.2.0" @@ -7531,6 +7553,11 @@ mini-create-react-context@^0.3.0: gud "^1.0.0" tiny-warning "^1.0.2" +minify-css-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minify-css-string/-/minify-css-string-1.0.0.tgz#201bd949271e19f6e0af0a1dc0ccc583de47c630" + integrity sha1-IBvZSSceGfbgrwodwMzFg95HxjA= + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"