diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index fe6b81eb8c..c2242f61f4 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -33,12 +33,22 @@ let BrowserCell = ({ type, value, hidden, width, current, onSelect, onEditChange content =  ; classes.push(styles.empty); } else if (type === 'Pointer') { + if (value && value.__type) { + const object = new Parse.Object(value.className); + object.id = value.objectId; + value = object; + } content = ( ); } else if (type === 'Date') { + if (typeof value === 'object' && value.__type) { + value = new Date(value.iso); + } else if (typeof value === 'string') { + value = new Date(value); + } content = dateStringUTC(value); } else if (type === 'Boolean') { content = value ? 'True' : 'False'; diff --git a/src/components/BrowserMenu/BrowserMenu.react.js b/src/components/BrowserMenu/BrowserMenu.react.js index 32f4854ff9..516baff850 100644 --- a/src/components/BrowserMenu/BrowserMenu.react.js +++ b/src/components/BrowserMenu/BrowserMenu.react.js @@ -47,9 +47,19 @@ export default class BrowserMenu extends React.Component { ); } + const classes = [styles.entry]; + if (this.props.disabled) { + classes.push(styles.disabled); + } + let onClick = null; + if (!this.props.disabled) { + onClick = () => { + this.setState({ open: true }); + }; + } return (
-
this.setState({ open: true })}> +
{this.props.title}
diff --git a/src/components/BrowserMenu/BrowserMenu.scss b/src/components/BrowserMenu/BrowserMenu.scss index 7e9030a11e..189d8f1efa 100644 --- a/src/components/BrowserMenu/BrowserMenu.scss +++ b/src/components/BrowserMenu/BrowserMenu.scss @@ -24,6 +24,15 @@ fill: white; } } + + &.disabled { + cursor: not-allowed; + color: #66637A; + + &:hover svg { + fill: #66637A; + } + } } .title { diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index ecea5cbcff..fbab9aa92d 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -71,6 +71,9 @@ class Browser extends DashboardView { lastNote: null, relationCount: 0, + + isUnique: false, + uniqueField: null, }; this.prefetchData = this.prefetchData.bind(this); @@ -333,7 +336,21 @@ class Browser extends DashboardView { } query.limit(200); - const data = await query.find({ useMasterKey: true }); + + let promise = query.find({ useMasterKey: true }); + let isUnique = false; + let uniqueField = null; + filters.forEach(async (filter) => { + if (filter.get('constraint') == 'unique') { + const field = filter.get('field'); + promise = query.distinct(field); + isUnique = true; + uniqueField = field; + } + }); + await this.setState({ isUnique, uniqueField }); + + const data = await promise; return data; } @@ -347,7 +364,11 @@ class Browser extends DashboardView { const data = await this.fetchParseData(source, filters); var filteredCounts = { ...this.state.filteredCounts }; if (filters.size > 0) { - filteredCounts[source] = await this.fetchParseDataCount(source,filters); + if (this.state.isUnique) { + filteredCounts[source] = data.length; + } else { + filteredCounts[source] = await this.fetchParseDataCount(source, filters); + } } else { delete filteredCounts[source]; } @@ -372,7 +393,7 @@ class Browser extends DashboardView { } fetchNextPage() { - if (!this.state.data) { + if (!this.state.data || this.state.isUnique) { return null; } let className = this.props.params.className; @@ -889,11 +910,17 @@ class Browser extends DashboardView { let columns = { objectId: { type: 'String' } }; + if (this.state.isUnique) { + columns = {}; + } let userPointers = []; classes.get(className).forEach((field, name) => { if (name === 'objectId') { return; } + if (this.state.isUnique && name !== this.state.uniqueField) { + return; + } let info = { type: field.type }; if (field.targetClass) { info.targetClass = field.targetClass; @@ -916,6 +943,8 @@ class Browser extends DashboardView { } browser = ( {this.props.order.map(({ name, width }, j) => { let type = this.props.columns[name].type; - let attr = attributes[name]; - if (name === 'objectId') { - attr = obj.id; - } else if (name === 'ACL' && this.props.className === '_User' && !attr) { - attr = new Parse.ACL({ '*': { read: true }, [obj.id]: { read: true, write: true }}); - } else if (type === 'Relation' && !attr && obj.id) { - attr = new Parse.Relation(obj, name); - attr.targetClassName = this.props.columns[name].targetClass; + let attr = obj; + if (!this.props.isUnique) { + attr = attributes[name]; + if (name === 'objectId') { + attr = obj.id; + } else if (name === 'ACL' && this.props.className === '_User' && !attr) { + attr = new Parse.ACL({ '*': { read: true }, [obj.id]: { read: true, write: true }}); + } else if (type === 'Relation' && !attr && obj.id) { + attr = new Parse.Relation(obj, name); + attr.targetClassName = this.props.columns[name].targetClass; + } } let current = this.props.current && this.props.current.row === row && this.props.current.col === j; let hidden = false; @@ -118,7 +121,7 @@ export default class BrowserTable extends React.Component { -1} + readonly={this.props.isUnique || READ_ONLY.indexOf(name) > -1} width={width} current={current} onSelect={() => this.props.setCurrent({ row: row, col: j })} @@ -187,16 +190,21 @@ export default class BrowserTable extends React.Component { if (visible) { let { name, width } = this.props.order[this.props.current.col]; let { type, targetClass } = this.props.columns[name]; - let readonly = READ_ONLY.indexOf(name) > -1; + let readonly = this.props.isUnique || READ_ONLY.indexOf(name) > -1; if (name === 'sessionToken') { if (this.props.className === '_User' || this.props.className === '_Session') { readonly = true; } } let obj = this.props.current.row < 0 ? this.props.newObject : this.props.data[this.props.current.row]; - let value = obj.get(name); + let value = obj; + if (!this.props.isUnique) { + value = obj.get(name); + } if (name === 'objectId') { - value = obj.id; + if (!this.props.isUnique) { + value = obj.id; + } } else if (name === 'ACL' && this.props.className === '_User' && !value) { value = new Parse.ACL({ '*': { read: true }, [obj.id]: { read: true, write: true }}); } else if (name === 'password' && this.props.className === '_User') { @@ -222,27 +230,28 @@ export default class BrowserTable extends React.Component { for (let i = 0; i < this.props.current.col; i++) { wrapLeft += this.props.order[i].width; } - - editor = ( - { - if (newValue !== value) { - this.props.updateRow( - this.props.current.row, - name, - newValue - ); - } - this.props.setEditing(false); - }} /> - ); + if (!this.props.isUnique) { + editor = ( + { + if (newValue !== value) { + this.props.updateRow( + this.props.current.row, + name, + newValue + ); + } + this.props.setEditing(false); + }} /> + ); + } } } @@ -264,7 +273,7 @@ export default class BrowserTable extends React.Component { />
); - } else { + } else if (!this.props.isUnique) { addRow = (
@@ -324,7 +333,7 @@ export default class BrowserTable extends React.Component { selectAll={this.props.selectRow.bind(null, '*')} headers={headers} updateOrdering={this.props.updateOrdering} - readonly={!!this.props.relation} + readonly={!!this.props.relation || !!this.props.isUnique} handleDragDrop={this.props.handleHeaderDragDrop} onResize={this.props.handleResize} onAddColumn={this.props.onAddColumn} diff --git a/src/dashboard/Data/Browser/BrowserToolbar.react.js b/src/dashboard/Data/Browser/BrowserToolbar.react.js index c7d07a7943..67fd5cc4b9 100644 --- a/src/dashboard/Data/Browser/BrowserToolbar.react.js +++ b/src/dashboard/Data/Browser/BrowserToolbar.react.js @@ -41,12 +41,14 @@ let BrowserToolbar = ({ onChangeCLP, onRefresh, hidePerms, + isUnique, enableDeleteAllRows, enableExportClass, enableSecurityDialog, + enableColumnManipulation, - enableClassManipulation + enableClassManipulation, }) => { let selectionLength = Object.keys(selection).length; let details = []; @@ -58,7 +60,7 @@ let BrowserToolbar = ({ } } - if (!relation) { + if (!relation && !isUnique) { if (perms && !hidePerms) { let read = perms.get && perms.find && perms.get['*'] && perms.find['*']; let write = perms.create && perms.update && perms.delete && perms.create['*'] && perms.update['*'] && perms.delete['*']; @@ -93,7 +95,7 @@ let BrowserToolbar = ({ ); } else { menu = ( - + {enableColumnManipulation ? :