@projectcaluma/ember-form 14.7.0 → 14.8.0
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.
|
@@ -3,7 +3,7 @@ import { destroy, registerDestructor } from "@ember/destroyable";
|
|
|
3
3
|
import { inject as service } from "@ember/service";
|
|
4
4
|
import Component from "@glimmer/component";
|
|
5
5
|
import { queryManager } from "ember-apollo-client";
|
|
6
|
-
import {
|
|
6
|
+
import { task } from "ember-concurrency";
|
|
7
7
|
import { trackedTask } from "reactiveweb/ember-concurrency";
|
|
8
8
|
|
|
9
9
|
import getDocumentAnswersQuery from "@projectcaluma/ember-form/gql/queries/document-answers.graphql";
|
|
@@ -125,32 +125,33 @@ export default class CfContentComponent extends Component {
|
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@dropTask
|
|
131
|
-
*fetchData() {
|
|
128
|
+
fetchData = task({ drop: true }, async () => {
|
|
132
129
|
if (this.document) destroy(this.document);
|
|
133
130
|
if (this.navigation) destroy(this.navigation);
|
|
134
131
|
|
|
135
132
|
if (!this.args.documentId) return;
|
|
136
133
|
|
|
137
|
-
const [answerDocument] = (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
134
|
+
const [answerDocument] = (
|
|
135
|
+
await this.apollo.query(
|
|
136
|
+
{
|
|
137
|
+
query: getDocumentAnswersQuery,
|
|
138
|
+
fetchPolicy: "network-only",
|
|
139
|
+
variables: { id: this.args.documentId },
|
|
140
|
+
},
|
|
141
|
+
"allDocuments.edges",
|
|
142
|
+
)
|
|
143
|
+
).map(({ node }) => node);
|
|
144
|
+
|
|
145
|
+
const [form] = (
|
|
146
|
+
await this.apollo.query(
|
|
147
|
+
{
|
|
148
|
+
query: getDocumentFormsQuery,
|
|
149
|
+
fetchPolicy: "cache-first",
|
|
150
|
+
variables: { slug: answerDocument.form.slug },
|
|
151
|
+
},
|
|
152
|
+
"allForms.edges",
|
|
153
|
+
)
|
|
154
|
+
).map(({ node }) => node);
|
|
154
155
|
|
|
155
156
|
const owner = getOwner(this);
|
|
156
157
|
const Document = owner.factoryFor("caluma-model:document").class;
|
|
@@ -171,5 +172,7 @@ export default class CfContentComponent extends Component {
|
|
|
171
172
|
});
|
|
172
173
|
|
|
173
174
|
return { document, navigation };
|
|
174
|
-
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
data = trackedTask(this, this.fetchData, () => [this.args.documentId]);
|
|
175
178
|
}
|
|
@@ -4,7 +4,7 @@ import { inject as service } from "@ember/service";
|
|
|
4
4
|
import Component from "@glimmer/component";
|
|
5
5
|
import { tracked } from "@glimmer/tracking";
|
|
6
6
|
import { queryManager } from "ember-apollo-client";
|
|
7
|
-
import {
|
|
7
|
+
import { task } from "ember-concurrency";
|
|
8
8
|
import { confirm } from "ember-uikit";
|
|
9
9
|
|
|
10
10
|
import removeDocumentMutation from "@projectcaluma/ember-form/gql/mutations/remove-document.graphql";
|
|
@@ -44,9 +44,8 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
44
44
|
return this.questions.slice(0, 4);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const raw = yield this.apollo.mutate(
|
|
47
|
+
add = task({ drop: true }, async () => {
|
|
48
|
+
const raw = await this.apollo.mutate(
|
|
50
49
|
{
|
|
51
50
|
mutation: saveDocumentMutation,
|
|
52
51
|
variables: {
|
|
@@ -66,11 +65,10 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
66
65
|
this.documentToEditIsNew = true;
|
|
67
66
|
this.documentToEdit = newDocument;
|
|
68
67
|
this.showAddModal = true;
|
|
69
|
-
}
|
|
68
|
+
});
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!(yield confirm(this.intl.t("caluma.form.deleteRow")))) {
|
|
70
|
+
delete = task({ drop: true }, async (document) => {
|
|
71
|
+
if (!(await confirm(this.intl.t("caluma.form.deleteRow")))) {
|
|
74
72
|
return;
|
|
75
73
|
}
|
|
76
74
|
|
|
@@ -78,20 +76,19 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
78
76
|
(doc) => doc.pk !== document.pk,
|
|
79
77
|
);
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
79
|
+
await this.args.onSave(remainingDocuments);
|
|
80
|
+
await this.removeOrphan(document);
|
|
81
|
+
});
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
*save(validate) {
|
|
83
|
+
save = task({ drop: true }, async (validate) => {
|
|
87
84
|
try {
|
|
88
|
-
if (!(
|
|
85
|
+
if (!(await validate())) {
|
|
89
86
|
return;
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
const newDocument = this.documentToEdit;
|
|
93
90
|
|
|
94
|
-
|
|
91
|
+
await Promise.all(newDocument.fields.map((f) => f.validate.perform()));
|
|
95
92
|
|
|
96
93
|
if (newDocument.fields.some((field) => field.isInvalid)) {
|
|
97
94
|
return;
|
|
@@ -101,7 +98,7 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
101
98
|
|
|
102
99
|
if (!rows.find((doc) => doc.pk === newDocument.pk)) {
|
|
103
100
|
// add document to table
|
|
104
|
-
|
|
101
|
+
await this.args.onSave([...rows, newDocument]);
|
|
105
102
|
|
|
106
103
|
this.notification.success(
|
|
107
104
|
this.intl.t("caluma.form.notification.table.add.success"),
|
|
@@ -110,29 +107,28 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
110
107
|
|
|
111
108
|
this.documentToEditIsNew = false;
|
|
112
109
|
|
|
113
|
-
|
|
110
|
+
await this.close.perform();
|
|
114
111
|
} catch {
|
|
115
112
|
this.notification.danger(
|
|
116
113
|
this.intl.t("caluma.form.notification.table.add.error"),
|
|
117
114
|
);
|
|
118
115
|
}
|
|
119
|
-
}
|
|
116
|
+
});
|
|
120
117
|
|
|
121
|
-
|
|
122
|
-
*close() {
|
|
118
|
+
close = task({ drop: true }, async () => {
|
|
123
119
|
if (this.documentToEditIsNew) {
|
|
124
|
-
|
|
120
|
+
await this.removeOrphan(this.documentToEdit);
|
|
125
121
|
|
|
126
122
|
this.documentToEditIsNew = false;
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
if (!this.args.disabled) {
|
|
130
|
-
|
|
126
|
+
await this.args.field.validate.perform();
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
this.showAddModal = false;
|
|
134
130
|
this.documentToEdit = null;
|
|
135
|
-
}
|
|
131
|
+
});
|
|
136
132
|
|
|
137
133
|
async removeOrphan(calumaDocument) {
|
|
138
134
|
// Remove orphaned document from database.
|
|
@@ -2,7 +2,7 @@ import { action } from "@ember/object";
|
|
|
2
2
|
import { service } from "@ember/service";
|
|
3
3
|
import { macroCondition, isTesting } from "@embroider/macros";
|
|
4
4
|
import Component from "@glimmer/component";
|
|
5
|
-
import { timeout,
|
|
5
|
+
import { timeout, task } from "ember-concurrency";
|
|
6
6
|
|
|
7
7
|
import { hasQuestionType } from "@projectcaluma/ember-core/helpers/has-question-type";
|
|
8
8
|
|
|
@@ -88,22 +88,21 @@ export default class CfFieldComponent extends Component {
|
|
|
88
88
|
* @method save
|
|
89
89
|
* @param {String|Number|String[]} value The new value to save to the field
|
|
90
90
|
*/
|
|
91
|
-
|
|
92
|
-
*save(value) {
|
|
91
|
+
save = task({ restartable: true }, async (value) => {
|
|
93
92
|
if (typeof this.args.onSave === "function") {
|
|
94
|
-
return
|
|
93
|
+
return await this.args.onSave(this.args.field, value);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
/* istanbul ignore next */
|
|
98
97
|
if (macroCondition(!isTesting())) {
|
|
99
|
-
|
|
98
|
+
await timeout(500);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
if (this.args.field.answer) {
|
|
103
102
|
this.args.field.answer.value = value;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
await this.args.field.validate.perform();
|
|
107
106
|
|
|
108
107
|
if (this.args.field.isInvalid) {
|
|
109
108
|
// If the frontend validation fails, we don't need to try saving the value
|
|
@@ -111,8 +110,8 @@ export default class CfFieldComponent extends Component {
|
|
|
111
110
|
return;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
return
|
|
115
|
-
}
|
|
113
|
+
return await this.args.field.save.unlinked().perform();
|
|
114
|
+
});
|
|
116
115
|
|
|
117
116
|
@action
|
|
118
117
|
refreshDynamicOptions() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { action } from "@ember/object";
|
|
2
2
|
import Component from "@glimmer/component";
|
|
3
|
-
import {
|
|
3
|
+
import { task } from "ember-concurrency";
|
|
4
4
|
import { cached } from "tracked-toolbox";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -43,8 +43,15 @@ export default class DocumentValidity extends Component {
|
|
|
43
43
|
return this._validate.isRunning;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
_validateField = task(async (field) => {
|
|
47
|
+
await field.validate.linked().perform();
|
|
48
|
+
|
|
49
|
+
if (field.question.hasFormatValidators) {
|
|
50
|
+
await field.save.linked().perform();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
_validate = task({ restartable: true }, async () => {
|
|
48
55
|
const saveTasks = this.args.document.fields
|
|
49
56
|
.flatMap((field) => [
|
|
50
57
|
...[...(field._components ?? [])].map((c) => c.save.last),
|
|
@@ -54,16 +61,12 @@ export default class DocumentValidity extends Component {
|
|
|
54
61
|
|
|
55
62
|
// Wait until all currently running save tasks in the UI and in the field
|
|
56
63
|
// itself are finished
|
|
57
|
-
|
|
64
|
+
await Promise.all(saveTasks);
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
this.args.document.fields.map(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (field.question.hasFormatValidators) {
|
|
64
|
-
await field.save.linked().perform();
|
|
65
|
-
}
|
|
66
|
-
}),
|
|
66
|
+
await Promise.all(
|
|
67
|
+
this.args.document.fields.map((field) =>
|
|
68
|
+
this._validateField.perform(field),
|
|
69
|
+
),
|
|
67
70
|
);
|
|
68
71
|
|
|
69
72
|
if (this.isValid) {
|
|
@@ -73,7 +76,7 @@ export default class DocumentValidity extends Component {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
return this.isValid;
|
|
76
|
-
}
|
|
79
|
+
});
|
|
77
80
|
|
|
78
81
|
@action
|
|
79
82
|
validate() {
|
package/addon/lib/field.js
CHANGED
|
@@ -5,7 +5,7 @@ import { inject as service } from "@ember/service";
|
|
|
5
5
|
import { camelize } from "@ember/string";
|
|
6
6
|
import { tracked } from "@glimmer/tracking";
|
|
7
7
|
import { queryManager } from "ember-apollo-client";
|
|
8
|
-
import {
|
|
8
|
+
import { task } from "ember-concurrency";
|
|
9
9
|
import { validate } from "ember-validators";
|
|
10
10
|
import isEqual from "lodash.isequal";
|
|
11
11
|
import { cached } from "tracked-toolbox";
|
|
@@ -325,11 +325,10 @@ export default class Field extends Base {
|
|
|
325
325
|
* @return {Object[]} Formerly used dynamic options
|
|
326
326
|
* @private
|
|
327
327
|
*/
|
|
328
|
-
|
|
329
|
-
*_fetchUsedDynamicOptions() {
|
|
328
|
+
_fetchUsedDynamicOptions = task({ drop: true }, async () => {
|
|
330
329
|
if (!this.question.isDynamic) return null;
|
|
331
330
|
|
|
332
|
-
const edges =
|
|
331
|
+
const edges = await this.apollo.query(
|
|
333
332
|
{
|
|
334
333
|
query: getDocumentUsedDynamicOptionsQuery,
|
|
335
334
|
fetchPolicy: "cache-first",
|
|
@@ -345,15 +344,16 @@ export default class Field extends Base {
|
|
|
345
344
|
slug,
|
|
346
345
|
label,
|
|
347
346
|
}));
|
|
348
|
-
}
|
|
347
|
+
});
|
|
349
348
|
|
|
350
349
|
/**
|
|
351
350
|
* The formerly used dynamic options for this question.
|
|
352
351
|
*
|
|
353
352
|
* @property {Object[]} usedDynamicOptions
|
|
354
353
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
get usedDynamicOptions() {
|
|
355
|
+
return this._fetchUsedDynamicOptions.lastSuccessful?.value;
|
|
356
|
+
}
|
|
357
357
|
|
|
358
358
|
/**
|
|
359
359
|
* The available options for choice questions. This only works for the
|
|
@@ -606,8 +606,7 @@ export default class Field extends Base {
|
|
|
606
606
|
* @method save
|
|
607
607
|
* @return {Object} The response from the server
|
|
608
608
|
*/
|
|
609
|
-
|
|
610
|
-
*save() {
|
|
609
|
+
save = task({ restartable: true }, async () => {
|
|
611
610
|
if (this.question.isCalculated) {
|
|
612
611
|
return;
|
|
613
612
|
}
|
|
@@ -628,8 +627,10 @@ export default class Field extends Base {
|
|
|
628
627
|
input.dataSourceContext = JSON.stringify(this.document.dataSourceContext);
|
|
629
628
|
}
|
|
630
629
|
|
|
630
|
+
await this.beforeSave();
|
|
631
|
+
|
|
631
632
|
try {
|
|
632
|
-
const response =
|
|
633
|
+
const response = await this.apollo.mutate(
|
|
633
634
|
{
|
|
634
635
|
mutation: MUTATION_MAP[type],
|
|
635
636
|
variables: { input },
|
|
@@ -653,12 +654,16 @@ export default class Field extends Base {
|
|
|
653
654
|
this._errors = this._errors.filter(({ type }) => type !== "format");
|
|
654
655
|
}
|
|
655
656
|
|
|
657
|
+
await this.afterSave(response);
|
|
658
|
+
|
|
656
659
|
return response;
|
|
657
660
|
} catch (e) {
|
|
658
661
|
const validationError = e.errors.find(
|
|
659
662
|
(err) => err.extensions?.code === "format_validation_failed",
|
|
660
663
|
);
|
|
661
664
|
|
|
665
|
+
await this.onSaveError(e);
|
|
666
|
+
|
|
662
667
|
if (validationError) {
|
|
663
668
|
this._errors = [
|
|
664
669
|
...this._errors,
|
|
@@ -672,7 +677,32 @@ export default class Field extends Base {
|
|
|
672
677
|
throw e;
|
|
673
678
|
}
|
|
674
679
|
}
|
|
675
|
-
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Hook method that can be overridden to add functionality before saving the field.
|
|
684
|
+
*
|
|
685
|
+
* @method beforeSave
|
|
686
|
+
*/
|
|
687
|
+
async beforeSave() {}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Hook method that can be overridden to add functionality after saving the field.
|
|
691
|
+
*
|
|
692
|
+
* @method afterSave
|
|
693
|
+
* @param {Object} response The response from the server
|
|
694
|
+
*/
|
|
695
|
+
// eslint-disable-next-line no-unused-vars
|
|
696
|
+
async afterSave(response) {}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Hook method that can be overridden to add functionality when saving the field fails.
|
|
700
|
+
*
|
|
701
|
+
* @method onSaveError
|
|
702
|
+
* @param {Object} error The error thrown during saving
|
|
703
|
+
*/
|
|
704
|
+
// eslint-disable-next-line no-unused-vars
|
|
705
|
+
async onSaveError(error) {}
|
|
676
706
|
|
|
677
707
|
/**
|
|
678
708
|
* The translated error messages
|
|
@@ -696,8 +726,7 @@ export default class Field extends Base {
|
|
|
696
726
|
*
|
|
697
727
|
* @method validate
|
|
698
728
|
*/
|
|
699
|
-
|
|
700
|
-
*validate() {
|
|
729
|
+
validate = task({ restartable: true }, async () => {
|
|
701
730
|
const specificValidation = this[`_validate${this.questionType}`];
|
|
702
731
|
|
|
703
732
|
assert(
|
|
@@ -710,22 +739,23 @@ export default class Field extends Base {
|
|
|
710
739
|
specificValidation,
|
|
711
740
|
];
|
|
712
741
|
|
|
713
|
-
const errors = (
|
|
714
|
-
|
|
715
|
-
|
|
742
|
+
const errors = (
|
|
743
|
+
await Promise.all(
|
|
744
|
+
validationFns.map(async (fn) => {
|
|
745
|
+
const res = await fn.call(this);
|
|
716
746
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
747
|
+
return Array.isArray(res) ? res : [res];
|
|
748
|
+
}),
|
|
749
|
+
)
|
|
750
|
+
)
|
|
720
751
|
.reduce((arr, e) => [...arr, ...e], []) // flatten the array
|
|
721
752
|
.filter((e) => typeof e === "object");
|
|
722
753
|
|
|
723
754
|
this._errors = errors;
|
|
724
|
-
}
|
|
755
|
+
});
|
|
725
756
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const response = yield this.apollo.query(
|
|
757
|
+
refreshAnswer = task({ drop: true }, async () => {
|
|
758
|
+
const response = await this.apollo.query(
|
|
729
759
|
{
|
|
730
760
|
query: refreshAnswerQuery,
|
|
731
761
|
fetchPolicy: "network-only",
|
|
@@ -744,9 +774,9 @@ export default class Field extends Base {
|
|
|
744
774
|
this.answer.raw[key] = value;
|
|
745
775
|
});
|
|
746
776
|
|
|
747
|
-
|
|
777
|
+
await this.validate.linked().perform();
|
|
748
778
|
}
|
|
749
|
-
}
|
|
779
|
+
});
|
|
750
780
|
|
|
751
781
|
/**
|
|
752
782
|
* Method to validate if a question is required or not.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projectcaluma/ember-form",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.8.0",
|
|
4
4
|
"description": "Ember addon for rendering Caluma forms.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ember-cli-htmlbars": "^6.3.0",
|
|
23
23
|
"ember-cli-showdown": "^9.0.1",
|
|
24
24
|
"ember-composable-helpers": "^5.0.0",
|
|
25
|
-
"ember-concurrency": "^4.0.2",
|
|
25
|
+
"ember-concurrency": "^4.0.2 || ^5.1.0",
|
|
26
26
|
"ember-flatpickr": "^8.0.1",
|
|
27
27
|
"ember-in-viewport": "^4.1.0",
|
|
28
28
|
"ember-intl": "^7.1.1",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"luxon": "^3.5.0",
|
|
40
40
|
"reactiveweb": "^1.3.0",
|
|
41
41
|
"tracked-toolbox": "^2.0.0",
|
|
42
|
-
"@projectcaluma/ember-core": "^14.
|
|
42
|
+
"@projectcaluma/ember-core": "^14.8.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@ember/optional-features": "2.3.0",
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"uikit": "3.25.6",
|
|
75
75
|
"uuid": "13.0.0",
|
|
76
76
|
"webpack": "5.104.1",
|
|
77
|
-
"@projectcaluma/ember-
|
|
78
|
-
"@projectcaluma/ember-
|
|
77
|
+
"@projectcaluma/ember-workflow": "14.8.0",
|
|
78
|
+
"@projectcaluma/ember-testing": "14.8.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"ember-source": ">= 4.0.0",
|
|
82
|
-
"@projectcaluma/ember-workflow": "^14.
|
|
82
|
+
"@projectcaluma/ember-workflow": "^14.8.0"
|
|
83
83
|
},
|
|
84
84
|
"dependenciesMeta": {
|
|
85
85
|
"@projectcaluma/ember-core": {
|