@journeyapps-labs/reactor-mod-data-browser 3.6.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/@types/core/SchemaModelObject.d.ts +3 -0
  3. package/dist/@types/core/query/AbstractQuery.d.ts +3 -1
  4. package/dist/actions/schema-definitions/CreateModelAction.js +1 -1
  5. package/dist/actions/schema-definitions/CreateModelAction.js.map +1 -1
  6. package/dist/core/AbstractConnection.js +2 -1
  7. package/dist/core/AbstractConnection.js.map +1 -1
  8. package/dist/core/SchemaModelDefinition.js +2 -2
  9. package/dist/core/SchemaModelDefinition.js.map +1 -1
  10. package/dist/core/SchemaModelObject.js +52 -9
  11. package/dist/core/SchemaModelObject.js.map +1 -1
  12. package/dist/core/query/AbstractQuery.js +48 -10
  13. package/dist/core/query/AbstractQuery.js.map +1 -1
  14. package/dist/core/query/widgets/SmartFilterWidget.js +1 -1
  15. package/dist/core/query/widgets/SmartFilterWidget.js.map +1 -1
  16. package/dist/forms/SchemaModelForm.js +9 -3
  17. package/dist/forms/SchemaModelForm.js.map +1 -1
  18. package/dist/panels/_shared/SharedModelPanelFactory.js +4 -2
  19. package/dist/panels/_shared/SharedModelPanelFactory.js.map +1 -1
  20. package/dist/panels/model/ModelPanelWidget.js +3 -3
  21. package/dist/panels/model/ModelPanelWidget.js.map +1 -1
  22. package/dist/panels/query/TableControlsWidget.js +11 -2
  23. package/dist/panels/query/TableControlsWidget.js.map +1 -1
  24. package/dist/panels/query/table-controls/ChangesControlsWidget.js +1 -1
  25. package/dist/panels/query/table-controls/ChangesControlsWidget.js.map +1 -1
  26. package/dist/panels/query/table-controls/PageControlsWidget.js +8 -5
  27. package/dist/panels/query/table-controls/PageControlsWidget.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/dist-module/bundle.js +36 -36
  30. package/dist-module/bundle.js.map +1 -1
  31. package/package.json +6 -6
  32. package/src/actions/schema-definitions/CreateModelAction.ts +1 -1
  33. package/src/core/AbstractConnection.ts +2 -1
  34. package/src/core/SchemaModelDefinition.ts +3 -5
  35. package/src/core/SchemaModelObject.ts +30 -7
  36. package/src/core/query/AbstractQuery.ts +16 -2
  37. package/src/core/query/widgets/SmartFilterWidget.tsx +1 -1
  38. package/src/forms/SchemaModelForm.tsx +7 -2
  39. package/src/panels/_shared/SharedModelPanelFactory.tsx +4 -2
  40. package/src/panels/model/ModelPanelWidget.tsx +3 -3
  41. package/src/panels/query/TableControlsWidget.tsx +22 -2
  42. package/src/panels/query/table-controls/ChangesControlsWidget.tsx +1 -1
  43. package/src/panels/query/table-controls/PageControlsWidget.tsx +7 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@journeyapps-labs/reactor-mod-data-browser",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "main": "./dist/index.js",
5
5
  "typings": "./dist/@types/index",
6
6
  "publishConfig": {
@@ -15,10 +15,10 @@
15
15
  "license": "Apache-2.0",
16
16
  "dependencies": {
17
17
  "@emotion/styled": "^11.14.1",
18
- "@fortawesome/react-fontawesome": "^3.3.0",
18
+ "@fortawesome/react-fontawesome": "^3.3.1",
19
19
  "@journeyapps-labs/client-backend-v4": "1.0.0",
20
20
  "@journeyapps-labs/common-ioc": "^1.0.1",
21
- "@journeyapps-labs/common-sdk": "^1.0.3",
21
+ "@journeyapps-labs/common-sdk": "^1.0.4",
22
22
  "@journeyapps-labs/common-utils": "^1.0.1",
23
23
  "@journeyapps-labs/lib-reactor-data-layer": "1.0.11",
24
24
  "@journeyapps-labs/reactor-mod": "5.6.0",
@@ -27,11 +27,11 @@
27
27
  "@journeyapps/parser-schema": "^8.2.5",
28
28
  "@projectstorm/react-workspaces-core": "4.2.3",
29
29
  "async": "^3.2.6",
30
- "lodash": "^4.17.23",
30
+ "lodash": "^4.18.1",
31
31
  "mobx": "^6.15.0",
32
32
  "mobx-react": "^9.2.1",
33
- "react": "19.2.4",
34
- "uuid": "^13.0.0"
33
+ "react": "19.2.5",
34
+ "uuid": "^14.0.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@journeyapps-labs/lib-reactor-builder": "3.0.2",
@@ -20,7 +20,7 @@ export class CreateModelAction extends EntityAction<SchemaModelDefinition> {
20
20
  super({
21
21
  id: CreateModelAction.ID,
22
22
  name: 'Create schema model',
23
- icon: 'search',
23
+ icon: 'plus',
24
24
  target: DataBrowserEntities.SCHEMA_MODEL_DEFINITION
25
25
  });
26
26
  }
@@ -90,7 +90,8 @@ export abstract class AbstractConnection extends BaseObserver<AbstractConnection
90
90
  }
91
91
  await batch.execute();
92
92
  for (let model of models) {
93
- model.reload();
93
+ await model.reload();
94
+ model.clearEdits();
94
95
  }
95
96
  }
96
97
 
@@ -6,13 +6,11 @@ import { SchemaModelObject } from './SchemaModelObject';
6
6
  import { LifecycleModel } from '@journeyapps-labs/lib-reactor-data-layer';
7
7
  import { BaseObserver } from '@journeyapps-labs/common-utils';
8
8
  import { queue, QueueObject } from 'async';
9
- import { v4 } from 'uuid';
10
- import { V4Index } from '@journeyapps-labs/client-backend-v4';
11
- import { action, observable } from 'mobx';
9
+ import { observable } from 'mobx';
12
10
  import { IndexModel } from './IndexModel';
13
11
  import { TypeEngine } from '../forms/TypeEngine';
14
- import { STANDARD_MODEL_FIELD_LABELS, StandardModelFields, idVariable } from './query/StandardModelFields';
15
- import { SchemaFieldOrderValue, SchemaFieldOrderingPreference } from '../preferences/SchemaOrderingPreferences';
12
+ import { idVariable, STANDARD_MODEL_FIELD_LABELS, StandardModelFields } from './query/StandardModelFields';
13
+ import { SchemaFieldOrderingPreference, SchemaFieldOrderValue } from '../preferences/SchemaOrderingPreferences';
16
14
 
17
15
  export interface SchemaModelDefinitionListener {
18
16
  resolved: (event: { object: SchemaModelObject }) => any;
@@ -1,6 +1,7 @@
1
1
  import { ApiObjectData, DatabaseAdapter, DatabaseObject } from '@journeyapps/db';
2
2
  import { SchemaModelDefinition } from './SchemaModelDefinition';
3
- import { action, observable } from 'mobx';
3
+ import { action, observable, runInAction } from 'mobx';
4
+ import { inject, NotificationStore, NotificationType, VisorStore } from '@journeyapps-labs/reactor-mod';
4
5
 
5
6
  export interface SchemaModelObjectOptions {
6
7
  definition: SchemaModelDefinition;
@@ -9,6 +10,12 @@ export interface SchemaModelObjectOptions {
9
10
  }
10
11
 
11
12
  export class SchemaModelObject {
13
+ @inject(VisorStore)
14
+ accessor visorStore: VisorStore;
15
+
16
+ @inject(NotificationStore)
17
+ accessor notificationStore: NotificationStore;
18
+
12
19
  @observable
13
20
  accessor data: ApiObjectData;
14
21
 
@@ -31,30 +38,42 @@ export class SchemaModelObject {
31
38
  async applyPatches() {
32
39
  if (!this.model) {
33
40
  const collection = await this.definition.getCollection();
34
- this.model = collection.create();
41
+ runInAction(() => {
42
+ this.model = collection.create();
43
+ });
35
44
  }
36
45
  for (let entry of this.patch.entries()) {
37
46
  if (this.definition.definition.belongsTo[entry[0]]) {
38
- this.model[entry[0]](entry[1].model);
47
+ this.model[entry[0]](entry[1]?.model || null);
39
48
  } else {
40
49
  this.model[entry[0]] = entry[1];
41
50
  }
42
51
  }
43
- this.patch.clear();
52
+ this.clearEdits();
44
53
  }
45
54
 
46
55
  async save() {
47
- await this.definition.connection.batchSave([this]);
56
+ await this.visorStore.wrap(`Saving ${this.definition.definition.label}`, async () => {
57
+ await this.definition.connection.batchSave([this]);
58
+ });
59
+ this.notificationStore.showNotification({
60
+ title: 'Model updated',
61
+ description: `${this.definition.definition.label} was updated`,
62
+ type: NotificationType.SUCCESS
63
+ });
48
64
  }
49
65
 
66
+ @action
50
67
  clearEdits() {
51
68
  this.patch.clear();
52
69
  }
53
70
 
71
+ @action
54
72
  revert(field: string) {
55
73
  this.patch.delete(field);
56
74
  }
57
75
 
76
+ @action
58
77
  set(field: string, value: any) {
59
78
  if (this.model?.[field] === value) {
60
79
  this.patch.delete(field);
@@ -72,7 +91,11 @@ export class SchemaModelObject {
72
91
  }
73
92
 
74
93
  async reload() {
75
- await this.definition.load(this.id);
94
+ const model = await this.definition.load(this.id);
95
+ if (model && model !== this) {
96
+ this.setData(model.data);
97
+ this.definition.cache.set(this.id, this);
98
+ }
76
99
  }
77
100
 
78
101
  get definition(): SchemaModelDefinition {
@@ -80,6 +103,6 @@ export class SchemaModelObject {
80
103
  }
81
104
 
82
105
  get id() {
83
- return this.data.id;
106
+ return this.data?.id;
84
107
  }
85
108
  }
@@ -1,10 +1,16 @@
1
1
  import { Page } from './Page';
2
2
  import { AbstractConnection } from '../AbstractConnection';
3
3
  import { v4 } from 'uuid';
4
- import { TableColumn } from '@journeyapps-labs/reactor-mod';
4
+ import { inject, NotificationStore, NotificationType, TableColumn, VisorStore } from '@journeyapps-labs/reactor-mod';
5
5
  import { SchemaModelObject } from '../SchemaModelObject';
6
6
 
7
7
  export abstract class AbstractQuery {
8
+ @inject(VisorStore)
9
+ accessor visorStore: VisorStore;
10
+
11
+ @inject(NotificationStore)
12
+ accessor notificationStore: NotificationStore;
13
+
8
14
  id: string;
9
15
 
10
16
  constructor(
@@ -15,7 +21,15 @@ export abstract class AbstractQuery {
15
21
  }
16
22
 
17
23
  async batchSave() {
18
- await this.connection.batchSave(this.getDirtyObjects());
24
+ const dirtyObjects = this.getDirtyObjects();
25
+ await this.visorStore.wrap(`Saving ${dirtyObjects.length} models`, async () => {
26
+ await this.connection.batchSave(dirtyObjects);
27
+ });
28
+ this.notificationStore.showNotification({
29
+ title: 'Models updated',
30
+ description: `${dirtyObjects.length} ${dirtyObjects.length === 1 ? 'model was' : 'models were'} updated`,
31
+ type: NotificationType.SUCCESS
32
+ });
19
33
  }
20
34
 
21
35
  abstract getDirtyObjects(): SchemaModelObject[];
@@ -73,7 +73,7 @@ export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
73
73
  active={isActive}
74
74
  {...setupTooltipProps({
75
75
  tooltip: getFilterTooltip(props.filter),
76
- tooltipPos: TooltipPosition.BOTTOM
76
+ tooltipPos: TooltipPosition.BOTTOM_RIGHT
77
77
  })}
78
78
  onClick={async (event) => {
79
79
  const filter = await props.setupFilter({
@@ -81,7 +81,7 @@ export class TypedBinding extends Binding {
81
81
  super({
82
82
  ...options,
83
83
  resolve: async () => {
84
- return options.model?.model[options.name];
84
+ return options.model?.model?.[options.name];
85
85
  }
86
86
  });
87
87
  this.handler = this.typeEngine.getHandler(options.variable.type);
@@ -110,6 +110,7 @@ export class SchemaModelForm extends FormModel {
110
110
  .map((entry) => {
111
111
  if (entry.type === OrderedSchemaFieldType.BELONGS_TO) {
112
112
  const relationship = entry.object;
113
+ const idVariable = options.definition.getBelongsToIdVariableForRelationship(relationship.name);
113
114
  const definition = options.definition.connection.getSchemaModelDefinitionByName(
114
115
  relationship.foreignType.name
115
116
  );
@@ -130,7 +131,11 @@ export class SchemaModelForm extends FormModel {
130
131
  model: this.options.object,
131
132
  input: entity,
132
133
  resolve: () => {
133
- if (!options.object.data.belongs_to[relationship.name]) {
134
+ if (idVariable && options.object?.model) {
135
+ const objectId = options.object.model[idVariable.name];
136
+ return objectId ? definition.resolve(objectId) : null;
137
+ }
138
+ if (!options.object?.data?.belongs_to?.[relationship.name]) {
134
139
  return null;
135
140
  }
136
141
  return definition.resolve(options.object.data.belongs_to[relationship.name]);
@@ -36,8 +36,10 @@ export class SharedModelPanelModel extends ReactorPanelModel {
36
36
  }
37
37
 
38
38
  async decodeEntities(data: ReturnType<this['encodeEntities']>) {
39
- this.definition = data.definition;
40
- this.model = data.model || (await data.definition.generateNewModelObject());
39
+ const definition = data.definition;
40
+ const model = definition ? data.model || (await definition.generateNewModelObject()) : null;
41
+ this.definition = definition;
42
+ this.model = model;
41
43
  }
42
44
  }
43
45
 
@@ -42,7 +42,7 @@ export const ModelPanelWidget: React.FC<QueryPanelWidgetProps> = observer((props
42
42
  const _theme = ioc.get(ThemeStore).getCurrentTheme(theme);
43
43
 
44
44
  useEffect(() => {
45
- if (!props.model.definition) {
45
+ if (!props.model.definition || !props.model.model) {
46
46
  return;
47
47
  }
48
48
  let _form = new SchemaModelForm({
@@ -95,8 +95,8 @@ export const ModelPanelWidget: React.FC<QueryPanelWidgetProps> = observer((props
95
95
  label="Save"
96
96
  icon="save"
97
97
  iconColor={_theme.status.success}
98
- action={() => {
99
- props.model.model.save();
98
+ action={async () => {
99
+ await props.model.model.save();
100
100
  }}
101
101
  />
102
102
  <PanelButtonWidget
@@ -1,5 +1,13 @@
1
1
  import * as React from 'react';
2
- import { BooleanSetting, IconWidget, ioc, PrefsStore, styled } from '@journeyapps-labs/reactor-mod';
2
+ import {
3
+ BooleanSetting,
4
+ IconWidget,
5
+ ioc,
6
+ PanelButtonMode,
7
+ PanelButtonWidget,
8
+ PrefsStore,
9
+ styled
10
+ } from '@journeyapps-labs/reactor-mod';
3
11
  import { AbstractQuery } from '../../core/query/AbstractQuery';
4
12
  import { observer } from 'mobx-react';
5
13
  import * as _ from 'lodash';
@@ -13,6 +21,7 @@ import { ChangesControlsWidget } from './table-controls/ChangesControlsWidget';
13
21
  import { FilterControlsWidget } from './table-controls/FilterControlsWidget';
14
22
  import { QueryControlPreferences } from '../../preferences/QueryControlPreferences';
15
23
  import { SelectionControlsWidget } from './table-controls/SelectionControlsWidget';
24
+ import { CreateModelAction } from '../../actions/schema-definitions/CreateModelAction';
16
25
 
17
26
  export interface TableControlsWidgetProps {
18
27
  current_page: Page;
@@ -39,9 +48,20 @@ export const TableControlsWidget: React.FC<TableControlsWidgetProps> = observer(
39
48
  const showFilterControls = prefsStore.getPreference<BooleanSetting>(
40
49
  QueryControlPreferences.SHOW_FILTER_CONTROLS
41
50
  ).checked;
51
+ const createModelButton = simpleQuery?.options.definition
52
+ ? CreateModelAction.get().renderAsButton(
53
+ (btn) => {
54
+ return <PanelButtonWidget {...btn} label="Create model" mode={PanelButtonMode.PRIMARY} />;
55
+ },
56
+ {
57
+ targetEntity: simpleQuery.options.definition
58
+ }
59
+ )
60
+ : null;
42
61
 
43
62
  return (
44
63
  <S.Container className={props.className}>
64
+ {createModelButton}
45
65
  <PageControlsWidget query={props.query} currentPage={props.current_page} goToPage={props.goToPage} />
46
66
  <QueryControlsWidget
47
67
  query={props.query}
@@ -69,7 +89,7 @@ namespace S {
69
89
  column-gap: 20px;
70
90
  row-gap: 20px;
71
91
  padding: 5px 5px;
72
- align-items: center;
92
+ align-items: flex-end;
73
93
  flex-wrap: wrap;
74
94
  `;
75
95
 
@@ -36,7 +36,7 @@ export const ChangesControlsWidget: React.FC<ChangesControlsWidgetProps> = obser
36
36
  icon="save"
37
37
  iconColor={_theme.status.success}
38
38
  action={async () => {
39
- props.query.batchSave();
39
+ await props.query.batchSave();
40
40
  }}
41
41
  />
42
42
  <PanelButtonWidget
@@ -21,6 +21,9 @@ export interface PageControlsWidgetProps {
21
21
  export const PageControlsWidget: React.FC<PageControlsWidgetProps> = observer((props) => {
22
22
  const hasCurrentPage = !!props.currentPage;
23
23
  const currentPageIndex = props.currentPage?.index ?? 0;
24
+ const totalPages = props.query.totalPages;
25
+ const totalPagesLoading = totalPages === 0 && !!props.currentPage?.loading;
26
+ const canGoNext = hasCurrentPage && totalPages > currentPageIndex + 1;
24
27
 
25
28
  return (
26
29
  <InputContainerWidget label="Page">
@@ -36,7 +39,7 @@ export const PageControlsWidget: React.FC<PageControlsWidgetProps> = observer((p
36
39
  }}
37
40
  />
38
41
  <PanelButtonWidget
39
- disabled={!hasCurrentPage || props.query.totalPages === currentPageIndex + 1}
42
+ disabled={!canGoNext}
40
43
  label="Next"
41
44
  action={() => {
42
45
  if (!hasCurrentPage) {
@@ -47,20 +50,19 @@ export const PageControlsWidget: React.FC<PageControlsWidgetProps> = observer((p
47
50
  />
48
51
  <S.PageSelector>
49
52
  <PanelDropdownWidget
53
+ disabled={totalPages === 0}
50
54
  onChange={({ key }) => {
51
55
  props.goToPage?.(parseInt(key));
52
56
  }}
53
57
  selected={`${currentPageIndex}`}
54
- items={_.range(0, props.query.totalPages).map((r) => {
58
+ items={_.range(0, totalPages).map((r) => {
55
59
  return {
56
60
  title: `${r + 1}`,
57
61
  key: `${r}`
58
62
  } as ComboBoxItem;
59
63
  })}
60
64
  />
61
- <S.TotalPages>
62
- / {props.query.totalPages === 0 ? <S.Spinner icon="spinner" spin={true} /> : props.query.totalPages}
63
- </S.TotalPages>
65
+ <S.TotalPages>/ {totalPagesLoading ? <S.Spinner icon="spinner" spin={true} /> : totalPages}</S.TotalPages>
64
66
  </S.PageSelector>
65
67
  </S.Group>
66
68
  </InputContainerWidget>