From 9fdd9a53c6e2a2cfdeb0dc4746cfa2608528a963 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 29 Jun 2023 15:38:54 +1000 Subject: [PATCH 1/7] feat: add ability to select script field --- .../BrowserCell/BrowserCell.react.js | 31 +++++++++++++++++-- .../ContextMenu/ContextMenu.react.js | 4 +++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 1e46dda562..7c04fad109 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -283,14 +283,41 @@ export default class BrowserCell extends Component { }); } - const { className, objectId } = this.props; - const validScripts = (this.props.scripts || []).filter(script => script.classes?.includes(this.props.className)); + const { className, objectId,field, scripts = [] } = this.props; + let validator = null; + const validScripts = (scripts || []).filter(script => { + if (script.classes?.includes(className)) { + return true; + } + for (const script of script?.classes || []) { + if (script?.name !== className) { + continue; + } + const fields = script?.fields || []; + if (script?.fields.includes(field) || script?.fields.includes('*')) { + return true; + } + for (const currentField of fields) { + if (Object.prototype.toString.call(currentField) === '[object Object]') { + if (currentField.name === field) { + if (typeof currentField.validator === 'string') { + validator = eval(currentField.validator); + } else { + validator = currentField.validator; + } + return true; + } + } + } + } + }); if (validScripts.length) { onEditSelectedRow && contextMenuOptions.push({ text: 'Scripts', items: validScripts.map(script => { return { text: script.title, + disabled: validator?.(this.props.value, field) === false, callback: () => { this.selectedScript = { ...script, className, objectId }; if(script.showConfirmationDialog) diff --git a/src/components/ContextMenu/ContextMenu.react.js b/src/components/ContextMenu/ContextMenu.react.js index 611e1e0a99..3fec49f681 100644 --- a/src/components/ContextMenu/ContextMenu.react.js +++ b/src/components/ContextMenu/ContextMenu.react.js @@ -71,7 +71,11 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
  • { + if (item.disabled === true) { + return; + } item.callback && item.callback(); hide(); }} From af603346ad67302c7808d879115534668b5b6d4b Mon Sep 17 00:00:00 2001 From: dblythy Date: Fri, 30 Jun 2023 13:30:18 +1000 Subject: [PATCH 2/7] update syntax --- src/components/BrowserCell/BrowserCell.react.js | 10 ++++++---- src/components/BrowserRow/BrowserRow.react.js | 3 ++- src/dashboard/Data/Browser/BrowserTable.react.js | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 7c04fad109..e7e2a46707 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -283,7 +283,7 @@ export default class BrowserCell extends Component { }); } - const { className, objectId,field, scripts = [] } = this.props; + const { className, objectId,field, scripts = [], rowValue } = this.props; let validator = null; const validScripts = (scripts || []).filter(script => { if (script.classes?.includes(className)) { @@ -317,13 +317,15 @@ export default class BrowserCell extends Component { items: validScripts.map(script => { return { text: script.title, - disabled: validator?.(this.props.value, field) === false, + disabled: validator?.(this.props.rowValue, field) === false, callback: () => { this.selectedScript = { ...script, className, objectId }; - if(script.showConfirmationDialog) + if (script.showConfirmationDialog) { this.toggleConfirmationDialog(); - else + } + else { this.executeSript(script); + } } } }) diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index 7a6c99a74e..851463e26d 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, onPointerCmdClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange, markRequiredFieldRow } = this.props; + const { className, columns, currentCol, isUnique, obj, onPointerClick, onPointerCmdClick, order, readOnlyFields, row, rowValue, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange, markRequiredFieldRow } = this.props; let attributes = obj.attributes; let requiredCols = []; Object.entries(columns).reduce((acc, cur) => { @@ -82,6 +82,7 @@ export default class BrowserRow extends Component { className={className} field={name} row={row} + rowValue={rowValue} col={j} type={type} readonly={isUnique || readOnlyFields.indexOf(name) > -1} diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 2fb8ec22aa..2744cf011d 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -149,6 +149,7 @@ export default class BrowserTable extends React.Component { order={this.props.order} readOnlyFields={READ_ONLY} row={index} + rowValue={this.props.data[index]} rowWidth={rowWidth} selection={this.props.selection} selectRow={this.props.selectRow} @@ -265,6 +266,7 @@ export default class BrowserTable extends React.Component { order={this.props.order} readOnlyFields={READ_ONLY} row={i} + rowValue={this.props.data[i]} rowWidth={rowWidth} selection={this.props.selection} selectRow={this.props.selectRow} From 71493467b56705a3dc46ced41b7d8b40683c6bc8 Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 18 Jul 2023 10:31:14 +1000 Subject: [PATCH 3/7] run lint --- Parse-Dashboard/parse-dashboard-config.json | 20 +++++++++++++++---- .../BrowserCell/BrowserCell.react.js | 2 +- src/components/BrowserRow/BrowserRow.react.js | 2 +- .../Data/Browser/BrowserTable.react.js | 3 +-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Parse-Dashboard/parse-dashboard-config.json b/Parse-Dashboard/parse-dashboard-config.json index 84d5d15396..edd04e0318 100644 --- a/Parse-Dashboard/parse-dashboard-config.json +++ b/Parse-Dashboard/parse-dashboard-config.json @@ -1,13 +1,25 @@ { "apps": [ { - "serverURL": "http://localhost:1337/parse", - "appId": "hello", - "masterKey": "world", + "serverURL": "http://localhost:8378/1", + "appId": "test", + "masterKey": "test", "appName": "", "iconName": "", "primaryBackgroundColor": "", - "secondaryBackgroundColor": "" + "secondaryBackgroundColor": "", + "scripts": [ + { + "title": "Delete account", + "classes": [ + { + "name": "_User", + "fields": [{ "name": "createdAt", "validator": "(row, field) => row.get(field) > new Date(\"2025\")" }] + } + ], + "cloudCodeFunction": "deleteAccount" + } + ] } ], "iconsFolder": "icons" diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 8fabefedc6..a83ef22728 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -368,7 +368,7 @@ export default class BrowserCell extends Component { items: validScripts.map(script => { return { text: script.title, - disabled: validator?.(this.props.rowValue, field) === false, + disabled: validator?.(rowValue, field) === false, callback: () => { this.selectedScript = { ...script, className, objectId }; if (script.showConfirmationDialog) { diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index a07d5161ce..d3a48df591 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -21,7 +21,7 @@ export default class BrowserRow extends Component { render() { const { className, columns, currentCol, isUnique, obj, onPointerClick, onPointerCmdClick, order, readOnlyFields, row, rowValue, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange, markRequiredFieldRow } = this.props; - let attributes = obj.attributes; + const attributes = obj.attributes; let requiredCols = []; Object.entries(columns).reduce((acc, cur) => { if (cur[1].required) { diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 734e414cc8..0690ce2584 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -312,8 +312,7 @@ export default class BrowserTable extends React.Component { showNote={this.props.showNote} onRefresh={this.props.onRefresh} scripts={this.context.scripts} - /> - ); + /> } if (this.props.editing) { From e1c9da275cf56fe610b51f6d1ee4a0bfff1aaf81 Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 18 Jul 2023 10:31:31 +1000 Subject: [PATCH 4/7] Update BrowserTable.react.js --- src/dashboard/Data/Browser/BrowserTable.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 0690ce2584..3c7fd1db27 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -312,7 +312,7 @@ export default class BrowserTable extends React.Component { showNote={this.props.showNote} onRefresh={this.props.onRefresh} scripts={this.context.scripts} - /> + /> } if (this.props.editing) { From 0f5859e8fe8284c578aa779fcbdb9e115b5cea5f Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 18 Jul 2023 10:31:37 +1000 Subject: [PATCH 5/7] Update BrowserTable.react.js --- src/dashboard/Data/Browser/BrowserTable.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 3c7fd1db27..0690ce2584 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -312,7 +312,7 @@ export default class BrowserTable extends React.Component { showNote={this.props.showNote} onRefresh={this.props.onRefresh} scripts={this.context.scripts} - /> + /> } if (this.props.editing) { From 307a4ee441ab17fff014fd2941c4712320c149f9 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 20 Jul 2023 02:26:01 +1000 Subject: [PATCH 6/7] run lint and prettier --- Parse-Dashboard/parse-dashboard-config.json | 20 ++---- .../BrowserCell/BrowserCell.react.js | 38 +++++----- src/components/BrowserRow/BrowserRow.react.js | 25 ++++++- .../ContextMenu/ContextMenu.react.js | 2 +- .../Data/Browser/BrowserTable.react.js | 69 ++++++++++--------- 5 files changed, 84 insertions(+), 70 deletions(-) diff --git a/Parse-Dashboard/parse-dashboard-config.json b/Parse-Dashboard/parse-dashboard-config.json index edd04e0318..84d5d15396 100644 --- a/Parse-Dashboard/parse-dashboard-config.json +++ b/Parse-Dashboard/parse-dashboard-config.json @@ -1,25 +1,13 @@ { "apps": [ { - "serverURL": "http://localhost:8378/1", - "appId": "test", - "masterKey": "test", + "serverURL": "http://localhost:1337/parse", + "appId": "hello", + "masterKey": "world", "appName": "", "iconName": "", "primaryBackgroundColor": "", - "secondaryBackgroundColor": "", - "scripts": [ - { - "title": "Delete account", - "classes": [ - { - "name": "_User", - "fields": [{ "name": "createdAt", "validator": "(row, field) => row.get(field) > new Date(\"2025\")" }] - } - ], - "cloudCodeFunction": "deleteAccount" - } - ] + "secondaryBackgroundColor": "" } ], "iconsFolder": "icons" diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index a83ef22728..6b0fa722f9 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -334,7 +334,7 @@ export default class BrowserCell extends Component { }); } - const { className, objectId,field, scripts = [], rowValue } = this.props; + const { className, objectId, field, scripts = [], rowValue } = this.props; let validator = null; const validScripts = (scripts || []).filter(script => { if (script.classes?.includes(className)) { @@ -363,24 +363,24 @@ export default class BrowserCell extends Component { } }); if (validScripts.length) { - onEditSelectedRow && contextMenuOptions.push({ - text: 'Scripts', - items: validScripts.map(script => { - return { - text: script.title, - disabled: validator?.(rowValue, field) === false, - callback: () => { - this.selectedScript = { ...script, className, objectId }; - if (script.showConfirmationDialog) { - this.toggleConfirmationDialog(); - } - else { - this.executeScript(script); - } - } - } - }) - }); + onEditSelectedRow && + contextMenuOptions.push({ + text: 'Scripts', + items: validScripts.map(script => { + return { + text: script.title, + disabled: validator?.(rowValue, field) === false, + callback: () => { + this.selectedScript = { ...script, className, objectId }; + if (script.showConfirmationDialog) { + this.toggleConfirmationDialog(); + } else { + this.executeScript(script); + } + }, + }; + }), + }); } return contextMenuOptions; diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index d3a48df591..a00bdcadf0 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -20,7 +20,30 @@ export default class BrowserRow extends Component { } render() { - const { className, columns, currentCol, isUnique, obj, onPointerClick, onPointerCmdClick, order, readOnlyFields, row, rowValue, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow, setContextMenu, onFilterChange, markRequiredFieldRow } = this.props; + const { + className, + columns, + currentCol, + isUnique, + obj, + onPointerClick, + onPointerCmdClick, + order, + readOnlyFields, + row, + rowValue, + rowWidth, + selection, + selectRow, + setCopyableValue, + setCurrent, + setEditing, + setRelation, + onEditSelectedRow, + setContextMenu, + onFilterChange, + markRequiredFieldRow, + } = this.props; const attributes = obj.attributes; let requiredCols = []; Object.entries(columns).reduce((acc, cur) => { diff --git a/src/components/ContextMenu/ContextMenu.react.js b/src/components/ContextMenu/ContextMenu.react.js index dc04f776ea..40c33462da 100644 --- a/src/components/ContextMenu/ContextMenu.react.js +++ b/src/components/ContextMenu/ContextMenu.react.js @@ -71,7 +71,7 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
  • { if (item.disabled === true) { return; diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 0690ce2584..9c6fb83aaa 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -280,39 +280,42 @@ export default class BrowserTable extends React.Component { // Needed in order to force BrowserRow to update and re-render (and possibly update columns values), // since the "obj" instance will only be updated when the update request is done. - const isEditingRow = this.props.current && this.props.current.row === i && !!this.props.editing; - rows[index] = + const isEditingRow = + this.props.current && this.props.current.row === i && !!this.props.editing; + rows[index] = ( + + ); } if (this.props.editing) { From bfd3b015e5abd7060b3df7dba12a843a104523a8 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 22 Jul 2023 00:30:21 +1000 Subject: [PATCH 7/7] Update README.md --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index c55beb58b9..62eab0de43 100644 --- a/README.md +++ b/README.md @@ -397,6 +397,31 @@ You can specify scripts to execute Cloud Functions with the `scripts` option: ] ``` +You can also specify custom fields with the `scrips` option: + +```json +"apps": [ + { + "scripts": [ + { + "title": "Delete account", + "classes": [ + { + "name": "_User", + "fields": [ + { "name": "createdAt", "validator": "value => value > new Date(\"2025\")" } + ] + } + ], + "cloudCodeFunction": "deleteAccount" + } + ] + } +] + +``` + + Next, define the Cloud Function in Parse Server that will be called. The object that has been selected in the data browser will be made available as a request parameter: ```js