Skip to content

Commit 93e1635

Browse files
committed
optim
1 parent 4ad36a7 commit 93e1635

File tree

4 files changed

+164
-106
lines changed

4 files changed

+164
-106
lines changed

src/components/BrowserCell/BrowserCell.react.js

Lines changed: 15 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import * as Filters from 'lib/Filters';
99
import { List, Map } from 'immutable';
1010
import { dateStringUTC } from 'lib/DateUtils';
1111
import getFileName from 'lib/getFileName';
12+
import { getValidScripts, executeScript } from 'lib/ScriptUtils';
1213
import Parse from 'parse';
1314
import Pill from 'components/Pill/Pill.react';
1415
import React, { Component } from 'react';
16+
import ScriptConfirmationModal from 'components/ScriptConfirmationModal/ScriptConfirmationModal.react';
1517
import styles from 'components/BrowserCell/BrowserCell.scss';
1618
import baseStyles from 'stylesheets/base.scss';
1719
import * as ColumnPreferences from 'lib/ColumnPreferences';
1820
import labelStyles from 'components/Label/Label.scss';
19-
import Modal from 'components/Modal/Modal.react';
2021

2122
export default class BrowserCell extends Component {
2223
constructor() {
@@ -348,33 +349,8 @@ export default class BrowserCell extends Component {
348349
}
349350

350351
const { className, objectId, field, scripts = [], rowValue } = this.props;
351-
let validator = null;
352-
const validScripts = (scripts || []).filter(script => {
353-
if (script.classes?.includes(className)) {
354-
return true;
355-
}
356-
for (const script of script?.classes || []) {
357-
if (script?.name !== className) {
358-
continue;
359-
}
360-
const fields = script?.fields || [];
361-
if (script?.fields.includes(field) || script?.fields.includes('*')) {
362-
return true;
363-
}
364-
for (const currentField of fields) {
365-
if (Object.prototype.toString.call(currentField) === '[object Object]') {
366-
if (currentField.name === field) {
367-
if (typeof currentField.validator === 'string') {
368-
validator = eval(currentField.validator);
369-
} else {
370-
validator = currentField.validator;
371-
}
372-
return true;
373-
}
374-
}
375-
}
376-
}
377-
});
352+
const { validScripts, validator } = getValidScripts(scripts, className, field);
353+
378354
if (validScripts.length) {
379355
onEditSelectedRow &&
380356
contextMenuOptions.push({
@@ -400,24 +376,13 @@ export default class BrowserCell extends Component {
400376
}
401377

402378
async executeScript(script) {
403-
try {
404-
const object = Parse.Object.extend(this.props.className).createWithoutData(
405-
this.props.objectId
406-
);
407-
const response = await Parse.Cloud.run(
408-
script.cloudCodeFunction,
409-
{ object: object.toPointer() },
410-
{ useMasterKey: true }
411-
);
412-
this.props.showNote(
413-
response ||
414-
`Ran script "${script.title}" on "${this.props.className}" object "${object.id}".`
415-
);
416-
this.props.onRefresh();
417-
} catch (e) {
418-
this.props.showNote(e.message, true);
419-
console.log(`Could not run ${script.title}: ${e}`);
420-
}
379+
await executeScript(
380+
script,
381+
this.props.className,
382+
this.props.objectId,
383+
this.props.showNote,
384+
this.props.onRefresh
385+
);
421386
}
422387

423388
toggleConfirmationDialog() {
@@ -590,26 +555,14 @@ export default class BrowserCell extends Component {
590555
let extras = null;
591556
if (this.state.showConfirmationDialog) {
592557
extras = (
593-
<Modal
594-
type={
595-
this.selectedScript.confirmationDialogStyle === 'critical'
596-
? Modal.Types.DANGER
597-
: Modal.Types.INFO
598-
}
599-
icon="warn-outline"
600-
title={this.selectedScript.title}
601-
confirmText="Continue"
602-
cancelText="Cancel"
558+
<ScriptConfirmationModal
559+
script={this.selectedScript}
603560
onCancel={() => this.toggleConfirmationDialog()}
604561
onConfirm={() => {
605-
this.executeSript(this.selectedScript);
562+
this.executeScript(this.selectedScript);
606563
this.toggleConfirmationDialog();
607564
}}
608-
>
609-
<div className={[labelStyles.label, labelStyles.text, styles.action].join(' ')}>
610-
{`Do you want to run script "${this.selectedScript.title}" on "${this.selectedScript.className}" object "${this.selectedScript.objectId}"?`}
611-
</div>
612-
</Modal>
565+
/>
613566
);
614567
}
615568

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2016-present, Parse, LLC
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the license found in the LICENSE file in
6+
* the root directory of this source tree.
7+
*/
8+
import React from 'react';
9+
import Modal from 'components/Modal/Modal.react';
10+
import labelStyles from 'components/Label/Label.scss';
11+
import browserCellStyles from 'components/BrowserCell/BrowserCell.scss';
12+
13+
/**
14+
* Confirmation dialog for executing scripts
15+
*/
16+
export default function ScriptConfirmationModal({ script, onConfirm, onCancel }) {
17+
if (!script) {
18+
return null;
19+
}
20+
21+
return (
22+
<Modal
23+
type={script.confirmationDialogStyle === 'critical' ? Modal.Types.DANGER : Modal.Types.INFO}
24+
icon="warn-outline"
25+
title={script.title}
26+
confirmText="Continue"
27+
cancelText="Cancel"
28+
onCancel={onCancel}
29+
onConfirm={onConfirm}
30+
>
31+
<div className={[labelStyles.label, labelStyles.text, browserCellStyles.action].join(' ')}>
32+
{`Do you want to run script "${script.title}" on "${script.className}" object "${script.objectId}"?`}
33+
</div>
34+
</Modal>
35+
);
36+
}

src/dashboard/Data/Browser/DataBrowser.react.js

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import * as ColumnPreferences from 'lib/ColumnPreferences';
1313
import { CurrentApp } from 'context/currentApp';
1414
import { dateStringUTC } from 'lib/DateUtils';
1515
import getFileName from 'lib/getFileName';
16+
import { getValidScripts, executeScript } from '../../../lib/ScriptUtils';
1617
import Parse from 'parse';
1718
import React from 'react';
1819
import { ResizableBox } from 'react-resizable';
20+
import ScriptConfirmationModal from '../../../components/ScriptConfirmationModal/ScriptConfirmationModal.react';
1921
import styles from './Databrowser.scss';
2022

2123
import AggregationPanel from '../../../components/AggregationPanel/AggregationPanel';
@@ -144,6 +146,11 @@ export default class DataBrowser extends React.Component {
144146
multiPanelData: {}, // Object mapping objectId to panel data
145147
_objectsToFetch: [], // Temporary field for async fetch handling
146148
loadingObjectIds: new Set(),
149+
showScriptConfirmationDialog: false,
150+
selectedScript: null,
151+
contextMenuX: null,
152+
contextMenuY: null,
153+
contextMenuItems: null,
147154
};
148155

149156
this.handleResizeDiv = this.handleResizeDiv.bind(this);
@@ -971,37 +978,9 @@ export default class DataBrowser extends React.Component {
971978
const className = this.props.className;
972979
const field = 'objectId';
973980

974-
const menuItems = [];
975-
let validator = null;
981+
const { validScripts, validator } = getValidScripts(scripts, className, field);
976982

977-
// Filter scripts valid for this class and field
978-
const validScripts = (scripts || []).filter(script => {
979-
if (script.classes?.includes(className)) {
980-
return true;
981-
}
982-
for (const scriptClass of script?.classes || []) {
983-
if (scriptClass?.name !== className) {
984-
continue;
985-
}
986-
const fields = scriptClass?.fields || [];
987-
if (scriptClass?.fields.includes(field) || scriptClass?.fields.includes('*')) {
988-
return true;
989-
}
990-
for (const currentField of fields) {
991-
if (Object.prototype.toString.call(currentField) === '[object Object]') {
992-
if (currentField.name === field) {
993-
if (typeof currentField.validator === 'string') {
994-
validator = eval(currentField.validator);
995-
} else {
996-
validator = currentField.validator;
997-
}
998-
return true;
999-
}
1000-
}
1001-
}
1002-
}
1003-
return false;
1004-
});
983+
const menuItems = [];
1005984

1006985
// Add Scripts menu if there are valid scripts
1007986
if (validScripts.length && this.props.onEditSelectedRow) {
@@ -1011,21 +990,21 @@ export default class DataBrowser extends React.Component {
1011990
return {
1012991
text: script.title,
1013992
disabled: validator?.(objectId, field) === false,
1014-
callback: async () => {
1015-
try {
1016-
const object = Parse.Object.extend(className).createWithoutData(objectId);
1017-
const response = await Parse.Cloud.run(
1018-
script.cloudCodeFunction,
1019-
{ object: object.toPointer() },
1020-
{ useMasterKey: true }
1021-
);
1022-
this.props.showNote(
1023-
response || `Ran script "${script.title}" on "${className}" object "${objectId}".`
993+
callback: () => {
994+
const selectedScript = { ...script, className, objectId };
995+
if (script.showConfirmationDialog) {
996+
this.setState({
997+
showScriptConfirmationDialog: true,
998+
selectedScript
999+
});
1000+
} else {
1001+
executeScript(
1002+
script,
1003+
className,
1004+
objectId,
1005+
this.props.showNote,
1006+
this.props.onRefresh
10241007
);
1025-
this.props.onRefresh();
1026-
} catch (e) {
1027-
this.props.showNote(e.message, true);
1028-
console.log(`Could not run ${script.title}: ${e}`);
10291008
}
10301009
},
10311010
};
@@ -1827,6 +1806,22 @@ export default class DataBrowser extends React.Component {
18271806
items={this.state.contextMenuItems}
18281807
/>
18291808
)}
1809+
{this.state.showScriptConfirmationDialog && (
1810+
<ScriptConfirmationModal
1811+
script={this.state.selectedScript}
1812+
onCancel={() => this.setState({ showScriptConfirmationDialog: false })}
1813+
onConfirm={() => {
1814+
executeScript(
1815+
this.state.selectedScript,
1816+
this.state.selectedScript.className,
1817+
this.state.selectedScript.objectId,
1818+
this.props.showNote,
1819+
this.props.onRefresh
1820+
);
1821+
this.setState({ showScriptConfirmationDialog: false });
1822+
}}
1823+
/>
1824+
)}
18301825
</div>
18311826
);
18321827
}

src/lib/ScriptUtils.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2016-present, Parse, LLC
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the license found in the LICENSE file in
6+
* the root directory of this source tree.
7+
*/
8+
import Parse from 'parse';
9+
10+
/**
11+
* Filters scripts to only those valid for the given className and field
12+
* @param {Array} scripts - Array of script configurations
13+
* @param {string} className - The Parse class name
14+
* @param {string} field - The field name
15+
* @returns {Object} - { validScripts: Array, validator: Function|null }
16+
*/
17+
export function getValidScripts(scripts, className, field) {
18+
let validator = null;
19+
const validScripts = (scripts || []).filter(script => {
20+
if (script.classes?.includes(className)) {
21+
return true;
22+
}
23+
for (const scriptClass of script?.classes || []) {
24+
if (scriptClass?.name !== className) {
25+
continue;
26+
}
27+
const fields = scriptClass?.fields || [];
28+
if (scriptClass?.fields.includes(field) || scriptClass?.fields.includes('*')) {
29+
return true;
30+
}
31+
for (const currentField of fields) {
32+
if (Object.prototype.toString.call(currentField) === '[object Object]') {
33+
if (currentField.name === field) {
34+
if (typeof currentField.validator === 'string') {
35+
validator = eval(currentField.validator);
36+
} else {
37+
validator = currentField.validator;
38+
}
39+
return true;
40+
}
41+
}
42+
}
43+
}
44+
return false;
45+
});
46+
47+
return { validScripts, validator };
48+
}
49+
50+
/**
51+
* Executes a Parse Cloud Code script
52+
* @param {Object} script - The script configuration
53+
* @param {string} className - The Parse class name
54+
* @param {string} objectId - The object ID
55+
* @param {Function} showNote - Callback to show notification
56+
* @param {Function} onRefresh - Callback to refresh data
57+
*/
58+
export async function executeScript(script, className, objectId, showNote, onRefresh) {
59+
try {
60+
const object = Parse.Object.extend(className).createWithoutData(objectId);
61+
const response = await Parse.Cloud.run(
62+
script.cloudCodeFunction,
63+
{ object: object.toPointer() },
64+
{ useMasterKey: true }
65+
);
66+
showNote(
67+
response || `Ran script "${script.title}" on "${className}" object "${object.id}".`
68+
);
69+
onRefresh();
70+
} catch (e) {
71+
showNote(e.message, true);
72+
console.log(`Could not run ${script.title}: ${e}`);
73+
}
74+
}

0 commit comments

Comments
 (0)