@projectcaluma/ember-distribution 11.0.0-beta.33 → 11.0.0-beta.35

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.
@@ -4,101 +4,131 @@ import { DateTime } from "luxon";
4
4
 
5
5
  import config from "@projectcaluma/ember-distribution/config";
6
6
 
7
+ /**
8
+ * This class contains all permission definitions for inquiries. To improve
9
+ * performance there are a few helpers and rules for optimal permission
10
+ * computation. The permissions need to be ordered by how expensive their
11
+ * computation is: the least expensive first and the most expensive last and so
12
+ * on:
13
+ *
14
+ * 1. Static config properties (e.g. `enableReminders`)
15
+ * 2. Base permission which checks for the correct task and the readonly config
16
+ * 3. Simple work item property checks (e.g. `isReady` or `isSuspended`)
17
+ * 4. Addressed / controlling group affiliation (e.g. `isAddressed` or `isControlling`)
18
+ * 5. All other computations (e.g. whether the deadline is overdue)
19
+ * 6. Custom permissions served by the host app (using `hasCustomPermission`)
20
+ */
7
21
  export default class InquiryAbility extends Ability {
8
22
  @service calumaOptions;
9
23
 
10
24
  @config config;
11
25
 
12
- get canEdit() {
26
+ hasCustomPermission(permissionName, ...args) {
27
+ return this.config.permissions[permissionName]?.(...args) ?? true;
28
+ }
29
+
30
+ get hasBasePermission() {
13
31
  return (
14
32
  !this.config.ui.readonly &&
15
- this.model?.task.slug === this.config.inquiry.task &&
16
- ["SUSPENDED", "READY"].includes(this.model?.status) &&
17
- this.model?.controllingGroups
18
- .map(String)
19
- .includes(String(this.calumaOptions.currentGroupId))
33
+ this.model?.task.slug === this.config.inquiry.task
34
+ );
35
+ }
36
+
37
+ get isReady() {
38
+ return this.model.status === "READY";
39
+ }
40
+
41
+ get isSuspended() {
42
+ return this.model.status === "SUSPENDED";
43
+ }
44
+
45
+ get isAddressed() {
46
+ return this.model.addressedGroups
47
+ .map(String)
48
+ .includes(String(this.calumaOptions.currentGroupId));
49
+ }
50
+
51
+ get isControlling() {
52
+ return this.model.controllingGroups
53
+ .map(String)
54
+ .includes(String(this.calumaOptions.currentGroupId));
55
+ }
56
+
57
+ get canEdit() {
58
+ return (
59
+ this.hasBasePermission &&
60
+ // Since editing in the status ready has the same character as sending an
61
+ // inquiry, we need to make sure that permission would be given
62
+ (this.isSuspended ||
63
+ (this.isReady &&
64
+ this.hasCustomPermission("sendInquiry", this.model))) &&
65
+ this.isControlling
20
66
  );
21
67
  }
22
68
 
23
69
  get canSend() {
24
70
  return (
25
- !this.config.ui.readonly &&
26
- this.model?.task.slug === this.config.inquiry.task &&
27
- this.model?.status === "SUSPENDED" &&
28
- (this.config.permissions.sendInquiry?.(this.model) ?? true) &&
29
- this.model?.controllingGroups
30
- .map(String)
31
- .includes(String(this.calumaOptions.currentGroupId))
71
+ this.hasBasePermission &&
72
+ this.isSuspended &&
73
+ this.isControlling &&
74
+ this.hasCustomPermission("sendInquiry", this.model)
32
75
  );
33
76
  }
34
77
 
35
78
  get canWithdraw() {
36
79
  return (
37
- !this.config.ui.readonly &&
38
- this.model?.task.slug === this.config.inquiry.task &&
39
- this.model?.status === "SUSPENDED" &&
40
- (this.config.permissions.withdrawInquiry?.(this.model) ?? true) &&
41
- this.model?.controllingGroups
42
- .map(String)
43
- .includes(String(this.calumaOptions.currentGroupId))
80
+ this.hasBasePermission &&
81
+ this.isSuspended &&
82
+ this.isControlling &&
83
+ this.hasCustomPermission("withdrawInquiry", this.model)
44
84
  );
45
85
  }
46
86
 
47
87
  get canAnswer() {
48
- return (
49
- !this.config.ui.readonly &&
50
- this.model?.task.slug === this.config.inquiry.task &&
51
- this.model?.status === "READY" &&
52
- this.model?.addressedGroups
53
- .map(String)
54
- .includes(String(this.calumaOptions.currentGroupId))
55
- );
88
+ return this.hasBasePermission && this.isReady && this.isAddressed;
56
89
  }
57
90
 
58
91
  get canEditAnswerForm() {
59
92
  return (
60
- !this.config.ui.readonly &&
61
93
  this.canAnswer &&
62
- this.model?.childCase.workItems.edges.some(
63
- (edge) => edge.node.task.__typename === "CompleteWorkflowFormTask"
94
+ this.model.childCase.workItems.edges.some(
95
+ (edge) =>
96
+ edge.node.status === "READY" &&
97
+ edge.node.task.__typename === "CompleteWorkflowFormTask"
64
98
  )
65
99
  );
66
100
  }
67
101
 
68
102
  get canCompleteChildWorkItem() {
69
103
  return (
70
- this.config.permissions.completeInquiryChildWorkItem?.(
104
+ this.hasBasePermission &&
105
+ this.hasCustomPermission(
106
+ "completeInquiryChildWorkItem",
71
107
  this.model,
72
108
  this.task
73
- ) ?? true
109
+ )
74
110
  );
75
111
  }
76
112
 
77
113
  get canReopen() {
78
114
  return (
115
+ this.hasBasePermission &&
79
116
  this.model.isRedoable &&
80
- this.model?.controllingGroups
81
- .map(String)
82
- .includes(String(this.calumaOptions.currentGroupId)) &&
83
- (this.config.permissions.reopenInquiry?.(this.model) ?? true)
117
+ this.isControlling &&
118
+ this.hasCustomPermission("reopenInquiry", this.model)
84
119
  );
85
120
  }
86
121
 
87
122
  get canSendReminder() {
88
- const deadline = DateTime.fromISO(
89
- this.model.document?.deadline.edges[0]?.node.value
90
- );
91
-
92
123
  return (
93
- !this.config.ui.readonly &&
94
124
  this.config.enableReminders &&
95
- this.model?.task.slug === this.config.inquiry.task &&
96
- this.model?.status === "READY" &&
97
- this.model?.controllingGroups
98
- .map(String)
99
- .includes(String(this.calumaOptions.currentGroupId)) &&
100
- deadline.diffNow("days").days <= 0 &&
101
- (this.config.permissions.sendReminder?.(this.model) ?? true)
125
+ this.hasBasePermission &&
126
+ this.isReady &&
127
+ this.isControlling &&
128
+ DateTime.fromISO(
129
+ this.model.document?.deadline.edges[0]?.node.value
130
+ ).diffNow("days").days <= 0 &&
131
+ this.hasCustomPermission("sendReminder", this.model)
102
132
  );
103
133
  }
104
134
  }
@@ -1,11 +1,11 @@
1
1
  <div class="uk-text-large uk-flex uk-flex-middle" data-test-document-header>
2
- <div class="uk-width-expand">
2
+ <div class="uk-width-expand uk-flex-inline uk-flex-middle">
3
3
  {{@name}}
4
4
  {{#if @group}}
5
5
  ({{group-name @group}})
6
6
  {{/if}}
7
7
  {{#if @status}}
8
- <UkLabel @label={{@status}} class="uk-margin-left" />
8
+ <UkLabel @label={{@status}} class="uk-margin-left uk-text-default" />
9
9
  {{/if}}
10
10
  </div>
11
11
  {{yield}}
@@ -2,14 +2,15 @@
2
2
  <div class="uk-flex uk-flex-middle uk-text-large" data-test-title>
3
3
  {{#if (eq @type "request")}}
4
4
  <UkIcon @icon="forward" class="uk-margin-small-right" />
5
- <div class="uk-width-expand uk-margin-small-right">
5
+ <div
6
+ class="uk-width-expand uk-margin-small-right uk-flex-inline uk-flex-middle"
7
+ >
6
8
  {{group-name @inquiry.controllingGroups}}
7
- {{#if
8
- (and
9
- (can "answer inquiry" @inquiry) (not @disabled) this.answerStatus
10
- )
11
- }}
12
- <UkLabel @label={{this.answerStatus}} class="uk-margin-left" />
9
+ {{#if this.status}}
10
+ <UkLabel
11
+ @label={{this.status}}
12
+ class="uk-margin-left uk-text-default"
13
+ />
13
14
  {{/if}}
14
15
  </div>
15
16
  {{else if (eq @type "answer")}}
@@ -18,6 +18,7 @@ export default class CdInquiryDialogInquiryPartComponent extends Component {
18
18
  @service router;
19
19
  @service intl;
20
20
  @service calumaOptions;
21
+ @service abilities;
21
22
 
22
23
  @queryManager apollo;
23
24
 
@@ -25,6 +26,25 @@ export default class CdInquiryDialogInquiryPartComponent extends Component {
25
26
 
26
27
  @inquiryAnswerStatus answerStatus;
27
28
 
29
+ get status() {
30
+ if (!this.args.type === "request" || this.args.disabled) {
31
+ return null;
32
+ }
33
+
34
+ const inquiry = this.args.inquiry;
35
+
36
+ if (
37
+ inquiry.status === "SUSPENDED" &&
38
+ this.abilities.can("edit inquiry", inquiry)
39
+ ) {
40
+ return this.intl.t("caluma.distribution.status.draft");
41
+ } else if (this.abilities.can("answer inquiry", inquiry)) {
42
+ return this.answerStatus;
43
+ }
44
+
45
+ return null;
46
+ }
47
+
28
48
  get date() {
29
49
  const key = this.args.type === "request" ? "createdAt" : "closedAt";
30
50
 
@@ -24,7 +24,12 @@ export default class CdInquiryDialogComponent extends Component {
24
24
  }
25
25
 
26
26
  get inquiries() {
27
- return this._inquiries.value?.allWorkItems.edges.map((edge) => edge.node);
27
+ return this._inquiries.value?.allWorkItems.edges
28
+ .map((edge) => edge.node)
29
+ .filter(
30
+ // suspended inquiries should only be visible to its creator
31
+ (node) => this.currentGroupIsCreator || node.status !== "SUSPENDED"
32
+ );
28
33
  }
29
34
 
30
35
  _inquiries = trackedTask(this, this.fetchDialog, () => [
@@ -38,6 +43,7 @@ export default class CdInquiryDialogComponent extends Component {
38
43
  *fetchDialog(from, to, caseId, config) {
39
44
  const response = yield this.apollo.watchQuery({
40
45
  query: inquiryDialogQuery,
46
+ fetchPolicy: "cache-and-network",
41
47
  variables: {
42
48
  from,
43
49
  to,
@@ -14,6 +14,9 @@ export default class CdNavigationStatusIndicatorComponent extends Component {
14
14
  @inquiryDeadline deadline;
15
15
 
16
16
  get showDeadlineIndicator() {
17
- return this.deadline.isOverdue || this.deadline.isWarning;
17
+ return (
18
+ ["addressed", "controlling"].includes(this.args.type) &&
19
+ (this.deadline.isOverdue || this.deadline.isWarning)
20
+ );
18
21
  }
19
22
  }
package/addon/config.js CHANGED
@@ -6,6 +6,7 @@ export const INQUIRY_STATUS = {
6
6
  DRAFT: "draft",
7
7
  SKIPPED: "skipped",
8
8
  SENT: "sent",
9
+ IN_PROGRESS: "in-progress",
9
10
  POSITIVE: "positive",
10
11
  NEGATIVE: "negative",
11
12
  NEEDS_INTERACTION: "needs-interaction",
@@ -45,7 +46,11 @@ export default function config(target, property) {
45
46
  "confirm-inquiry-answer": {
46
47
  color: "primary",
47
48
  label: "caluma.distribution.answer.buttons.confirm.label",
48
- status: "caluma.distribution.answer.buttons.confirm.status",
49
+ status: {
50
+ label: "caluma.distribution.answer.buttons.confirm.status",
51
+ color: { addressed: "muted", controlling: "emphasis" },
52
+ icon: "user",
53
+ },
49
54
  willCompleteInquiry: true,
50
55
  },
51
56
  "revise-inquiry-answer": {
@@ -28,6 +28,7 @@ fragment InquiryAnswerButtons on Case {
28
28
  id
29
29
  status
30
30
  closedAt
31
+ closedByUser
31
32
  task {
32
33
  id
33
34
  slug
@@ -39,6 +40,7 @@ fragment InquiryAnswerButtons on Case {
39
40
 
40
41
  fragment InquiryAnswer on Document {
41
42
  ...InquiryAnswerStatus
43
+ modifiedContentAt
42
44
  info: answers(filter: [{ questions: $answerInfoQuestions }]) {
43
45
  edges {
44
46
  node {
@@ -1,4 +1,4 @@
1
- # import InquiryAnswerStatus from '../fragments/inquiry-answer.graphql'
1
+ # import InquiryAnswerStatus, InquiryAnswerButtons from '../fragments/inquiry-answer.graphql'
2
2
  # import InquiryRequestDeadline from '../fragments/inquiry-request.graphql'
3
3
 
4
4
  fragment NavigationInquiry on WorkItem {
@@ -14,8 +14,10 @@ fragment NavigationInquiry on WorkItem {
14
14
  id
15
15
  document {
16
16
  id
17
+ modifiedContentAt
17
18
  ...InquiryAnswerStatus
18
19
  }
20
+ ...InquiryAnswerButtons
19
21
  }
20
22
  }
21
23
 
@@ -25,6 +27,7 @@ query Navigation(
25
27
  $deadlineQuestion: ID
26
28
  $currentGroup: String!
27
29
  $caseId: ID!
30
+ $buttonTasks: [String]!
28
31
  ) {
29
32
  controlling: allWorkItems(
30
33
  filter: [
@@ -9,7 +9,6 @@ import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
9
9
  import config from "@projectcaluma/ember-distribution/config";
10
10
  import createInquiryMutation from "@projectcaluma/ember-distribution/gql/mutations/create-inquiry.graphql";
11
11
  import controlsQuery from "@projectcaluma/ember-distribution/gql/queries/controls.graphql";
12
- import inquiryDialogQuery from "@projectcaluma/ember-distribution/gql/queries/inquiry-dialog.graphql";
13
12
  import navigationQuery from "@projectcaluma/ember-distribution/gql/queries/navigation.graphql";
14
13
  import uniqueByGroups from "@projectcaluma/ember-distribution/utils/unique-by-groups";
15
14
 
@@ -75,6 +74,7 @@ export default class DistributionService extends Service {
75
74
  currentGroup: String(this.calumaOptions.currentGroupId),
76
75
  statusQuestion: this.config.inquiry.answer.statusQuestion,
77
76
  deadlineQuestion: this.config.inquiry.deadlineQuestion,
77
+ buttonTasks: Object.keys(this.config.inquiry.answer.buttons),
78
78
  },
79
79
  });
80
80
 
@@ -107,22 +107,6 @@ export default class DistributionService extends Service {
107
107
  // get create inquiry work item to complete
108
108
  const createId = decodeId(this.controls.value?.create.edges[0].node.id);
109
109
 
110
- // refetch dialog queries of the groups that will have a new inquiry
111
- const refetchQueries = groups.map((group) => ({
112
- query: inquiryDialogQuery,
113
- variables: {
114
- from: String(this.calumaOptions.currentGroupId),
115
- to: String(group),
116
- caseId: this.caseId,
117
- task: this.config.inquiry.task,
118
- infoQuestion: this.config.inquiry.infoQuestion,
119
- deadlineQuestion: this.config.inquiry.deadlineQuestion,
120
- statusQuestion: this.config.inquiry.answer.statusQuestion,
121
- answerInfoQuestions: this.config.inquiry.answer.infoQuestions,
122
- buttonTasks: Object.keys(this.config.inquiry.answer.buttons),
123
- },
124
- }));
125
-
126
110
  // create new inquiries
127
111
  yield this.apollo.mutate({
128
112
  mutation: createInquiryMutation,
@@ -133,7 +117,6 @@ export default class DistributionService extends Service {
133
117
  addressed_groups: groups.map(String),
134
118
  }),
135
119
  },
136
- refetchQueries,
137
120
  });
138
121
 
139
122
  // refetch navigation and controls data
@@ -26,7 +26,9 @@ function decorator(
26
26
  this.config.inquiry.answer.buttons
27
27
  ).find(([task]) => readyWorkItems.includes(task))?.[1];
28
28
 
29
- return buttonConfig?.status ? this.intl.t(buttonConfig.status) : null;
29
+ return buttonConfig?.status
30
+ ? this.intl.t(buttonConfig.status.label ?? buttonConfig.status)
31
+ : null;
30
32
  },
31
33
  };
32
34
  }
@@ -8,6 +8,7 @@ export const ICON_MAP = {
8
8
  [INQUIRY_STATUS.DRAFT]: "commenting",
9
9
  [INQUIRY_STATUS.SKIPPED]: "lock",
10
10
  [INQUIRY_STATUS.SENT]: "comment",
11
+ [INQUIRY_STATUS.IN_PROGRESS]: "file-edit",
11
12
  [INQUIRY_STATUS.POSITIVE]: "check",
12
13
  [INQUIRY_STATUS.NEGATIVE]: "close",
13
14
  [INQUIRY_STATUS.NEEDS_INTERACTION]: "file-text",
@@ -17,6 +18,7 @@ export const COLOR_MAP = {
17
18
  [INQUIRY_STATUS.DRAFT]: "muted",
18
19
  [INQUIRY_STATUS.SKIPPED]: "muted",
19
20
  [INQUIRY_STATUS.SENT]: "emphasis",
21
+ [INQUIRY_STATUS.IN_PROGRESS]: { addressed: "muted", controlling: "emphasis" },
20
22
  [INQUIRY_STATUS.POSITIVE]: "success",
21
23
  [INQUIRY_STATUS.NEGATIVE]: "danger",
22
24
  [INQUIRY_STATUS.NEEDS_INTERACTION]: "warning",
@@ -41,16 +43,51 @@ function decorator(
41
43
  return {
42
44
  get() {
43
45
  const inquiry = get(this, inquiryProperty);
44
- const isAddressed = get(this, inquiryTypeProperty) === "addressed";
46
+ const inquiryType = get(this, inquiryTypeProperty);
47
+ const isAddressed = inquiryType === "addressed";
48
+ const isControlling = inquiryType === "controlling";
49
+
45
50
  const isDraft = isAddressed
46
51
  ? inquiry.status === "READY"
47
52
  : inquiry.status === "SUSPENDED";
48
53
  const isSent = !isAddressed && inquiry.status === "READY";
49
54
  const isSkipped = inquiry.status === "SKIPPED";
55
+ const isInProgress =
56
+ (isAddressed || isControlling) &&
57
+ inquiry.status === "READY" &&
58
+ inquiry.childCase?.document?.modifiedContentAt;
59
+
60
+ const buttonConfig = this.config.inquiry.answer.buttons;
61
+ const inquiryAnswerStatus =
62
+ buttonConfig &&
63
+ (isAddressed || isControlling) &&
64
+ inquiry.status === "READY"
65
+ ? inquiry?.childCase?.workItems.edges
66
+ .filter((edge) => edge.node.status === "READY")
67
+ .map((edge) => {
68
+ const config = buttonConfig[edge.node.task.slug]?.status;
69
+ if (!config?.icon) {
70
+ return null;
71
+ }
72
+
73
+ return {
74
+ ...config,
75
+ label: this.intl.t(config.label),
76
+ color: config.color[inquiryType] ?? config.color,
77
+ };
78
+ })
79
+ .filter(Boolean)[0]
80
+ : null;
81
+
82
+ if (inquiryAnswerStatus) {
83
+ return inquiryAnswerStatus;
84
+ }
50
85
 
51
86
  const answer = inquiry.childCase?.document.status.edges[0]?.node;
52
87
  const slug = isSkipped
53
88
  ? INQUIRY_STATUS.SKIPPED
89
+ : isInProgress
90
+ ? INQUIRY_STATUS.IN_PROGRESS
54
91
  : isDraft
55
92
  ? INQUIRY_STATUS.DRAFT
56
93
  : isSent
@@ -63,7 +100,7 @@ function decorator(
63
100
  !isSkipped && !isDraft && !isSent
64
101
  ? answer?.selectedOption.label
65
102
  : this.intl.t(`caluma.distribution.status.${slug}`),
66
- color: COLOR_MAP[slug],
103
+ color: COLOR_MAP[slug][inquiryType] ?? COLOR_MAP[slug],
67
104
  icon: ICON_MAP[slug],
68
105
  };
69
106
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-distribution",
3
- "version": "11.0.0-beta.33",
3
+ "version": "11.0.0-beta.35",
4
4
  "description": "Ember engine for the Caluma distribution module.",
5
5
  "keywords": [
6
6
  "ember-addon",
@@ -23,9 +23,9 @@
23
23
  "@embroider/macros": "^1.9.0",
24
24
  "@glimmer/component": "^1.1.2",
25
25
  "@glimmer/tracking": "^1.1.2",
26
- "@projectcaluma/ember-core": "^11.0.0-beta.33",
27
- "@projectcaluma/ember-form": "^11.0.0-beta.33",
28
- "@projectcaluma/ember-workflow": "^11.0.0-beta.33",
26
+ "@projectcaluma/ember-core": "^11.0.0-beta.35",
27
+ "@projectcaluma/ember-form": "^11.0.0-beta.35",
28
+ "@projectcaluma/ember-workflow": "^11.0.0-beta.35",
29
29
  "ember-apollo-client": "~4.0.2",
30
30
  "ember-auto-import": "^2.4.3",
31
31
  "ember-can": "^4.2.0",
@@ -36,8 +36,8 @@
36
36
  "ember-fetch": "^8.1.2",
37
37
  "ember-flatpickr": "^3.2.3",
38
38
  "ember-intl": "^5.7.2",
39
- "ember-resources": "^5.4.0",
40
- "ember-svg-jar": "^2.4.1",
39
+ "ember-resources": "^5.5.0",
40
+ "ember-svg-jar": "^2.4.2",
41
41
  "ember-test-selectors": "^6.0.0",
42
42
  "ember-uikit": "^6.1.0",
43
43
  "graphql": "^15.8.0",
@@ -51,7 +51,7 @@
51
51
  "@ember/test-helpers": "2.7.0",
52
52
  "@embroider/test-setup": "1.8.3",
53
53
  "@faker-js/faker": "7.6.0",
54
- "@projectcaluma/ember-testing": "11.0.0-beta.33",
54
+ "@projectcaluma/ember-testing": "11.0.0-beta.35",
55
55
  "broccoli-asset-rev": "3.0.0",
56
56
  "ember-cli": "4.8.0",
57
57
  "ember-cli-code-coverage": "1.0.3",
@@ -72,7 +72,7 @@
72
72
  "loader.js": "4.7.0",
73
73
  "miragejs": "0.1.45",
74
74
  "npm-run-all": "4.1.5",
75
- "qunit": "2.19.2",
75
+ "qunit": "2.19.3",
76
76
  "qunit-dom": "2.0.0",
77
77
  "sass": "1.55.0",
78
78
  "webpack": "5.74.0"
@@ -103,6 +103,7 @@ caluma:
103
103
  draft: "Entwurf"
104
104
  skipped: "Vorzeitig beendet"
105
105
  sent: "Versendet"
106
+ in-progress: "In Bearbeitung"
106
107
  positive: "Positiv"
107
108
  negative: "Negativ"
108
109
  needs-interaction: "Aktion erforderlich"
@@ -103,6 +103,7 @@ caluma:
103
103
  draft: "Draft"
104
104
  skipped: "Aborted"
105
105
  sent: "Sent"
106
+ in-progress: "In Progress"
106
107
  positive: "Positive"
107
108
  negative: "Negative"
108
109
  needs-interaction: "Needs interaction"
@@ -15,7 +15,7 @@ caluma:
15
15
  demandes ouvertes}} </b> sur la circulation actuelle. Si vous clôturez la
16
16
  circulation, toutes les demandes ouvertes seront annulées. Voulez-vous
17
17
  continuer ?"
18
- complete-confirm-empty: "Vous voulez vraiment fermer la circulation ?"
18
+ complete-confirm-empty: "Vous voulez vraiment clore la circulation ?"
19
19
  skip-confirm: "Vous voulez vraiment sauter la circulation ?"
20
20
  reopen-confirm: "Vous voulez vraiment rouvrir la circulation ?"
21
21
  send-error: "Erreur lors de l'envoi des demandes ouvertes"
@@ -77,11 +77,11 @@ caluma:
77
77
  title: "Nouvelle demande"
78
78
  search: "Chercher..."
79
79
  suggestions: "Propositions"
80
- empty: "Aucune services n'a été trouvée"
80
+ empty: "Aucune service n'a été trouvée"
81
81
  groups: "{count} {count, plural, =1 {service} other {services}}"
82
82
  selected: "{count, plural, =1 {sélectionné} other {sélectionnés}}"
83
83
  reset: "Réinitialiser"
84
- create-draft: "Créer un brouillon"
84
+ create-draft: "Créer le brouillon"
85
85
  continue: "Continuer"
86
86
  back: "Retour"
87
87
  error: "Error lors de la création {count, plural, =1 {de la demande} other {des demandes}}"
@@ -90,7 +90,7 @@ caluma:
90
90
  link: "Envoyer un rappel"
91
91
  confirm: "Voulez-vous vraiment envoyer un rappel pour cette demande ?"
92
92
  title: "Rappels envoyés"
93
- no-reminders: Aucun rappel n'a encore été envoyé pour cette demande.
93
+ no-reminders: "Aucun rappel n'a encore été envoyé pour cette demande."
94
94
  success: "Le rappel a été envoyé avec succès"
95
95
  error: "Erreur lors de l'envoi du rappel"
96
96
 
@@ -103,6 +103,7 @@ caluma:
103
103
  draft: "Brouillon"
104
104
  skipped: "Terminé prématurément"
105
105
  sent: "Envoyé"
106
+ in-progress: "En cours"
106
107
  positive: "Positif"
107
108
  negative: "Negatif"
108
109
  needs-interaction: "Action nécessaire"