Skip to content

Commit ea1b0c7

Browse files
authored
Support for clone object (#946)
1 parent f09ae40 commit ea1b0c7

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import EmptyState from 'components/EmptyState/EmptyState
1818
import ExportDialog from 'dashboard/Data/Browser/ExportDialog.react';
1919
import AttachRowsDialog from 'dashboard/Data/Browser/AttachRowsDialog.react';
2020
import AttachSelectedRowsDialog from 'dashboard/Data/Browser/AttachSelectedRowsDialog.react';
21+
import CloneSelectedRowsDialog from 'dashboard/Data/Browser/CloneSelectedRowsDialog.react';
2122
import history from 'dashboard/history';
2223
import { List, Map } from 'immutable';
2324
import Notification from 'dashboard/Data/Browser/Notification.react';
@@ -86,6 +87,9 @@ class Browser extends DashboardView {
8687
this.showAttachSelectedRowsDialog = this.showAttachSelectedRowsDialog.bind(this);
8788
this.confirmAttachSelectedRows = this.confirmAttachSelectedRows.bind(this);
8889
this.cancelAttachSelectedRows = this.cancelAttachSelectedRows.bind(this);
90+
this.showCloneSelectedRowsDialog = this.showCloneSelectedRowsDialog.bind(this);
91+
this.confirmCloneSelectedRows = this.confirmCloneSelectedRows.bind(this);
92+
this.cancelCloneSelectedRows = this.cancelCloneSelectedRows.bind(this);
8993
this.getClassRelationColumns = this.getClassRelationColumns.bind(this);
9094
this.showCreateClass = this.showCreateClass.bind(this);
9195
this.refresh = this.refresh.bind(this);
@@ -656,7 +660,8 @@ class Browser extends DashboardView {
656660
this.state.showExportDialog ||
657661
this.state.rowsToDelete ||
658662
this.state.showAttachRowsDialog ||
659-
this.state.showAttachSelectedRowsDialog
663+
this.state.showAttachSelectedRowsDialog ||
664+
this.state.showCloneSelectedRowsDialog
660665
);
661666
}
662667

@@ -732,6 +737,41 @@ class Browser extends DashboardView {
732737
});
733738
}
734739

740+
showCloneSelectedRowsDialog() {
741+
this.setState({
742+
showCloneSelectedRowsDialog: true,
743+
});
744+
}
745+
746+
cancelCloneSelectedRows() {
747+
this.setState({
748+
showCloneSelectedRowsDialog: false,
749+
});
750+
}
751+
752+
async confirmCloneSelectedRows() {
753+
const objectIds = [];
754+
for (const objectId in this.state.selection) {
755+
objectIds.push(objectId);
756+
}
757+
const query = new Parse.Query(this.props.params.className);
758+
query.containedIn('objectId', objectIds);
759+
const objects = await query.find({ useMasterKey: true });
760+
const toClone = [];
761+
for (const object of objects) {
762+
toClone.push(object.clone());
763+
}
764+
await Parse.Object.saveAll(toClone, { useMasterKey: true });
765+
this.setState({
766+
selection: {},
767+
data: [
768+
...toClone,
769+
...this.state.data,
770+
],
771+
showCloneSelectedRowsDialog: false,
772+
});
773+
}
774+
735775
getClassRelationColumns(className) {
736776
const currentClassName = this.props.params.className;
737777
return this.getClassColumns(className, false)
@@ -883,6 +923,7 @@ class Browser extends DashboardView {
883923
onRefresh={this.refresh}
884924
onAttachRows={this.showAttachRowsDialog}
885925
onAttachSelectedRows={this.showAttachSelectedRowsDialog}
926+
onCloneSelectedRows={this.showCloneSelectedRowsDialog}
886927

887928
columns={columns}
888929
className={className}
@@ -978,6 +1019,15 @@ class Browser extends DashboardView {
9781019
onConfirm={this.confirmAttachSelectedRows}
9791020
/>
9801021
);
1022+
} else if (this.state.showCloneSelectedRowsDialog) {
1023+
extras = (
1024+
<CloneSelectedRowsDialog
1025+
className={className}
1026+
selection={this.state.selection}
1027+
onCancel={this.cancelCloneSelectedRows}
1028+
onConfirm={this.confirmCloneSelectedRows}
1029+
/>
1030+
);
9811031
}
9821032

9831033
let notification = null;

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ let BrowserToolbar = ({
3333
onAddClass,
3434
onAttachRows,
3535
onAttachSelectedRows,
36+
onCloneSelectedRows,
3637
onExport,
3738
onRemoveColumn,
3839
onDeleteRows,
@@ -101,6 +102,12 @@ let BrowserToolbar = ({
101102
onClick={onAttachSelectedRows}
102103
/>
103104
<Separator />
105+
<MenuItem
106+
disabled={!selectionLength}
107+
text={`Clone ${selectionLength <= 1 ? 'this row' : 'these rows'}`}
108+
onClick={onCloneSelectedRows}
109+
/>
110+
<Separator />
104111
<MenuItem
105112
disabled={selectionLength === 0}
106113
text={selectionLength === 1 && !selection['*'] ? 'Delete this row' : 'Delete these rows'}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 Field from 'components/Field/Field.react';
9+
import Label from 'components/Label/Label.react';
10+
import Modal from 'components/Modal/Modal.react';
11+
import React from 'react';
12+
import TextInput from 'components/TextInput/TextInput.react';
13+
14+
export default class CloneSelectedRowsDialog extends React.Component {
15+
constructor() {
16+
super();
17+
18+
this.state = {
19+
confirmation: ''
20+
};
21+
}
22+
23+
valid() {
24+
if (this.state.confirmation === this.props.className) {
25+
return true;
26+
}
27+
if (!this.props.selection['*'] && Object.keys(this.props.selection).length < 10) {
28+
return true;
29+
}
30+
return false;
31+
}
32+
33+
render() {
34+
let content = null;
35+
let selectionLength = Object.keys(this.props.selection).length;
36+
if (this.props.selection['*'] || selectionLength >= 10) {
37+
content = (
38+
<Field
39+
label={
40+
<Label
41+
text='Confirm this action'
42+
description='Enter the current class name to continue.' />
43+
}
44+
input={
45+
<TextInput
46+
placeholder='Current class name'
47+
value={this.state.confirmation}
48+
onChange={(confirmation) => this.setState({ confirmation })} />
49+
} />
50+
);
51+
}
52+
return (
53+
<Modal
54+
type={Modal.Types.DANGER}
55+
icon='warn-outline'
56+
title={this.props.selection['*'] ? 'Clone all rows?' : (selectionLength === 1 ? 'Clone this row?' : `Clone ${selectionLength} rows?`)}
57+
subtitle={''}
58+
disabled={!this.valid()}
59+
confirmText={'Yes clone'}
60+
cancelText={'Never mind, don\u2019t.'}
61+
onCancel={this.props.onCancel}
62+
onConfirm={this.props.onConfirm}>
63+
{content}
64+
</Modal>
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)