@projectcaluma/ember-form 9.1.2 → 10.0.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 (33) hide show
  1. package/addon/components/cf-field/input/action-button.hbs +19 -0
  2. package/addon/components/cf-field/input/action-button.js +75 -0
  3. package/addon/components/cf-field/input/file.hbs +24 -13
  4. package/addon/components/cf-field/input/file.js +19 -9
  5. package/addon/components/cf-field/input/table.hbs +94 -95
  6. package/addon/components/cf-field/input/table.js +97 -80
  7. package/addon/components/cf-field/input.hbs +1 -0
  8. package/addon/components/cf-field/input.js +2 -1
  9. package/addon/components/cf-field.hbs +14 -12
  10. package/addon/components/cf-field.js +9 -1
  11. package/addon/components/document-validity.js +3 -5
  12. package/addon/gql/fragments/{field-question.graphql → field.graphql} +61 -0
  13. package/addon/gql/mutations/remove-answer.graphql +7 -0
  14. package/addon/gql/mutations/save-document-date-answer.graphql +2 -2
  15. package/addon/gql/mutations/save-document-file-answer.graphql +2 -2
  16. package/addon/gql/mutations/save-document-float-answer.graphql +2 -2
  17. package/addon/gql/mutations/save-document-integer-answer.graphql +2 -2
  18. package/addon/gql/mutations/save-document-list-answer.graphql +2 -2
  19. package/addon/gql/mutations/save-document-string-answer.graphql +2 -2
  20. package/addon/gql/mutations/save-document-table-answer.graphql +2 -2
  21. package/addon/gql/mutations/save-document.graphql +2 -2
  22. package/addon/gql/queries/get-document-answers.graphql +18 -2
  23. package/addon/gql/queries/get-document-forms.graphql +2 -2
  24. package/addon/gql/queries/get-dynamic-options.graphql +1 -1
  25. package/addon/gql/queries/get-fileanswer-info.graphql +1 -1
  26. package/addon/lib/document.js +17 -1
  27. package/addon/lib/field.js +12 -1
  28. package/app/components/cf-field/input/action-button.js +1 -0
  29. package/package.json +11 -10
  30. package/translations/de.yaml +3 -15
  31. package/translations/en.yaml +3 -15
  32. package/translations/fr.yaml +3 -16
  33. package/addon/gql/fragments/field-answer.graphql +0 -57
@@ -0,0 +1,19 @@
1
+ {{#unless @disabled}}
2
+ <DocumentValidity
3
+ @document={{@field.document}}
4
+ @validateOnEnter={{this.validateOnEnter}}
5
+ as |isValid validate|
6
+ >
7
+ <WorkItemButton
8
+ @workItemId={{this.workItem}}
9
+ @mutation={{this.action}}
10
+ @label={{@field.question.label}}
11
+ @disabled={{and (not-eq isValid null) (not isValid)}}
12
+ @color={{this.color}}
13
+ @beforeMutate={{fn this.beforeMutate validate}}
14
+ @onSuccess={{this.onSuccess}}
15
+ @onError={{this.onError}}
16
+ @type={{this.type}}
17
+ />
18
+ </DocumentValidity>
19
+ {{/unless}}
@@ -0,0 +1,75 @@
1
+ import { assert } from "@ember/debug";
2
+ import { action } from "@ember/object";
3
+ import Component from "@glimmer/component";
4
+ import UIkit from "uikit";
5
+
6
+ async function confirm(text) {
7
+ try {
8
+ await UIkit.modal.confirm(text);
9
+
10
+ return true;
11
+ } catch (error) {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ export default class CfFieldInputActionButtonComponent extends Component {
17
+ constructor(...args) {
18
+ super(...args);
19
+
20
+ assert(
21
+ "The document must have a `workItemUuid` for `<CfField::Input::ActionButton />` to work.",
22
+ this.args.field.document.workItemUuid
23
+ );
24
+ }
25
+
26
+ get workItem() {
27
+ return (
28
+ this.args.context?.actionButtonWorkItemId ||
29
+ this.args.field.document.workItemUuid
30
+ );
31
+ }
32
+
33
+ get action() {
34
+ return this.args.field.question.action.toLowerCase();
35
+ }
36
+
37
+ get color() {
38
+ return this.args.field.question.color.toLowerCase();
39
+ }
40
+
41
+ get type() {
42
+ return this.args.field.question.action === "COMPLETE" ? "submit" : "button";
43
+ }
44
+
45
+ get validateOnEnter() {
46
+ return (
47
+ this.args.field.question.action === "COMPLETE" &&
48
+ this.args.field.question.validateOnEnter
49
+ );
50
+ }
51
+
52
+ @action
53
+ async beforeMutate(validateFn) {
54
+ if (
55
+ this.args.field.question.action === "COMPLETE" &&
56
+ !(await validateFn())
57
+ ) {
58
+ return false;
59
+ }
60
+
61
+ const confirmText = this.args.field.question.infoText;
62
+
63
+ return !confirmText || confirm(confirmText);
64
+ }
65
+
66
+ @action
67
+ onSuccess() {
68
+ return this.args.context?.actionButtonOnSuccess?.();
69
+ }
70
+
71
+ @action
72
+ onError(error) {
73
+ return this.args.context?.actionButtonOnError?.(error);
74
+ }
75
+ }
@@ -1,21 +1,32 @@
1
- {{#unless @disabled}}
2
- <div class="uk-width-1-1" uk-form-custom="target: true">
1
+ <div class="uk-flex-middle uk-grid-divider uk-grid-column-small" uk-grid>
2
+ <div uk-form-custom="target: true">
3
3
 
4
4
  <input
5
5
  type="file"
6
6
  name={{@field.pk}}
7
7
  id={{@field.pk}}
8
+ disabled={{@disabled}}
8
9
  {{on "change" this.save}}
9
10
  />
10
-
11
- {{! label is in another component }}
12
- {{! template-lint-disable require-input-label }}
13
- <input class="uk-input" type="text" placeholder={{@placeholder}} readonly />
11
+ <UkButton @color="primary" @disabled={{@disabled}}>
12
+ {{t "caluma.form.selectFile"}}
13
+ </UkButton>
14
14
  </div>
15
- {{/unless}}
16
-
17
- {{#if (and this.downloadUrl this.downloadName)}}
18
- <UkButton @color="link" @on-click={{this.download}}>
19
- {{this.downloadName}}
20
- </UkButton>
21
- {{/if}}
15
+ {{#if (and this.downloadUrl this.downloadName)}}
16
+ <div>
17
+ <UkButton
18
+ data-test-download-link
19
+ @color="link"
20
+ @on-click={{this.download}}
21
+ >
22
+ {{this.downloadName}}
23
+ </UkButton>
24
+ <UkIcon
25
+ class="uk-icon-button uk-margin-small-left"
26
+ role="button"
27
+ @icon="trash"
28
+ {{on "click" this.delete}}
29
+ />
30
+ </div>
31
+ {{/if}}
32
+ </div>
@@ -3,6 +3,7 @@ import { inject as service } from "@ember/service";
3
3
  import Component from "@glimmer/component";
4
4
  import { queryManager } from "ember-apollo-client";
5
5
 
6
+ import removeAnswerMutation from "@projectcaluma/ember-form/gql/mutations/remove-answer.graphql";
6
7
  import getFileAnswerInfoQuery from "@projectcaluma/ember-form/gql/queries/get-fileanswer-info.graphql";
7
8
 
8
9
  export default class CfFieldInputFileComponent extends Component {
@@ -18,14 +19,6 @@ export default class CfFieldInputFileComponent extends Component {
18
19
  return this.args.field?.answer?.value?.name;
19
20
  }
20
21
 
21
- get placeholder() {
22
- return this.intl.t(
23
- this.args.field?.answer.value
24
- ? "caluma.form.changeFile"
25
- : "caluma.form.selectFile"
26
- );
27
- }
28
-
29
22
  @action
30
23
  async download() {
31
24
  const { downloadUrl } = await this.apollo.watchQuery(
@@ -74,7 +67,24 @@ export default class CfFieldInputFileComponent extends Component {
74
67
  } finally {
75
68
  // eslint-disable-next-line require-atomic-updates
76
69
  target.value = "";
77
- target.parentNode.querySelector("[type=text]").value = "";
70
+ }
71
+ }
72
+
73
+ @action
74
+ async delete() {
75
+ try {
76
+ await this.apollo.mutate({
77
+ mutation: removeAnswerMutation,
78
+ variables: {
79
+ input: {
80
+ answer: this.args.field.answer.uuid,
81
+ },
82
+ },
83
+ });
84
+
85
+ await this.args.onSave(null);
86
+ } catch (error) {
87
+ set(this.args.field, "_errors", [{ type: "deleteFailed" }]);
78
88
  }
79
89
  }
80
90
  }
@@ -1,125 +1,124 @@
1
- {{#with field.question.rowForm.questions.edges as |questions|}}
2
- <table class="uk-table">
3
- <thead>
1
+ <table class="uk-table uk-table-divider">
2
+ <thead>
3
+ <tr>
4
+ {{#each this.columns as |column|}}
5
+ <th>{{column.label}}</th>
6
+ {{/each}}
7
+ <th></th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ {{#each @field.answer.value as |document|}}
4
12
  <tr>
5
- {{#each columnHeaders as |edge|}}
6
- <th>{{edge.node.label}}</th>
13
+ {{#each this.columns as |column|}}
14
+ <td>
15
+ <CfFieldValue
16
+ @field={{find-by "question.slug" column.slug document.fields}}
17
+ />
18
+ </td>
7
19
  {{/each}}
8
- <th></th>
9
- </tr>
10
- </thead>
11
- <tbody>
12
- {{#each field.answer.value as |document|}}
13
- <tr>
14
- {{#if field.question.meta.columnsToDisplay.length}}
15
- {{#each document.fields as |answerField|}}
16
- {{#if
17
- (includes
18
- answerField.answer.question.slug
19
- field.question.meta.columnsToDisplay
20
- )
21
- }}
22
- <td>{{cf-field-value field=answerField}}</td>
23
- {{/if}}
24
- {{/each}}
25
- {{else}}
26
- {{#each (take 4 document.fields) as |field|}}
27
- <td>{{cf-field-value field=field}}</td>
28
- {{/each}}
29
- {{/if}}
30
- <td class="uk-text-right">
20
+ <td class="uk-table-middle">
21
+ <div class="uk-flex uk-flex-middle uk-flex-right">
22
+ {{#if (includes false (map-by "isValid" document.fields))}}
23
+ <UkIcon
24
+ @icon="warning"
25
+ class="uk-animation-fade uk-text-danger"
26
+ />
27
+ {{/if}}
31
28
  <button
32
29
  data-test-edit-row
33
30
  type="button"
34
- class="uk-icon-button"
35
- uk-icon="pencil"
31
+ class="uk-button-link uk-flex-inline uk-margin-small-left"
36
32
  title={{t "caluma.form.edit"}}
37
- {{action "editRow" document}}
33
+ {{on "click" (fn this.edit document)}}
38
34
  >
35
+ <UkIcon @icon="pencil" />
39
36
  <span class="uk-hidden">{{t "caluma.form.edit"}}</span>
40
37
  </button>
41
- {{#unless disabled}}
38
+ {{#unless @disabled}}
42
39
  <button
43
40
  data-test-delete-row
44
41
  type="button"
45
- class="uk-icon-button"
46
- uk-icon="trash"
42
+ class="uk-button-link uk-flex-inline uk-margin-small-left"
47
43
  title={{t "caluma.form.delete"}}
48
- {{action "deleteRow" document}}
44
+ {{on "click" (fn (perform this.delete) document)}}
49
45
  >
46
+ <UkIcon @icon="trash" />
50
47
  <span class="uk-hidden">{{t "caluma.form.delete"}}</span>
51
48
  </button>
52
49
  {{/unless}}
53
- </td>
54
- </tr>
55
- {{/each}}
56
- </tbody>
50
+ </div>
51
+ </td>
52
+ </tr>
53
+ {{/each}}
54
+ </tbody>
55
+ {{#unless @disabled}}
57
56
  <tfoot>
58
- {{#unless disabled}}
59
- <tr>
60
- <td colspan={{add (count-keys questions) 1}} class="uk-text-center">
61
- <button
62
- type="button"
63
- class="uk-button uk-button-default uk-button-small"
64
- title={{t "caluma.form.addRow"}}
65
- {{on "click" (perform addRow)}}
66
- >
67
- <UkIcon @icon="plus" />
68
- <span class="uk-hidden">{{t "caluma.form.addRow"}}</span>
69
- </button>
70
- </td>
71
- </tr>
72
- {{/unless}}
57
+ <tr>
58
+ <td colspan={{add this.columns.length 1}} class="uk-text-center">
59
+ <UkButton
60
+ @size="small"
61
+ @color="default"
62
+ @on-click={{perform this.add}}
63
+ data-test-add-row
64
+ >
65
+ <UkIcon @icon="plus" />
66
+ <span class="uk-hidden">{{t "caluma.form.addRow"}}</span>
67
+ </UkButton>
68
+ </td>
69
+ </tr>
73
70
  </tfoot>
74
- </table>
75
-
76
- <UkModal
77
- @visible={{and showAddModal documentToEdit}}
78
- @on-hide={{action "closeModal" "showAddModal"}}
79
- @btnClose={{true}}
80
- @bgClose={{false}}
81
- as |modal|
82
- >
83
- <modal.body>
84
- {{#if documentToEdit}}
85
- {{cf-form-wrapper
86
- document=documentToEdit
87
- fieldset=(object-at 0 documentToEdit.fieldsets)
88
- disabled=disabled
89
- }}
90
- {{/if}}
91
- </modal.body>
92
-
93
- {{#unless disabled}}
94
- <modal.footer @class="uk-text-right">
95
- {{uk-button
96
- label=(t "caluma.form.save")
97
- disabled=save.isRunning
98
- loading=save.isRunning
99
- on-click=(perform save)
100
- }}
101
- </modal.footer>
102
- {{/unless}}
103
- </UkModal>
71
+ {{/unless}}
72
+ </table>
104
73
 
74
+ {{#if this.documentToEdit}}
105
75
  <UkModal
106
- @visible={{and showDeleteModal documentToDelete}}
107
- @on-hide={{action "closeModal" "showDeleteModal"}}
108
- @btnClose={{true}}
76
+ @visible={{this.showAddModal}}
77
+ @on-hide={{perform this.closeModal}}
109
78
  @bgClose={{false}}
110
79
  as |modal|
111
80
  >
112
81
  <modal.body>
113
- {{t "caluma.form.deleteRow"}}
82
+ <CfFormWrapper
83
+ @document={{this.documentToEdit}}
84
+ @fieldset={{object-at 0 this.documentToEdit.fieldsets}}
85
+ @disabled={{@disabled}}
86
+ />
114
87
  </modal.body>
115
88
 
116
89
  <modal.footer @class="uk-text-right">
117
- {{uk-button
118
- label=(t "caluma.form.delete")
119
- disabled=deleteRow.isRunning
120
- loading=deleteRow.isRunning
121
- on-click=(perform deleteRow documentToDelete)
122
- }}
90
+ {{#if @disabled}}
91
+ <UkButton
92
+ @label={{t "caluma.form.close"}}
93
+ @color="primary"
94
+ @on-click={{perform this.close}}
95
+ @disabled={{this.close.isRunning}}
96
+ @loading={{this.close.isRunning}}
97
+ data-test-close
98
+ />
99
+ {{else}}
100
+ <UkButton
101
+ @label={{t "caluma.form.cancel"}}
102
+ @on-click={{perform this.close}}
103
+ @disabled={{this.close.isRunning}}
104
+ @loading={{this.close.isRunning}}
105
+ data-test-cancel
106
+ />
107
+ <DocumentValidity
108
+ @document={{this.documentToEdit}}
109
+ as |isValid validate|
110
+ >
111
+ <UkButton
112
+ @label={{t "caluma.form.save"}}
113
+ @color="primary"
114
+ @type="submit"
115
+ @disabled={{or this.save.isRunning (not isValid)}}
116
+ @loading={{this.save.isRunning}}
117
+ @on-click={{fn (perform this.save) validate}}
118
+ data-test-save
119
+ />
120
+ </DocumentValidity>
121
+ {{/if}}
123
122
  </modal.footer>
124
123
  </UkModal>
125
- {{/with}}
124
+ {{/if}}
@@ -1,49 +1,65 @@
1
1
  import { getOwner } from "@ember/application";
2
- import Component from "@ember/component";
3
- import { computed } from "@ember/object";
2
+ import { action } from "@ember/object";
4
3
  import { inject as service } from "@ember/service";
4
+ import Component from "@glimmer/component";
5
+ import { tracked } from "@glimmer/tracking";
5
6
  import { queryManager } from "ember-apollo-client";
6
- import { task, all } from "ember-concurrency";
7
+ import { dropTask } from "ember-concurrency-decorators";
8
+ import UIkit from "uikit";
7
9
 
8
10
  import removeDocumentMutation from "@projectcaluma/ember-form/gql/mutations/remove-document.graphql";
9
11
  import saveDocumentMutation from "@projectcaluma/ember-form/gql/mutations/save-document.graphql";
10
12
  import { parseDocument } from "@projectcaluma/ember-form/lib/parsers";
11
13
 
12
- export default Component.extend({
13
- notification: service(),
14
- intl: service(),
15
- calumaStore: service(),
14
+ async function confirm(text) {
15
+ try {
16
+ await UIkit.modal.confirm(text);
16
17
 
17
- apollo: queryManager(),
18
+ return true;
19
+ } catch (error) {
20
+ return false;
21
+ }
22
+ }
18
23
 
19
- showAddModal: false,
20
- showDeleteModal: false,
21
- documentToEdit: null,
22
- documentToDelete: null,
24
+ export default class CfFieldInputTableComponent extends Component {
25
+ @service notification;
26
+ @service intl;
27
+ @service calumaStore;
28
+
29
+ @queryManager apollo;
30
+
31
+ @tracked showAddModal = false;
32
+ @tracked documentToEdit = null;
33
+ @tracked documentToEditIsNew = false;
23
34
 
24
35
  parseDocument(raw) {
25
36
  return parseDocument(raw);
26
- },
27
-
28
- columnHeaders: computed(
29
- "field.question.{rowForm.questions.edges.@each,meta.columnsToDisplay.[]}",
30
- function () {
31
- if (this.get("field.question.meta.columnsToDisplay.length")) {
32
- return this.get("field.question.rowForm.questions.edges").filter((n) =>
33
- this.get("field.question.meta.columnsToDisplay").includes(n.node.slug)
34
- );
35
- }
36
- return this.get("field.question.rowForm.questions.edges").slice(0, 4);
37
+ }
38
+
39
+ get questions() {
40
+ return this.args.field.question.rowForm.questions.edges.map(
41
+ (edge) => edge.node
42
+ );
43
+ }
44
+
45
+ get columns() {
46
+ const config = this.args.field.question.meta.columnsToDisplay;
47
+
48
+ if (config?.length) {
49
+ return this.questions.filter((question) =>
50
+ config.includes(question.slug)
51
+ );
37
52
  }
38
- ),
39
53
 
40
- addRow: task(function* () {
54
+ return this.questions.slice(0, 4);
55
+ }
56
+
57
+ @dropTask
58
+ *add() {
41
59
  const raw = yield this.apollo.mutate(
42
60
  {
43
61
  mutation: saveDocumentMutation,
44
- variables: {
45
- input: { form: this.get("field.question.rowForm.slug") },
46
- },
62
+ variables: { input: { form: this.args.field.question.rowForm.slug } },
47
63
  },
48
64
  "saveDocument.document"
49
65
  );
@@ -52,89 +68,90 @@ export default Component.extend({
52
68
  .factoryFor("caluma-model:document")
53
69
  .create({
54
70
  raw: this.parseDocument(raw),
55
- parentDocument: this.field.document,
71
+ parentDocument: this.args.field.document,
56
72
  });
57
73
 
58
- this.setProperties({
59
- documentToEdit: newDocument,
60
- showAddModal: true,
61
- });
62
- }).drop(),
74
+ this.documentToEditIsNew = true;
75
+ this.documentToEdit = newDocument;
76
+ this.showAddModal = true;
77
+ }
63
78
 
64
- deleteRow: task(function* (document) {
65
- if (!this.field.answer.value) return;
79
+ @dropTask
80
+ *delete(document) {
81
+ if (!(yield confirm(this.intl.t("caluma.form.deleteRow")))) {
82
+ return;
83
+ }
66
84
 
67
- const remainingDocuments = this.field.answer.value.filter(
85
+ const remainingDocuments = this.args.field.answer.value.filter(
68
86
  (doc) => doc.pk !== document.pk
69
87
  );
70
88
 
71
- yield this.onSave(remainingDocuments);
72
-
73
- // Remove orphaned document from database.
74
- yield this.apollo.mutate({
75
- mutation: removeDocumentMutation,
76
- variables: { input: { document: document.uuid } },
77
- });
78
-
79
- // Remove orphand document from Caluma store.
80
- this.calumaStore.delete(document.pk);
89
+ yield this.args.onSave(remainingDocuments);
90
+ yield this.removeOrphan(document);
91
+ }
81
92
 
82
- this.closeModal("showDeleteModal");
83
- }),
84
-
85
- save: task(function* () {
93
+ @dropTask
94
+ *save(validate) {
86
95
  try {
96
+ if (!(yield validate())) {
97
+ return;
98
+ }
99
+
87
100
  const newDocument = this.documentToEdit;
88
- yield all(newDocument.fields.map((f) => f.validate.perform()));
101
+
102
+ yield Promise.all(newDocument.fields.map((f) => f.validate.perform()));
89
103
 
90
104
  if (newDocument.fields.some((field) => field.isInvalid)) {
91
105
  return;
92
106
  }
93
107
 
94
- const rows = this.get("field.answer.value") || [];
108
+ const rows = this.args.field.answer.value ?? [];
95
109
 
96
110
  if (!rows.find((doc) => doc.pk === newDocument.pk)) {
97
111
  // add document to table
98
- yield this.onSave([...rows, newDocument]);
112
+ yield this.args.onSave([...rows, newDocument]);
99
113
 
100
114
  this.notification.success(
101
115
  this.intl.t("caluma.form.notification.table.add.success")
102
116
  );
103
117
  }
104
118
 
105
- this.closeModal("showAddModal");
119
+ this.documentToEditIsNew = false;
120
+
121
+ yield this.close.perform();
106
122
  } catch (e) {
107
- // eslint-disable-next-line no-console
108
- console.error(e);
109
123
  this.notification.danger(
110
124
  this.intl.t("caluma.form.notification.table.add.error")
111
125
  );
112
126
  }
113
- }),
127
+ }
114
128
 
115
- closeModal(propertyName) {
116
- this.set(propertyName, false);
129
+ @dropTask
130
+ *close() {
131
+ if (this.documentToEditIsNew) {
132
+ yield this.removeOrphan(this.documentToEdit);
117
133
 
118
- this.field.validate.perform();
119
- },
134
+ this.documentToEditIsNew = false;
135
+ }
120
136
 
121
- actions: {
122
- closeModal(propertyName) {
123
- this.closeModal(propertyName);
124
- },
137
+ this.showAddModal = false;
138
+ this.documentToEdit = null;
139
+ }
125
140
 
126
- editRow(document) {
127
- this.setProperties({
128
- documentToEdit: document,
129
- showAddModal: true,
130
- });
131
- },
141
+ async removeOrphan(calumaDocument) {
142
+ // Remove orphaned document from database.
143
+ await this.apollo.mutate({
144
+ mutation: removeDocumentMutation,
145
+ variables: { input: { document: calumaDocument.uuid } },
146
+ });
132
147
 
133
- deleteRow(document) {
134
- this.setProperties({
135
- documentToDelete: document,
136
- showDeleteModal: true,
137
- });
138
- },
139
- },
140
- });
148
+ // Remove orphaned document from Caluma store.
149
+ this.calumaStore.delete(calumaDocument.pk);
150
+ }
151
+
152
+ @action
153
+ edit(document) {
154
+ this.documentToEdit = document;
155
+ this.showAddModal = true;
156
+ }
157
+ }
@@ -5,6 +5,7 @@
5
5
  @field={{@field}}
6
6
  @disabled={{@disabled}}
7
7
  @onSave={{@onSave}}
8
+ @context={{@context}}
8
9
  />
9
10
  </div>
10
11
  {{/let}}
@@ -1,3 +1,4 @@
1
+ import { dasherize } from "@ember/string";
1
2
  import Component from "@glimmer/component";
2
3
 
3
4
  const mapping = {
@@ -25,7 +26,7 @@ export default class CfFieldInputComponent extends Component {
25
26
 
26
27
  return (
27
28
  typename &&
28
- (mapping[typename] || typename.replace(/Question$/, "").toLowerCase())
29
+ (mapping[typename] || dasherize(typename.replace(/Question$/, "")))
29
30
  );
30
31
  }
31
32
  }
@@ -16,21 +16,23 @@
16
16
  {{/let}}
17
17
  </div>
18
18
 
19
- {{#if @field.question.infoText}}
19
+ {{#if (and @field.question.infoText this.infoTextVisible)}}
20
20
  <CfField::info @text={{@field.question.infoText}} />
21
21
  {{/if}}
22
22
 
23
- <div
24
- class="cf-field__icon uk-padding-remove-vertical uk-flex uk-flex-middle uk-flex-center"
25
- >
26
- {{#if @field.save.isRunning}}
27
- <UkSpinner class="uk-animation-fade" />
28
- {{else if (or @field.save.last.isError @field.isInvalid)}}
29
- <UkIcon @icon="warning" class="uk-animation-fade uk-text-danger" />
30
- {{else if @field.save.last.isSuccessful}}
31
- <UkIcon @icon="check" class="uk-animation-fade uk-text-success" />
32
- {{/if}}
33
- </div>
23
+ {{#if this.saveIndicatorVisible}}
24
+ <div
25
+ class="cf-field__icon uk-padding-remove-vertical uk-flex uk-flex-middle uk-flex-center"
26
+ >
27
+ {{#if @field.save.isRunning}}
28
+ <UkSpinner class="uk-animation-fade" />
29
+ {{else if (or @field.save.last.isError @field.isInvalid)}}
30
+ <UkIcon @icon="warning" class="uk-animation-fade uk-text-danger" />
31
+ {{else if @field.save.last.isSuccessful}}
32
+ <UkIcon @icon="check" class="uk-animation-fade uk-text-success" />
33
+ {{/if}}
34
+ </div>
35
+ {{/if}}
34
36
  </div>
35
37
 
36
38
  {{#if @field.errors.length}}
@@ -29,10 +29,18 @@ export default class CfFieldComponent extends Component {
29
29
  get labelVisible() {
30
30
  return (
31
31
  !this.args.field?.question.meta.hideLabel &&
32
- !hasQuestionType(this.args.field?.question, "static")
32
+ !hasQuestionType(this.args.field?.question, "static", "action-button")
33
33
  );
34
34
  }
35
35
 
36
+ get infoTextVisible() {
37
+ return !hasQuestionType(this.args.field?.question, "action-button");
38
+ }
39
+
40
+ get saveIndicatorVisible() {
41
+ return !hasQuestionType(this.args.field?.question, "action-button");
42
+ }
43
+
36
44
  /**
37
45
  * Task to save a field. This will set the passed value to the answer and
38
46
  * save the field to the API after a timeout off 500 milliseconds.
@@ -37,11 +37,9 @@ export default class DocumentValidity extends Component {
37
37
 
38
38
  @restartableTask
39
39
  *_validate() {
40
- yield Promise.all(
41
- this.args.document.fields.map((field) =>
42
- field.validate.linked().perform()
43
- )
44
- );
40
+ for (const field of this.args.document.fields) {
41
+ yield field.validate.linked().perform();
42
+ }
45
43
 
46
44
  if (this.isValid) {
47
45
  this.args.onValid?.();
@@ -76,6 +76,11 @@ fragment SimpleQuestion on Question {
76
76
  ... on CalculatedFloatQuestion {
77
77
  calcExpression
78
78
  }
79
+ ... on ActionButtonQuestion {
80
+ action
81
+ color
82
+ validateOnEnter
83
+ }
79
84
  }
80
85
 
81
86
  fragment FieldTableQuestion on Question {
@@ -157,3 +162,59 @@ fragment FieldQuestion on Question {
157
162
  }
158
163
  }
159
164
  }
165
+
166
+ fragment SimpleAnswer on Answer {
167
+ id
168
+ question {
169
+ slug
170
+ }
171
+ ... on StringAnswer {
172
+ stringValue: value
173
+ }
174
+ ... on IntegerAnswer {
175
+ integerValue: value
176
+ }
177
+ ... on FloatAnswer {
178
+ floatValue: value
179
+ }
180
+ ... on ListAnswer {
181
+ listValue: value
182
+ }
183
+ ... on FileAnswer {
184
+ fileValue: value {
185
+ uploadUrl
186
+ downloadUrl
187
+ metadata
188
+ name
189
+ }
190
+ }
191
+ ... on DateAnswer {
192
+ dateValue: value
193
+ }
194
+ }
195
+
196
+ fragment FieldAnswer on Answer {
197
+ ...SimpleAnswer
198
+ ... on TableAnswer {
199
+ tableValue: value {
200
+ id
201
+ form {
202
+ slug
203
+ questions {
204
+ edges {
205
+ node {
206
+ ...FieldQuestion
207
+ }
208
+ }
209
+ }
210
+ }
211
+ answers {
212
+ edges {
213
+ node {
214
+ ...SimpleAnswer
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
@@ -0,0 +1,7 @@
1
+ mutation RemoveAnswer($input: RemoveAnswerInput!) {
2
+ removeAnswer(input: $input) {
3
+ answer {
4
+ id
5
+ }
6
+ }
7
+ }
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentDateAnswerInput!) {
3
+ mutation SaveDocumentDateAnswer($input: SaveDocumentDateAnswerInput!) {
4
4
  saveDocumentDateAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentFileAnswerInput!) {
3
+ mutation SaveDocumentFileAnswer($input: SaveDocumentFileAnswerInput!) {
4
4
  saveDocumentFileAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentFloatAnswerInput!) {
3
+ mutation SaveDocumentFloatAnswer($input: SaveDocumentFloatAnswerInput!) {
4
4
  saveDocumentFloatAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentIntegerAnswerInput!) {
3
+ mutation SaveDocumentIntegerAnswer($input: SaveDocumentIntegerAnswerInput!) {
4
4
  saveDocumentIntegerAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentListAnswerInput!) {
3
+ mutation SaveDocumentListAnswer($input: SaveDocumentListAnswerInput!) {
4
4
  saveDocumentListAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentStringAnswerInput!) {
3
+ mutation SaveDocumentStringAnswer($input: SaveDocumentStringAnswerInput!) {
4
4
  saveDocumentStringAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentTableAnswerInput!) {
3
+ mutation saveDocumentTableAnswer($input: SaveDocumentTableAnswerInput!) {
4
4
  saveDocumentTableAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- mutation ($input: SaveDocumentInput!) {
3
+ mutation SaveDocument($input: SaveDocumentInput!) {
4
4
  saveDocument(input: $input) {
5
5
  document {
6
6
  id
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-answer.graphql'
1
+ #import * from '../fragments/field.graphql'
2
2
 
3
- query ($id: ID!) {
3
+ query GetDocumentAnswers($id: ID!) {
4
4
  allDocuments(filter: [{ id: $id }]) {
5
5
  edges {
6
6
  node {
@@ -8,6 +8,22 @@ query ($id: ID!) {
8
8
  form {
9
9
  slug
10
10
  }
11
+ workItem {
12
+ id
13
+ }
14
+ case {
15
+ id
16
+ workItems {
17
+ edges {
18
+ node {
19
+ id
20
+ task {
21
+ id
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
11
27
  answers {
12
28
  edges {
13
29
  node {
@@ -1,6 +1,6 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-question.graphql'
1
+ #import FieldQuestion, FieldTableQuestion, SimpleQuestion from '../fragments/field.graphql'
2
2
 
3
- query ($slug: String!) {
3
+ query GetDocumentForms($slug: String!) {
4
4
  allForms(filter: [{ slug: $slug }]) {
5
5
  edges {
6
6
  node {
@@ -1,4 +1,4 @@
1
- query ($question: String!) {
1
+ query GetDynamicOptions($question: String!) {
2
2
  allQuestions(filter: [{ slug: $question }], first: 1) {
3
3
  edges {
4
4
  node {
@@ -1,4 +1,4 @@
1
- query getFileAnswerInfo($id: ID!) {
1
+ query GetFileAnswerInfo($id: ID!) {
2
2
  node(id: $id) {
3
3
  id
4
4
  ... on FileAnswer {
@@ -83,6 +83,22 @@ export default Base.extend({
83
83
  return decodeId(this.raw.id);
84
84
  }),
85
85
 
86
+ workItemUuid: computed(
87
+ "raw.{workItem.id,case.workItems.edges.[]}",
88
+ function () {
89
+ // The document is either directly attached to a work item (via
90
+ // CompleteTaskFormTask) or it's the case document and therefore
91
+ // indirectly attached to a work item (via CompleteWorkflowFormTask)
92
+ const rawId =
93
+ this.raw.workItem?.id ||
94
+ this.raw.case?.workItems.edges.find(
95
+ (edge) => edge.node.task.__typename === "CompleteWorkflowFormTask"
96
+ )?.node.id;
97
+
98
+ return rawId ? decodeId(rawId) : null;
99
+ }
100
+ ),
101
+
86
102
  /**
87
103
  * The root form of this document
88
104
  *
@@ -230,7 +246,7 @@ export default Base.extend({
230
246
  }
231
247
 
232
248
  if (field.hidden || [undefined, null].includes(field.value)) {
233
- return field.question.isMultipleChoice ? [] : null;
249
+ return defaultValue ?? field.question.isMultipleChoice ? [] : null;
234
250
  }
235
251
 
236
252
  if (field.question.__typename === "TableQuestion") {
@@ -564,7 +564,7 @@ export default Base.extend({
564
564
  "fieldset.field.hidden",
565
565
  "jexlContext",
566
566
  "optionalDependencies.@each.{hidden,value}",
567
- "question.isRequired",
567
+ "question.{__typename,isRequired}",
568
568
  "pk",
569
569
  function () {
570
570
  if (
@@ -936,4 +936,15 @@ export default Base.extend({
936
936
  _validateCalculatedFloatQuestion() {
937
937
  return resolve(true);
938
938
  },
939
+
940
+ /**
941
+ * Dummy method for the validation of work item button fields
942
+ *
943
+ * @method _validateActionButtonQuestion
944
+ * @return {RSVP.Promise}
945
+ * @private
946
+ */
947
+ _validateActionButtonQuestion() {
948
+ return resolve(true);
949
+ },
939
950
  });
@@ -0,0 +1 @@
1
+ export { default } from "@projectcaluma/ember-form/components/cf-field/input/action-button";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-form",
3
- "version": "9.1.2",
3
+ "version": "10.0.1",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@glimmer/component": "^1.0.4",
18
18
  "@glimmer/tracking": "^1.0.4",
19
- "@projectcaluma/ember-core": "9.1.0",
19
+ "@projectcaluma/ember-core": "^10.0.1",
20
20
  "ember-apollo-client": "^3.2.0",
21
21
  "ember-auto-import": "^2.2.3",
22
22
  "ember-cli-babel": "^7.26.6",
@@ -26,9 +26,9 @@
26
26
  "ember-fetch": "^8.0.4",
27
27
  "ember-in-viewport": "^3.10.3",
28
28
  "ember-intl": "^5.7.0",
29
- "ember-math-helpers": "^2.17.3",
29
+ "ember-math-helpers": "^2.18.0",
30
30
  "ember-pikaday": "^3.0.0",
31
- "ember-power-select": "^4.1.6",
31
+ "ember-power-select": "^4.1.7",
32
32
  "ember-uikit": "^4.0.0",
33
33
  "graphql": "^15.6.1",
34
34
  "jexl": "^2.3.0",
@@ -39,10 +39,11 @@
39
39
  "devDependencies": {
40
40
  "@ember/optional-features": "2.0.0",
41
41
  "@ember/test-helpers": "2.6.0",
42
- "@embroider/test-setup": "0.47.1",
43
- "@projectcaluma/ember-testing": "9.0.0",
42
+ "@embroider/test-setup": "0.47.2",
43
+ "@projectcaluma/ember-testing": "9.1.0",
44
+ "@projectcaluma/ember-workflow": "10.0.0",
44
45
  "broccoli-asset-rev": "3.0.0",
45
- "ember-cli": "3.28.3",
46
+ "ember-cli": "3.28.4",
46
47
  "ember-cli-code-coverage": "1.0.3",
47
48
  "ember-cli-dependency-checker": "3.2.0",
48
49
  "ember-cli-inject-live-reload": "2.1.0",
@@ -57,17 +58,17 @@
57
58
  "ember-resolver": "8.0.3",
58
59
  "ember-source": "3.28.6",
59
60
  "ember-source-channel-url": "3.0.0",
60
- "ember-try": "1.4.0",
61
+ "ember-try": "2.0.0",
61
62
  "faker": "5.5.3",
62
63
  "loader.js": "4.7.0",
63
64
  "npm-run-all": "4.1.5",
64
65
  "qunit": "2.17.2",
65
66
  "qunit-dom": "2.0.0",
66
67
  "uuid": "8.3.2",
67
- "webpack": "5.61.0"
68
+ "webpack": "5.64.1"
68
69
  },
69
70
  "engines": {
70
- "node": "10.* || >= 12"
71
+ "node": "12.* || 14.* || >= 16"
71
72
  },
72
73
  "ember": {
73
74
  "edition": "octane"
@@ -1,24 +1,12 @@
1
1
  caluma:
2
- caluma-query:
3
- work-item:
4
- status:
5
- READY: "Offen"
6
- COMPLETED: "Erledigt"
7
- CANCELED: "Abgebrochen"
8
- SKIPPED: "Übersprungen"
9
-
10
- case:
11
- status:
12
- RUNNING: "In Bearbeitung"
13
- COMPLETED: "Abgeschlossen"
14
- CANCELED: "Abgebrochen"
15
2
  form:
16
3
  optional: "Optional"
17
4
  save: "Speichern"
18
5
  delete: "Löschen"
19
6
  edit: "Bearbeiten"
20
- selectFile: "Klicken um Datei hochzuladen"
21
- changeFile: "Klicken um andere Datei hochzuladen"
7
+ cancel: "Abbrechen"
8
+ close: "Schliessen"
9
+ selectFile: "Durchsuchen..."
22
10
  deleteRow: "Möchten Sie diese Zeile wirklich löschen?"
23
11
  addRow: "Zeile hinzufügen"
24
12
  optionNotAvailable: "Diese Option ist nicht mehr verfügbar"
@@ -1,24 +1,12 @@
1
1
  caluma:
2
- caluma-query:
3
- work-item:
4
- status:
5
- READY: "Pending"
6
- COMPLETED: "Completed"
7
- CANCELED: "Canceled"
8
- SKIPPED: "Skipped"
9
- case:
10
- status:
11
- RUNNING: "Pending"
12
- COMPLETED: "Completed"
13
- CANCELED: "Canceled"
14
-
15
2
  form:
16
3
  optional: "Optional"
17
4
  save: "Save"
18
5
  delete: "Delete"
19
6
  edit: "Edit"
20
- selectFile: "Click to upload a file"
21
- changeFile: "Click to upload a different file"
7
+ cancel: "Cancel"
8
+ close: "Close"
9
+ selectFile: "Browse..."
22
10
  deleteRow: "Do you really want to delete this row?"
23
11
  addRow: "Add row"
24
12
  optionNotAvailable: "This option is not available anymore"
@@ -1,25 +1,12 @@
1
1
  caluma:
2
- caluma-query:
3
- work-item:
4
- status:
5
- READY: "En attente"
6
- COMPLETED: "Terminé"
7
- CANCELED: "Annulé"
8
- SKIPPED: "Sauté"
9
-
10
- case:
11
- status:
12
- RUNNING: "En cours"
13
- COMPLETED: "Terminé"
14
- CANCELED: "Annulé"
15
-
16
2
  form:
17
3
  optional: "optional"
18
4
  save: "sauvegarder"
19
5
  delete: "supprimer"
20
6
  edit: "modifier"
21
- selectFile: "cliquez pour télécharger le ficher"
22
- changeFile: "cliquez pour télécharger un autre fichier"
7
+ cancel: "annuler"
8
+ close: "fermer"
9
+ selectFile: "Sélectionner..."
23
10
  deleteRow: "Voulez-vous supprimer cette ligne?"
24
11
  addRow: "Ajouter une ligne"
25
12
  optionNotAvailable: "Cette option n'est plus disponible"
@@ -1,57 +0,0 @@
1
- # import * from '@projectcaluma/ember-form/gql/fragments/field-question.graphql'
2
-
3
- fragment SimpleAnswer on Answer {
4
- id
5
- question {
6
- slug
7
- }
8
- ... on StringAnswer {
9
- stringValue: value
10
- }
11
- ... on IntegerAnswer {
12
- integerValue: value
13
- }
14
- ... on FloatAnswer {
15
- floatValue: value
16
- }
17
- ... on ListAnswer {
18
- listValue: value
19
- }
20
- ... on FileAnswer {
21
- fileValue: value {
22
- uploadUrl
23
- downloadUrl
24
- metadata
25
- name
26
- }
27
- }
28
- ... on DateAnswer {
29
- dateValue: value
30
- }
31
- }
32
-
33
- fragment FieldAnswer on Answer {
34
- ...SimpleAnswer
35
- ... on TableAnswer {
36
- tableValue: value {
37
- id
38
- form {
39
- slug
40
- questions {
41
- edges {
42
- node {
43
- ...FieldQuestion
44
- }
45
- }
46
- }
47
- }
48
- answers {
49
- edges {
50
- node {
51
- ...SimpleAnswer
52
- }
53
- }
54
- }
55
- }
56
- }
57
- }