From 84327c98f605dea8c48bc270c71b65f227683130 Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Fri, 26 Jul 2019 14:30:51 -0300 Subject: [PATCH 01/10] feat: Add additional styles for toggle component --- src/components/Toggle/Toggle.react.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Toggle/Toggle.react.js b/src/components/Toggle/Toggle.react.js index 03837ffd08..5cb9d07f66 100644 --- a/src/components/Toggle/Toggle.react.js +++ b/src/components/Toggle/Toggle.react.js @@ -100,7 +100,7 @@ export default class Toggle extends React.Component { toggleClasses.push(styles.darkBg); } return ( -
+
{labelLeft} {labelRight} @@ -122,6 +122,7 @@ Toggle.propTypes = { labelRight: PropTypes.string.describe('Custom right toggle label, case when label does not equal content. [For Toggle.Type.CUSTOM]'), colored: PropTypes.bool.describe('Flag describing is toggle is colored. [For Toggle.Type.CUSTOM]'), darkBg: PropTypes.bool, + additionalStyles: PropTypes.object.describe('Additional styles for Toggle component.'), }; Toggle.Types = { From 5c79c603fc46c51ec39cd689bc59cf4a65befb7c Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Fri, 26 Jul 2019 14:33:49 -0300 Subject: [PATCH 02/10] feat: Add required property on create new column action --- src/dashboard/Data/Browser/AddColumnDialog.react.js | 13 ++++++++++--- src/dashboard/Data/Browser/Browser.react.js | 5 +++-- src/dashboard/Data/Browser/Browser.scss | 10 ++++++++++ src/lib/stores/SchemaStore.js | 3 ++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/dashboard/Data/Browser/AddColumnDialog.react.js b/src/dashboard/Data/Browser/AddColumnDialog.react.js index 335c1d1531..2bc64265a2 100644 --- a/src/dashboard/Data/Browser/AddColumnDialog.react.js +++ b/src/dashboard/Data/Browser/AddColumnDialog.react.js @@ -12,6 +12,8 @@ import Modal from 'components/Modal/Modal.react'; import Option from 'components/Dropdown/Option.react'; import React from 'react'; import TextInput from 'components/TextInput/TextInput.react'; +import Toggle from 'components/Toggle/Toggle.react'; +import styles from 'dashboard/Data/Browser/Browser.scss'; import { DataTypes, SpecialClasses @@ -27,7 +29,8 @@ export default class AddColumnDialog extends React.Component { this.state = { type: 'String', target: props.classes[0], - name: '' + name: '', + required: false }; } @@ -74,13 +77,13 @@ export default class AddColumnDialog extends React.Component { cancelText={'Never mind, don\u2019t.'} onCancel={this.props.onCancel} onConfirm={() => { - this.props.onConfirm(this.state.type, this.state.name, this.state.target); + this.props.onConfirm(this.state); }}> - } + } input={typeDropdown} /> {this.state.type === 'Pointer' || this.state.type === 'Relation' ? } input={ this.setState({ name })} />}/> + } + input={ this.setState({ required })} additionalStyles={{ margin: '0px' }}/>} + className={styles.addColumnToggleWrapper}/> ); } diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index fbab9aa92d..e02fb8b1ca 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -258,12 +258,13 @@ class Browser extends DashboardView { }); } - addColumn(type, name, target) { + addColumn({ type, name, target, required }) { let payload = { className: this.props.params.className, columnType: type, name: name, - targetClass: target + targetClass: target, + required }; this.props.schema.dispatch(ActionTypes.ADD_COLUMN, payload).finally(() => { this.setState({ showAddColumnDialog: false }); diff --git a/src/dashboard/Data/Browser/Browser.scss b/src/dashboard/Data/Browser/Browser.scss index f7c9e391b8..7e389ed62e 100644 --- a/src/dashboard/Data/Browser/Browser.scss +++ b/src/dashboard/Data/Browser/Browser.scss @@ -138,6 +138,16 @@ } } +.addColumnToggleWrapper { + >:nth-child(2) { + display: flex; + align-items: center; + justify-content: center; + width: 50%; + background: #f6fafb; + } +} + .notificationMessage, .notificationError { @include animation('fade-in 0.2s ease-out'); position: absolute; diff --git a/src/lib/stores/SchemaStore.js b/src/lib/stores/SchemaStore.js index 17b8fd9c07..f3258c90ef 100644 --- a/src/lib/stores/SchemaStore.js +++ b/src/lib/stores/SchemaStore.js @@ -74,7 +74,8 @@ function SchemaStore(state, action) { case ActionTypes.ADD_COLUMN: let newField = { [action.name]: { - type: action.columnType + type: action.columnType, + required: action.required } }; if (action.columnType === 'Pointer' || action.columnType === 'Relation') { From 7e67d78895d06573e3a1043fc82d88ba17a2fb1e Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Fri, 26 Jul 2019 14:34:13 -0300 Subject: [PATCH 03/10] fix: PIG routes --- src/parse-interface-guide/routes.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/parse-interface-guide/routes.js b/src/parse-interface-guide/routes.js index fd40c3d036..c270bb8f64 100644 --- a/src/parse-interface-guide/routes.js +++ b/src/parse-interface-guide/routes.js @@ -11,7 +11,7 @@ import { Router, Route } from 'react-router'; import { createBrowserHistory } from 'history'; const history = createBrowserHistory({}); -module.exports = ( +const routes = (
{ @@ -20,3 +20,5 @@ module.exports = (
); + +export default routes From 324065a1ad1380bc9a0a89cd61f9b443d6593608 Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Fri, 26 Jul 2019 18:50:53 -0300 Subject: [PATCH 04/10] feat: Add default value option on add column --- .../Data/Browser/AddColumnDialog.react.js | 98 +++++++++++++++++-- src/dashboard/Data/Browser/Browser.react.js | 5 +- src/lib/stores/SchemaStore.js | 3 +- 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/dashboard/Data/Browser/AddColumnDialog.react.js b/src/dashboard/Data/Browser/AddColumnDialog.react.js index 2bc64265a2..b219647422 100644 --- a/src/dashboard/Data/Browser/AddColumnDialog.react.js +++ b/src/dashboard/Data/Browser/AddColumnDialog.react.js @@ -13,7 +13,12 @@ import Option from 'components/Dropdown/Option.react'; import React from 'react'; import TextInput from 'components/TextInput/TextInput.react'; import Toggle from 'components/Toggle/Toggle.react'; +import DateTimeInput from 'components/DateTimeInput/DateTimeInput.react'; +import SegmentSelect from 'components/SegmentSelect/SegmentSelect.react'; +import FileInput from 'components/FileInput/FileInput.react'; import styles from 'dashboard/Data/Browser/Browser.scss'; +import Parse from 'parse' +import validateNumeric from 'lib/validateNumeric'; import { DataTypes, SpecialClasses @@ -30,8 +35,13 @@ export default class AddColumnDialog extends React.Component { type: 'String', target: props.classes[0], name: '', - required: false + required: false, + defaultValue: undefined, + isDefaultValueValid: true }; + console.log('props', this) + this.renderDefaultValueInput = this.renderDefaultValueInput.bind(this) + this.handleDefaultValueChange = this.handleDefaultValueChange.bind(this) } valid() { @@ -57,6 +67,71 @@ export default class AddColumnDialog extends React.Component { ); } + async handleDefaultValueChange(defaultValue) { + const { type, target } = this.state + let formattedValue = undefined + let isDefaultValueValid = true + + try { + switch (type) { + case 'Number': + if (!validateNumeric(defaultValue)) throw 'Invalid number' + formattedValue = defaultValue + break + case 'Array': + case 'Object': + formattedValue = JSON.parse(defaultValue) + break + case 'Date': + formattedValue = new Date(defaultValue) + break + case 'Polygon': + formattedValue = new Parse.Polygon(defaultValue) + break + case 'GeoPoint': + formattedValue = new Parse.GeoPoint(defaultValue) + break; + case 'Pointer': + const targetClass = new Parse.Object.extend(target) + const query = new Parse.Query(targetClass) + const result = await query.get(defaultValue) + formattedValue = result.toPointer() + break + case 'Boolean': + formattedValue = (defaultValue === 'True' ? true : (defaultValue === 'False' ? false : undefined)) + break + default: + formattedValue = defaultValue + } + } catch(e) { + isDefaultValueValid = false + } + return await this.setState({ defaultValue: formattedValue, isDefaultValueValid }) + } + + renderDefaultValueInput() { + const { type } = this.state + switch (type) { + case 'Array': + case 'Object': + case 'Polygon': + case 'GeoPoint': + return await this.handleDefaultValueChange(defaultValue)} /> + case 'Number': + case 'String': + case 'Pointer': + return await this.handleDefaultValueChange(defaultValue)} /> + case 'Date': + return await this.handleDefaultValueChange(defaultValue)} /> + case 'Boolean': + return await this.handleDefaultValueChange(defaultValue)} /> + } + } + + render() { let typeDropdown = ( { + console.log(this.state) this.props.onConfirm(this.state); }}> } input={ this.setState({ name })} />}/> - } - input={ this.setState({ required })} additionalStyles={{ margin: '0px' }}/>} - className={styles.addColumnToggleWrapper}/> + { + this.state.type !== 'Relation' ? + <> + } + input={this.renderDefaultValueInput()} + className={styles.addColumnToggleWrapper} /> + } + input={ this.setState({ required })} additionalStyles={{ margin: '0px' }}/>} + className={styles.addColumnToggleWrapper} /> + + : null + } ); } diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index e02fb8b1ca..b193e2514d 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -258,13 +258,14 @@ class Browser extends DashboardView { }); } - addColumn({ type, name, target, required }) { + addColumn({ type, name, target, required, defaultValue }) { let payload = { className: this.props.params.className, columnType: type, name: name, targetClass: target, - required + required, + defaultValue }; this.props.schema.dispatch(ActionTypes.ADD_COLUMN, payload).finally(() => { this.setState({ showAddColumnDialog: false }); diff --git a/src/lib/stores/SchemaStore.js b/src/lib/stores/SchemaStore.js index f3258c90ef..6e433fcfe7 100644 --- a/src/lib/stores/SchemaStore.js +++ b/src/lib/stores/SchemaStore.js @@ -75,7 +75,8 @@ function SchemaStore(state, action) { let newField = { [action.name]: { type: action.columnType, - required: action.required + required: action.required, + defaultValue: action.defaultValue } }; if (action.columnType === 'Pointer' || action.columnType === 'Relation') { From 6bd187d65b4cb1296a7ad8ebd1c624bdc774e5bf Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Mon, 29 Jul 2019 16:29:17 -0300 Subject: [PATCH 05/10] fix: Default value for files not working --- .../Data/Browser/AddColumnDialog.react.js | 101 ++++++++++++------ 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/src/dashboard/Data/Browser/AddColumnDialog.react.js b/src/dashboard/Data/Browser/AddColumnDialog.react.js index b219647422..1a3d9de254 100644 --- a/src/dashboard/Data/Browser/AddColumnDialog.react.js +++ b/src/dashboard/Data/Browser/AddColumnDialog.react.js @@ -17,8 +17,8 @@ import DateTimeInput from 'components/DateTimeInput/DateTimeInput.react'; import SegmentSelect from 'components/SegmentSelect/SegmentSelect.react'; import FileInput from 'components/FileInput/FileInput.react'; import styles from 'dashboard/Data/Browser/Browser.scss'; -import Parse from 'parse' -import validateNumeric from 'lib/validateNumeric'; +import Parse from 'parse' +import validateNumeric from 'lib/validateNumeric'; import { DataTypes, SpecialClasses @@ -39,22 +39,45 @@ export default class AddColumnDialog extends React.Component { defaultValue: undefined, isDefaultValueValid: true }; - console.log('props', this) this.renderDefaultValueInput = this.renderDefaultValueInput.bind(this) this.handleDefaultValueChange = this.handleDefaultValueChange.bind(this) } valid() { - if (this.state.name.length === 0) { - return false; - } - if (!validColumnName(this.state.name)) { - return false; - } - if (this.props.currentColumns.indexOf(this.state.name) > -1) { - return false; + const { name, isDefaultValueValid } = this.state + + return ( + name && + name.length > 0 && + validColumnName(this.state.name) && + this.props.currentColumns.indexOf(this.state.name) === -1 && + isDefaultValueValid + ) + } + + async handlePointer(objectId, target) { + const targetClass = new Parse.Object.extend(target) + const query = new Parse.Query(targetClass) + const result = await query.get(objectId) + return result.toPointer() + } + + getBase64(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result); + reader.onerror = error => reject(error); + }); + } + + async handleFile(file) { + if (file) { + let base64 = await this.getBase64(file); + const parseFile = new Parse.File(file.name, { base64 }); + await parseFile.save(); + return parseFile } - return true; } renderClassDropdown() { @@ -74,37 +97,38 @@ export default class AddColumnDialog extends React.Component { try { switch (type) { + case 'String': + formattedValue = defaultValue.toString() + break case 'Number': if (!validateNumeric(defaultValue)) throw 'Invalid number' - formattedValue = defaultValue + formattedValue = +defaultValue break case 'Array': case 'Object': formattedValue = JSON.parse(defaultValue) break case 'Date': - formattedValue = new Date(defaultValue) + formattedValue = { __type: 'Date', iso: new Date(defaultValue) } break case 'Polygon': - formattedValue = new Parse.Polygon(defaultValue) + formattedValue = new Parse.Polygon(JSON.parse(defaultValue)) break case 'GeoPoint': formattedValue = new Parse.GeoPoint(defaultValue) break; case 'Pointer': - const targetClass = new Parse.Object.extend(target) - const query = new Parse.Query(targetClass) - const result = await query.get(defaultValue) - formattedValue = result.toPointer() + formattedValue = await this.handlePointer(defaultValue, target) break case 'Boolean': formattedValue = (defaultValue === 'True' ? true : (defaultValue === 'False' ? false : undefined)) break - default: - formattedValue = defaultValue + case 'File': + formattedValue = await this.handleFile(defaultValue) + break } - } catch(e) { - isDefaultValueValid = false + } catch (e) { + isDefaultValueValid = defaultValue === '' } return await this.setState({ defaultValue: formattedValue, isDefaultValueValid }) } @@ -116,18 +140,29 @@ export default class AddColumnDialog extends React.Component { case 'Object': case 'Polygon': case 'GeoPoint': - return await this.handleDefaultValueChange(defaultValue)} /> + return await this.handleDefaultValueChange(defaultValue)} /> case 'Number': case 'String': case 'Pointer': - return await this.handleDefaultValueChange(defaultValue)} /> + return await this.handleDefaultValueChange(defaultValue)} /> case 'Date': - return await this.handleDefaultValueChange(defaultValue)} /> + return await this.handleDefaultValueChange(defaultValue)} /> case 'Boolean': return await this.handleDefaultValueChange(defaultValue)} /> + values={['False', 'None', 'True']} + current={(this.state.defaultValue ? 'True' : (this.state.defaultValue === false ? 'False' : 'None'))} + onChange={async (defaultValue) => await this.handleDefaultValueChange(defaultValue)} /> + case 'File': + return await this.handleDefaultValueChange(defaultValue)} /> } } @@ -136,7 +171,7 @@ export default class AddColumnDialog extends React.Component { let typeDropdown = ( this.setState({ type: type })}> + onChange={(type) => this.setState({ type: type, defaultValue: undefined, required: false })}> {DataTypes.map((t) => )} ); @@ -147,7 +182,7 @@ export default class AddColumnDialog extends React.Component { iconSize={30} title='Add a new column' subtitle='Store another type of data in this class.' - disabled={!this.valid() || !this.state.isDefaultValueValid} + disabled={!this.valid()} confirmText='Add column' cancelText={'Never mind, don\u2019t.'} onCancel={this.props.onCancel} @@ -167,7 +202,7 @@ export default class AddColumnDialog extends React.Component { input={this.renderClassDropdown()} /> : null} } - input={ this.setState({ name })} />}/> + input={ this.setState({ name })} />} /> { this.state.type !== 'Relation' ? <> @@ -177,7 +212,7 @@ export default class AddColumnDialog extends React.Component { className={styles.addColumnToggleWrapper} /> } - input={ this.setState({ required })} additionalStyles={{ margin: '0px' }}/>} + input={ this.setState({ required })} additionalStyles={{ margin: '0px' }} />} className={styles.addColumnToggleWrapper} /> : null From 987ba25524c1ef7f200e7523d3b6a947d53b4d32 Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Tue, 30 Jul 2019 10:31:01 -0300 Subject: [PATCH 06/10] fix: GeoPoint validation --- src/dashboard/Data/Browser/AddColumnDialog.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard/Data/Browser/AddColumnDialog.react.js b/src/dashboard/Data/Browser/AddColumnDialog.react.js index 1a3d9de254..00221a4fc8 100644 --- a/src/dashboard/Data/Browser/AddColumnDialog.react.js +++ b/src/dashboard/Data/Browser/AddColumnDialog.react.js @@ -115,7 +115,7 @@ export default class AddColumnDialog extends React.Component { formattedValue = new Parse.Polygon(JSON.parse(defaultValue)) break case 'GeoPoint': - formattedValue = new Parse.GeoPoint(defaultValue) + formattedValue = new Parse.GeoPoint(JSON.parse(defaultValue)) break; case 'Pointer': formattedValue = await this.handlePointer(defaultValue, target) From 2399c61ffc64e3731bfdc652995abdeafb54b084 Mon Sep 17 00:00:00 2001 From: alencarlucas Date: Tue, 30 Jul 2019 11:56:19 -0300 Subject: [PATCH 07/10] feat: Check parse-server version before render schema options --- package.json | 3 ++- .../Data/Browser/AddColumnDialog.react.js | 15 +++++++++++++-- src/dashboard/Data/Browser/Browser.react.js | 4 +++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 526023160c..e9d4abe790 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "react-helmet": "5.2.1", "react-redux": "5.1.1", "react-router": "5.0.1", - "react-router-dom": "5.0.1" + "react-router-dom": "5.0.1", + "semver": "^6.3.0" }, "devDependencies": { "@babel/core": "7.5.5", diff --git a/src/dashboard/Data/Browser/AddColumnDialog.react.js b/src/dashboard/Data/Browser/AddColumnDialog.react.js index 00221a4fc8..6dab9d03b7 100644 --- a/src/dashboard/Data/Browser/AddColumnDialog.react.js +++ b/src/dashboard/Data/Browser/AddColumnDialog.react.js @@ -5,19 +5,21 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ + +import Parse from 'parse' +import React from 'react'; +import semver from 'semver'; import Dropdown from 'components/Dropdown/Dropdown.react'; import Field from 'components/Field/Field.react'; import Label from 'components/Label/Label.react'; import Modal from 'components/Modal/Modal.react'; import Option from 'components/Dropdown/Option.react'; -import React from 'react'; import TextInput from 'components/TextInput/TextInput.react'; import Toggle from 'components/Toggle/Toggle.react'; import DateTimeInput from 'components/DateTimeInput/DateTimeInput.react'; import SegmentSelect from 'components/SegmentSelect/SegmentSelect.react'; import FileInput from 'components/FileInput/FileInput.react'; import styles from 'dashboard/Data/Browser/Browser.scss'; -import Parse from 'parse' import validateNumeric from 'lib/validateNumeric'; import { DataTypes, @@ -39,6 +41,8 @@ export default class AddColumnDialog extends React.Component { defaultValue: undefined, isDefaultValueValid: true }; + + this.parseServerVersion = this.props.parseServerVersion this.renderDefaultValueInput = this.renderDefaultValueInput.bind(this) this.handleDefaultValueChange = this.handleDefaultValueChange.bind(this) } @@ -204,6 +208,13 @@ export default class AddColumnDialog extends React.Component { label={