@startinblox/components-ds4go 3.1.2 → 3.1.4

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.
@@ -2,7 +2,12 @@ import { OrbitComponent } from "@helpers";
2
2
  import { localized, msg, str } from "@lit/localize";
3
3
  import { Task } from "@lit/task";
4
4
  import type {
5
+ Asset,
6
+ ContractAgreement,
7
+ ContractNegotiation,
8
+ NegotiationState,
5
9
  OrbitComponent as OrbitComponentConfig,
10
+ PolicyDefinition,
6
11
  Resource,
7
12
  } from "@src/component";
8
13
 
@@ -10,6 +15,11 @@ import ModalStyle from "@styles/modal/ds4go-customer-modal.scss?inline";
10
15
  import { css, html, nothing, unsafeCSS } from "lit";
11
16
  import { customElement, state } from "lit/decorators.js";
12
17
 
18
+ type ExtendedContractNegotiation = ContractNegotiation & {
19
+ agreement?: ContractAgreement;
20
+ relatedBundle?: Resource;
21
+ };
22
+
13
23
  @customElement("ds4go-customer-modal")
14
24
  @localized()
15
25
  export class Ds4goCustomerModal extends OrbitComponent {
@@ -24,6 +34,33 @@ export class Ds4goCustomerModal extends OrbitComponent {
24
34
  @state()
25
35
  dspConnector: OrbitComponentConfig | undefined;
26
36
 
37
+ @state()
38
+ assets: Asset[] = [];
39
+
40
+ @state()
41
+ policies: PolicyDefinition[] = [];
42
+
43
+ @state()
44
+ bundles: Resource[] = [];
45
+
46
+ @state()
47
+ negotiations: ExtendedContractNegotiation[] = [];
48
+
49
+ @state()
50
+ providerNegotiations: ExtendedContractNegotiation[] = [];
51
+
52
+ @state()
53
+ consumerNegotiations: ExtendedContractNegotiation[] = [];
54
+
55
+ @state()
56
+ allAgreements: ContractAgreement[] = [];
57
+
58
+ @state()
59
+ providerAgreements: ContractAgreement[] = [];
60
+
61
+ @state()
62
+ consumerAgreements: ContractAgreement[] = [];
63
+
27
64
  async _afterAttach() {
28
65
  // use this.dspConnector.instance to reach the connector
29
66
  this.dspConnector = this.orbit?.components.find(
@@ -37,13 +74,170 @@ export class Ds4goCustomerModal extends OrbitComponent {
37
74
  this.dispatchEvent(new CustomEvent("close"));
38
75
  }
39
76
 
77
+ _getNegotiationTags(negotiation: ExtendedContractNegotiation) {
78
+ const tags: Array<{ name: string; type: string }> = [
79
+ {
80
+ name: `State: ${negotiation.state}`,
81
+ type: this._getNegotiationStateType(negotiation.state),
82
+ },
83
+ ];
84
+
85
+ if (negotiation.counterPartyId) {
86
+ const counterPartyId = negotiation.counterPartyId.split("/").pop() || "";
87
+ const truncatedCounterPartyId =
88
+ counterPartyId.length > 15
89
+ ? `${counterPartyId.substring(0, 15)}...`
90
+ : counterPartyId;
91
+ tags.push({
92
+ name: `Counterparty: ${truncatedCounterPartyId}`,
93
+ type: "information",
94
+ });
95
+ }
96
+
97
+ if (negotiation.agreement) {
98
+ tags.push({
99
+ name: msg("Has Agreement"),
100
+ type: "success",
101
+ });
102
+ }
103
+
104
+ return tags;
105
+ }
106
+
107
+ _getNegotiationStateType(state: NegotiationState): string {
108
+ const successStates = ["FINALIZED", "VERIFIED", "AGREED"];
109
+ const warningStates = ["TERMINATING", "TERMINATED"];
110
+
111
+ if (successStates.includes(state)) {
112
+ return "success";
113
+ }
114
+ if (warningStates.includes(state)) {
115
+ return "warning";
116
+ }
117
+ return "information";
118
+ }
119
+
40
120
  _getResource = new Task(this, {
41
121
  task: async () => {
42
122
  if (!this.object) return;
43
123
 
44
124
  if (this.dspConnector?.instance) {
45
- await this.dspConnector.instance.loadContracts();
46
- console.log(this.dspConnector.instance.contracts);
125
+ // Get all data from DSP connector
126
+ await this.dspConnector.instance.loadAll();
127
+
128
+ this.assets = this.dspConnector.instance.assets || [];
129
+ this.policies = this.dspConnector.instance.policies || [];
130
+
131
+ // Get all fact bundles from the connector's datas
132
+ const allDatas = this.dspConnector.instance.datas || [];
133
+ this.bundles = allDatas.filter((data: Resource) => {
134
+ const type = data["@type"];
135
+ if (Array.isArray(type)) {
136
+ return type.includes("ds4go:FactBundle");
137
+ }
138
+ return type === "ds4go:FactBundle";
139
+ });
140
+
141
+ const participantId = this.object.participant_id;
142
+ if (participantId) {
143
+ // Load agreements for all negotiations to find consumer/provider relationships
144
+ const allNegotiations = this.dspConnector.instance.negotiations || [];
145
+ const negotiationToAgreementMap = new Map<
146
+ string,
147
+ ContractAgreement
148
+ >();
149
+
150
+ await Promise.all(
151
+ allNegotiations.map(async (n: ContractNegotiation) => {
152
+ if (n.contractAgreementId) {
153
+ const agreement =
154
+ await this.dspConnector?.instance?.loadAgreement(n["@id"]);
155
+ if (agreement) {
156
+ this.allAgreements.push(agreement);
157
+ negotiationToAgreementMap.set(n["@id"], agreement);
158
+ }
159
+ }
160
+ }),
161
+ );
162
+
163
+ // Separate agreements by provider/consumer role
164
+ this.providerAgreements = this.allAgreements.filter(
165
+ (a: ContractAgreement) => {
166
+ if (!a.providerId) return false;
167
+ const providerParticipantId = a.providerId.split("/").pop();
168
+ return providerParticipantId?.startsWith(participantId);
169
+ },
170
+ );
171
+
172
+ this.consumerAgreements = this.allAgreements.filter(
173
+ (a: ContractAgreement) => {
174
+ if (!a.consumerId) return false;
175
+ const consumerParticipantId = a.consumerId.split("/").pop();
176
+ return consumerParticipantId?.startsWith(participantId);
177
+ },
178
+ );
179
+
180
+ // Get negotiation IDs where participant is consumer or provider in the agreement
181
+ const negotiationIdsWithParticipant = Array.from(
182
+ negotiationToAgreementMap.entries(),
183
+ )
184
+ .filter(([, agreement]) => {
185
+ const providerParticipantId = agreement.providerId
186
+ ?.split("/")
187
+ .pop();
188
+ const consumerParticipantId = agreement.consumerId
189
+ ?.split("/")
190
+ .pop();
191
+ return (
192
+ providerParticipantId?.startsWith(participantId) ||
193
+ consumerParticipantId?.startsWith(participantId)
194
+ );
195
+ })
196
+ .map(([negotiationId]) => negotiationId);
197
+
198
+ // Filter negotiations where counterPartyId === participant_id OR participant is in agreements
199
+ this.negotiations = allNegotiations.filter((n: ContractNegotiation) => {
200
+ const counterPartyId = n.counterPartyId;
201
+ if (counterPartyId) {
202
+ const counterPartyParticipantId = counterPartyId.split("/").pop();
203
+ if (counterPartyParticipantId?.startsWith(participantId)) {
204
+ return true;
205
+ }
206
+ }
207
+ // Also include negotiations that have agreements where participant is consumer or provider
208
+ return negotiationIdsWithParticipant.includes(n["@id"]);
209
+ });
210
+
211
+ // Load agreements for filtered negotiations for display
212
+ await Promise.all(
213
+ this.negotiations.map(async (n: ExtendedContractNegotiation) => {
214
+ if (n.contractAgreementId) {
215
+ n.agreement = await this.dspConnector?.instance?.loadAgreement(
216
+ n["@id"],
217
+ );
218
+ }
219
+ }),
220
+ );
221
+
222
+ // Split negotiations by provider/consumer role based on agreement
223
+ this.providerNegotiations = this.negotiations.filter(
224
+ (n: ExtendedContractNegotiation) => {
225
+ if (!n.agreement) return false;
226
+ if (!n.agreement.providerId) return false;
227
+ const providerParticipantId = n.agreement.providerId.split("/").pop();
228
+ return providerParticipantId?.startsWith(participantId);
229
+ },
230
+ );
231
+
232
+ this.consumerNegotiations = this.negotiations.filter(
233
+ (n: ExtendedContractNegotiation) => {
234
+ if (!n.agreement) return false;
235
+ if (!n.agreement.consumerId) return false;
236
+ const consumerParticipantId = n.agreement.consumerId.split("/").pop();
237
+ return consumerParticipantId?.startsWith(participantId);
238
+ },
239
+ );
240
+ }
47
241
  } else {
48
242
  console.warn(
49
243
  "DSP Connector not found. Please ensure the DSP Connector is properly initialized.",
@@ -57,7 +251,7 @@ export class Ds4goCustomerModal extends OrbitComponent {
57
251
 
58
252
  render() {
59
253
  return this._getResource.render({
60
- pending: () => nothing,
254
+ pending: () => html`<solid-loader></solid-loader>`,
61
255
  complete: (obj?: Resource) => {
62
256
  if (!obj) {
63
257
  this._closeModal();
@@ -80,34 +274,86 @@ export class Ds4goCustomerModal extends OrbitComponent {
80
274
  >
81
275
  <tems-division type="body-m"
82
276
  ><div>
83
- ${
84
- obj.participant_id
85
- ? msg(
86
- str`Participant ID: ${String(obj.participant_id)}`,
87
- )
88
- : ""
89
- }
277
+ ${obj.participant_id
278
+ ? msg(str`Participant ID: ${String(obj.participant_id)}`)
279
+ : ""}
90
280
  </div></tems-division
91
281
  >
92
- ${
93
- obj.signed_contracts
94
- ? html`<tems-division type="h4"
95
- ><div>
96
- ${msg("Contracts signed with the customer")}
97
- </div></tems-division
282
+ ${obj.balance !== undefined && obj.balance !== null
283
+ ? html`<tems-division type="body-m"
284
+ ><div>
285
+ ${msg(str`Balance: ${String(obj.balance)}`)}
286
+ </div></tems-division
287
+ >`
288
+ : nothing}
289
+ ${this.providerNegotiations && this.providerNegotiations.length > 0
290
+ ? html`<tems-division type="h4"
291
+ ><div>${msg("Provider Negotiations")}</div></tems-division
98
292
  >
99
293
  <div class="card-grid card-grid-vertical">
100
- ${obj.signed_contracts?.map((contract: Resource) => {
101
- return html`<ds4go-card-catalog
102
- .object=${import.meta.env.DEV ? contract : nothing}
103
- .header=${contract.name || nothing}
104
- .content=${contract.description || nothing}
105
- date=${contract.updated_at || nothing}
106
- ></ds4go-card-catalog>`;
107
- })}
294
+ ${this.providerNegotiations.map(
295
+ (negotiation: ExtendedContractNegotiation) =>
296
+ html`<ds4go-card-catalog
297
+ .object=${import.meta.env.DEV
298
+ ? negotiation
299
+ : nothing}
300
+ .header=${negotiation["@id"].split("/").pop()}
301
+ .content=${negotiation.agreement
302
+ ? `Agreement: ${negotiation.agreement["@id"]
303
+ .split("/")
304
+ .pop()}`
305
+ : msg("No agreement")}
306
+ .tags=${this._getNegotiationTags(negotiation)}
307
+ date=${negotiation.createdAt
308
+ ? new Date(
309
+ negotiation.createdAt,
310
+ ).toLocaleString()
311
+ : nothing}
312
+ ></ds4go-card-catalog>`,
313
+ )}
108
314
  </div>`
109
- : nothing
110
- }
315
+ : nothing}
316
+ ${this.consumerNegotiations && this.consumerNegotiations.length > 0
317
+ ? html`<tems-division type="h4"
318
+ ><div>${msg("Customer Negotiations")}</div></tems-division
319
+ >
320
+ <div class="card-grid card-grid-vertical">
321
+ ${this.consumerNegotiations.map(
322
+ (negotiation: ExtendedContractNegotiation) =>
323
+ html`<ds4go-card-catalog
324
+ .object=${import.meta.env.DEV
325
+ ? negotiation
326
+ : nothing}
327
+ .header=${negotiation["@id"].split("/").pop()}
328
+ .content=${negotiation.agreement
329
+ ? `Agreement: ${negotiation.agreement["@id"]
330
+ .split("/")
331
+ .pop()}`
332
+ : msg("No agreement")}
333
+ .tags=${this._getNegotiationTags(negotiation)}
334
+ date=${negotiation.createdAt
335
+ ? new Date(
336
+ negotiation.createdAt,
337
+ ).toLocaleString()
338
+ : nothing}
339
+ ></ds4go-card-catalog>`,
340
+ )}
341
+ </div>`
342
+ : nothing}
343
+ ${this.consumerAgreements && this.consumerAgreements.length > 0
344
+ ? html`<tems-division type="h4"
345
+ ><div>${msg("Customer Agreements")}</div></tems-division
346
+ >
347
+ ${this.consumerAgreements.map(
348
+ (agreement: ContractAgreement) =>
349
+ html`<ds4go-customer-contract-modal-part
350
+ .agreement=${agreement}
351
+ .assets=${this.assets}
352
+ .bundles=${this.bundles}
353
+ .policies=${this.policies}
354
+ ></ds4go-customer-contract-modal-part>`,
355
+ )}`
356
+ : nothing}
111
357
  <tems-division type="body-sm"
112
358
  ><div>
113
359
  <div>
@@ -1,82 +1,221 @@
1
- import { ComponentObjectHandler } from "@helpers";
1
+ import { OrbitComponent } from "@helpers";
2
2
  import { localized, msg } from "@lit/localize";
3
- import type { Resource } from "@src/component";
3
+ import { Task } from "@lit/task";
4
+ import type {
5
+ Asset,
6
+ ContractAgreement,
7
+ ContractDefinition,
8
+ ContractNegotiation,
9
+ OrbitComponent as OrbitComponentConfig,
10
+ PolicyDefinition,
11
+ Resource,
12
+ } from "@src/component";
4
13
 
5
14
  import ModalStyle from "@styles/modal/ds4go-fact-bundle-modal.scss?inline";
6
15
  import { css, html, nothing, unsafeCSS } from "lit";
7
- import { customElement, property } from "lit/decorators.js";
16
+ import { customElement, state } from "lit/decorators.js";
17
+
18
+ type ExtendedContractNegotiation = ContractNegotiation & {
19
+ agreement?: ContractAgreement;
20
+ };
8
21
 
9
22
  @customElement("ds4go-fact-bundle-modal")
10
23
  @localized()
11
- export class Ds4goFactBundleModal extends ComponentObjectHandler {
24
+ export class Ds4goFactBundleModal extends OrbitComponent {
25
+ constructor() {
26
+ super({ setupSubscriptions: false, ignoreRouter: true });
27
+ }
28
+
12
29
  static styles = css`
13
30
  ${unsafeCSS(ModalStyle)}
14
31
  `;
15
32
 
16
- @property({ attribute: false, type: Object })
17
- object: Resource = { "@id": "" };
33
+ @state()
34
+ dspConnector: OrbitComponentConfig | undefined;
35
+
36
+ async _afterAttach() {
37
+ // use this.dspConnector.instance to reach the connector
38
+ this.dspConnector = this.orbit?.components.find(
39
+ (c) => c.type === "dsp-connector",
40
+ );
41
+
42
+ return Promise.resolve();
43
+ }
18
44
 
19
45
  _closeModal() {
20
46
  this.dispatchEvent(new CustomEvent("close"));
21
47
  }
22
48
 
49
+ @state()
50
+ assets: Asset[] = [];
51
+
52
+ @state()
53
+ policies: PolicyDefinition[] = [];
54
+
55
+ @state()
56
+ contracts: ContractDefinition[] = [];
57
+
58
+ @state()
59
+ negotiations: ExtendedContractNegotiation[] = [];
60
+
61
+ _getResource = new Task(this, {
62
+ task: async () => {
63
+ if (!this.object) return;
64
+
65
+ if (this.dspConnector?.instance) {
66
+ this.assets = this.dspConnector.instance.assets.filter(
67
+ (a: Asset) =>
68
+ a.dataAddress?.baseUrl ===
69
+ "https://api.stg.ds4go.startinblox.com/factbundles/21/",
70
+ );
71
+
72
+ this.contracts = this.dspConnector.instance.contracts.filter(
73
+ (c: ContractDefinition) =>
74
+ (this.assets || []).find((a: Asset) => {
75
+ if (!c.assetsSelector) {
76
+ return false;
77
+ }
78
+ if (Array.isArray(c.assetsSelector)) {
79
+ return c.assetsSelector.find(
80
+ (s) => s.operandRight === a["@id"],
81
+ );
82
+ }
83
+ return c.assetsSelector.operandRight === a["@id"];
84
+ }),
85
+ );
86
+
87
+ this.policies = [
88
+ ...(this.contracts || []).flatMap((c: ContractDefinition) => {
89
+ if (!c.accessPolicyId && !c.contractPolicyId) return [];
90
+ return this.dspConnector?.instance?.policies.filter(
91
+ (p: PolicyDefinition) =>
92
+ p["@id"] === c.accessPolicyId ||
93
+ p["@id"] === c.contractPolicyId,
94
+ );
95
+ }),
96
+ ];
97
+
98
+ this.negotiations = this.dspConnector.instance.negotiations.filter(
99
+ (c: ContractNegotiation) => {
100
+ if (!c.assetId) return false;
101
+ return this.assets.find((a: Asset) => a["@id"] === c.assetId);
102
+ },
103
+ );
104
+
105
+ this.negotiations.forEach((n: ExtendedContractNegotiation) => {
106
+ if (n.contractAgreementId) {
107
+ n.agreement = this.dspConnector?.instance?.loadAgreement(n["@id"]);
108
+ }
109
+ });
110
+ } else {
111
+ console.warn(
112
+ "DSP Connector not found. Please ensure the DSP Connector is properly initialized.",
113
+ );
114
+ }
115
+
116
+ return this.object;
117
+ },
118
+ args: () => [this.caching, this.currentRoute],
119
+ });
120
+
23
121
  render() {
24
- return html`<div class="modal">
25
- <div class="topbar">
26
- <tems-button
27
- @click=${this._closeModal}
28
- type="outline-gray"
29
- .iconLeft=${html`<icon-material-symbols-close-rounded></icon-material-symbols-close-rounded>`}
30
- ></tems-button>
31
- </div>
32
- <div class="modal-content-wrapper">
33
- <div class="modal-box">
34
- <div class="modal-content">
35
- <tems-division type="h3"
36
- ><div>${String(this.object.name || "")}</div></tems-division
37
- >
38
- <tems-division type="body-m"
39
- ><div>${String(this.object.description || "")}</div></tems-division
40
- >
41
- <tems-division type="h4"
42
- ><div>${msg("All facts in this bundle")}</div></tems-division
43
- >
44
- <div class="card-grid card-grid-vertical">
45
- ${this.object.facts.map((fact: Resource) => {
46
- const tags = fact.categories.map((c: Resource) => ({
47
- name: c.name,
48
- type: "information",
49
- }));
50
- if (tags.length > 3) {
51
- const overflowTags = tags.length - 3;
52
- tags.splice(3, overflowTags);
53
- tags.push({
54
- name: `+${overflowTags} ${msg("more")}`,
55
- type: "information",
56
- });
57
- }
58
- return html`<ds4go-card-catalog
59
- .object=${import.meta.env.DEV ? fact : nothing}
60
- .tags=${tags}
61
- .header=${fact.name || nothing}
62
- .content=${fact.description || nothing}
63
- date=${fact.updated_at || nothing}
64
- ></ds4go-card-catalog>`;
65
- })}
66
- </div>
67
- <tems-division type="body-sm"
68
- ><div>
69
- <div>
70
- ${msg("Bundle created on")} ${String(this.object.created_at)}
71
- </div>
72
- <div>
73
- ${msg("Bundle updated on")} ${String(this.object.updated_at)}
122
+ return this._getResource.render({
123
+ pending: () => html`<solid-loader></solid-loader>`,
124
+ complete: (obj?: Resource) => {
125
+ if (!obj) {
126
+ this._closeModal();
127
+ return nothing;
128
+ }
129
+
130
+ return html`<div class="modal">
131
+ <div class="topbar">
132
+ <tems-button
133
+ @click=${this._closeModal}
134
+ type="outline-gray"
135
+ .iconLeft=${html`<icon-material-symbols-close-rounded></icon-material-symbols-close-rounded>`}
136
+ ></tems-button>
137
+ </div>
138
+ <div class="modal-content-wrapper">
139
+ <div class="modal-box">
140
+ <div class="modal-content">
141
+ <tems-division type="h3"
142
+ ><div>${String(obj.name || "")}</div></tems-division
143
+ >
144
+ <tems-division type="body-m"
145
+ ><div>${String(obj.description || "")}</div></tems-division
146
+ >
147
+ <tems-division type="h4"
148
+ ><div>${msg("All facts in this bundle")}</div></tems-division
149
+ >
150
+ <div class="card-grid card-grid-vertical">
151
+ ${obj.facts.map((fact: Resource) => {
152
+ const tags = fact.categories.map((c: Resource) => ({
153
+ name: c.name,
154
+ type: "information",
155
+ }));
156
+ if (tags.length > 3) {
157
+ const overflowTags = tags.length - 3;
158
+ tags.splice(3, overflowTags);
159
+ tags.push({
160
+ name: `+${overflowTags} ${msg("more")}`,
161
+ type: "information",
162
+ });
163
+ }
164
+ return html`<ds4go-card-catalog
165
+ .object=${import.meta.env.DEV ? fact : nothing}
166
+ .tags=${tags}
167
+ .header=${fact.name || nothing}
168
+ .content=${fact.description || nothing}
169
+ date=${fact.updated_at || nothing}
170
+ ></ds4go-card-catalog>`;
171
+ })}
74
172
  </div>
75
- </div></tems-division
76
- >
173
+ <tems-division type="body-sm"
174
+ ><div>
175
+ <div>
176
+ ${msg("Bundle created on")} ${String(obj.created_at)}
177
+ </div>
178
+ <div>
179
+ ${msg("Bundle updated on")} ${String(obj.updated_at)}
180
+ </div>
181
+ </div></tems-division
182
+ >
183
+ <tems-division type="h4"
184
+ ><div>${msg("Technical informations")}</div></tems-division
185
+ >
186
+ ${this.contracts && this.contracts.length > 0
187
+ ? html`${this.contracts.map(
188
+ (contract: ContractDefinition) =>
189
+ html`<ds4go-contract-modal-part
190
+ .contract=${contract}
191
+ .assets=${this.assets}
192
+ .policies=${this.policies}
193
+ ></ds4go-contract-modal-part>`,
194
+ )}`
195
+ : nothing}
196
+ ${this.negotiations && this.negotiations.length > 0
197
+ ? html`<ds4go-negotiations-modal-part
198
+ .negotiations=${this.negotiations}
199
+ .assets=${this.assets}
200
+ .type=${"PROVIDER"}
201
+ ></ds4go-negotiations-modal-part>
202
+ <ds4go-negotiations-modal-part
203
+ .negotiations=${this.negotiations}
204
+ .assets=${this.assets}
205
+ .type=${"CONSUMER"}
206
+ ></ds4go-negotiations-modal-part>`
207
+ : nothing}
208
+ </div>
209
+ </div>
77
210
  </div>
78
- </div>
79
- </div>
80
- </div>`;
211
+ </div>`;
212
+ },
213
+ error: (e) => {
214
+ if (import.meta.env.DEV) {
215
+ console.error(e, this);
216
+ }
217
+ return nothing;
218
+ },
219
+ });
81
220
  }
82
221
  }