Skip to content
20 changes: 16 additions & 4 deletions Parse-Dashboard/parse-dashboard-config.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
69 changes: 47 additions & 22 deletions src/components/BrowserCell/BrowserCell.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,34 +334,59 @@ 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 = [], rowValue } = 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,
callback: () => {
this.selectedScript = { ...script, className, objectId };
if (script.showConfirmationDialog) {
this.toggleConfirmationDialog();
} else {
this.executeSript(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;
}

async executeSript(script) {
async executeScript(script) {
try {
const object = Parse.Object.extend(this.props.className).createWithoutData(
this.props.objectId
Expand Down
25 changes: 2 additions & 23 deletions src/components/BrowserRow/BrowserRow.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,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;
const attributes = obj.attributes;
let requiredCols = [];
Object.entries(columns).reduce((acc, cur) => {
Expand Down Expand Up @@ -114,6 +92,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}
Expand Down
4 changes: 4 additions & 0 deletions src/components/ContextMenu/ContextMenu.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
<li
key={`menu-section-${level}-${index}`}
className={styles.option}
style={item.disabled ? {'opacity': 0.5, cursor: 'not-allowed'} : {}}
onClick={() => {
if (item.disabled === true) {
return;
}
item.callback && item.callback();
hide();
}}
Expand Down
69 changes: 34 additions & 35 deletions src/dashboard/Data/Browser/BrowserTable.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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}
Expand Down Expand Up @@ -279,41 +280,39 @@ 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] = (
<BrowserRow
appId={this.props.appId}
key={index}
isEditing={isEditingRow}
className={this.props.className}
columns={this.props.columns}
schema={this.props.schema}
simplifiedSchema={this.props.simplifiedSchema}
filters={this.props.filters}
currentCol={currentCol}
isUnique={this.props.isUnique}
obj={obj}
onPointerClick={this.props.onPointerClick}
onPointerCmdClick={this.props.onPointerCmdClick}
onFilterChange={this.props.onFilterChange}
order={this.props.order}
readOnlyFields={READ_ONLY}
row={i}
rowWidth={rowWidth}
selection={this.props.selection}
selectRow={this.props.selectRow}
setCurrent={this.props.setCurrent}
setEditing={this.props.setEditing}
setRelation={this.props.setRelation}
setCopyableValue={this.props.setCopyableValue}
setContextMenu={this.props.setContextMenu}
onEditSelectedRow={this.props.onEditSelectedRow}
showNote={this.props.showNote}
onRefresh={this.props.onRefresh}
scripts={this.context.scripts}
/>
);
const isEditingRow = this.props.current && this.props.current.row === i && !!this.props.editing;
rows[index] = <BrowserRow
appId={this.props.appId}
key={index}
isEditing={isEditingRow}
className={this.props.className}
columns={this.props.columns}
schema={this.props.schema}
simplifiedSchema={this.props.simplifiedSchema}
filters={this.props.filters}
currentCol={currentCol}
isUnique={this.props.isUnique}
obj={obj}
onPointerClick={this.props.onPointerClick}
onPointerCmdClick={this.props.onPointerCmdClick}
onFilterChange={this.props.onFilterChange}
order={this.props.order}
readOnlyFields={READ_ONLY}
row={i}
rowValue={this.props.data[i]}
rowWidth={rowWidth}
selection={this.props.selection}
selectRow={this.props.selectRow}
setCurrent={this.props.setCurrent}
setEditing={this.props.setEditing}
setRelation={this.props.setRelation}
setCopyableValue={this.props.setCopyableValue}
setContextMenu={this.props.setContextMenu}
onEditSelectedRow={this.props.onEditSelectedRow}
showNote={this.props.showNote}
onRefresh={this.props.onRefresh}
scripts={this.context.scripts}
/>
}

if (this.props.editing) {
Expand Down