@projectcaluma/ember-form 9.2.0 → 10.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) 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 +29 -31
  4. package/addon/components/cf-field/input/file.js +0 -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} +66 -0
  13. package/addon/gql/mutations/save-document-date-answer.graphql +2 -2
  14. package/addon/gql/mutations/save-document-file-answer.graphql +2 -2
  15. package/addon/gql/mutations/save-document-float-answer.graphql +2 -2
  16. package/addon/gql/mutations/save-document-integer-answer.graphql +2 -2
  17. package/addon/gql/mutations/save-document-list-answer.graphql +2 -2
  18. package/addon/gql/mutations/save-document-string-answer.graphql +2 -2
  19. package/addon/gql/mutations/save-document-table-answer.graphql +2 -2
  20. package/addon/gql/mutations/save-document.graphql +2 -2
  21. package/addon/gql/queries/get-document-answers.graphql +18 -2
  22. package/addon/gql/queries/get-document-forms.graphql +2 -2
  23. package/addon/gql/queries/get-dynamic-options.graphql +1 -1
  24. package/addon/gql/queries/get-fileanswer-info.graphql +1 -1
  25. package/addon/lib/document.js +17 -1
  26. package/addon/lib/field.js +12 -1
  27. package/app/components/cf-field/input/action-button.js +1 -0
  28. package/package.json +11 -10
  29. package/translations/de.yaml +3 -15
  30. package/translations/en.yaml +3 -15
  31. package/translations/fr.yaml +3 -16
  32. 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,34 +1,32 @@
1
- {{#unless @disabled}}
2
- <div uk-grid>
3
- <div class="uk-width-expand" 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">
4
3
 
5
- <input
6
- type="file"
7
- name={{@field.pk}}
8
- id={{@field.pk}}
9
- {{on "change" this.save}}
10
- />
11
-
12
- {{! label is in another component }}
13
- {{! template-lint-disable require-input-label }}
14
- <input
15
- class="uk-input"
16
- type="text"
17
- placeholder={{this.placeholder}}
18
- disabled
4
+ <input
5
+ type="file"
6
+ name={{@field.pk}}
7
+ id={{@field.pk}}
8
+ disabled={{@disabled}}
9
+ {{on "change" this.save}}
10
+ />
11
+ <UkButton @color="primary" @disabled={{@disabled}}>
12
+ {{t "caluma.form.selectFile"}}
13
+ </UkButton>
14
+ </div>
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}}
19
29
  />
20
30
  </div>
21
-
22
- {{#if this.downloadName}}
23
- <div class="uk-flex uk-flex-middle uk-width-auto">
24
- <UkIcon @icon="trash" role="button" {{on "click" this.delete}} />
25
- </div>
26
- {{/if}}
27
- </div>
28
- {{/unless}}
29
-
30
- {{#if (and this.downloadUrl this.downloadName)}}
31
- <UkButton @color="link" @on-click={{this.download}}>
32
- {{this.downloadName}}
33
- </UkButton>
34
- {{/if}}
31
+ {{/if}}
32
+ </div>
@@ -19,14 +19,6 @@ export default class CfFieldInputFileComponent extends Component {
19
19
  return this.args.field?.answer?.value?.name;
20
20
  }
21
21
 
22
- get placeholder() {
23
- return this.intl.t(
24
- this.args.field?.answer?.value
25
- ? "caluma.form.changeFile"
26
- : "caluma.form.selectFile"
27
- );
28
- }
29
-
30
22
  @action
31
23
  async download() {
32
24
  const { downloadUrl } = await this.apollo.watchQuery(
@@ -75,7 +67,6 @@ export default class CfFieldInputFileComponent extends Component {
75
67
  } finally {
76
68
  // eslint-disable-next-line require-atomic-updates
77
69
  target.value = "";
78
- target.parentNode.querySelector("[type=text]").value = "";
79
70
  }
80
71
  }
81
72
 
@@ -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?.();
@@ -1,3 +1,8 @@
1
+ # We can not symlink this file so an exact copy exists in another package:
2
+ # packages/form-builder/addon/gql/fragments/field.graphql
3
+ #
4
+ # When changing this file the other must also receive the same changes.
5
+
1
6
  fragment SimpleQuestion on Question {
2
7
  slug
3
8
  label
@@ -76,6 +81,11 @@ fragment SimpleQuestion on Question {
76
81
  ... on CalculatedFloatQuestion {
77
82
  calcExpression
78
83
  }
84
+ ... on ActionButtonQuestion {
85
+ action
86
+ color
87
+ validateOnEnter
88
+ }
79
89
  }
80
90
 
81
91
  fragment FieldTableQuestion on Question {
@@ -157,3 +167,59 @@ fragment FieldQuestion on Question {
157
167
  }
158
168
  }
159
169
  }
170
+
171
+ fragment SimpleAnswer on Answer {
172
+ id
173
+ question {
174
+ slug
175
+ }
176
+ ... on StringAnswer {
177
+ stringValue: value
178
+ }
179
+ ... on IntegerAnswer {
180
+ integerValue: value
181
+ }
182
+ ... on FloatAnswer {
183
+ floatValue: value
184
+ }
185
+ ... on ListAnswer {
186
+ listValue: value
187
+ }
188
+ ... on FileAnswer {
189
+ fileValue: value {
190
+ uploadUrl
191
+ downloadUrl
192
+ metadata
193
+ name
194
+ }
195
+ }
196
+ ... on DateAnswer {
197
+ dateValue: value
198
+ }
199
+ }
200
+
201
+ fragment FieldAnswer on Answer {
202
+ ...SimpleAnswer
203
+ ... on TableAnswer {
204
+ tableValue: value {
205
+ id
206
+ form {
207
+ slug
208
+ questions {
209
+ edges {
210
+ node {
211
+ ...FieldQuestion
212
+ }
213
+ }
214
+ }
215
+ }
216
+ answers {
217
+ edges {
218
+ node {
219
+ ...SimpleAnswer
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
@@ -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.2.0",
3
+ "version": "10.0.2",
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.2",
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": "10.0.0",
44
+ "@projectcaluma/ember-workflow": "10.0.1",
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
- }