@projectcaluma/ember-form 14.12.0 → 14.14.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.
- package/addon/components/cf-content.js +12 -12
- package/addon/components/cf-field/input/powerselect.hbs +1 -0
- package/addon/components/cf-field/input/powerselect.js +1 -5
- package/addon/components/cf-field/input/table.hbs +12 -8
- package/addon/components/cf-field/input/table.js +66 -27
- package/addon/components/cf-field/input-compare.hbs +1 -1
- package/addon/components/cf-field.hbs +1 -1
- package/addon/gql/queries/document-answers-compare.graphql +2 -0
- package/addon/gql/queries/document-answers.graphql +24 -16
- package/addon/lib/document.js +12 -20
- package/package.json +5 -5
- package/addon/gql/fragments/case-form-and-workflow.graphql +0 -30
|
@@ -160,18 +160,17 @@ export default class CfContentComponent extends Component {
|
|
|
160
160
|
|
|
161
161
|
let answerDocument = null;
|
|
162
162
|
let historicalDocument = null;
|
|
163
|
+
let jexlContext = null;
|
|
163
164
|
|
|
164
165
|
if (!this.args.compare) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
)
|
|
174
|
-
).map(({ node }) => node);
|
|
166
|
+
const data = await this.apollo.query({
|
|
167
|
+
query: getDocumentAnswersQuery,
|
|
168
|
+
fetchPolicy: "network-only",
|
|
169
|
+
variables: { id: this.args.documentId },
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
answerDocument = data.allDocuments.edges[0].node;
|
|
173
|
+
jexlContext = data.jexlContext;
|
|
175
174
|
} else {
|
|
176
175
|
const { from, to } = this.args.compare;
|
|
177
176
|
|
|
@@ -186,6 +185,7 @@ export default class CfContentComponent extends Component {
|
|
|
186
185
|
});
|
|
187
186
|
historicalDocument = data.fromRevision;
|
|
188
187
|
answerDocument = data.toRevision;
|
|
188
|
+
jexlContext = data.jexlContext;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
const [form] = (
|
|
@@ -199,14 +199,14 @@ export default class CfContentComponent extends Component {
|
|
|
199
199
|
)
|
|
200
200
|
).map(({ node }) => node);
|
|
201
201
|
|
|
202
|
-
const raw = parseDocument({ ...answerDocument, form });
|
|
202
|
+
const raw = parseDocument({ ...answerDocument, form, jexlContext });
|
|
203
203
|
|
|
204
204
|
const document = new Document({
|
|
205
205
|
raw,
|
|
206
206
|
owner,
|
|
207
207
|
dataSourceContext: this.args.context,
|
|
208
208
|
historicalDocument: historicalDocument
|
|
209
|
-
? parseDocument({ ...historicalDocument, form })
|
|
209
|
+
? parseDocument({ ...historicalDocument, form, jexlContext })
|
|
210
210
|
: null,
|
|
211
211
|
compare: this.args.compare,
|
|
212
212
|
});
|
|
@@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
|
|
|
3
3
|
import { ensureSafeComponent } from "@embroider/util";
|
|
4
4
|
import Component from "@glimmer/component";
|
|
5
5
|
import PowerSelectComponent from "ember-power-select/components/power-select";
|
|
6
|
-
import PowerSelectMultipleComponent from "ember-power-select/components/power-select-multiple";
|
|
7
6
|
|
|
8
7
|
import getConfig from "@projectcaluma/ember-core/utils/get-config";
|
|
9
8
|
|
|
@@ -33,10 +32,7 @@ export default class CfFieldInputPowerselectComponent extends Component {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
get selectComponent() {
|
|
36
|
-
return ensureSafeComponent(
|
|
37
|
-
this.multiple ? PowerSelectMultipleComponent : PowerSelectComponent,
|
|
38
|
-
this,
|
|
39
|
-
);
|
|
35
|
+
return ensureSafeComponent(PowerSelectComponent, this);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
get searchEnabled() {
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
<UkButton
|
|
71
71
|
@color="link"
|
|
72
72
|
@onClick={{fn this.edit document}}
|
|
73
|
+
@disabled={{or this.add.isRunning this.delete.isRunning}}
|
|
73
74
|
title={{t "caluma.form.edit"}}
|
|
74
75
|
class="uk-flex-inline uk-margin-small-left table-controls"
|
|
75
76
|
data-test-edit-row
|
|
@@ -79,7 +80,8 @@
|
|
|
79
80
|
</UkButton>
|
|
80
81
|
<UkButton
|
|
81
82
|
@color="link"
|
|
82
|
-
@onClick={{fn
|
|
83
|
+
@onClick={{fn this.delete.perform document}}
|
|
84
|
+
@disabled={{or this.add.isRunning this.delete.isRunning}}
|
|
83
85
|
title={{t "caluma.form.delete"}}
|
|
84
86
|
class="uk-flex-inline uk-margin-small-left table-controls"
|
|
85
87
|
data-test-delete-row
|
|
@@ -99,7 +101,9 @@
|
|
|
99
101
|
<td colspan={{add this.columns.length 1}} class="uk-text-center">
|
|
100
102
|
<UkButton
|
|
101
103
|
@color="link"
|
|
102
|
-
@onClick={{
|
|
104
|
+
@onClick={{this.add.perform}}
|
|
105
|
+
@disabled={{this.add.isRunning}}
|
|
106
|
+
@loading={{this.add.isRunning}}
|
|
103
107
|
title={{t "caluma.form.addRow"}}
|
|
104
108
|
data-test-add-row
|
|
105
109
|
>
|
|
@@ -115,7 +119,7 @@
|
|
|
115
119
|
{{#if this.documentToEdit}}
|
|
116
120
|
<UkModal
|
|
117
121
|
@visible={{this.showAddModal}}
|
|
118
|
-
@onHide={{
|
|
122
|
+
@onHide={{this.close.perform}}
|
|
119
123
|
@bgClose={{false}}
|
|
120
124
|
as |modal|
|
|
121
125
|
>
|
|
@@ -123,7 +127,7 @@
|
|
|
123
127
|
<CfFormWrapper
|
|
124
128
|
@document={{this.documentToEdit}}
|
|
125
129
|
@fieldset={{object-at 0 this.documentToEdit.fieldsets}}
|
|
126
|
-
@disabled={{@disabled}}
|
|
130
|
+
@disabled={{or @disabled this.add.isRunning}}
|
|
127
131
|
@context={{@context}}
|
|
128
132
|
@compare={{@compare}}
|
|
129
133
|
/>
|
|
@@ -134,7 +138,7 @@
|
|
|
134
138
|
<UkButton
|
|
135
139
|
@label={{t "caluma.form.close"}}
|
|
136
140
|
@color="primary"
|
|
137
|
-
@onClick={{
|
|
141
|
+
@onClick={{this.close.perform}}
|
|
138
142
|
@disabled={{this.close.isRunning}}
|
|
139
143
|
@loading={{this.close.isRunning}}
|
|
140
144
|
data-test-close
|
|
@@ -142,8 +146,8 @@
|
|
|
142
146
|
{{else}}
|
|
143
147
|
<UkButton
|
|
144
148
|
@label={{t "caluma.form.cancel"}}
|
|
145
|
-
@onClick={{
|
|
146
|
-
@disabled={{this.close.isRunning}}
|
|
149
|
+
@onClick={{this.close.perform}}
|
|
150
|
+
@disabled={{or this.close.isRunning this.add.isRunning}}
|
|
147
151
|
@loading={{this.close.isRunning}}
|
|
148
152
|
data-test-cancel
|
|
149
153
|
/>
|
|
@@ -158,7 +162,7 @@
|
|
|
158
162
|
@type="submit"
|
|
159
163
|
@disabled={{or this.save.isRunning (not isValid)}}
|
|
160
164
|
@loading={{this.save.isRunning}}
|
|
161
|
-
@onClick={{fn
|
|
165
|
+
@onClick={{fn this.save.perform validate}}
|
|
162
166
|
data-test-save
|
|
163
167
|
/>
|
|
164
168
|
</DocumentValidity>
|
|
@@ -74,22 +74,68 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
74
74
|
owner,
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
// Open the modal immediately - attaching the row to the table (below) is
|
|
78
|
+
// debounced and would otherwise noticeably delay opening the modal. While
|
|
79
|
+
// this task runs, the row's fields and the cancel button are disabled in
|
|
80
|
+
// the modal, so no answer can be saved to (and validated by) the backend
|
|
81
|
+
// and the row cannot be removed again before it is attached to the table.
|
|
77
82
|
this.documentToEditIsNew = true;
|
|
78
83
|
this.documentToEdit = newDocument;
|
|
79
84
|
this.showAddModal = true;
|
|
80
|
-
});
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
// Attach the row document to the table answer, so the backend has the
|
|
87
|
+
// full context to validate and evaluate the row's fields while editing.
|
|
88
|
+
// `documentToEditIsNew` marks the row as preliminary, so it will be
|
|
89
|
+
// removed again if the edit dialog is cancelled.
|
|
90
|
+
try {
|
|
91
|
+
const rows = this.args.field.answer.value ?? [];
|
|
92
|
+
await this.args.onSave([...rows, newDocument]);
|
|
93
|
+
} catch {
|
|
94
|
+
this.notification.danger(
|
|
95
|
+
this.intl.t("caluma.form.notification.table.add.error"),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Attaching failed - remove the preliminary row and close the modal.
|
|
99
|
+
// `close` cannot be performed here, as it waits for this task to finish.
|
|
100
|
+
await this.deleteRow(this.documentToEdit);
|
|
101
|
+
this.documentToEditIsNew = false;
|
|
102
|
+
this.showAddModal = false;
|
|
103
|
+
this.documentToEdit = null;
|
|
85
104
|
}
|
|
105
|
+
});
|
|
86
106
|
|
|
87
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Delete row without asking. Remove from the table, then remove row document
|
|
109
|
+
*
|
|
110
|
+
* @async
|
|
111
|
+
* @method deleteRow
|
|
112
|
+
* @param {Document} document The row document to delete
|
|
113
|
+
* @return {Promise<Void>}
|
|
114
|
+
*/
|
|
115
|
+
async deleteRow(document) {
|
|
116
|
+
const remainingDocuments = (this.args.field.answer.value ?? []).filter(
|
|
88
117
|
(doc) => doc.pk !== document.pk,
|
|
89
118
|
);
|
|
90
119
|
|
|
120
|
+
// remove row from table
|
|
91
121
|
await this.args.onSave(remainingDocuments);
|
|
92
|
-
|
|
122
|
+
|
|
123
|
+
// delete row document
|
|
124
|
+
await this.apollo.mutate({
|
|
125
|
+
mutation: removeDocumentMutation,
|
|
126
|
+
variables: { input: { document: document.uuid } },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Remove orphaned document from Caluma store.
|
|
130
|
+
this.calumaStore.delete(document.pk);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
delete = task({ drop: true }, async (document) => {
|
|
134
|
+
if (!(await confirm(this.intl.t("caluma.form.deleteRow")))) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await this.deleteRow(document);
|
|
93
139
|
});
|
|
94
140
|
|
|
95
141
|
save = task({ drop: true }, async (validate) => {
|
|
@@ -106,19 +152,13 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
106
152
|
return;
|
|
107
153
|
}
|
|
108
154
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (!rows.find((doc) => doc.pk === newDocument.pk)) {
|
|
112
|
-
// add document to table
|
|
113
|
-
await this.args.onSave([...rows, newDocument]);
|
|
114
|
-
|
|
155
|
+
if (this.documentToEditIsNew) {
|
|
115
156
|
this.notification.success(
|
|
116
157
|
this.intl.t("caluma.form.notification.table.add.success"),
|
|
117
158
|
);
|
|
159
|
+
this.documentToEditIsNew = false;
|
|
118
160
|
}
|
|
119
161
|
|
|
120
|
-
this.documentToEditIsNew = false;
|
|
121
|
-
|
|
122
162
|
await this.close.perform();
|
|
123
163
|
} catch {
|
|
124
164
|
this.notification.danger(
|
|
@@ -128,10 +168,20 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
128
168
|
});
|
|
129
169
|
|
|
130
170
|
close = task({ drop: true }, async () => {
|
|
131
|
-
|
|
132
|
-
|
|
171
|
+
// Wait for a pending "add" to finish attaching the new row to the table
|
|
172
|
+
// before closing - otherwise removing or validating the row would race
|
|
173
|
+
// with the debounced save of the table answer.
|
|
174
|
+
if (this.add.isRunning) {
|
|
175
|
+
await this.add.last;
|
|
176
|
+
}
|
|
133
177
|
|
|
178
|
+
if (this.documentToEditIsNew) {
|
|
179
|
+
await this.deleteRow(this.documentToEdit);
|
|
134
180
|
this.documentToEditIsNew = false;
|
|
181
|
+
this.showAddModal = false;
|
|
182
|
+
this.documentToEdit = null;
|
|
183
|
+
// No validation for new documents that are deleted
|
|
184
|
+
return;
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
if (!this.args.disabled) {
|
|
@@ -142,17 +192,6 @@ export default class CfFieldInputTableComponent extends Component {
|
|
|
142
192
|
this.documentToEdit = null;
|
|
143
193
|
});
|
|
144
194
|
|
|
145
|
-
async removeOrphan(calumaDocument) {
|
|
146
|
-
// Remove orphaned document from database.
|
|
147
|
-
await this.apollo.mutate({
|
|
148
|
-
mutation: removeDocumentMutation,
|
|
149
|
-
variables: { input: { document: calumaDocument.uuid } },
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Remove orphaned document from Caluma store.
|
|
153
|
-
this.calumaStore.delete(calumaDocument.pk);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
195
|
@action
|
|
157
196
|
edit(document) {
|
|
158
197
|
this.documentToEdit = document;
|
|
@@ -108,6 +108,8 @@ query (
|
|
|
108
108
|
$to: DateTime!
|
|
109
109
|
$context: JSONString
|
|
110
110
|
) {
|
|
111
|
+
jexlContext: documentGlobalJexlContext(id: $documentId)
|
|
112
|
+
|
|
111
113
|
fromRevision: documentAsOf(id: $documentId, asOf: $from) {
|
|
112
114
|
...RevisionDocument
|
|
113
115
|
answers: historicalAnswers(asOf: $from, excludeDeleted: true) {
|
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
#import * from '../fragments/field.graphql'
|
|
2
|
-
#import * from '../fragments/case-form-and-workflow.graphql'
|
|
3
2
|
|
|
4
3
|
query DocumentAnswers($id: ID!) {
|
|
4
|
+
# Global JEXL context (case & work item) for JEXL evaluations. This will be
|
|
5
|
+
# enriched with the document depending data in `lib/document.js` and
|
|
6
|
+
# `lib/field.js` before being passed into the JEXL evaluation
|
|
7
|
+
jexlContext: documentGlobalJexlContext(id: $id)
|
|
8
|
+
|
|
5
9
|
allDocuments(filter: [{ id: $id }]) {
|
|
6
10
|
edges {
|
|
7
11
|
node {
|
|
8
12
|
id
|
|
13
|
+
|
|
14
|
+
# Basic form information
|
|
9
15
|
form {
|
|
10
16
|
id
|
|
11
17
|
slug
|
|
12
18
|
}
|
|
19
|
+
|
|
20
|
+
# All answers of the document, up to three levels deep (top form -> sub
|
|
21
|
+
# form -> table form)
|
|
22
|
+
answers {
|
|
23
|
+
edges {
|
|
24
|
+
node {
|
|
25
|
+
...FieldAnswer
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Work item and case work items to determine the actionable work item
|
|
31
|
+
# for action buttons. This is either the direct work item below or a
|
|
32
|
+
# case work item of the type complete workflow form.
|
|
33
|
+
# See `workItemUuid` in `lib/document.js`
|
|
13
34
|
workItem {
|
|
14
35
|
id
|
|
15
|
-
meta
|
|
16
|
-
task {
|
|
17
|
-
slug
|
|
18
|
-
}
|
|
19
|
-
case {
|
|
20
|
-
...CaseFormAndWorkflow
|
|
21
|
-
}
|
|
22
36
|
}
|
|
23
37
|
case {
|
|
24
|
-
...CaseFormAndWorkflow
|
|
25
38
|
workItems {
|
|
26
39
|
edges {
|
|
27
40
|
node {
|
|
28
41
|
id
|
|
42
|
+
# `__typename` is what identifies the complete workflow form
|
|
43
|
+
# work item, the `task` selection is only there to request it
|
|
29
44
|
task {
|
|
30
45
|
id
|
|
31
46
|
}
|
|
@@ -33,13 +48,6 @@ query DocumentAnswers($id: ID!) {
|
|
|
33
48
|
}
|
|
34
49
|
}
|
|
35
50
|
}
|
|
36
|
-
answers {
|
|
37
|
-
edges {
|
|
38
|
-
node {
|
|
39
|
-
...FieldAnswer
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
51
|
}
|
|
44
52
|
}
|
|
45
53
|
}
|
package/addon/lib/document.js
CHANGED
|
@@ -232,38 +232,30 @@ export default class Document extends Base {
|
|
|
232
232
|
* @property {Object} jexlContext
|
|
233
233
|
*/
|
|
234
234
|
get jexlContext() {
|
|
235
|
-
const
|
|
236
|
-
|
|
235
|
+
const globalContext = this.raw.jexlContext ?? {
|
|
236
|
+
info: {
|
|
237
|
+
// Expressions rely on these being `null` rather than absent when the
|
|
238
|
+
// document is not attached to a case or work item.
|
|
239
|
+
case: null,
|
|
240
|
+
workItem: null,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
237
243
|
|
|
238
244
|
return (
|
|
239
245
|
this.parentDocument?.jexlContext ?? {
|
|
246
|
+
...globalContext,
|
|
240
247
|
// JEXL interprets null in an expression as variable instead of a
|
|
241
248
|
// primitive. This resolves that issue.
|
|
242
249
|
null: null,
|
|
250
|
+
// The information about the root form needs to be constructed on the
|
|
251
|
+
// first document as it doesn't depend on the field.
|
|
243
252
|
form: this.rootForm.slug,
|
|
244
253
|
info: {
|
|
254
|
+
...globalContext.info,
|
|
245
255
|
root: {
|
|
246
256
|
form: this.rootForm.slug,
|
|
247
257
|
formMeta: this.rootForm.raw.meta,
|
|
248
258
|
},
|
|
249
|
-
case: _case
|
|
250
|
-
? {
|
|
251
|
-
form: _case.document?.form.slug,
|
|
252
|
-
workflow: _case.workflow.slug,
|
|
253
|
-
meta: _case.meta,
|
|
254
|
-
root: {
|
|
255
|
-
form: _case.family.document?.form.slug,
|
|
256
|
-
workflow: _case.family.workflow.slug,
|
|
257
|
-
meta: _case.family.meta,
|
|
258
|
-
},
|
|
259
|
-
}
|
|
260
|
-
: null,
|
|
261
|
-
workItem: workItem
|
|
262
|
-
? {
|
|
263
|
-
task: workItem.task.slug,
|
|
264
|
-
meta: workItem.meta,
|
|
265
|
-
}
|
|
266
|
-
: null,
|
|
267
259
|
},
|
|
268
260
|
}
|
|
269
261
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projectcaluma/ember-form",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.14.0",
|
|
4
4
|
"description": "Ember addon for rendering Caluma forms.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -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.14.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-testing": "14.
|
|
78
|
-
"@projectcaluma/ember-workflow": "14.
|
|
77
|
+
"@projectcaluma/ember-testing": "14.14.0",
|
|
78
|
+
"@projectcaluma/ember-workflow": "14.14.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"ember-source": ">= 4.0.0",
|
|
82
|
-
"@projectcaluma/ember-workflow": "^14.
|
|
82
|
+
"@projectcaluma/ember-workflow": "^14.14.0"
|
|
83
83
|
},
|
|
84
84
|
"dependenciesMeta": {
|
|
85
85
|
"@projectcaluma/ember-core": {
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
fragment CaseFormAndWorkflow on Case {
|
|
2
|
-
id
|
|
3
|
-
meta
|
|
4
|
-
workflow {
|
|
5
|
-
id
|
|
6
|
-
slug
|
|
7
|
-
}
|
|
8
|
-
document {
|
|
9
|
-
id
|
|
10
|
-
form {
|
|
11
|
-
id
|
|
12
|
-
slug
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
family {
|
|
16
|
-
id
|
|
17
|
-
meta
|
|
18
|
-
workflow {
|
|
19
|
-
id
|
|
20
|
-
slug
|
|
21
|
-
}
|
|
22
|
-
document {
|
|
23
|
-
id
|
|
24
|
-
form {
|
|
25
|
-
id
|
|
26
|
-
slug
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|