@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.
@@ -0,0 +1,145 @@
1
+ import { localized, msg } from "@lit/localize";
2
+ import type {
3
+ Asset,
4
+ ContractDefinition,
5
+ PolicyDefinition,
6
+ } from "@src/component";
7
+ import ComponentStyle from "@styles/modal/fact-bundle-modal/ds4go-contract-modal-part.scss?inline";
8
+ import { css, html, LitElement, nothing, unsafeCSS } from "lit";
9
+ import { customElement, state } from "lit/decorators.js";
10
+
11
+ @customElement("ds4go-contract-modal-part")
12
+ @localized()
13
+ export class DS4GOContractModalPart extends LitElement {
14
+ static styles = css`
15
+ ${unsafeCSS(ComponentStyle)}
16
+ `;
17
+
18
+ @state()
19
+ contract?: ContractDefinition;
20
+
21
+ @state()
22
+ assets?: Asset[] = [];
23
+
24
+ @state()
25
+ policies?: PolicyDefinition[] = [];
26
+
27
+ @state()
28
+ expandedAssets = false;
29
+
30
+ @state()
31
+ expandedPolicies = false;
32
+
33
+ _toggleSection(section: "assets" | "policies") {
34
+ if (section === "assets") {
35
+ this.expandedAssets = !this.expandedAssets;
36
+ } else {
37
+ this.expandedPolicies = !this.expandedPolicies;
38
+ }
39
+ }
40
+
41
+ render() {
42
+ if (!this.contract) {
43
+ return nothing;
44
+ }
45
+
46
+ const contractId = this.contract["@id"];
47
+ const relatedAssets = this.assets?.filter((a: Asset) => {
48
+ if (!this.contract?.assetsSelector) return false;
49
+ if (Array.isArray(this.contract.assetsSelector)) {
50
+ return this.contract.assetsSelector.find(
51
+ (s) => s.operandRight === a["@id"],
52
+ );
53
+ }
54
+ return this.contract.assetsSelector.operandRight === a["@id"];
55
+ });
56
+
57
+ const relatedPolicies = this.policies?.filter(
58
+ (p: PolicyDefinition) =>
59
+ p["@id"] === this.contract?.accessPolicyId ||
60
+ p["@id"] === this.contract?.contractPolicyId,
61
+ );
62
+
63
+ return html`<div class="contract-section">
64
+ <div class="contract-header">
65
+ <tems-division type="h5">${msg("Contract")}</tems-division>
66
+ <tems-division type="body-sm"
67
+ >${contractId.split("/").pop()}</tems-division
68
+ >
69
+ ${this.contract.createdAt
70
+ ? html`<tems-division type="body-sm"
71
+ >${msg("Created")}:
72
+ ${new Date(
73
+ this.contract.createdAt,
74
+ ).toLocaleString()}</tems-division
75
+ >`
76
+ : nothing}
77
+ </div>
78
+
79
+ ${relatedAssets && relatedAssets.length > 0
80
+ ? html`<div class="contract-subsection">
81
+ <div
82
+ class="collapsible-header"
83
+ @click=${() => this._toggleSection("assets")}
84
+ >
85
+ <tems-division type="body-m">${msg("Assets")}</tems-division>
86
+ <icon-material-symbols-expand-more
87
+ class="chevron ${this.expandedAssets ? "expanded" : ""}"
88
+ ></icon-material-symbols-expand-more>
89
+ </div>
90
+ <div
91
+ class="collapsible-content ${this.expandedAssets
92
+ ? "expanded"
93
+ : ""}"
94
+ >
95
+ <div class="assets-list">
96
+ ${relatedAssets.map(
97
+ (asset: Asset) =>
98
+ html`<div class="asset-item">
99
+ <tems-division type="body-sm"
100
+ >${asset["@id"].split("/").pop()}</tems-division
101
+ >
102
+ <tems-division type="body-xs"
103
+ >${asset.dataAddress?.baseUrl ||
104
+ msg("No base URL")}</tems-division
105
+ >
106
+ ${asset.dataAddress?.type
107
+ ? html`<tems-division type="body-xs"
108
+ >${msg("Type")}:
109
+ ${asset.dataAddress.type}</tems-division
110
+ >`
111
+ : nothing}
112
+ </div>`,
113
+ )}
114
+ </div>
115
+ </div>
116
+ </div>`
117
+ : nothing}
118
+ ${relatedPolicies && relatedPolicies.length > 0
119
+ ? html`<div class="contract-subsection">
120
+ <div
121
+ class="collapsible-header"
122
+ @click=${() => this._toggleSection("policies")}
123
+ >
124
+ <tems-division type="body-m">${msg("Policies")}</tems-division>
125
+ <icon-material-symbols-expand-more
126
+ class="chevron ${this.expandedPolicies ? "expanded" : ""}"
127
+ ></icon-material-symbols-expand-more>
128
+ </div>
129
+ <div
130
+ class="collapsible-content ${this.expandedPolicies
131
+ ? "expanded"
132
+ : ""}"
133
+ >
134
+ ${relatedPolicies.map(
135
+ (policy: PolicyDefinition) =>
136
+ html`<ds4go-odrl-policy-modal-part
137
+ .policy=${policy}
138
+ ></ds4go-odrl-policy-modal-part>`,
139
+ )}
140
+ </div>
141
+ </div>`
142
+ : nothing}
143
+ </div>`;
144
+ }
145
+ }
@@ -0,0 +1,132 @@
1
+ import { localized, msg } from "@lit/localize";
2
+ import type {
3
+ Asset,
4
+ ContractAgreement,
5
+ ContractNegotiation,
6
+ } from "@src/component";
7
+ import ComponentStyle from "@styles/modal/fact-bundle-modal/ds4go-negotiations-modal-part.scss?inline";
8
+ import { css, html, LitElement, nothing, unsafeCSS } from "lit";
9
+ import { customElement, state } from "lit/decorators.js";
10
+
11
+ type ExtendedContractNegotiation = ContractNegotiation & {
12
+ agreement?: ContractAgreement;
13
+ };
14
+
15
+ @customElement("ds4go-negotiations-modal-part")
16
+ @localized()
17
+ export class DS4GONegotiationsModalPart extends LitElement {
18
+ static styles = css`
19
+ ${unsafeCSS(ComponentStyle)}
20
+ `;
21
+
22
+ @state()
23
+ negotiations?: ExtendedContractNegotiation[] = [];
24
+
25
+ @state()
26
+ assets?: Asset[] = [];
27
+
28
+ @state()
29
+ type: "PROVIDER" | "CONSUMER" = "PROVIDER";
30
+
31
+ _getNegotiationTags(negotiation: ExtendedContractNegotiation) {
32
+ const tags: Array<{ name: string; type: string }> = [
33
+ {
34
+ name: `State: ${negotiation.state}`,
35
+ type:
36
+ negotiation.state === "FINALIZED" || negotiation.state === "VERIFIED"
37
+ ? "success"
38
+ : "information",
39
+ },
40
+ ];
41
+
42
+ if (negotiation.counterPartyId) {
43
+ tags.push({
44
+ name: `Counterparty: ${negotiation.counterPartyId.split("/").pop()}`,
45
+ type: "information",
46
+ });
47
+ }
48
+
49
+ if (negotiation.agreement) {
50
+ tags.push({
51
+ name: msg("Has Agreement"),
52
+ type: "success",
53
+ });
54
+ }
55
+
56
+ return tags;
57
+ }
58
+
59
+ _getAgreementTags(agreement: ContractAgreement) {
60
+ const tags: Array<{ name: string; type: string }> = [];
61
+
62
+ if (agreement.consumerId) {
63
+ tags.push({
64
+ name: `Consumer: ${agreement.consumerId.split("/").pop()}`,
65
+ type: "information",
66
+ });
67
+ }
68
+
69
+ if (agreement.providerId) {
70
+ tags.push({
71
+ name: `Provider: ${agreement.providerId.split("/").pop()}`,
72
+ type: "information",
73
+ });
74
+ }
75
+
76
+ if (agreement.contractSigningDate) {
77
+ tags.push({
78
+ name: `Signed: ${new Date(
79
+ agreement.contractSigningDate,
80
+ ).toLocaleDateString()}`,
81
+ type: "success",
82
+ });
83
+ }
84
+
85
+ return tags;
86
+ }
87
+
88
+ _renderNegotiationCard(negotiation: ExtendedContractNegotiation) {
89
+ const tags = this._getNegotiationTags(negotiation);
90
+ const relatedAsset = this.assets?.find(
91
+ (a: Asset) => a["@id"] === negotiation.assetId,
92
+ );
93
+
94
+ return html`<ds4go-card-catalog
95
+ .object=${import.meta.env.DEV ? negotiation : nothing}
96
+ .header=${negotiation["@id"].split("/").pop()}
97
+ .content=${relatedAsset
98
+ ? `Asset: ${relatedAsset["@id"].split("/").pop()}`
99
+ : msg("No associated asset")}
100
+ .tags=${tags}
101
+ date=${negotiation.createdAt
102
+ ? new Date(negotiation.createdAt).toLocaleString()
103
+ : nothing}
104
+ ></ds4go-card-catalog>`;
105
+ }
106
+
107
+ render() {
108
+ if (!this.negotiations || this.negotiations.length === 0) {
109
+ return nothing;
110
+ }
111
+
112
+ const filteredNegotiations = this.negotiations.filter(
113
+ (n: ContractNegotiation) => n.type === this.type,
114
+ );
115
+
116
+ if (filteredNegotiations.length === 0) {
117
+ return nothing;
118
+ }
119
+
120
+ const title =
121
+ this.type === "PROVIDER"
122
+ ? msg("Provider Negotiations")
123
+ : msg("Consumer Negotiations");
124
+
125
+ return html`<tems-division type="body-m"><div>${title}</div></tems-division>
126
+ <div class="card-grid card-grid-vertical">
127
+ ${filteredNegotiations.map((negotiation: ExtendedContractNegotiation) =>
128
+ this._renderNegotiationCard(negotiation),
129
+ )}
130
+ </div>`;
131
+ }
132
+ }
@@ -0,0 +1,233 @@
1
+ import { localized, msg } from "@lit/localize";
2
+ import type {
3
+ Constraint,
4
+ Duty,
5
+ OdrlPermission,
6
+ OdrlPolicy,
7
+ PolicyDefinition,
8
+ Prohibition,
9
+ Resource,
10
+ } from "@src/component";
11
+ import ComponentStyle from "@styles/modal/fact-bundle-modal/ds4go-odrl-policy-modal-part.scss?inline";
12
+ import { css, html, LitElement, nothing, unsafeCSS } from "lit";
13
+ import { customElement, state } from "lit/decorators.js";
14
+
15
+ type ODSLPolicyReformater = OdrlPolicy & {
16
+ "odrl:target"?: OdrlPolicy["target"];
17
+ "odrl:permission"?: (OdrlPermission & {
18
+ "odrl:action": OdrlPermission["action"];
19
+ "odrl:target"?: OdrlPermission["target"];
20
+ "odrl:constraint"?: OdrlPermission["constraint"][];
21
+ })[];
22
+ "odrl:prohibition"?: (Prohibition & {
23
+ "odrl:action": Prohibition["action"];
24
+ "odrl:target"?: Prohibition["target"];
25
+ "odrl:constraint"?: Prohibition["constraint"][];
26
+ })[];
27
+ "odrl:obligation"?: (Duty & {
28
+ "odrl:action": Duty["action"];
29
+ "odrl:constraint"?: Duty["constraint"][];
30
+ })[];
31
+ };
32
+
33
+ @customElement("ds4go-odrl-policy-modal-part")
34
+ @localized()
35
+ export class DS4GOOdrlPolicyModalPart extends LitElement {
36
+ static styles = css`
37
+ ${unsafeCSS(ComponentStyle)}
38
+ `;
39
+
40
+ @state()
41
+ policy?: PolicyDefinition;
42
+
43
+ _formatAction(action: string | string[] | Resource | Resource[]): string {
44
+ if (Array.isArray(action)) {
45
+ return action
46
+ .map((a) => {
47
+ if ((a as Resource)["@id"]) {
48
+ return (a as Resource)["@id"];
49
+ }
50
+ return a;
51
+ })
52
+ .join(", ");
53
+ }
54
+ if (typeof action === "object") {
55
+ return action["@id"];
56
+ }
57
+ return action;
58
+ }
59
+
60
+ _renderConstraint(constraint: Constraint) {
61
+ return html`<div class="constraint-item">
62
+ <tems-division type="body-xs"
63
+ >${constraint.leftOperand} ${constraint.operator}
64
+ ${String(constraint.rightOperand)}</tems-division
65
+ >
66
+ ${constraint["@type"]
67
+ ? html`<tems-division type="body-xs"
68
+ >${msg("Type")}: ${constraint["@type"]}</tems-division
69
+ >`
70
+ : nothing}
71
+ </div>`;
72
+ }
73
+
74
+ render() {
75
+ if (!this.policy) {
76
+ return nothing;
77
+ }
78
+ const odrlPolicy: ODSLPolicyReformater = this.policy.policy;
79
+
80
+ if (!Array.isArray(odrlPolicy?.["odrl:permission"])) {
81
+ odrlPolicy["odrl:permission"] = odrlPolicy["odrl:permission"]
82
+ ? [odrlPolicy["odrl:permission"]]
83
+ : [];
84
+ }
85
+
86
+ if (!Array.isArray(odrlPolicy?.["odrl:prohibition"])) {
87
+ odrlPolicy["odrl:prohibition"] = odrlPolicy["odrl:prohibition"]
88
+ ? [odrlPolicy["odrl:prohibition"]]
89
+ : [];
90
+ }
91
+
92
+ if (!Array.isArray(odrlPolicy?.["odrl:obligation"])) {
93
+ odrlPolicy["odrl:obligation"] = odrlPolicy["odrl:obligation"]
94
+ ? [odrlPolicy["odrl:obligation"]]
95
+ : [];
96
+ }
97
+
98
+ return html`<div class="policy-card">
99
+ <div class="policy-header">
100
+ <tems-division type="body-m"
101
+ >${msg("Policy")}:
102
+ ${this.policy["@id"].split("/").pop()}</tems-division
103
+ >
104
+ <tems-division type="body-xs"
105
+ >${msg("Type")}: ${odrlPolicy?.["@type"] || "Unknown"}</tems-division
106
+ >
107
+ ${this.policy.createdAt
108
+ ? html`<tems-division type="body-xs"
109
+ >${msg("Created")}:
110
+ ${new Date(this.policy.createdAt).toLocaleString()}</tems-division
111
+ >`
112
+ : nothing}
113
+ </div>
114
+
115
+ ${odrlPolicy?.["odrl:permission"] &&
116
+ odrlPolicy["odrl:permission"].length > 0
117
+ ? html`<div class="policy-section">
118
+ <tems-division type="body-sm" class="policy-section-title"
119
+ >${msg("Permissions")}</tems-division
120
+ >
121
+ ${odrlPolicy["odrl:permission"].map(
122
+ (permission) =>
123
+ html`<div class="policy-item">
124
+ <tems-division type="body-xs"
125
+ >${msg("Action")}:
126
+ ${this._formatAction(
127
+ permission.action || permission["odrl:action"],
128
+ )}</tems-division
129
+ >
130
+ ${permission.target || permission["odrl:target"]
131
+ ? html`<tems-division type="body-xs"
132
+ >${msg("Target")}:
133
+ ${(permission.target || permission["odrl:target"])
134
+ ?.split("/")
135
+ .pop()}</tems-division
136
+ >`
137
+ : nothing}
138
+ ${(permission.constraint || permission["odrl:constraint"]) &&
139
+ (permission.constraint || permission["odrl:constraint"] || [])
140
+ .length > 0
141
+ ? html`<div class="constraints-list">
142
+ ${(
143
+ permission.constraint ||
144
+ permission["odrl:constraint"] ||
145
+ []
146
+ ).map((constraint) =>
147
+ this._renderConstraint(constraint as Constraint),
148
+ )}
149
+ </div>`
150
+ : nothing}
151
+ </div>`,
152
+ )}
153
+ </div>`
154
+ : nothing}
155
+ ${odrlPolicy?.["odrl:prohibition"] &&
156
+ odrlPolicy["odrl:prohibition"].length > 0
157
+ ? html`<div class="policy-section">
158
+ <tems-division type="body-sm" class="policy-section-title"
159
+ >${msg("Prohibitions")}</tems-division
160
+ >
161
+ ${odrlPolicy["odrl:prohibition"].map(
162
+ (prohibition) =>
163
+ html`<div class="policy-item">
164
+ <tems-division type="body-xs"
165
+ >${msg("Action")}:
166
+ ${this._formatAction(
167
+ prohibition.action || prohibition["odrl:action"],
168
+ )}</tems-division
169
+ >
170
+ ${prohibition.target || prohibition["odrl:target"]
171
+ ? html`<tems-division type="body-xs"
172
+ >${msg("Target")}:
173
+ ${(prohibition.target || prohibition["odrl:target"])
174
+ ?.split("/")
175
+ .pop()}</tems-division
176
+ >`
177
+ : nothing}
178
+ ${(prohibition.constraint ||
179
+ prohibition["odrl:constraint"]) &&
180
+ (
181
+ prohibition.constraint ||
182
+ prohibition["odrl:constraint"] ||
183
+ []
184
+ ).length > 0
185
+ ? html`<div class="constraints-list">
186
+ ${(
187
+ prohibition.constraint ||
188
+ prohibition["odrl:constraint"] ||
189
+ []
190
+ ).map((constraint) =>
191
+ this._renderConstraint(constraint as Constraint),
192
+ )}
193
+ </div>`
194
+ : nothing}
195
+ </div>`,
196
+ )}
197
+ </div>`
198
+ : nothing}
199
+ ${odrlPolicy?.["odrl:obligation"] &&
200
+ odrlPolicy["odrl:obligation"].length > 0
201
+ ? html`<div class="policy-section">
202
+ <tems-division type="body-sm" class="policy-section-title"
203
+ >${msg("Obligations")}</tems-division
204
+ >
205
+ ${odrlPolicy["odrl:obligation"].map(
206
+ (obligation) =>
207
+ html`<div class="policy-item">
208
+ <tems-division type="body-xs"
209
+ >${msg("Action")}:
210
+ ${this._formatAction(
211
+ obligation.action || obligation["odrl:action"],
212
+ )}</tems-division
213
+ >
214
+ ${(obligation.constraint || obligation["odrl:constraint"]) &&
215
+ (obligation.constraint || obligation["odrl:constraint"] || [])
216
+ .length > 0
217
+ ? html`<div class="constraints-list">
218
+ ${(
219
+ obligation.constraint ||
220
+ obligation["odrl:constraint"] ||
221
+ []
222
+ ).map((constraint: any) =>
223
+ this._renderConstraint(constraint as Constraint),
224
+ )}
225
+ </div>`
226
+ : nothing}
227
+ </div>`,
228
+ )}
229
+ </div>`
230
+ : nothing}
231
+ </div>`;
232
+ }
233
+ }
@@ -49,7 +49,15 @@ export class SolidCustomerList extends OrbitComponent {
49
49
  { key: "updated_at", value: "updated_at", cast: formatDate },
50
50
  { key: "name", value: "name" },
51
51
  { key: "participant_id", value: "participant_id" },
52
- { key: "balance", value: "balance" },
52
+ {
53
+ key: "balance",
54
+ value: "balance",
55
+ cast: (balance: string) =>
56
+ new Intl.NumberFormat(undefined, {
57
+ style: "currency",
58
+ currency: "EUR",
59
+ }).format(Number(balance)),
60
+ },
53
61
  ];
54
62
 
55
63
  async _afterAttach() {
@@ -2,34 +2,54 @@ import { Task } from "@lit/task";
2
2
  import type {
3
3
  Asset,
4
4
  AssetInput,
5
+ ContractAgreement,
5
6
  ContractDefinition,
6
7
  ContractDefinitionInput,
8
+ ContractNegotiation,
7
9
  PolicyDefinition,
8
10
  PolicyDefinitionInput,
9
11
  Resource,
10
12
  } from "@src/component";
11
13
  import { OrbitDSPComponent } from "@startinblox/solid-tems-shared";
12
14
  import { nothing } from "lit";
13
- import { customElement, state } from "lit/decorators.js";
14
-
15
- export interface DSPProviderConfig {
16
- name: string;
17
- address: string;
18
- color?: string;
19
- participantId?: string;
20
- }
15
+ import { customElement, property, state } from "lit/decorators.js";
21
16
 
22
17
  @customElement("solid-dsp-connector")
23
18
  export class SolidDspConnector extends OrbitDSPComponent {
24
- async _responseAdaptator(response: Resource): Promise<Resource> {
25
- if (response.providers) {
26
- if (!Array.isArray(response.providers)) {
27
- response.providers = [response.providers].filter((i: any) => i);
28
- }
29
- } else if (response.provider) {
30
- response.providers = [response.provider].filter((i: any) => i);
19
+ async _afterAttach(): Promise<void> {
20
+ await super._afterAttach();
21
+ await this.loadAll();
22
+ this.setupAutoRefresh();
23
+ return Promise.resolve();
24
+ }
25
+
26
+ @property({ attribute: "auto-refresh", type: Number })
27
+ autoRefreshInterval = 0;
28
+
29
+ private refreshTimer?: number;
30
+
31
+ disconnectedCallback() {
32
+ super.disconnectedCallback();
33
+ if (this.refreshTimer) {
34
+ clearInterval(this.refreshTimer);
31
35
  }
32
- return response;
36
+ }
37
+
38
+ private setupAutoRefresh() {
39
+ if (this.autoRefreshInterval > 0) {
40
+ this.refreshTimer = window.setInterval(async () => {
41
+ await this.loadAll();
42
+ }, this.autoRefreshInterval * 1000);
43
+ }
44
+ }
45
+
46
+ async loadAll() {
47
+ await Promise.all([
48
+ this.loadAssets(),
49
+ this.loadPolicies(),
50
+ this.loadContracts(),
51
+ this.loadNegotiations(),
52
+ ]);
33
53
  }
34
54
 
35
55
  @state()
@@ -152,6 +172,40 @@ export class SolidDspConnector extends OrbitDSPComponent {
152
172
  }
153
173
  }
154
174
 
175
+ @state()
176
+ negotiations: ContractNegotiation[] = [];
177
+
178
+ private async loadNegotiations() {
179
+ if (!this.storeService) {
180
+ console.error("Store not initialized. Check connector configuration.");
181
+ return;
182
+ }
183
+
184
+ try {
185
+ const negotiations = await this.storeService.getAllContractNegotiations();
186
+ this.negotiations = Array.isArray(negotiations) ? negotiations : [];
187
+ } catch (e) {
188
+ console.error("Failed to load contract negotiations", e);
189
+ }
190
+ }
191
+
192
+ async loadAgreement(
193
+ negotiationId: ContractNegotiation["@id"],
194
+ ): Promise<ContractAgreement | undefined> {
195
+ if (!this.storeService) {
196
+ console.error("Store not initialized. Check connector configuration.");
197
+ return Promise.resolve(undefined);
198
+ }
199
+
200
+ try {
201
+ return this.storeService.getContractAgreement(negotiationId);
202
+ } catch (e) {
203
+ console.error("Failed to load contract negotiations", e);
204
+ }
205
+
206
+ return Promise.resolve(undefined);
207
+ }
208
+
155
209
  _getResource = new Task(this, {
156
210
  task: async () => {
157
211
  if (!this.orbit) return;