@projectcaluma/ember-distribution 11.0.0-beta.29 → 11.0.0-beta.30

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.
@@ -30,6 +30,15 @@ export default class DistributionAbility extends Ability {
30
30
  );
31
31
  }
32
32
 
33
+ get canCheckInquiries() {
34
+ return (
35
+ !this.config.ui.readonly &&
36
+ (this.config.permissions.checkInquiries?.() ?? true) &&
37
+ this.distribution.controls.value?.check.edges.filter(hasStatus("READY"))
38
+ .length > 0
39
+ );
40
+ }
41
+
33
42
  get canComplete() {
34
43
  return (
35
44
  !this.config.ui.readonly &&
@@ -13,7 +13,7 @@ export default class InquiryAbility extends Ability {
13
13
  return (
14
14
  !this.config.ui.readonly &&
15
15
  this.model?.task.slug === this.config.inquiry.task &&
16
- this.model?.status === "SUSPENDED" &&
16
+ ["SUSPENDED", "READY"].includes(this.model?.status) &&
17
17
  this.model?.controllingGroups
18
18
  .map(String)
19
19
  .includes(String(this.calumaOptions.currentGroupId))
@@ -31,7 +31,7 @@
31
31
  )
32
32
  }}
33
33
  <div class="uk-alert uk-alert-warning uk-flex uk-flex-middle">
34
- <UkIcon @icon="warning" class="uk-margin-small-right" />
34
+ <UkIcon @icon="warning" class="uk-margin-small-right uk-flex-none" />
35
35
  {{t "caluma.distribution.answer.complete-not-allowed"}}
36
36
  {{t "caluma.distribution.not-allowed-hint"}}
37
37
  </div>
@@ -45,8 +45,21 @@
45
45
  <UkButton
46
46
  @type="submit"
47
47
  @color={{buttonConfig.color}}
48
- @disabled={{or (not isValid) this.completeWorkItem.isRunning}}
49
- @loading={{this.completeWorkItem.isRunning}}
48
+ @disabled={{or
49
+ (not isValid)
50
+ (and
51
+ this.completeWorkItem.isRunning
52
+ (includes
53
+ buttonConfig.workItemId this.completeWorkItem.lastRunning.args
54
+ )
55
+ )
56
+ }}
57
+ @loading={{and
58
+ this.completeWorkItem.isRunning
59
+ (includes
60
+ buttonConfig.workItemId this.completeWorkItem.lastRunning.args
61
+ )
62
+ }}
50
63
  @onClick={{fn
51
64
  (perform this.completeWorkItem)
52
65
  buttonConfig.workItemId
@@ -58,8 +71,18 @@
58
71
  <UkButton
59
72
  @type="button"
60
73
  @color={{buttonConfig.color}}
61
- @disabled={{this.completeWorkItem.isRunning}}
62
- @loading={{this.completeWorkItem.isRunning}}
74
+ @disabled={{and
75
+ this.completeWorkItem.isRunning
76
+ (includes
77
+ buttonConfig.workItemId this.completeWorkItem.lastRunning.args
78
+ )
79
+ }}
80
+ @loading={{and
81
+ this.completeWorkItem.isRunning
82
+ (includes
83
+ buttonConfig.workItemId this.completeWorkItem.lastRunning.args
84
+ )
85
+ }}
63
86
  @onClick={{fn
64
87
  (perform this.completeWorkItem)
65
88
  buttonConfig.workItemId
@@ -23,12 +23,23 @@
23
23
  )
24
24
  }}
25
25
  <div class="uk-alert uk-alert-warning uk-flex uk-flex-middle">
26
- <UkIcon @icon="warning" class="uk-margin-small-right" />
26
+ <UkIcon @icon="warning" class="uk-margin-small-right uk-flex-none" />
27
27
  {{t "caluma.distribution.edit.send-not-allowed"}}
28
28
  {{t "caluma.distribution.not-allowed-hint"}}
29
29
  </div>
30
30
  {{/if}}
31
31
 
32
+ {{#if
33
+ (and (can "edit inquiry" this.inquiry) (eq this.inquiry.status "READY"))
34
+ }}
35
+ <div class="uk-alert uk-alert-warning uk-flex uk-flex-middle">
36
+ <UkIcon @icon="warning" class="uk-margin-small-right uk-flex-none" />
37
+ <p class="uk-margin-remove">
38
+ {{t "caluma.distribution.edit.edit-sent" htmlSafe=true}}
39
+ </p>
40
+ </div>
41
+ {{/if}}
42
+
32
43
  <content.form />
33
44
 
34
45
  {{#if
@@ -0,0 +1,20 @@
1
+ {{#if this.document.isLoading}}
2
+ <div class="uk-text-center"><UkSpinner @ratio={{2}} /></div>
3
+ {{else if this.document.value}}
4
+ <CfFormWrapper
5
+ @document={{this.document.value}}
6
+ @fieldset={{this.fieldset}}
7
+ @onSave={{this.saveField}}
8
+ />
9
+ <DocumentValidity @document={{this.document.value}} as |isValid validate|>
10
+ <UkButton
11
+ @color="primary"
12
+ @label={{t "caluma.distribution.new.create-draft"}}
13
+ @type="submit"
14
+ @loading={{this.submit.isRunning}}
15
+ @disabled={{or (not isValid) this.submit.isRunning}}
16
+ @onClick={{perform this.submit validate}}
17
+ data-test-submit
18
+ />
19
+ </DocumentValidity>
20
+ {{/if}}
@@ -0,0 +1,113 @@
1
+ import { getOwner } from "@ember/application";
2
+ import { action } from "@ember/object";
3
+ import { guidFor } from "@ember/object/internals";
4
+ import { inject as service } from "@ember/service";
5
+ import Component from "@glimmer/component";
6
+ import { queryManager } from "ember-apollo-client";
7
+ import { dropTask } from "ember-concurrency";
8
+ import { trackedFunction } from "ember-resources/util/function";
9
+ import { DateTime } from "luxon";
10
+
11
+ import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
12
+ import config from "@projectcaluma/ember-distribution/config";
13
+ import inquiryFormQuery from "@projectcaluma/ember-distribution/gql/queries/inquiry-form.graphql";
14
+ import { parseDocument } from "@projectcaluma/ember-form/lib/parsers";
15
+
16
+ export default class CdInquiryNewFormBulkEditComponent extends Component {
17
+ @service router;
18
+ @service distribution;
19
+
20
+ @queryManager apollo;
21
+
22
+ @config config;
23
+
24
+ answers = {};
25
+
26
+ document = trackedFunction(this, async () => {
27
+ // Fetch the full form (like in cf-content) of the inquiry task
28
+ const response = await this.apollo.query({
29
+ query: inquiryFormQuery,
30
+ variables: {
31
+ inquiryTask: this.config.inquiry.task,
32
+ },
33
+ });
34
+ const form = response.allTasks.edges[0].node.form;
35
+ const answers = { edges: [] };
36
+
37
+ // If we configured a default deadline lead time, we need to calculate the
38
+ // deadline that should be displayed in the form per default and add it to
39
+ // the fake document data
40
+ if (this.config.new.defaultDeadlineLeadTime) {
41
+ const deadline = DateTime.now()
42
+ .plus({ days: this.config.new.defaultDeadlineLeadTime })
43
+ .toISODate();
44
+
45
+ answers.edges.push({
46
+ node: {
47
+ dateValue: deadline,
48
+ question: {
49
+ slug: this.config.inquiry.deadlineQuestion,
50
+ },
51
+ __typename: "DateAnswer",
52
+ },
53
+ });
54
+
55
+ this.answers[this.config.inquiry.deadlineQuestion] = deadline;
56
+ }
57
+
58
+ // Generate a parsed raw data object which can be used for creating a caluma
59
+ // form lib layer document which we need for displaying a form. This is
60
+ // normally done in the cf-content component which fetches the form (like we
61
+ // do above) and the document from the backend and then merges it together
62
+ // with this function. However, since we need a document without existence
63
+ // in the backend, we need to create this object ourselves.
64
+ const raw = parseDocument({
65
+ id: btoa(`Document:inquiry-document-${guidFor(this)}`),
66
+ __typename: "Document",
67
+ answers,
68
+ form,
69
+ });
70
+
71
+ const owner = getOwner(this);
72
+ const Document = owner.factoryFor("caluma-model:document").class;
73
+
74
+ return new Document({ raw, owner });
75
+ });
76
+
77
+ get fieldset() {
78
+ return this.document.value?.fieldsets[0];
79
+ }
80
+
81
+ @action
82
+ async saveField(field, value) {
83
+ field.answer.value = value;
84
+
85
+ await field.validate.perform();
86
+
87
+ this.answers[field.question.slug] = value;
88
+ }
89
+
90
+ @dropTask
91
+ *submit(validate, e) {
92
+ e.preventDefault();
93
+
94
+ if (!this.args.selectedGroups.length || !(yield validate())) return;
95
+
96
+ yield this.distribution.createInquiry.perform(this.args.selectedGroups, {
97
+ answers: this.answers,
98
+ });
99
+
100
+ const lastControlling =
101
+ this.distribution.navigation.value.controlling.edges[0].node;
102
+
103
+ // transition to last added inquiry
104
+ this.router.transitionTo(
105
+ "inquiry.detail.index",
106
+ {
107
+ from: lastControlling.controllingGroups[0],
108
+ to: lastControlling.addressedGroups[0],
109
+ },
110
+ decodeId(lastControlling.id)
111
+ );
112
+ }
113
+ }
@@ -0,0 +1,71 @@
1
+ <div class="uk-margin-bottom uk-button-group">
2
+ {{#each-in this.config.new.types as |slug config|}}
3
+ {{#unless config.disabled}}
4
+ <UkButton
5
+ data-test-type={{slug}}
6
+ @label={{t config.label}}
7
+ @color={{if (includes slug @selectedTypes) "primary" "default"}}
8
+ @onClick={{fn this.updateSelectedTypes slug}}
9
+ />
10
+ {{/unless}}
11
+ {{/each-in}}
12
+ </div>
13
+
14
+ <div class="uk-search uk-search-default uk-width-1-1">
15
+ <span class="uk-search-icon-flip" uk-search-icon></span>
16
+ <input
17
+ placeholder={{t "caluma.distribution.new.search"}}
18
+ aria-label={{t "caluma.distribution.new.search"}}
19
+ class="uk-search-input"
20
+ type="search"
21
+ value={{@search}}
22
+ data-test-search
23
+ {{on "input" (perform this.updateSearch)}}
24
+ />
25
+ </div>
26
+
27
+ {{#if this.groups.isRunning}}
28
+ <div class="uk-text-center uk-margin">
29
+ <UkSpinner @ratio={{2}} />
30
+ </div>
31
+ {{else if this.groups.value.length}}
32
+ <table
33
+ class="uk-table uk-table-striped uk-table-hover uk-table-small uk-table-middle group-list"
34
+ >
35
+ <tbody>
36
+ {{#each this.groups.value as |group|}}
37
+ {{! template-lint-disable require-presentational-children }}
38
+ <tr
39
+ role="checkbox"
40
+ data-test-group={{group.identifier}}
41
+ {{on "click" (fn this.updateSelectedGroups group.identifier)}}
42
+ >
43
+ <td class="uk-padding-remove-right">
44
+ {{! template-lint-disable require-input-label no-nested-interactive }}
45
+ <input
46
+ type="checkbox"
47
+ class="uk-checkbox"
48
+ checked={{includes group.identifier @selectedGroups}}
49
+ />
50
+ </td>
51
+ <td class="uk-width-expand">{{group-name group.identifier}}</td>
52
+ <td class="uk-text-right">
53
+ {{#if group.config.icon}}
54
+ <UkIcon
55
+ @icon={{group.config.icon}}
56
+ class="uk-display-block uk-text-{{group.config.iconColor}}"
57
+ />
58
+ {{/if}}
59
+ </td>
60
+ </tr>
61
+ {{/each}}
62
+ </tbody>
63
+ </table>
64
+ {{else}}
65
+ <div class="uk-text-center">
66
+ <UkIcon @icon="search" @ratio={{10}} class="uk-margin-top" />
67
+ <p class="uk-text-muted">
68
+ {{t "caluma.distribution.new.empty"}}
69
+ </p>
70
+ </div>
71
+ {{/if}}
@@ -0,0 +1,76 @@
1
+ import { action } from "@ember/object";
2
+ import { inject as service } from "@ember/service";
3
+ import { macroCondition, isTesting } from "@embroider/macros";
4
+ import Component from "@glimmer/component";
5
+ import { timeout, restartableTask } from "ember-concurrency";
6
+ import { trackedTask } from "ember-resources/util/ember-concurrency";
7
+
8
+ import config from "@projectcaluma/ember-distribution/config";
9
+
10
+ const toggle = (value, array) => {
11
+ const set = new Set(array);
12
+
13
+ set.delete(value) || set.add(value);
14
+
15
+ return [...set];
16
+ };
17
+
18
+ export default class CdInquiryNewFormSelectComponent extends Component {
19
+ @service calumaOptions;
20
+
21
+ @config config;
22
+
23
+ groups = trackedTask(this, this.fetchGroups, () => [
24
+ this.args.selectedTypes,
25
+ this.args.search,
26
+ ]);
27
+
28
+ @action
29
+ updateSelectedTypes(type, e) {
30
+ e.preventDefault();
31
+
32
+ this.args.onChangeSelectedTypes(toggle(type, this.args.selectedTypes));
33
+ }
34
+
35
+ @action
36
+ updateSelectedGroups(identifier) {
37
+ this.args.onChangeSelectedGroups(
38
+ toggle(identifier, this.args.selectedGroups)
39
+ );
40
+ }
41
+
42
+ @restartableTask
43
+ *updateSearch(e) {
44
+ e.preventDefault();
45
+
46
+ /* istanbul ignore next */
47
+ if (macroCondition(isTesting())) {
48
+ // no timeout
49
+ } else {
50
+ yield timeout(500);
51
+ }
52
+
53
+ this.args.onChangeSearch(e.target.value);
54
+ }
55
+
56
+ @restartableTask
57
+ *fetchGroups(types, search) {
58
+ // https://github.com/ember-cli/eslint-plugin-ember/issues/1413
59
+ yield Promise.resolve();
60
+
61
+ const typedGroups = yield this.calumaOptions.fetchTypedGroups(
62
+ types,
63
+ search
64
+ );
65
+
66
+ return Object.entries(typedGroups)
67
+ .flatMap(([type, groups]) => {
68
+ return groups.map((group) => ({
69
+ identifier: group[this.calumaOptions.groupIdentifierProperty],
70
+ name: group[this.calumaOptions.groupNameProperty],
71
+ config: this.config.new.types[type],
72
+ }));
73
+ })
74
+ .sort((a, b) => a.name.localeCompare(b.name));
75
+ }
76
+ }
@@ -1,6 +1,4 @@
1
- {{#if this.controls.workItems.isRunning}}
2
- <div class="uk-text-center"><UkSpinner @ratio={{2}} /></div>
3
- {{else if (can "create inquiry of distribution")}}
1
+ {{#if (can "create inquiry of distribution")}}
4
2
  <p class="uk-text-large">{{t "caluma.distribution.new.title"}}</p>
5
3
 
6
4
  <hr />
@@ -30,95 +28,40 @@
30
28
  }}
31
29
  </div>
32
30
  <div>
33
- <UkButton
34
- @label={{t "caluma.distribution.new.reset"}}
35
- @onClick={{this.clearSelectedGroups}}
36
- data-test-reset
37
- />
38
- <UkButton
39
- @color="primary"
40
- @label={{t "caluma.distribution.new.create-draft"}}
41
- @type="submit"
42
- @loading={{this.submit.isRunning}}
43
- @disabled={{this.submit.isRunning}}
44
- @onClick={{perform this.submit}}
45
- data-test-submit
46
- />
31
+ {{#if this.edit}}
32
+ <UkButton
33
+ @label={{t "caluma.distribution.new.back"}}
34
+ @onClick={{fn (mut this.edit) false}}
35
+ />
36
+ {{else}}
37
+ <UkButton
38
+ @label={{t "caluma.distribution.new.reset"}}
39
+ @onClick={{this.clearSelectedGroups}}
40
+ data-test-reset
41
+ />
42
+ <UkButton
43
+ @color="primary"
44
+ @label={{t "caluma.distribution.new.continue"}}
45
+ @onClick={{fn (mut this.edit) true}}
46
+ data-test-continue
47
+ />
48
+ {{/if}}
47
49
  </div>
48
50
  </div>
49
51
  <hr />
50
52
  {{/if}}
51
53
 
52
- <div class="uk-margin-bottom uk-button-group">
53
- {{#each-in this.config.new.types as |slug config|}}
54
- {{#unless config.disabled}}
55
- <UkButton
56
- data-test-type={{slug}}
57
- @label={{t config.label}}
58
- @color={{if (includes slug @selectedTypes) "primary" "default"}}
59
- @onClick={{fn this.updateSelectedTypes slug}}
60
- />
61
- {{/unless}}
62
- {{/each-in}}
63
- </div>
64
-
65
- <div class="uk-search uk-search-default uk-width-1-1">
66
- <span class="uk-search-icon-flip" uk-search-icon></span>
67
- <input
68
- placeholder={{t "caluma.distribution.new.search"}}
69
- aria-label={{t "caluma.distribution.new.search"}}
70
- class="uk-search-input"
71
- type="search"
72
- value={{@search}}
73
- data-test-search
74
- {{on "input" (perform this.updateSearch)}}
75
- />
76
- </div>
77
-
78
- {{#if this.groups.isRunning}}
79
- <div class="uk-text-center uk-margin">
80
- <UkSpinner @ratio={{2}} />
81
- </div>
82
- {{else if this.groups.value.length}}
83
- <table
84
- class="uk-table uk-table-striped uk-table-hover uk-table-small uk-table-middle group-list"
85
- >
86
- <tbody>
87
- {{#each this.groups.value as |group|}}
88
- {{! template-lint-disable require-presentational-children }}
89
- <tr
90
- role="checkbox"
91
- data-test-group={{group.identifier}}
92
- {{on "click" (fn this.updateSelectedGroups group.identifier)}}
93
- >
94
- <td class="uk-padding-remove-right">
95
- {{! template-lint-disable require-input-label no-nested-interactive }}
96
- <input
97
- type="checkbox"
98
- class="uk-checkbox"
99
- checked={{includes group.identifier this.selectedGroups}}
100
- />
101
- </td>
102
- <td class="uk-width-expand">{{group-name group.identifier}}</td>
103
- <td class="uk-text-right">
104
- {{#if group.config.icon}}
105
- <UkIcon
106
- @icon={{group.config.icon}}
107
- class="uk-display-block uk-text-{{group.config.iconColor}}"
108
- />
109
- {{/if}}
110
- </td>
111
- </tr>
112
- {{/each}}
113
- </tbody>
114
- </table>
54
+ {{#if this.edit}}
55
+ <CdInquiryNewForm::BulkEdit @selectedGroups={{this.selectedGroups}} />
115
56
  {{else}}
116
- <div class="uk-text-center">
117
- <UkIcon @icon="search" @ratio={{10}} class="uk-margin-top" />
118
- <p class="uk-text-muted">
119
- {{t "caluma.distribution.new.empty"}}
120
- </p>
121
- </div>
57
+ <CdInquiryNewForm::Select
58
+ @search={{@search}}
59
+ @selectedGroups={{this.selectedGroups}}
60
+ @selectedTypes={{@selectedTypes}}
61
+ @onChangeSearch={{@onChangeSearch}}
62
+ @onChangeSelectedGroups={{fn (mut this.selectedGroups)}}
63
+ @onChangeSelectedTypes={{@onChangeSelectedTypes}}
64
+ />
122
65
  {{/if}}
123
66
  {{else}}
124
67
  <CdNotfound />
@@ -1,114 +1,15 @@
1
1
  import { action } from "@ember/object";
2
- import { inject as service } from "@ember/service";
3
- import { macroCondition, isTesting } from "@embroider/macros";
4
2
  import Component from "@glimmer/component";
5
3
  import { tracked } from "@glimmer/tracking";
6
- import { queryManager } from "ember-apollo-client";
7
- import { timeout, restartableTask, dropTask, task } from "ember-concurrency";
8
- import { trackedTask } from "ember-resources/util/ember-concurrency";
9
-
10
- import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
11
- import config from "@projectcaluma/ember-distribution/config";
12
-
13
- const toggle = (value, array) => {
14
- const set = new Set(array);
15
-
16
- set.delete(value) || set.add(value);
17
-
18
- return [...set];
19
- };
20
4
 
21
5
  export default class CdInquiryNewFormComponent extends Component {
22
- @service calumaOptions;
23
- @service notification;
24
- @service intl;
25
- @service router;
26
- @service distribution;
27
-
28
- @queryManager apollo;
29
-
30
- @config config;
31
-
6
+ @tracked edit = false;
32
7
  @tracked selectedGroups = [];
33
8
 
34
- groups = trackedTask(this, this.fetchGroups, () => [
35
- this.args.selectedTypes,
36
- this.args.search,
37
- ]);
38
-
39
- @action
40
- updateSelectedTypes(type, e) {
41
- e.preventDefault();
42
-
43
- this.args.onChangeSelectedTypes(toggle(type, this.args.selectedTypes));
44
- }
45
-
46
- @action
47
- updateSelectedGroups(identifier) {
48
- this.selectedGroups = toggle(identifier, this.selectedGroups);
49
- }
50
-
51
9
  @action
52
10
  clearSelectedGroups(e) {
53
11
  e.preventDefault();
54
12
 
55
13
  this.selectedGroups = [];
56
14
  }
57
-
58
- @restartableTask
59
- *updateSearch(e) {
60
- e.preventDefault();
61
-
62
- /* istanbul ignore next */
63
- if (macroCondition(isTesting())) {
64
- // no timeout
65
- } else {
66
- yield timeout(500);
67
- }
68
-
69
- this.args.onChangeSearch(e.target.value);
70
- }
71
-
72
- @dropTask
73
- *submit(e) {
74
- e.preventDefault();
75
-
76
- if (!this.selectedGroups.length) return;
77
-
78
- yield this.distribution.createInquiry.perform(this.selectedGroups);
79
-
80
- const lastControlling =
81
- this.distribution.navigation.value.controlling.edges[0].node;
82
-
83
- // transition to last added inquiry
84
- this.router.transitionTo(
85
- "inquiry.detail.index",
86
- {
87
- from: lastControlling.controllingGroups[0],
88
- to: lastControlling.addressedGroups[0],
89
- },
90
- decodeId(lastControlling.id)
91
- );
92
- }
93
-
94
- @task
95
- *fetchGroups(types, search) {
96
- // https://github.com/ember-cli/eslint-plugin-ember/issues/1413
97
- yield Promise.resolve();
98
-
99
- const typedGroups = yield this.calumaOptions.fetchTypedGroups(
100
- types,
101
- search
102
- );
103
-
104
- return Object.entries(typedGroups)
105
- .flatMap(([type, groups]) => {
106
- return groups.map((group) => ({
107
- identifier: group[this.calumaOptions.groupIdentifierProperty],
108
- name: group[this.calumaOptions.groupNameProperty],
109
- config: this.config.new.types[type],
110
- }));
111
- })
112
- .sort((a, b) => a.name.localeCompare(b.name));
113
- }
114
15
  }