Skip to content

Commit 0ac246f

Browse files
committed
feature: save configuration on server
1 parent d74638f commit 0ac246f

File tree

7 files changed

+148
-19
lines changed

7 files changed

+148
-19
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,19 @@ For example:
399399

400400
You can conveniently create a filter definition without having to write it by hand by first saving a filter in the data browser, then exporting the filter definition under *App Settings > Export Class Preferences*.
401401

402+
### Saving Configuration of the server
403+
404+
You can save the confiugration on the server by specifying `preferencesClassName`
405+
```json
406+
"apps": [
407+
{
408+
"preferencesClassName": "DashboardPreferences"
409+
}
410+
]
411+
```
412+
413+
Once this is set, you can visit `AppSettings > Dashboard` to save the current Column and Class settings.
414+
402415
### Scripts
403416

404417
You can specify scripts to execute Cloud Functions with the `scripts` option:

jsconfig.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{
22
"compilerOptions": {
3-
"experimentalDecorators": true
3+
"experimentalDecorators": true,
4+
"baseUrl": "src",
5+
"paths": {
6+
"lib/*": ["lib/*"],
7+
"components/*": ["components/*"],
8+
"stylesheets/*": ["stylesheets/*"]
9+
}
410
},
511
"typeAcquisition": {
612
"include": [

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ class Browser extends DashboardView {
204204
this.action = new SidebarAction('Create a class', this.showCreateClass.bind(this));
205205
}
206206

207+
if (this.context.preferencesClassName) {
208+
ColumnPreferences.load(this.context.preferencesClassName);
209+
ClassPreferences.load(this.context.preferencesClassName);
210+
}
211+
207212
this.props.schema.dispatch(ActionTypes.FETCH).then(() => this.handleFetchedSchema());
208213
if (!this.props.params.className && this.props.schema.data.get('classes')) {
209214
this.redirectToFirstClass(this.props.schema.data.get('classes'));

src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ import * as ClassPreferences from 'lib/ClassPreferences';
2020
import bcrypt from 'bcryptjs';
2121
import * as OTPAuth from 'otpauth';
2222
import QRCode from 'qrcode';
23+
import Parse from 'parse';
24+
import { CurrentApp } from 'context/currentApp';
2325

2426
export default class DashboardSettings extends DashboardView {
27+
static contextType = CurrentApp;
2528
constructor() {
2629
super();
2730
this.section = 'App Settings';
@@ -49,6 +52,7 @@ export default class DashboardSettings extends DashboardView {
4952
show: false,
5053
mfa: '',
5154
},
55+
showSavePreferences: !!this.context?.preferencesClassName,
5256
};
5357
}
5458

@@ -63,6 +67,35 @@ export default class DashboardSettings extends DashboardView {
6367
});
6468
}
6569

70+
async saveColumns() {
71+
const data = ColumnPreferences.getAllPreferences(this.context.applicationId);
72+
let preferences = await new Parse.Query(this.context.preferencesClassName)
73+
.equalTo('applicationId', this.context.applicationId)
74+
.equalTo('key', 'columnPreferences')
75+
.equalTo('user', Parse.User.current())
76+
.first({ useMasterKey: true });
77+
78+
if (!preferences) {
79+
preferences = new Parse.Object(this.context.preferencesClassName);
80+
preferences.set('applicationId', this.context.applicationId);
81+
preferences.set('key', 'columnPreferences');
82+
preferences.set('user', Parse.User.current());
83+
preferences.setACL(
84+
new Parse.ACL(Parse.User.current())
85+
);
86+
}
87+
88+
preferences.set('value', JSON.stringify(data));
89+
90+
try {
91+
await preferences.save(null, { useMasterKey: true });
92+
this.showNote('Column preferences saved successfully');
93+
} catch (error) {
94+
this.showNote(`Error saving column preferences: ${error.message}`);
95+
}
96+
97+
}
98+
6699
getClasses() {
67100
const data = ClassPreferences.getAllPreferences(this.context.applicationId);
68101
this.setState({
@@ -74,6 +107,31 @@ export default class DashboardSettings extends DashboardView {
74107
});
75108
}
76109

110+
async saveClasses() {
111+
const data = ClassPreferences.getAllPreferences(this.context.applicationId);
112+
let preferences = await new Parse.Query(this.context.preferencesClassName)
113+
.equalTo('applicationId', this.context.applicationId)
114+
.equalTo('key', 'classPreferences')
115+
.equalTo('user', Parse.User.current())
116+
.first({ useMasterKey: true });
117+
if (!preferences) {
118+
preferences = new Parse.Object(this.context.preferencesClassName);
119+
preferences.set('applicationId', this.context.applicationId);
120+
preferences.set('key', 'classPreferences');
121+
preferences.set('user', Parse.User.current());
122+
preferences.setACL(
123+
new Parse.ACL(Parse.User.current())
124+
);
125+
}
126+
preferences.set('value', JSON.stringify(data));
127+
try {
128+
await preferences.save(null, { useMasterKey: true });
129+
this.showNote('Class preferences saved successfully');
130+
} catch (error) {
131+
this.showNote(`Error saving class preferences: ${error.message}`);
132+
}
133+
}
134+
77135
copy(data, label) {
78136
navigator.clipboard.writeText(data);
79137
this.showNote(`${label} copied to clipboard`);
@@ -367,10 +425,18 @@ export default class DashboardSettings extends DashboardView {
367425
label={<Label text="Export Column Preferences" />}
368426
input={<FormButton color="blue" value="Export" onClick={() => this.getColumns()} />}
369427
/>
428+
{this.state.showSavePreferences && <Field
429+
label={<Label text="Save Column Preferences" />}
430+
input={<FormButton color="blue" value="Save" onClick={() => this.saveColumns()} />}
431+
/>}
370432
<Field
371433
label={<Label text="Export Class Preferences" />}
372434
input={<FormButton color="blue" value="Export" onClick={() => this.getClasses()} />}
373435
/>
436+
{this.state.showSavePreferences && <Field
437+
label={<Label text="Save Class Preferences" />}
438+
input={<FormButton color="blue" value="Save" onClick={() => this.getClasses()} />}
439+
/>}
374440
<Field
375441
label={<Label text="Create New User" />}
376442
input={

src/lib/ClassPreferences.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
11
const VERSION = 1; // In case we ever need to invalidate these
2+
import Parse from 'parse';
3+
4+
export const load = async (preferencesClassName) => {
5+
const preferences = await new Parse.Query(preferencesClassName)
6+
.equalTo('user', Parse.User.current())
7+
.equalTo('key', 'classPreferences')
8+
.first({ useMasterKey: true });
9+
10+
if (preferences) {
11+
const prefs = preferences.get('value');
12+
setClassPreferences(JSON.parse(prefs), Parse.applicationId);
13+
}
14+
15+
}
16+
17+
export function setClassPreferences(classPreference, appId) {
18+
if (!classPreference) {
19+
return;
20+
}
21+
22+
for (const className in classPreference) {
23+
const preferences = getPreferences(appId, className) || { filters: [] };
24+
const { filters } = classPreference[className];
25+
for (const filter of filters) {
26+
if (Array.isArray(filter.filter)) {
27+
filter.filter = JSON.stringify(filter.filter);
28+
}
29+
if (preferences.filters.some(row => JSON.stringify(row) === JSON.stringify(filter))) {
30+
continue;
31+
}
32+
preferences.filters.push(filter);
33+
}
34+
updatePreferences(preferences, appId, className);
35+
}
36+
}
237
export function updatePreferences(prefs, appId, className) {
338
try {
439
localStorage.setItem(path(appId, className), JSON.stringify(prefs));

src/lib/ColumnPreferences.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ const DEFAULT_WIDTH = 150;
1010
const COLUMN_SORT = '__columnClassesSort'; // Used for storing classes sort field
1111
const DEFAULT_COLUMN_SORT = '-createdAt'; // Default column sorting
1212
const cache = {};
13+
import Parse from 'parse';
14+
15+
export const load = async (preferencesClassName) => {
16+
const preferences = await new Parse.Query(preferencesClassName)
17+
.equalTo('user', Parse.User.current())
18+
.equalTo('key', 'columnPreferences')
19+
.first({ useMasterKey: true });
20+
21+
if (preferences) {
22+
const prefs = JSON.parse(preferences.get('value'));
23+
if (prefs) {
24+
for (const className in prefs) {
25+
updatePreferences(prefs[className], Parse.applicationId, className);
26+
}
27+
}
28+
}
29+
30+
}
1331

1432
export function updatePreferences(prefs, appId, className) {
1533
try {

src/lib/ParseApp.js

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import * as AJAX from 'lib/AJAX';
99
import encodeFormData from 'lib/encodeFormData';
1010
import Parse from 'parse';
11-
import { updatePreferences, getPreferences } from 'lib/ClassPreferences';
11+
import { setClassPreferences } from 'lib/ClassPreferences';
1212

1313
function setEnablePushSource(setting, enable) {
1414
const path = `/apps/${this.slug}/update_push_notifications`;
@@ -50,6 +50,7 @@ export default class ParseApp {
5050
classPreference,
5151
enableSecurityChecks,
5252
cloudConfigHistoryLimit,
53+
preferencesClassName
5354
}) {
5455
this.name = appName;
5556
this.createdAt = created_at ? new Date(created_at) : new Date();
@@ -79,6 +80,7 @@ export default class ParseApp {
7980
this.scripts = scripts;
8081
this.enableSecurityChecks = !!enableSecurityChecks;
8182
this.cloudConfigHistoryLimit = cloudConfigHistoryLimit;
83+
this.preferencesClassName = preferencesClassName;
8284

8385
if (!supportedPushLocales) {
8486
console.warn(
@@ -109,23 +111,7 @@ export default class ParseApp {
109111
};
110112

111113
this.hasCheckedForMigraton = false;
112-
113-
if (classPreference) {
114-
for (const className in classPreference) {
115-
const preferences = getPreferences(appId, className) || { filters: [] };
116-
const { filters } = classPreference[className];
117-
for (const filter of filters) {
118-
if (Array.isArray(filter.filter)) {
119-
filter.filter = JSON.stringify(filter.filter);
120-
}
121-
if (preferences.filters.some(row => JSON.stringify(row) === JSON.stringify(filter))) {
122-
continue;
123-
}
124-
preferences.filters.push(filter);
125-
}
126-
updatePreferences(preferences, appId, className);
127-
}
128-
}
114+
setClassPreferences(classPreference, appId);
129115
}
130116

131117
setParseKeys() {

0 commit comments

Comments
 (0)