Skip to content

Commit 0482e4d

Browse files
committed
UI for importing and exporting data
1 parent adb72d2 commit 0482e4d

File tree

7 files changed

+278
-5
lines changed

7 files changed

+278
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ npm-debug.log
99
// vim .swp
1010
*.swp
1111
.env
12+
1213
.idea/

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { DefaultColumns, SpecialClasses } from 'lib/Constants';
1515
import DeleteRowsDialog from 'dashboard/Data/Browser/DeleteRowsDialog.react';
1616
import DropClassDialog from 'dashboard/Data/Browser/DropClassDialog.react';
1717
import EmptyState from 'components/EmptyState/EmptyState.react';
18+
import ImportDialog from 'dashboard/Data/Browser/ImportDialog.react';
19+
import ImportRelationDialog from 'dashboard/Data/Browser/ImportRelationDialog.react';
1820
import ExportDialog from 'dashboard/Data/Browser/ExportDialog.react';
1921
import AttachRowsDialog from 'dashboard/Data/Browser/AttachRowsDialog.react';
2022
import AttachSelectedRowsDialog from 'dashboard/Data/Browser/AttachSelectedRowsDialog.react';
@@ -45,6 +47,8 @@ export default class Browser extends DashboardView {
4547
showAddColumnDialog: false,
4648
showRemoveColumnDialog: false,
4749
showDropClassDialog: false,
50+
showImportDialog: false,
51+
showImportRelationDialog: false,
4852
showExportDialog: false,
4953
showAttachRowsDialog: false,
5054
rowsToDelete: null,
@@ -74,6 +78,8 @@ export default class Browser extends DashboardView {
7478
this.showDeleteRows = this.showDeleteRows.bind(this);
7579
this.showDropClass = this.showDropClass.bind(this);
7680
this.showExport = this.showExport.bind(this);
81+
this.showImport = this.showImport.bind(this);
82+
this.showImportRelation = this.showImportRelation.bind(this);
7783
this.showAttachRowsDialog = this.showAttachRowsDialog.bind(this);
7884
this.cancelAttachRows = this.cancelAttachRows.bind(this);
7985
this.confirmAttachRows = this.confirmAttachRows.bind(this);
@@ -201,6 +207,14 @@ export default class Browser extends DashboardView {
201207
this.setState({ showDropClassDialog: true });
202208
}
203209

210+
showImport() {
211+
this.setState({ showImportDialog: true });
212+
}
213+
214+
showImportRelation() {
215+
this.setState({ showImportRelationDialog: true });
216+
}
217+
204218
showExport() {
205219
this.setState({ showExportDialog: true });
206220
}
@@ -228,6 +242,32 @@ export default class Browser extends DashboardView {
228242
});
229243
}
230244

245+
importClass(className, file) {
246+
return this.context.currentApp.importData(className, file)
247+
.then((res) => {
248+
return res;
249+
}, (error) => {
250+
console.log(error);
251+
return Promise.resolve({
252+
error: true,
253+
message: error.message
254+
});
255+
});
256+
}
257+
258+
importRelation(className, relationName, file) {
259+
return this.context.currentApp.importRelationData(className, relationName, file)
260+
.then((res) => {
261+
return res;
262+
}, (error) => {
263+
console.log(error);
264+
return Promise.resolve({
265+
error: true,
266+
message: error.message
267+
});
268+
});
269+
}
270+
231271
exportClass(className) {
232272
this.context.currentApp.exportClass(className).always(() => {
233273
this.setState({ showExportDialog: false });
@@ -287,6 +327,7 @@ export default class Browser extends DashboardView {
287327
newObject: null,
288328
lastMax: -1,
289329
selection: {},
330+
relation: null
290331
};
291332
if (relation) {
292333
await this.setState(initialState);
@@ -424,6 +465,8 @@ export default class Browser extends DashboardView {
424465
setRelation(relation, filters) {
425466
this.setState({
426467
relation: relation,
468+
relationCount: 0,
469+
selection: {},
427470
}, () => {
428471
let filterQueryString;
429472
if (filters && filters.size) {
@@ -595,6 +638,8 @@ export default class Browser extends DashboardView {
595638
this.state.showAddColumnDialog ||
596639
this.state.showRemoveColumnDialog ||
597640
this.state.showDropClassDialog ||
641+
this.state.showImportDialog ||
642+
this.state.showImportRelationDialog ||
598643
this.state.showExportDialog ||
599644
this.state.rowsToDelete ||
600645
this.state.showAttachRowsDialog ||
@@ -793,6 +838,8 @@ export default class Browser extends DashboardView {
793838
onDeleteRows={this.showDeleteRows}
794839
onDropClass={this.showDropClass}
795840
onExport={this.showExport}
841+
onImport={this.showImport}
842+
onImportRelation={this.showImportRelation}
796843
onChangeCLP={this.handleCLPChange}
797844
onRefresh={this.refresh}
798845
onAttachRows={this.showAttachRowsDialog}
@@ -866,6 +913,20 @@ export default class Browser extends DashboardView {
866913
})}
867914
onConfirm={() => this.dropClass(className)} />
868915
);
916+
} else if (this.state.showImportDialog) {
917+
extras = (
918+
<ImportDialog
919+
className={className}
920+
onCancel={() => this.setState({ showImportDialog: false })}
921+
onConfirm={(file) => this.importClass(className, file)} />
922+
);
923+
} else if (this.state.showImportRelationDialog) {
924+
extras = (
925+
<ImportRelationDialog
926+
className={className}
927+
onCancel={() => this.setState({ showImportRelationDialog: false })}
928+
onConfirm={(relationName, file) => this.importRelation(className, relationName, file)} />
929+
);
869930
} else if (this.state.showExportDialog) {
870931
extras = (
871932
<ExportDialog

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ let BrowserToolbar = ({
3333
onAddClass,
3434
onAttachRows,
3535
onAttachSelectedRows,
36+
onImport,
37+
onImportRelation,
3638
onExport,
3739
onRemoveColumn,
3840
onDeleteRows,
@@ -42,6 +44,7 @@ let BrowserToolbar = ({
4244
hidePerms,
4345

4446
enableDeleteAllRows,
47+
enableImport,
4548
enableExportClass,
4649
enableSecurityDialog,
4750
}) => {
@@ -108,7 +111,9 @@ let BrowserToolbar = ({
108111
<MenuItem text='Delete a column' onClick={onRemoveColumn} />
109112
{enableDeleteAllRows ? <MenuItem text='Delete all rows' onClick={() => onDeleteRows({ '*': true })} /> : <noscript />}
110113
<MenuItem text='Delete this class' onClick={onDropClass} />
111-
{enableExportClass ? <Separator /> : <noscript />}
114+
{enableImport || enableExportClass ? <Separator /> : <noscript />}
115+
{enableImport ? <MenuItem text='Import data' onClick={onImport} /> : <noscript />}
116+
{enableImport ? <MenuItem text='Import relation data' onClick={onImportRelation} /> : <noscript />}
112117
{enableExportClass ? <MenuItem text='Export this data' onClick={onExport} /> : <noscript />}
113118
</BrowserMenu>
114119
);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ export default class DataBrowser extends React.Component {
207207
setCurrent={this.setCurrent.bind(this)}
208208
enableDeleteAllRows={this.context.currentApp.serverInfo.features.schemas.clearAllDataFromClass}
209209
enableExportClass={this.context.currentApp.serverInfo.features.schemas.exportClass}
210+
enableImport={this.context.currentApp.serverInfo.features.schemas.import}
210211
enableSecurityDialog={this.context.currentApp.serverInfo.features.schemas.editClassLevelPermissions}
211212
{...other}/>
212213
</div>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 ParseApp from 'lib/ParseApp';
10+
import Modal from 'components/Modal/Modal.react';
11+
import Field from 'components/Field/Field.react';
12+
import TextInput from 'components/TextInput/TextInput.react';
13+
import FileInput from 'components/FileInput/FileInput.react';
14+
import Label from 'components/Label/Label.react';
15+
16+
export default class ImportDialog extends React.Component {
17+
constructor() {
18+
super();
19+
this.state = {
20+
file: undefined,
21+
startedImport: false,
22+
errorMessage: null
23+
};
24+
}
25+
26+
valid() {
27+
if (this.state.file != undefined && !this.state.startedImport) {
28+
return true;
29+
}
30+
return false;
31+
}
32+
33+
render() {
34+
return (
35+
<Modal
36+
type={Modal.Types.INFO}
37+
icon='down-outline'
38+
iconSize={40}
39+
title='Import data'
40+
subtitle={'You will receive an e-mail once your data is imported'}
41+
confirmText='Import'
42+
cancelText='Cancel'
43+
disabled={!this.valid()}
44+
buttonsInCenter={true}
45+
onCancel={this.props.onCancel}
46+
onConfirm={() => {
47+
this.props.onConfirm(this.state.file)
48+
.then((res) => {
49+
if (res.error) {
50+
this.setState({ errorMessage: res.message });
51+
} else {
52+
this.props.onCancel();
53+
}
54+
});
55+
}}>
56+
57+
<Field
58+
label={
59+
<Label
60+
text='Select a JSON file with your class data' />}
61+
input={
62+
<FileInput
63+
onChange={(file) => {this.setState({ file: file });}} />}
64+
/>
65+
{this.state.startedImport ?
66+
<div style={{ padding: 20 }}>We are importing your data. You will be notified by e-mail once it is completed.</div> : null }
67+
{this.state.errorMessage ?
68+
<div style={{ padding: 20, color: '#ff395e' }}>Import Request failed with the following error: "{ this.state.errorMessage }".</div> : null }
69+
</Modal>
70+
71+
);
72+
}
73+
}
74+
75+
ImportDialog.contextTypes = {
76+
currentApp: React.PropTypes.instanceOf(ParseApp)
77+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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 ParseApp from 'lib/ParseApp';
10+
import Modal from 'components/Modal/Modal.react';
11+
import Field from 'components/Field/Field.react';
12+
import TextInput from 'components/TextInput/TextInput.react';
13+
import FileInput from 'components/FileInput/FileInput.react';
14+
import Label from 'components/Label/Label.react';
15+
16+
export default class ImportRelationDialog extends React.Component {
17+
constructor() {
18+
super();
19+
this.state = {
20+
relationName: '',
21+
file: undefined,
22+
startedImport: false
23+
};
24+
}
25+
26+
valid() {
27+
if (this.state.relationName != '' && this.state.file != undefined && !this.state.startedImport) {
28+
return true;
29+
}
30+
return false;
31+
}
32+
33+
render() {
34+
return (
35+
<Modal
36+
type={Modal.Types.INFO}
37+
icon='down-outline'
38+
iconSize={40}
39+
title='Import relation data'
40+
subtitle={'You will receive an e-mail once your data is imported'}
41+
confirmText='Import'
42+
cancelText='Cancel'
43+
disabled={!this.valid()}
44+
buttonsInCenter={true}
45+
onCancel={this.props.onCancel}
46+
onConfirm={() => {
47+
this.props.onConfirm(this.state.relationName, this.state.file)
48+
.then((res) => {
49+
this.props.onCancel();
50+
});
51+
}}>
52+
53+
<Field
54+
label={
55+
<Label
56+
text='Enter the relation name' />
57+
}
58+
input={
59+
<TextInput
60+
placeholder='Relation name'
61+
value={this.state.relationName}
62+
onChange={(relationName) => this.setState({ relationName: relationName })} />
63+
} />
64+
<Field
65+
label={
66+
<Label
67+
text='Select a JSON file with your relation data' />}
68+
input={
69+
<FileInput
70+
onChange={(file) => {this.setState({ file: file });}} />}
71+
/>
72+
{this.state.startedImport ?
73+
<div style={{ padding: 20 }}>We are importing your data. You will be notified by e-mail once it is completed.</div> : null }
74+
{this.state.errorMessage ?
75+
<div style={{ padding: 20, color: '#ff395e' }}>Import Request failed with the following error: "{ this.state.errorMessage }".</div> : null }
76+
</Modal>
77+
);
78+
}
79+
}
80+
81+
ImportRelationDialog.contextTypes = {
82+
currentApp: React.PropTypes.instanceOf(ParseApp)
83+
};

0 commit comments

Comments
 (0)