diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js
index f640ea058e..3155557934 100644
--- a/src/components/BrowserCell/BrowserCell.react.js
+++ b/src/components/BrowserCell/BrowserCell.react.js
@@ -214,12 +214,13 @@ export default class BrowserCell extends Component {
//#endregion
render() {
- let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, row, col, field, onEditSelectedRow, readonly } = this.props;
+ let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, row, col, field, onEditSelectedRow, readonly, isRequired, markRequiredField } = this.props;
let content = value;
+ let isNewRow = row < 0;
this.copyableValue = content;
let classes = [styles.cell, unselectable];
if (hidden) {
- content = '(hidden)';
+ content = value !== undefined || !isNewRow ? '(hidden)' : isRequired ? '(required)' : '(undefined)';
classes.push(styles.empty);
} else if (value === undefined) {
if (type === 'ACL') {
@@ -228,6 +229,7 @@ export default class BrowserCell extends Component {
this.copyableValue = content = '(undefined)';
classes.push(styles.empty);
}
+ content = isNewRow && isRequired && value === undefined ? '(required)' : content;
} else if (value === null) {
this.copyableValue = content = '(null)';
classes.push(styles.empty);
@@ -303,6 +305,10 @@ export default class BrowserCell extends Component {
classes.push(styles.current);
}
+ if (markRequiredField && isRequired && !value) {
+ classes.push(styles.required);
+ }
+
return readonly ? (
- {row < 0 ? '(auto)' : content}
+ {isNewRow ? '(auto)' : content}
) : (
diff --git a/src/components/BrowserCell/BrowserCell.scss b/src/components/BrowserCell/BrowserCell.scss
index 4dea0e557a..fdcd93ee16 100644
--- a/src/components/BrowserCell/BrowserCell.scss
+++ b/src/components/BrowserCell/BrowserCell.scss
@@ -35,3 +35,18 @@
bottom: 0;
}
}
+
+.required {
+ position: relative;
+
+ &:after {
+ position: absolute;
+ pointer-events: none;
+ content: '';
+ border: 2px solid #ff395e;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+}
\ No newline at end of file
diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js
index da8302e5e9..d77d3f223d 100644
--- a/src/components/BrowserRow/BrowserRow.react.js
+++ b/src/components/BrowserRow/BrowserRow.react.js
@@ -19,7 +19,7 @@ export default class BrowserRow extends Component {
}
render() {
- const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange } = this.props;
+ const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange, markRequiredField, requiredColumnFields } = this.props;
let attributes = obj.attributes;
return (
@@ -58,6 +58,7 @@ export default class BrowserRow extends Component {
hidden = true;
}
}
+ let isRequired = requiredColumnFields && requiredColumnFields.includes(name);
return (
diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js
index 74422f9a41..6c713556ec 100644
--- a/src/dashboard/Data/Browser/Browser.react.js
+++ b/src/dashboard/Data/Browser/Browser.react.js
@@ -79,6 +79,8 @@ class Browser extends DashboardView {
isUnique: false,
uniqueField: null,
+ markRequiredField: false,
+ requiredColumnFields: []
};
this.prefetchData = this.prefetchData.bind(this);
@@ -123,6 +125,7 @@ class Browser extends DashboardView {
this.onDialogToggle = this.onDialogToggle.bind(this);
this.abortAddRow = this.abortAddRow.bind(this);
this.saveNewRow = this.saveNewRow.bind(this);
+ this.setRequiredColumnFields = this.setRequiredColumnFields.bind(this);
}
componentWillMount() {
@@ -280,7 +283,15 @@ class Browser extends DashboardView {
required,
defaultValue
};
- this.props.schema.dispatch(ActionTypes.ADD_COLUMN, payload).catch((err) => {
+ this.props.schema.dispatch(ActionTypes.ADD_COLUMN, payload).then(() => {
+ // if new required field column is added, then add field in requiredColumn
+ if (required) {
+ let requiredCols = [...this.state.requiredColumnFields, name];
+ this.setState({
+ requiredColumnFields: requiredCols
+ });
+ }
+ }).catch((err) => {
this.showNote(err.message, true);
}).finally(() => {
this.setState({ showAddColumnDialog: false });
@@ -288,6 +299,10 @@ class Browser extends DashboardView {
}
addRow() {
+ if (this.props.params.className === '_User') {
+ // if User class row, then reload requiredFields
+ this.setRequiredColumnFields();
+ }
if (!this.state.newObject) {
const relation = this.state.relation;
this.setState({
@@ -304,6 +319,11 @@ class Browser extends DashboardView {
newObject: null
});
}
+ if (this.state.markRequiredField) {
+ this.setState({
+ markRequiredField: false
+ });
+ }
}
saveNewRow(){
@@ -312,6 +332,45 @@ class Browser extends DashboardView {
return;
}
+ // check if required fields are missing
+ const className = this.props.params.className;
+ let requiredCols = [];
+ if (className) {
+ let classColumns = this.props.schema.data.get('classes').get(className);
+ classColumns.forEach(({ required }, name) => {
+ if (name === 'objectId' || this.state.isUnique && name !== this.state.uniqueField) {
+ return;
+ }
+ if (!!required) {
+ requiredCols.push(name);
+ }
+ if (className === '_User' && (name === 'username' || name === 'password')) {
+ if (!obj.get('authData')) {
+ requiredCols.push(name);
+ }
+ }
+ if (className === '_Role' && (name === 'name' || name === 'ACL')) {
+ requiredCols.push(name);
+ }
+ });
+ }
+ if (requiredCols.length) {
+ for (let idx = 0; idx < requiredCols.length; idx++) {
+ const name = requiredCols[idx];
+ if (!obj.get(name)) {
+ this.showNote("Please enter all required fields", true);
+ this.setState({
+ markRequiredField: true
+ });
+ return;
+ }
+ }
+ }
+ if (this.state.markRequiredField) {
+ this.setState({
+ markRequiredField: false
+ });
+ }
obj.save(null, { useMasterKey: true }).then(
objectSaved => {
let msg = objectSaved.className + ' with id \'' + objectSaved.id + '\' created';
@@ -483,6 +542,30 @@ class Browser extends DashboardView {
delete filteredCounts[source];
}
this.setState({ data: data, filters, lastMax: MAX_ROWS_FETCHED , filteredCounts: filteredCounts});
+ this.setRequiredColumnFields();
+ }
+
+ setRequiredColumnFields() {
+ if (!this.props.schema.data.get('classes')) {
+ return;
+ }
+ let classes = this.props.schema.data.get('classes');
+ const { className } = this.props.params;
+ let requiredCols = [];
+ classes.get(className).forEach(({ required }, name) => {
+ if (!!required) {
+ requiredCols.push(name);
+ }
+ if (className === '_User' && (name === 'username' || name === 'password' || name === 'authData')) {
+ requiredCols.push(name);
+ }
+ if (className === '_Role' && (name === 'name' || name === 'ACL')) {
+ requiredCols.push(name);
+ }
+ });
+ this.setState({
+ requiredColumnFields: requiredCols
+ });
}
async fetchRelation(relation, filters = new List()) {
@@ -636,7 +719,27 @@ class Browser extends DashboardView {
} else {
obj.set(attr, value);
}
- if(isNewObject){
+
+ if (isNewObject) {
+ // for dynamically changing required placeholder text for _User class new row object
+ if (obj.className === '_User' && attr === 'authData' && value !== undefined) {
+ // username & password are not required
+ this.setState({
+ requiredColumnFields: this.state.requiredColumnFields.filter(field => field !== 'username' && field !== 'password')
+ })
+ }
+
+ if (obj.className === '_User' && (attr === 'username' || attr === 'password') && value !== undefined) {
+ // authData is not required
+ this.setState({
+ requiredColumnFields: this.state.requiredColumnFields.filter(field => field !== 'authData')
+ })
+ }
+
+ if (obj.className === '_User' && obj.get('username') === undefined && obj.get('password') === undefined && obj.get('authData') === undefined) {
+ this.setRequiredColumnFields();
+ }
+
this.setState({
isNewObject: obj
});
@@ -1076,6 +1179,8 @@ class Browser extends DashboardView {
onSaveNewRow={this.saveNewRow}
onAbortAddRow={this.abortAddRow}
+ markRequiredField={this.state.markRequiredField}
+ requiredColumnFields={this.state.requiredColumnFields}
columns={columns}
className={className}
fetchNextPage={this.fetchNextPage}
diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js
index f68853ac59..a893eacbc9 100644
--- a/src/dashboard/Data/Browser/BrowserTable.react.js
+++ b/src/dashboard/Data/Browser/BrowserTable.react.js
@@ -98,7 +98,7 @@ export default class BrowserTable extends React.Component {
}
}
- let headers = this.props.order.map(({ name, width, visible, preventSort }) => (
+ let headers = this.props.order.map(({ name, width, visible, preventSort, required }) => (
{
width: width,
name: name,
@@ -106,7 +106,8 @@ export default class BrowserTable extends React.Component {
targetClass: this.props.columns[name].targetClass,
order: ordering.col === name ? ordering.direction : null,
visible,
- preventSort
+ preventSort,
+ required
}
));
let editor = null;
@@ -142,6 +143,8 @@ export default class BrowserTable extends React.Component {
setCopyableValue={this.props.setCopyableValue}
setContextMenu={this.props.setContextMenu}
onEditSelectedRow={this.props.onEditSelectedRow}
+ markRequiredField={this.props.markRequiredField}
+ requiredColumnFields={this.props.requiredColumnFields}
/>