@startinblox/components-ds4go 3.3.7 → 4.0.0
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.
- package/dist/index.js +2674 -3387
- package/locales/en.xlf +129 -3
- package/package.json +2 -2
- package/src/components/cards/ds4go-card-dataspace-catalog.ts +82 -227
- package/src/components/catalog/ds4go-catalog-data-holder.ts +158 -0
- package/src/components/modal/catalog-modal/agreement-info.ts +110 -0
- package/src/components/modal/catalog-modal/index.ts +4 -0
- package/src/components/modal/catalog-modal/negotiation-button.ts +111 -0
- package/src/components/modal/catalog-modal/policy-display.ts +66 -0
- package/src/components/modal/catalog-modal/policy-selection.ts +71 -0
- package/src/components/modal/ds4go-catalog-modal.ts +158 -1152
- package/src/components/odrl/policy-composer.ts +1 -1
- package/src/components/odrl-policy-viewer.ts +0 -21
- package/src/components/solid-dsp-catalog.ts +2 -43
- package/src/ds4go.d.ts +78 -1
- package/src/helpers/dsp/agreementStorage.ts +243 -0
- package/src/helpers/dsp/policyHelpers.ts +223 -0
- package/src/helpers/index.ts +7 -0
- package/src/styles/cards/ds4go-card-catalog.scss +1 -1
- package/src/styles/cards/ds4go-card-dataspace-catalog.scss +22 -165
- package/src/styles/modal/ds4go-catalog-modal.scss +1 -1
- package/src/components/modal/ds4go-catalog-data-holder.ts +0 -349
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dspAgreementStorage, dspPolicyHelpers } from "@helpers";
|
|
2
2
|
import { localized, msg } from "@lit/localize";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
DSPContractStorage,
|
|
6
|
-
offerKindActionHandler,
|
|
7
|
-
offerKindHandler,
|
|
8
|
-
rdf,
|
|
9
|
-
TemsObjectHandler,
|
|
10
|
-
} from "@startinblox/solid-tems-shared";
|
|
3
|
+
import { TemsObjectHandler } from "@startinblox/solid-tems-shared";
|
|
11
4
|
import ModalStyle from "@styles/modal/ds4go-catalog-modal.scss?inline";
|
|
12
|
-
import {
|
|
5
|
+
import type { DSPOffer, ODRLPolicy } from "@src/ds4go";
|
|
6
|
+
import { css, html, nothing, unsafeCSS } from "lit";
|
|
13
7
|
import { customElement, property, state } from "lit/decorators.js";
|
|
14
|
-
import { ifDefined } from "lit/directives/if-defined.js";
|
|
15
|
-
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
16
8
|
|
|
17
9
|
@customElement("ds4go-catalog-modal")
|
|
18
10
|
@localized()
|
|
@@ -27,6 +19,18 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
27
19
|
@property({ attribute: false })
|
|
28
20
|
participantId?: string;
|
|
29
21
|
|
|
22
|
+
@property({ attribute: false })
|
|
23
|
+
counterPartyId?: string;
|
|
24
|
+
|
|
25
|
+
@property({ attribute: false })
|
|
26
|
+
counterPartyAddress?: string;
|
|
27
|
+
|
|
28
|
+
@property({ attribute: false })
|
|
29
|
+
providerName?: string;
|
|
30
|
+
|
|
31
|
+
@property({ attribute: false })
|
|
32
|
+
providerColor?: string;
|
|
33
|
+
|
|
30
34
|
@state()
|
|
31
35
|
negotiationStatus:
|
|
32
36
|
| "idle"
|
|
@@ -64,8 +68,7 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
64
68
|
selectedPolicyIndex?: number;
|
|
65
69
|
|
|
66
70
|
@state()
|
|
67
|
-
availablePolicies?:
|
|
68
|
-
|
|
71
|
+
availablePolicies?: ODRLPolicy[];
|
|
69
72
|
|
|
70
73
|
/**
|
|
71
74
|
* Check for existing agreement when component connects
|
|
@@ -75,235 +78,6 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
75
78
|
this._checkExistingAgreement();
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
/**
|
|
79
|
-
* Get localStorage key for this asset
|
|
80
|
-
* Uses combination of provider ID and dataset ID for uniqueness across providers
|
|
81
|
-
*/
|
|
82
|
-
private _getStorageKey(): string {
|
|
83
|
-
const obj = this.object as any;
|
|
84
|
-
const datasetId = obj.datasetId || obj.assetId;
|
|
85
|
-
// Include provider ID to differentiate assets with same ID from different providers
|
|
86
|
-
const providerId =
|
|
87
|
-
obj.counterPartyId || obj._providerParticipantId || obj._provider || "";
|
|
88
|
-
|
|
89
|
-
// DEBUG: Log what provider info we're seeing
|
|
90
|
-
if (!datasetId) return "";
|
|
91
|
-
// Create composite key: provider-assetId
|
|
92
|
-
const key = providerId
|
|
93
|
-
? `dsp-agreement-${providerId}-${datasetId}`
|
|
94
|
-
: `dsp-agreement-${datasetId}`;
|
|
95
|
-
return key;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Save agreement info to localStorage
|
|
100
|
-
*/
|
|
101
|
-
private _saveAgreementInfo(
|
|
102
|
-
contractId: string,
|
|
103
|
-
negotiationId: string,
|
|
104
|
-
timestamp: number,
|
|
105
|
-
) {
|
|
106
|
-
const key = this._getStorageKey();
|
|
107
|
-
if (key) {
|
|
108
|
-
const obj = this.object as any;
|
|
109
|
-
const agreementInfo = {
|
|
110
|
-
contractId,
|
|
111
|
-
negotiationId,
|
|
112
|
-
timestamp,
|
|
113
|
-
assetId: obj.datasetId || obj.assetId,
|
|
114
|
-
providerId:
|
|
115
|
-
obj.counterPartyId ||
|
|
116
|
-
obj._providerParticipantId ||
|
|
117
|
-
obj._provider ||
|
|
118
|
-
"",
|
|
119
|
-
providerAddress: obj.counterPartyAddress || obj._providerAddress || "",
|
|
120
|
-
};
|
|
121
|
-
localStorage.setItem(key, JSON.stringify(agreementInfo));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Load agreement info from localStorage
|
|
127
|
-
*/
|
|
128
|
-
private _loadAgreementInfo(): {
|
|
129
|
-
contractId: string;
|
|
130
|
-
negotiationId: string;
|
|
131
|
-
timestamp: number;
|
|
132
|
-
assetId: string;
|
|
133
|
-
} | null {
|
|
134
|
-
const key = this._getStorageKey();
|
|
135
|
-
if (!key) return null;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
const stored = localStorage.getItem(key);
|
|
139
|
-
if (stored) {
|
|
140
|
-
const info = JSON.parse(stored);
|
|
141
|
-
return info;
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error("Failed to load agreement info:", error);
|
|
145
|
-
}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Save initial contract state when negotiation starts
|
|
151
|
-
*/
|
|
152
|
-
private _saveInitialContractState(negotiationId: string) {
|
|
153
|
-
try {
|
|
154
|
-
const obj = this.object as any;
|
|
155
|
-
|
|
156
|
-
// Check if contract already exists for this asset from this provider
|
|
157
|
-
const providerId = obj.counterPartyId || obj._providerParticipantId || "";
|
|
158
|
-
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
159
|
-
obj.assetId || obj.datasetId,
|
|
160
|
-
providerId,
|
|
161
|
-
);
|
|
162
|
-
const existingContract = existingContracts.find(
|
|
163
|
-
(c) => c.contractId === negotiationId,
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
if (!existingContract) {
|
|
167
|
-
// Debug: log asset object to see available properties
|
|
168
|
-
|
|
169
|
-
// Extract index endpoint URL from asset (dcat:endpointUrl)
|
|
170
|
-
const indexEndpointUrl =
|
|
171
|
-
obj["dcat:endpointUrl"] || obj.endpointUrl || obj["endpointUrl"];
|
|
172
|
-
|
|
173
|
-
const assetName = obj.name || obj.assetId || "Unknown Asset";
|
|
174
|
-
|
|
175
|
-
// Detect if this is an index asset
|
|
176
|
-
const isIndexAsset = assetName.toLowerCase().includes("index");
|
|
177
|
-
|
|
178
|
-
// Create new contract in REQUESTED state
|
|
179
|
-
DSPContractStorage.create({
|
|
180
|
-
assetId: obj.assetId || obj.datasetId,
|
|
181
|
-
datasetId: obj.datasetId || obj.assetId,
|
|
182
|
-
assetName,
|
|
183
|
-
assetDescription: obj.description,
|
|
184
|
-
providerName:
|
|
185
|
-
obj._provider || obj.provider?.name || "Unknown Provider",
|
|
186
|
-
providerAddress:
|
|
187
|
-
obj.counterPartyAddress || obj._providerAddress || "",
|
|
188
|
-
providerParticipantId:
|
|
189
|
-
obj.counterPartyId || obj._providerParticipantId || "",
|
|
190
|
-
providerColor: obj._providerColor,
|
|
191
|
-
policy: obj.policy,
|
|
192
|
-
state: "REQUESTED",
|
|
193
|
-
contractId: negotiationId,
|
|
194
|
-
// Index-specific fields
|
|
195
|
-
isIndexAsset,
|
|
196
|
-
indexEndpointUrl,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
} catch (error) {
|
|
200
|
-
console.error(
|
|
201
|
-
"[DSP Contract Catalog] Failed to save initial contract state:",
|
|
202
|
-
error,
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Save contract to DSP Contract Catalog for history tracking
|
|
209
|
-
*/
|
|
210
|
-
private _saveToContractCatalog(contractId: string, negotiationId: string) {
|
|
211
|
-
try {
|
|
212
|
-
const obj = this.object as any;
|
|
213
|
-
|
|
214
|
-
// Debug: log asset object to see available properties for endpoint URL
|
|
215
|
-
|
|
216
|
-
// Check if contract already exists - search by contractId (negotiationId) first,
|
|
217
|
-
// then by agreementId as fallback. Filter by provider to avoid cross-provider confusion.
|
|
218
|
-
const providerId = obj.counterPartyId || obj._providerParticipantId || "";
|
|
219
|
-
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
220
|
-
obj.assetId || obj.datasetId,
|
|
221
|
-
providerId,
|
|
222
|
-
);
|
|
223
|
-
const existingContract = existingContracts.find(
|
|
224
|
-
(c) => c.contractId === negotiationId || c.agreementId === contractId,
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
// Extract index endpoint URL from asset (dcat:endpointUrl)
|
|
228
|
-
// Try multiple possible property names - the mapping config adds 'indexEndpointUrl'
|
|
229
|
-
const indexEndpointUrl =
|
|
230
|
-
obj.indexEndpointUrl ||
|
|
231
|
-
obj["dcat:endpointUrl"] ||
|
|
232
|
-
obj["dcat:endpointURL"] ||
|
|
233
|
-
obj.endpointUrl ||
|
|
234
|
-
obj["endpointUrl"] ||
|
|
235
|
-
obj.endpointURL;
|
|
236
|
-
const assetName = obj.name || obj.assetId || "Unknown Asset";
|
|
237
|
-
|
|
238
|
-
// Detect if this is an index asset
|
|
239
|
-
const isIndexAsset = assetName.toLowerCase().includes("index");
|
|
240
|
-
|
|
241
|
-
if (existingContract) {
|
|
242
|
-
// Update existing contract with index metadata
|
|
243
|
-
DSPContractStorage.updateState(existingContract.id, "FINALIZED", {
|
|
244
|
-
agreementId: contractId,
|
|
245
|
-
contractId: negotiationId,
|
|
246
|
-
isIndexAsset,
|
|
247
|
-
indexEndpointUrl,
|
|
248
|
-
});
|
|
249
|
-
} else {
|
|
250
|
-
// Create new contract entry
|
|
251
|
-
DSPContractStorage.create({
|
|
252
|
-
assetId: obj.assetId || obj.datasetId,
|
|
253
|
-
datasetId: obj.datasetId || obj.assetId,
|
|
254
|
-
assetName,
|
|
255
|
-
assetDescription: obj.description,
|
|
256
|
-
providerName:
|
|
257
|
-
obj._provider || obj.provider?.name || "Unknown Provider",
|
|
258
|
-
providerAddress:
|
|
259
|
-
obj.counterPartyAddress || obj._providerAddress || "",
|
|
260
|
-
providerParticipantId:
|
|
261
|
-
obj.counterPartyId || obj._providerParticipantId || "",
|
|
262
|
-
providerColor: obj._providerColor,
|
|
263
|
-
policy: obj.policy,
|
|
264
|
-
state: "FINALIZED",
|
|
265
|
-
contractId: negotiationId,
|
|
266
|
-
agreementId: contractId,
|
|
267
|
-
// Index-specific fields
|
|
268
|
-
isIndexAsset,
|
|
269
|
-
indexEndpointUrl,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.error("[DSP Contract Catalog] Failed to save contract:", error);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Update contract state in catalog (for failures)
|
|
279
|
-
*/
|
|
280
|
-
private _updateContractState(
|
|
281
|
-
negotiationId: string,
|
|
282
|
-
state: "FAILED" | "TERMINATED",
|
|
283
|
-
error?: string,
|
|
284
|
-
) {
|
|
285
|
-
try {
|
|
286
|
-
const obj = this.object as any;
|
|
287
|
-
const providerId = obj.counterPartyId || obj._providerParticipantId || "";
|
|
288
|
-
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
289
|
-
obj.assetId || obj.datasetId,
|
|
290
|
-
providerId,
|
|
291
|
-
);
|
|
292
|
-
const existingContract = existingContracts.find(
|
|
293
|
-
(c) => c.contractId === negotiationId,
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
if (existingContract) {
|
|
297
|
-
DSPContractStorage.updateState(existingContract.id, state, { error });
|
|
298
|
-
}
|
|
299
|
-
} catch (error) {
|
|
300
|
-
console.error(
|
|
301
|
-
"[DSP Contract Catalog] Failed to update contract state:",
|
|
302
|
-
error,
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
81
|
/**
|
|
308
82
|
* Check if there's an existing agreement for this asset
|
|
309
83
|
*/
|
|
@@ -311,8 +85,10 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
311
85
|
if (this.existingAgreementChecked) return;
|
|
312
86
|
this.existingAgreementChecked = true;
|
|
313
87
|
|
|
88
|
+
const offer = this.object as DSPOffer;
|
|
89
|
+
|
|
314
90
|
// Try to load from localStorage
|
|
315
|
-
const storedInfo =
|
|
91
|
+
const storedInfo = dspAgreementStorage.loadAgreementInfo(offer);
|
|
316
92
|
if (storedInfo) {
|
|
317
93
|
this.contractId = storedInfo.contractId;
|
|
318
94
|
this.negotiationId = storedInfo.negotiationId;
|
|
@@ -323,24 +99,18 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
323
99
|
// Also check if DSP store has the agreement
|
|
324
100
|
try {
|
|
325
101
|
if (this.dspStore && storedInfo?.negotiationId) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
storedInfo.negotiationId,
|
|
336
|
-
providerId,
|
|
337
|
-
);
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.warn("Could not verify agreement in store:", error);
|
|
340
|
-
}
|
|
102
|
+
const providerId =
|
|
103
|
+
offer._provider?.participantId ||
|
|
104
|
+
this.counterPartyId ||
|
|
105
|
+
this.participantId ||
|
|
106
|
+
"";
|
|
107
|
+
await this.dspStore.getContractAgreement(
|
|
108
|
+
storedInfo.negotiationId,
|
|
109
|
+
providerId,
|
|
110
|
+
);
|
|
341
111
|
}
|
|
342
112
|
} catch (error) {
|
|
343
|
-
console.warn("
|
|
113
|
+
console.warn("Could not verify agreement in store:", error);
|
|
344
114
|
}
|
|
345
115
|
}
|
|
346
116
|
|
|
@@ -358,26 +128,15 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
358
128
|
return;
|
|
359
129
|
}
|
|
360
130
|
|
|
361
|
-
const
|
|
362
|
-
if (key) {
|
|
363
|
-
localStorage.removeItem(key);
|
|
364
|
-
}
|
|
131
|
+
const offer = this.object as DSPOffer;
|
|
365
132
|
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const existingContracts = DSPContractStorage.getByAssetAndProvider(
|
|
372
|
-
assetId,
|
|
373
|
-
providerId,
|
|
374
|
-
);
|
|
375
|
-
for (const contract of existingContracts) {
|
|
376
|
-
DSPContractStorage.delete(contract.id);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
133
|
+
// Clear from localStorage
|
|
134
|
+
dspAgreementStorage.clearAgreementInfo(offer);
|
|
135
|
+
|
|
136
|
+
// Delete from DSPContractStorage
|
|
137
|
+
dspAgreementStorage.deleteContractsForAsset(offer);
|
|
379
138
|
|
|
380
|
-
// Reset state
|
|
139
|
+
// Reset state
|
|
381
140
|
this.negotiationStatus = "idle";
|
|
382
141
|
this.contractId = undefined;
|
|
383
142
|
this.negotiationId = undefined;
|
|
@@ -386,7 +145,11 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
386
145
|
this.requestUpdate();
|
|
387
146
|
}
|
|
388
147
|
|
|
389
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Handle policy selection
|
|
150
|
+
*/
|
|
151
|
+
private _handlePolicySelected(e: CustomEvent) {
|
|
152
|
+
const { index } = e.detail;
|
|
390
153
|
this.selectedPolicyIndex = index;
|
|
391
154
|
this.showPolicySelection = false;
|
|
392
155
|
this.requestUpdate();
|
|
@@ -394,121 +157,21 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
394
157
|
this._negotiateAccess();
|
|
395
158
|
}
|
|
396
159
|
|
|
397
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Handle policy selection cancellation
|
|
162
|
+
*/
|
|
163
|
+
private _handlePolicyCancel() {
|
|
398
164
|
this.showPolicySelection = false;
|
|
399
165
|
this.selectedPolicyIndex = undefined;
|
|
400
166
|
this.availablePolicies = undefined;
|
|
401
167
|
this.requestUpdate();
|
|
402
168
|
}
|
|
403
169
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const parts: string[] = [];
|
|
408
|
-
|
|
409
|
-
// Policy ID
|
|
410
|
-
if (policy["@id"]) {
|
|
411
|
-
parts.push(
|
|
412
|
-
`<div class="policy-detail"><strong>Policy ID:</strong> ${policy["@id"]}</div>`,
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Policy Type
|
|
417
|
-
if (policy["@type"]) {
|
|
418
|
-
parts.push(
|
|
419
|
-
`<div class="policy-detail"><strong>Type:</strong> ${policy["@type"]}</div>`,
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Permissions
|
|
424
|
-
const permissions = policy["odrl:permission"];
|
|
425
|
-
if (permissions) {
|
|
426
|
-
const permArray = Array.isArray(permissions)
|
|
427
|
-
? permissions
|
|
428
|
-
: [permissions];
|
|
429
|
-
if (permArray.length > 0) {
|
|
430
|
-
parts.push(
|
|
431
|
-
'<div class="policy-detail"><strong>Permissions:</strong><ul>',
|
|
432
|
-
);
|
|
433
|
-
permArray.forEach((perm: any) => {
|
|
434
|
-
const action = perm["odrl:action"];
|
|
435
|
-
const actionStr = action?.["@id"] || action || "use";
|
|
436
|
-
parts.push(`<li>Action: ${actionStr}</li>`);
|
|
437
|
-
|
|
438
|
-
// Constraints
|
|
439
|
-
if (perm["odrl:constraint"]) {
|
|
440
|
-
const constraints = Array.isArray(perm["odrl:constraint"])
|
|
441
|
-
? perm["odrl:constraint"]
|
|
442
|
-
: [perm["odrl:constraint"]];
|
|
443
|
-
constraints.forEach((c: any) => {
|
|
444
|
-
parts.push(
|
|
445
|
-
`<li style="margin-left: 20px;">Constraint: ${c["odrl:leftOperand"]} ${c["odrl:operator"]} ${c["odrl:rightOperand"]}</li>`,
|
|
446
|
-
);
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
parts.push("</ul></div>");
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Prohibitions
|
|
455
|
-
const prohibitions = policy["odrl:prohibition"];
|
|
456
|
-
if (prohibitions) {
|
|
457
|
-
const prohibArray = Array.isArray(prohibitions)
|
|
458
|
-
? prohibitions
|
|
459
|
-
: [prohibitions];
|
|
460
|
-
if (prohibArray.length > 0) {
|
|
461
|
-
parts.push(
|
|
462
|
-
'<div class="policy-detail"><strong>Prohibitions:</strong><ul>',
|
|
463
|
-
);
|
|
464
|
-
prohibArray.forEach((prohib: any) => {
|
|
465
|
-
const action = prohib["odrl:action"];
|
|
466
|
-
const actionStr = action?.["@id"] || action || "unknown";
|
|
467
|
-
parts.push(`<li>Action: ${actionStr}</li>`);
|
|
468
|
-
});
|
|
469
|
-
parts.push("</ul></div>");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Obligations
|
|
474
|
-
const obligations = policy["odrl:obligation"];
|
|
475
|
-
if (obligations) {
|
|
476
|
-
const obligArray = Array.isArray(obligations)
|
|
477
|
-
? obligations
|
|
478
|
-
: [obligations];
|
|
479
|
-
if (obligArray.length > 0) {
|
|
480
|
-
parts.push(
|
|
481
|
-
'<div class="policy-detail"><strong>Obligations:</strong><ul>',
|
|
482
|
-
);
|
|
483
|
-
obligArray.forEach((oblig: any) => {
|
|
484
|
-
const action = oblig["odrl:action"];
|
|
485
|
-
const actionStr = action?.["@id"] || action || "unknown";
|
|
486
|
-
parts.push(`<li>Action: ${actionStr}</li>`);
|
|
487
|
-
});
|
|
488
|
-
parts.push("</ul></div>");
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Target
|
|
493
|
-
if (policy.target) {
|
|
494
|
-
parts.push(
|
|
495
|
-
`<div class="policy-detail"><strong>Target Asset:</strong> ${policy.target}</div>`,
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Assigner
|
|
500
|
-
if (policy.assigner) {
|
|
501
|
-
parts.push(
|
|
502
|
-
`<div class="policy-detail"><strong>Assigner:</strong> ${policy.assigner}</div>`,
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return parts.length > 0 ? parts.join("") : "No policy details available";
|
|
507
|
-
}
|
|
508
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Negotiate access to the dataset
|
|
172
|
+
*/
|
|
509
173
|
async _negotiateAccess() {
|
|
510
174
|
try {
|
|
511
|
-
// Use the DSP store passed as property
|
|
512
175
|
if (!this.dspStore) {
|
|
513
176
|
throw new Error(
|
|
514
177
|
"DSP connector not configured. Please provide participant-connector-uri and participant-api-key attributes.",
|
|
@@ -516,86 +179,29 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
516
179
|
}
|
|
517
180
|
|
|
518
181
|
const dspStore = this.dspStore;
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
console.warn(
|
|
535
|
-
"[tems-modal] obj.policy is an array! Converting to policies array.",
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
// Check if array object has a "target" property
|
|
539
|
-
const target = (rawPolicy as any).target;
|
|
540
|
-
|
|
541
|
-
// Filter out non-policy properties (like "target")
|
|
542
|
-
policies = rawPolicy.filter(
|
|
543
|
-
(item: any) => item && typeof item === "object" && item["@id"],
|
|
544
|
-
);
|
|
545
|
-
|
|
546
|
-
// Add target to each policy if it exists and policy doesn't have one
|
|
547
|
-
if (target) {
|
|
548
|
-
policies = policies.map((p: any) => {
|
|
549
|
-
if (!p.target && !p["odrl:target"]) {
|
|
550
|
-
return { ...p, target, "odrl:target": target };
|
|
551
|
-
}
|
|
552
|
-
return p;
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
rawPolicy = policies.length > 0 ? policies[0] : rawPolicy[0]; // Use first valid policy as default
|
|
557
|
-
}
|
|
558
|
-
// If obj.policy is an object with numeric keys (array-like object)
|
|
559
|
-
else if (rawPolicy && typeof rawPolicy === "object") {
|
|
560
|
-
const keys = Object.keys(rawPolicy);
|
|
561
|
-
const hasNumericKeys = keys.some((k) => /^\d+$/.test(k));
|
|
562
|
-
if (hasNumericKeys) {
|
|
563
|
-
console.warn(
|
|
564
|
-
"[tems-modal] obj.policy has numeric keys! Extracting policies array.",
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
// Check if object has a "target" property
|
|
568
|
-
const target = rawPolicy.target;
|
|
569
|
-
|
|
570
|
-
// Extract policies from numeric keys
|
|
571
|
-
const extractedPolicies = [];
|
|
572
|
-
for (const key in rawPolicy) {
|
|
573
|
-
if (/^\d+$/.test(key)) {
|
|
574
|
-
let policy = rawPolicy[key];
|
|
575
|
-
// Add target if it exists and policy doesn't have one
|
|
576
|
-
if (target && !policy.target && !policy["odrl:target"]) {
|
|
577
|
-
policy = { ...policy, target, "odrl:target": target };
|
|
578
|
-
}
|
|
579
|
-
extractedPolicies.push(policy);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
if (extractedPolicies.length > 0) {
|
|
583
|
-
policies = extractedPolicies;
|
|
584
|
-
rawPolicy = extractedPolicies[0]; // Use first as default
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
182
|
+
const offer = this.object as DSPOffer;
|
|
183
|
+
|
|
184
|
+
// Extract required fields from props and offer
|
|
185
|
+
const counterPartyAddress =
|
|
186
|
+
this.counterPartyAddress || offer._provider?.address || "";
|
|
187
|
+
const counterPartyId =
|
|
188
|
+
offer._provider?.participantId ||
|
|
189
|
+
this.counterPartyId ||
|
|
190
|
+
this.participantId;
|
|
191
|
+
const datasetId = offer["@id"] || offer.id;
|
|
192
|
+
|
|
193
|
+
// Extract and process policies from new format
|
|
194
|
+
const { policies, defaultPolicy } = dspPolicyHelpers.extractPolicies(
|
|
195
|
+
offer["odrl:hasPolicy"],
|
|
196
|
+
);
|
|
588
197
|
|
|
589
198
|
// Check if there are multiple policies available
|
|
590
|
-
|
|
591
199
|
if (
|
|
592
200
|
policies &&
|
|
593
201
|
policies.length > 1 &&
|
|
594
202
|
this.selectedPolicyIndex === undefined
|
|
595
203
|
) {
|
|
596
|
-
// Store policies in state for the modal to access
|
|
597
204
|
this.availablePolicies = policies;
|
|
598
|
-
// Show policy selection UI
|
|
599
205
|
this.showPolicySelection = true;
|
|
600
206
|
this.requestUpdate();
|
|
601
207
|
return;
|
|
@@ -607,18 +213,12 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
607
213
|
? this.availablePolicies[this.selectedPolicyIndex]
|
|
608
214
|
: this.selectedPolicyIndex !== undefined && policies
|
|
609
215
|
? policies[this.selectedPolicyIndex]
|
|
610
|
-
:
|
|
611
|
-
|
|
612
|
-
this.selectedPolicyIndex !== undefined && this.availablePolicies
|
|
613
|
-
? "availablePolicies[index]"
|
|
614
|
-
: this.selectedPolicyIndex !== undefined && policies
|
|
615
|
-
? "policies[index]"
|
|
616
|
-
: "rawPolicy (fallback)";
|
|
216
|
+
: defaultPolicy;
|
|
617
217
|
|
|
618
218
|
// Validate required fields
|
|
619
219
|
if (!counterPartyAddress) {
|
|
620
220
|
throw new Error(
|
|
621
|
-
"No provider endpoint URL (counterPartyAddress)
|
|
221
|
+
"No provider endpoint URL (counterPartyAddress) configured",
|
|
622
222
|
);
|
|
623
223
|
}
|
|
624
224
|
|
|
@@ -630,24 +230,16 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
630
230
|
throw new Error("No policy found for dataset");
|
|
631
231
|
}
|
|
632
232
|
|
|
633
|
-
//
|
|
634
|
-
if (policy
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
console.error(
|
|
639
|
-
"[tems-modal] ERROR: Policy still has numeric keys after processing!",
|
|
640
|
-
policy,
|
|
641
|
-
);
|
|
642
|
-
throw new Error(
|
|
643
|
-
"Invalid policy structure detected. Policy must be a single object, not an array.",
|
|
644
|
-
);
|
|
645
|
-
}
|
|
233
|
+
// Validate policy structure
|
|
234
|
+
if (!dspPolicyHelpers.validatePolicyStructure(policy)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
"Invalid policy structure detected. Policy must be a single object, not an array.",
|
|
237
|
+
);
|
|
646
238
|
}
|
|
647
239
|
|
|
648
240
|
if (!counterPartyId) {
|
|
649
241
|
throw new Error(
|
|
650
|
-
"No participant ID configured. Please provide participant-id attribute
|
|
242
|
+
"No participant ID configured. Please provide participant-id attribute.",
|
|
651
243
|
);
|
|
652
244
|
}
|
|
653
245
|
|
|
@@ -656,15 +248,11 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
656
248
|
this.negotiationError = undefined;
|
|
657
249
|
this.requestUpdate();
|
|
658
250
|
|
|
659
|
-
// The policy already has the target field set by FederatedCatalogueStore
|
|
660
|
-
// and all urn:tems: prefixes have been stripped
|
|
661
|
-
const processedPolicy = policy;
|
|
662
|
-
|
|
663
251
|
// Initiate contract negotiation
|
|
664
252
|
const negotiationId = await dspStore.negotiateContract(
|
|
665
253
|
counterPartyAddress,
|
|
666
254
|
datasetId,
|
|
667
|
-
|
|
255
|
+
policy,
|
|
668
256
|
counterPartyId,
|
|
669
257
|
);
|
|
670
258
|
|
|
@@ -673,7 +261,7 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
673
261
|
this.requestUpdate();
|
|
674
262
|
|
|
675
263
|
// Save initial contract state to catalog
|
|
676
|
-
|
|
264
|
+
dspAgreementStorage.saveInitialContractState(offer, negotiationId);
|
|
677
265
|
|
|
678
266
|
// Poll for negotiation status
|
|
679
267
|
await this._pollNegotiationStatus(dspStore, negotiationId);
|
|
@@ -684,7 +272,9 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
684
272
|
|
|
685
273
|
// Update contract state if negotiation was initiated
|
|
686
274
|
if (this.negotiationId) {
|
|
687
|
-
this.
|
|
275
|
+
const offer = this.object as DSPOffer;
|
|
276
|
+
dspAgreementStorage.updateContractState(
|
|
277
|
+
offer,
|
|
688
278
|
this.negotiationId,
|
|
689
279
|
"FAILED",
|
|
690
280
|
this.negotiationError,
|
|
@@ -695,6 +285,9 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
695
285
|
}
|
|
696
286
|
}
|
|
697
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Poll for negotiation status
|
|
290
|
+
*/
|
|
698
291
|
async _pollNegotiationStatus(dspStore: any, negotiationId: string) {
|
|
699
292
|
const maxAttempts = 8;
|
|
700
293
|
const pollInterval = 5000;
|
|
@@ -709,13 +302,13 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
709
302
|
this.requestUpdate();
|
|
710
303
|
|
|
711
304
|
if (status.state === "FINALIZED" || status.state === "AGREED") {
|
|
712
|
-
|
|
713
|
-
const obj = this.object as any;
|
|
305
|
+
const offer = this.object as DSPOffer;
|
|
714
306
|
const providerId =
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
307
|
+
offer._provider?.participantId ||
|
|
308
|
+
this.counterPartyId ||
|
|
309
|
+
this.participantId ||
|
|
718
310
|
"";
|
|
311
|
+
|
|
719
312
|
try {
|
|
720
313
|
const agreement = await dspStore.getContractAgreement(
|
|
721
314
|
negotiationId,
|
|
@@ -729,14 +322,23 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
729
322
|
this.contractId = status.contractAgreementId || negotiationId;
|
|
730
323
|
}
|
|
731
324
|
|
|
732
|
-
// Save agreement info to localStorage
|
|
325
|
+
// Save agreement info to localStorage
|
|
733
326
|
if (this.contractId && negotiationId) {
|
|
734
|
-
|
|
327
|
+
dspAgreementStorage.saveAgreementInfo(
|
|
328
|
+
offer,
|
|
329
|
+
this.contractId,
|
|
330
|
+
negotiationId,
|
|
331
|
+
Date.now(),
|
|
332
|
+
);
|
|
735
333
|
}
|
|
736
334
|
|
|
737
|
-
// Save contract to DSP Contract Storage
|
|
335
|
+
// Save contract to DSP Contract Storage
|
|
738
336
|
if (this.contractId) {
|
|
739
|
-
|
|
337
|
+
dspAgreementStorage.saveToContractCatalog(
|
|
338
|
+
offer,
|
|
339
|
+
this.contractId,
|
|
340
|
+
negotiationId,
|
|
341
|
+
);
|
|
740
342
|
}
|
|
741
343
|
|
|
742
344
|
this.negotiationStatus = "granted";
|
|
@@ -748,7 +350,9 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
748
350
|
this.negotiationStatus = "failed";
|
|
749
351
|
this.negotiationError =
|
|
750
352
|
status.errorDetail || "Negotiation terminated";
|
|
751
|
-
this.
|
|
353
|
+
const offer = this.object as DSPOffer;
|
|
354
|
+
dspAgreementStorage.updateContractState(
|
|
355
|
+
offer,
|
|
752
356
|
negotiationId,
|
|
753
357
|
"TERMINATED",
|
|
754
358
|
this.negotiationError,
|
|
@@ -771,660 +375,29 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
771
375
|
this.negotiationStatus = "failed";
|
|
772
376
|
this.negotiationError =
|
|
773
377
|
"Negotiation timeout after 40 seconds - may still be processing on provider side";
|
|
774
|
-
this.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (field) {
|
|
781
|
-
return html`<tems-badge class="badges" type="success" size="sm"
|
|
782
|
-
><icon-ci-check></icon-ci-check
|
|
783
|
-
></tems-badge>`;
|
|
784
|
-
}
|
|
785
|
-
return html`<tems-badge class="badges" type="error" size="sm"
|
|
786
|
-
><icon-material-symbols-close-rounded></icon-material-symbols-close-rounded
|
|
787
|
-
></tems-badge>`;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
_renderDivision(type: string, label: string): TemplateResult {
|
|
791
|
-
return html`<tems-division type="${type}"
|
|
792
|
-
><div>${unsafeHTML(String(label))}</div></tems-division
|
|
793
|
-
>`;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
_renderBadge(type?: string, label?: string): TemplateResultOrSymbol {
|
|
797
|
-
if (!label) return nothing;
|
|
798
|
-
return html`<tems-badge
|
|
799
|
-
type=${type}
|
|
800
|
-
label=${label}
|
|
801
|
-
size="sm"
|
|
802
|
-
></tems-badge>`;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
_renderButton(
|
|
806
|
-
iconLeft?: TemplateResult,
|
|
807
|
-
size?: string,
|
|
808
|
-
label?: string,
|
|
809
|
-
type?: string,
|
|
810
|
-
url?: string,
|
|
811
|
-
iconRight?: TemplateResult,
|
|
812
|
-
disabled?: boolean,
|
|
813
|
-
): TemplateResultOrSymbol {
|
|
814
|
-
if (!label) return nothing;
|
|
815
|
-
return html`<tems-button
|
|
816
|
-
.iconLeft=${ifDefined(iconLeft)}
|
|
817
|
-
.iconRight=${ifDefined(iconRight)}
|
|
818
|
-
size=${ifDefined(size)}
|
|
819
|
-
label=${ifDefined(label)}
|
|
820
|
-
type=${ifDefined(type)}
|
|
821
|
-
url=${ifDefined(url)}
|
|
822
|
-
disabled=${disabled || nothing}
|
|
823
|
-
></tems-button>`;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
_renderIframe(url: string): TemplateResult {
|
|
827
|
-
return html`<iframe src="${url}"></iframe>`;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
_renderKindBadgeComponent(
|
|
831
|
-
object: rdf.DataOffer | undefined = undefined,
|
|
832
|
-
): TemplateResultOrSymbol {
|
|
833
|
-
const data_offer = object || this.object;
|
|
834
|
-
if (!data_offer.offers || data_offer.offers.length === 0) return nothing;
|
|
835
|
-
|
|
836
|
-
return html`<div class="badges">
|
|
837
|
-
${data_offer.offers.map((offer: rdf.Offer) =>
|
|
838
|
-
this._renderBadge("information", offerKindHandler(offer.kind)),
|
|
839
|
-
)}
|
|
840
|
-
</div> `;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
_renderCategoryBadgeComponent(): TemplateResultOrSymbol {
|
|
844
|
-
const badgeType: string = this.isType(rdf.RDFTYPE_DATAOFFER)
|
|
845
|
-
? "default"
|
|
846
|
-
: "information";
|
|
847
|
-
if (!this.object.categories || this.object.categories.length === 0)
|
|
848
|
-
return nothing;
|
|
849
|
-
|
|
850
|
-
return html`<div class="badges">
|
|
851
|
-
${this.object.categories.length === 0
|
|
852
|
-
? this._renderBadge(badgeType, msg("No category"))
|
|
853
|
-
: this.object.categories.map((category: rdf.NamedResource) =>
|
|
854
|
-
this._renderBadge(badgeType, category.name || ""),
|
|
855
|
-
)}
|
|
856
|
-
</div>`;
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
_renderDescription(): TemplateResult {
|
|
860
|
-
return this._renderDivision("body-m", this.object.description);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
_renderTitleValueDivision(
|
|
864
|
-
title: string,
|
|
865
|
-
value: string | undefined,
|
|
866
|
-
): TemplateResultOrSymbol {
|
|
867
|
-
if (!value) return nothing;
|
|
868
|
-
return html`${this._renderDivision("h4", title)}
|
|
869
|
-
${this._renderDivision("body-m", value)}`;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
_renderLicences(): TemplateResult {
|
|
873
|
-
return html`<div>
|
|
874
|
-
${this.object.licences.length !== 0
|
|
875
|
-
? html`${this._renderDivision("h4", msg("Licences"))}
|
|
876
|
-
${this.object.licences.map((licence: rdf.Licence) => {
|
|
877
|
-
if (!licence.name) return nothing;
|
|
878
|
-
return html`<tems-division type="body-m">
|
|
879
|
-
${licence.url
|
|
880
|
-
? html`<a href=${licence.url} target="_blank"
|
|
881
|
-
>${licence.name || msg("See more")}
|
|
882
|
-
<icon-mingcute-arrow-right-up-line></icon-mingcute-arrow-right-up-line
|
|
883
|
-
></a>`
|
|
884
|
-
: html`${licence.name}`}</tems-division
|
|
885
|
-
> `;
|
|
886
|
-
})}`
|
|
887
|
-
: html`${this._renderDivision("h4", msg("Licences"))}
|
|
888
|
-
<tems-division type="body-m">-</tems-division>`}
|
|
889
|
-
</div>`;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
_renderBgImg(imgSrc: string, className: string) {
|
|
893
|
-
if (!imgSrc) {
|
|
894
|
-
return nothing;
|
|
895
|
-
}
|
|
896
|
-
return html`<div
|
|
897
|
-
class="${className}"
|
|
898
|
-
style="background-image: url('${imgSrc}')"
|
|
899
|
-
></div>`;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
_renderImageSingle(): TemplateResultOrSymbol {
|
|
903
|
-
if (!this.object.image && !this.object.images) {
|
|
904
|
-
return nothing;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const images = [];
|
|
908
|
-
|
|
909
|
-
if (this.object.image) {
|
|
910
|
-
images.push(this.object.image);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
if (this.object.images) {
|
|
914
|
-
images.push(...this.object.images);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
return html`<div class="default-image-grid">
|
|
918
|
-
${images.map((image: rdf.Image) => {
|
|
919
|
-
if (image.iframe && image.url) {
|
|
920
|
-
return html`${this._renderIframe(image.url)}`;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
return html`<img
|
|
924
|
-
class="default-img"
|
|
925
|
-
src=${image.url}
|
|
926
|
-
alt=${ifDefined(image.name)}
|
|
927
|
-
></div>`;
|
|
928
|
-
})}
|
|
929
|
-
</div>`;
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
_renderImageArray(): TemplateResultOrSymbol {
|
|
933
|
-
const iframe = this.object.images.filter(
|
|
934
|
-
(image: rdf.Image) => image.iframe && image.url,
|
|
378
|
+
const offer = this.object as DSPOffer;
|
|
379
|
+
dspAgreementStorage.updateContractState(
|
|
380
|
+
offer,
|
|
381
|
+
negotiationId,
|
|
382
|
+
"FAILED",
|
|
383
|
+
this.negotiationError,
|
|
935
384
|
);
|
|
936
|
-
|
|
937
|
-
return html`${this._renderIframe(iframe[0].url)}`;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
const filteredImages = this.object.images.filter(
|
|
941
|
-
(image: rdf.Image) => !image.iframe && image.url,
|
|
942
|
-
);
|
|
943
|
-
|
|
944
|
-
const imgCount = filteredImages.length;
|
|
945
|
-
|
|
946
|
-
switch (imgCount) {
|
|
947
|
-
case 0:
|
|
948
|
-
return nothing;
|
|
949
|
-
case 1:
|
|
950
|
-
return html`<div
|
|
951
|
-
class="main-img"
|
|
952
|
-
style="background-image: url(${filteredImages[0].url})"
|
|
953
|
-
></div>`;
|
|
954
|
-
case 2:
|
|
955
|
-
return html`<div class="main-img case-2">
|
|
956
|
-
${this._renderBgImg(filteredImages[0].url, "full-width")}
|
|
957
|
-
${this._renderBgImg(filteredImages[1].url, "full-width")}
|
|
958
|
-
</div>`;
|
|
959
|
-
case 3:
|
|
960
|
-
return html`<div class="main-img case-3">
|
|
961
|
-
${this._renderBgImg(filteredImages[0].url, "full-width")}
|
|
962
|
-
<div class="img-inner-row">
|
|
963
|
-
<div class="double-image">
|
|
964
|
-
${this._renderBgImg(filteredImages[1].url, "")}
|
|
965
|
-
${this._renderBgImg(filteredImages[2].url, "")}
|
|
966
|
-
</div>
|
|
967
|
-
</div>
|
|
968
|
-
</div>`;
|
|
969
|
-
default:
|
|
970
|
-
return html`<div class="main-img case-4">
|
|
971
|
-
${this._renderBgImg(filteredImages[0].url, "full-width")}
|
|
972
|
-
<div class="img-inner-row">
|
|
973
|
-
<div class="double-image">
|
|
974
|
-
${this._renderBgImg(filteredImages[1].url, "")}
|
|
975
|
-
${this._renderBgImg(filteredImages[2].url, "")}
|
|
976
|
-
</div>
|
|
977
|
-
${this._renderBgImg(filteredImages[3].url, "last-img")}
|
|
978
|
-
</div>
|
|
979
|
-
</div>`;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
_renderAboutProvider(): TemplateResultOrSymbol {
|
|
984
|
-
if (this.object.providers.length === 0) return nothing;
|
|
985
|
-
|
|
986
|
-
return html`${this._renderDivision("h4", msg("Providers"))}
|
|
987
|
-
${this.object.providers.map(
|
|
988
|
-
(provider: rdf.Provider) =>
|
|
989
|
-
html`<div>
|
|
990
|
-
<img
|
|
991
|
-
src="${provider.image?.url}"
|
|
992
|
-
alt=${provider.name}
|
|
993
|
-
class="default-img"
|
|
994
|
-
/>
|
|
995
|
-
</div>
|
|
996
|
-
${this._renderTitleValueDivision(
|
|
997
|
-
msg("About the providers"),
|
|
998
|
-
provider.description || msg("No description provided"),
|
|
999
|
-
)}`,
|
|
1000
|
-
)}`;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
_renderCompatibleServices(): TemplateResultOrSymbol {
|
|
1004
|
-
if (this.object.services.length === 0) return nothing;
|
|
1005
|
-
|
|
1006
|
-
return html`${this._renderDivision(
|
|
1007
|
-
"h4",
|
|
1008
|
-
this.isType(rdf.RDFTYPE_PROVIDER)
|
|
1009
|
-
? msg("Available Services")
|
|
1010
|
-
: msg("Compatible Services"),
|
|
1011
|
-
)}
|
|
1012
|
-
${this.object.services.map(
|
|
1013
|
-
(service: rdf.Service) =>
|
|
1014
|
-
html`<ds4go-card-dataspace-catalog
|
|
1015
|
-
type="vertical"
|
|
1016
|
-
header=${ifDefined(service.name)}
|
|
1017
|
-
background-img=${ifDefined(service.images?.[0]?.url)}
|
|
1018
|
-
full-size=""
|
|
1019
|
-
content=${ifDefined(service.description)}
|
|
1020
|
-
></ds4go-card-dataspace-catalog>`,
|
|
1021
|
-
)}`;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
_renderCompatibleDataOffers(): TemplateResultOrSymbol {
|
|
1025
|
-
if (this.object.data_offers.length === 0) return nothing;
|
|
1026
|
-
|
|
1027
|
-
return html`${this._renderDivision("h4", msg("Available Data Offers"))}
|
|
1028
|
-
${this.object.data_offers.map(
|
|
1029
|
-
(data_offer: rdf.DataOffer) =>
|
|
1030
|
-
html`<ds4go-card-dataspace-catalog
|
|
1031
|
-
type="vertical"
|
|
1032
|
-
header=${ifDefined(data_offer.name)}
|
|
1033
|
-
background-img=${ifDefined(data_offer.image?.url)}
|
|
1034
|
-
full-size=""
|
|
1035
|
-
content=${ifDefined(data_offer.description)}
|
|
1036
|
-
.tags=${[{ name: data_offer.name, type: "information" }]}
|
|
1037
|
-
></ds4go-card-dataspace-catalog>`,
|
|
1038
|
-
)}`;
|
|
1039
|
-
}
|
|
1040
|
-
// tags=${this._renderKindBadgeComponent(data_offer)}
|
|
1041
|
-
|
|
1042
|
-
_renderOffers(): TemplateResult {
|
|
1043
|
-
return html`${this._renderDivision("h4", msg("Offers"))}
|
|
1044
|
-
${this.object.offers.map((offer: rdf.Offer) => {
|
|
1045
|
-
const msgSubscribe: string = offerKindActionHandler(offer.kind);
|
|
1046
|
-
|
|
1047
|
-
if (!msgSubscribe) return nothing;
|
|
1048
|
-
return html`<ds4go-card-dataspace-catalog
|
|
1049
|
-
type="vertical"
|
|
1050
|
-
header=${ifDefined(offer.name)}
|
|
1051
|
-
content=${ifDefined(offer.description)}
|
|
1052
|
-
><div>
|
|
1053
|
-
${this._renderButton(
|
|
1054
|
-
undefined,
|
|
1055
|
-
"sm",
|
|
1056
|
-
msgSubscribe,
|
|
1057
|
-
"primary",
|
|
1058
|
-
undefined,
|
|
1059
|
-
undefined,
|
|
1060
|
-
true,
|
|
1061
|
-
)}
|
|
1062
|
-
</div></ds4go-card-dataspace-catalog
|
|
1063
|
-
>`;
|
|
1064
|
-
})}`;
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
_renderColumns(...columns: TemplateResultOrSymbol[]): TemplateResultOrSymbol {
|
|
1068
|
-
const filteredColumns = columns.filter((col) => col !== nothing);
|
|
1069
|
-
|
|
1070
|
-
if (filteredColumns.length === 1) {
|
|
1071
|
-
return columns[0];
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
return html`<div class="multiple-columns flex flex-row flex-1">
|
|
1075
|
-
${filteredColumns.map(
|
|
1076
|
-
(col) => html`<div class="half flex flex-column wrap">${col}</div>`,
|
|
1077
|
-
)}
|
|
1078
|
-
</div>`;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
_renderServiceSpecificModal(): TemplateResultOrSymbol {
|
|
1083
|
-
return html` ${this._renderColumns(
|
|
1084
|
-
html`${this.renderTemplateWhenWith(["long_description"], () =>
|
|
1085
|
-
this._renderTitleValueDivision(
|
|
1086
|
-
msg("Features & Functionalities"),
|
|
1087
|
-
this.object.long_description,
|
|
1088
|
-
),
|
|
1089
|
-
)}${this.renderTemplateWhenWith(
|
|
1090
|
-
[["is_in_app", "is_external", "is_api"]],
|
|
1091
|
-
() =>
|
|
1092
|
-
html`${this._renderDivision("h4", msg("Installation Possible"))}
|
|
1093
|
-
<div class="badges">
|
|
1094
|
-
${this.renderTemplateWhenWith(
|
|
1095
|
-
["is_in_app"],
|
|
1096
|
-
() =>
|
|
1097
|
-
html`${this._renderBadge("success", msg("In-App"))}</div>`,
|
|
1098
|
-
)}
|
|
1099
|
-
${this.renderTemplateWhenWith(
|
|
1100
|
-
["is_external"],
|
|
1101
|
-
() =>
|
|
1102
|
-
html`${this._renderBadge("success", msg("External"))}</div>`,
|
|
1103
|
-
)}
|
|
1104
|
-
${this.renderTemplateWhenWith(
|
|
1105
|
-
["is_api"],
|
|
1106
|
-
() => html`${this._renderBadge("success", msg("API"))}</div>`,
|
|
1107
|
-
)}
|
|
1108
|
-
</div>`,
|
|
1109
|
-
)}${this.renderTemplateWhenWith(
|
|
1110
|
-
["developper", "developper.url"],
|
|
1111
|
-
() =>
|
|
1112
|
-
html`${this._renderDivision("h4", msg("Developper"))}
|
|
1113
|
-
<img
|
|
1114
|
-
src="${this.object.developper.url}"
|
|
1115
|
-
alt=${this.object.developper.name}
|
|
1116
|
-
class="default-img"
|
|
1117
|
-
/>`,
|
|
1118
|
-
)}${this.renderTemplateWhenWith(["release_date"], () =>
|
|
1119
|
-
this._renderTitleValueDivision(
|
|
1120
|
-
msg("Release Date"),
|
|
1121
|
-
formatDate(this.object.release_date),
|
|
1122
|
-
),
|
|
1123
|
-
)}${this.renderTemplateWhenWith(["last_update"], () =>
|
|
1124
|
-
this._renderTitleValueDivision(
|
|
1125
|
-
msg("Last Update"),
|
|
1126
|
-
formatDate(this.object.last_update),
|
|
1127
|
-
),
|
|
1128
|
-
)}${this.renderTemplateWhenWith(["documentation_url"], () =>
|
|
1129
|
-
this._renderButton(
|
|
1130
|
-
undefined,
|
|
1131
|
-
"sm",
|
|
1132
|
-
"Read the full documentation",
|
|
1133
|
-
"outline-gray",
|
|
1134
|
-
this.object.documentation_url,
|
|
1135
|
-
),
|
|
1136
|
-
)}
|
|
1137
|
-
${this._renderPolicyDescription()} ${this._renderAgreementInfo()}`,
|
|
1138
|
-
)}`;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
_renderPolicyDescription(): TemplateResultOrSymbol {
|
|
1142
|
-
const obj = this.object as any;
|
|
1143
|
-
let policy = obj.policy;
|
|
1144
|
-
let policies = obj.policies;
|
|
1145
|
-
|
|
1146
|
-
// DEFENSIVE: Handle case where obj.policy might be an array
|
|
1147
|
-
if (Array.isArray(policy)) {
|
|
1148
|
-
// Extract valid policies from array
|
|
1149
|
-
const extractedPolicies = policy.filter(
|
|
1150
|
-
(item: any) => item && typeof item === "object" && item["@id"],
|
|
1151
|
-
);
|
|
1152
|
-
if (extractedPolicies.length > 0) {
|
|
1153
|
-
policies = extractedPolicies;
|
|
1154
|
-
policy = extractedPolicies[0];
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
// DEFENSIVE: Handle case where obj.policy has numeric keys
|
|
1158
|
-
else if (policy && typeof policy === "object") {
|
|
1159
|
-
const keys = Object.keys(policy);
|
|
1160
|
-
const hasNumericKeys = keys.some((k) => /^\d+$/.test(k));
|
|
1161
|
-
if (hasNumericKeys) {
|
|
1162
|
-
const extractedPolicies = [];
|
|
1163
|
-
for (const key in policy) {
|
|
1164
|
-
if (/^\d+$/.test(key)) {
|
|
1165
|
-
extractedPolicies.push(policy[key]);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
if (extractedPolicies.length > 0) {
|
|
1169
|
-
policies = extractedPolicies;
|
|
1170
|
-
policy = extractedPolicies[0];
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// Check if we have multiple policies
|
|
1176
|
-
const hasMultiplePolicies =
|
|
1177
|
-
policies && Array.isArray(policies) && policies.length > 1;
|
|
1178
|
-
|
|
1179
|
-
// Only show if there's a policy
|
|
1180
|
-
if (!policy && (!policies || policies.length === 0)) {
|
|
1181
|
-
return nothing;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
return html`
|
|
1185
|
-
<div
|
|
1186
|
-
style="margin-top: 24px; padding: 16px; background: #f0f7ff; border-radius: 8px; border: 1px solid #d0e7ff;"
|
|
1187
|
-
>
|
|
1188
|
-
${hasMultiplePolicies
|
|
1189
|
-
? html`
|
|
1190
|
-
${this._renderDivision("h4", msg("Access Policies"))}
|
|
1191
|
-
<div
|
|
1192
|
-
style="margin-bottom: 12px; color: #0066cc; font-size: 0.9em;"
|
|
1193
|
-
>
|
|
1194
|
-
${msg("Multiple contract policies available for this asset")}
|
|
1195
|
-
(${policies.length})
|
|
1196
|
-
</div>
|
|
1197
|
-
${policies.map(
|
|
1198
|
-
(p: any, index: number) => html`
|
|
1199
|
-
<div
|
|
1200
|
-
style="margin-bottom: 16px; padding: 12px; background: white; border-radius: 6px; border-left: 4px solid #0066cc;"
|
|
1201
|
-
>
|
|
1202
|
-
<div
|
|
1203
|
-
style="font-weight: 600; margin-bottom: 8px; color: #333;"
|
|
1204
|
-
>
|
|
1205
|
-
${msg("Policy")} ${index + 1}
|
|
1206
|
-
${p["@id"]
|
|
1207
|
-
? html`
|
|
1208
|
-
<span
|
|
1209
|
-
style="font-weight: normal; font-size: 0.85em; color: #666; display: block; margin-top: 4px; word-break: break-all; font-family: monospace;"
|
|
1210
|
-
>
|
|
1211
|
-
${p["@id"]}
|
|
1212
|
-
</span>
|
|
1213
|
-
`
|
|
1214
|
-
: nothing}
|
|
1215
|
-
</div>
|
|
1216
|
-
<odrl-policy-viewer .policy=${p}></odrl-policy-viewer>
|
|
1217
|
-
</div>
|
|
1218
|
-
`,
|
|
1219
|
-
)}
|
|
1220
|
-
`
|
|
1221
|
-
: html`
|
|
1222
|
-
${this._renderDivision("h4", msg("Access Policy"))}
|
|
1223
|
-
<odrl-policy-viewer .policy=${policy}></odrl-policy-viewer>
|
|
1224
|
-
`}
|
|
1225
|
-
</div>
|
|
1226
|
-
`;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
_renderAgreementInfo(): TemplateResultOrSymbol {
|
|
1230
|
-
// Show agreement info after successful negotiation, regardless of API Gateway config
|
|
1231
|
-
if (this.negotiationStatus !== "granted" || !this.contractId) {
|
|
1232
|
-
return nothing;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
const storedInfo = this._loadAgreementInfo();
|
|
1236
|
-
const agreementDate = storedInfo?.timestamp
|
|
1237
|
-
? new Date(storedInfo.timestamp).toLocaleString()
|
|
1238
|
-
: null;
|
|
1239
|
-
|
|
1240
|
-
// Get endpoint URL from asset
|
|
1241
|
-
const obj = this.object as any;
|
|
1242
|
-
const endpointUrl =
|
|
1243
|
-
obj?.endpointUrl ||
|
|
1244
|
-
obj?.["dcat:endpointURL"] ||
|
|
1245
|
-
obj?.distribution?.endpointUrl;
|
|
1246
|
-
|
|
1247
|
-
return html`
|
|
1248
|
-
<div
|
|
1249
|
-
style="margin-top: 24px; padding: 16px; background: #e8f5e9; border-radius: 8px;"
|
|
1250
|
-
>
|
|
1251
|
-
${this._renderDivision("h4", msg("Contract Agreement"))}
|
|
1252
|
-
|
|
1253
|
-
<div style="font-size: 0.9em; margin-top: 12px;">
|
|
1254
|
-
<div style="margin-bottom: 8px;">
|
|
1255
|
-
<strong>✅ ${msg("Agreement ID:")}</strong>
|
|
1256
|
-
<div
|
|
1257
|
-
style="font-family: monospace; background: white; padding: 8px; border-radius: 4px; margin-top: 4px; word-break: break-all;"
|
|
1258
|
-
>
|
|
1259
|
-
${this.contractId}
|
|
1260
|
-
</div>
|
|
1261
|
-
</div>
|
|
1262
|
-
|
|
1263
|
-
${endpointUrl
|
|
1264
|
-
? html`
|
|
1265
|
-
<div style="margin-bottom: 8px;">
|
|
1266
|
-
<strong>🔗 ${msg("Endpoint URL:")}</strong>
|
|
1267
|
-
<div
|
|
1268
|
-
style="font-family: monospace; background: white; padding: 8px; border-radius: 4px; margin-top: 4px; word-break: break-all;"
|
|
1269
|
-
>
|
|
1270
|
-
${endpointUrl}
|
|
1271
|
-
</div>
|
|
1272
|
-
</div>
|
|
1273
|
-
`
|
|
1274
|
-
: nothing}
|
|
1275
|
-
${agreementDate
|
|
1276
|
-
? html`
|
|
1277
|
-
<div style="opacity: 0.8; font-size: 0.85em;">
|
|
1278
|
-
<strong>${msg("Agreed on:")}</strong> ${agreementDate}
|
|
1279
|
-
</div>
|
|
1280
|
-
`
|
|
1281
|
-
: nothing}
|
|
1282
|
-
</div>
|
|
1283
|
-
|
|
1284
|
-
<div
|
|
1285
|
-
style="margin-top: 12px; padding: 12px; background: rgba(0,0,0,0.05); border-radius: 4px; font-size: 0.85em;"
|
|
1286
|
-
>
|
|
1287
|
-
<div style="margin-bottom: 4px;">
|
|
1288
|
-
<strong>ℹ️ ${msg("Note:")}</strong>
|
|
1289
|
-
</div>
|
|
1290
|
-
<div>
|
|
1291
|
-
${msg(
|
|
1292
|
-
"You can now use this agreement ID to access the service through the provider's API or data gateway.",
|
|
1293
|
-
)}
|
|
1294
|
-
</div>
|
|
1295
|
-
</div>
|
|
1296
|
-
|
|
1297
|
-
${storedInfo
|
|
1298
|
-
? html`
|
|
1299
|
-
<div
|
|
1300
|
-
style="margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(0,0,0,0.1);"
|
|
1301
|
-
>
|
|
1302
|
-
<button
|
|
1303
|
-
@click=${this._renewContract}
|
|
1304
|
-
style="font-size: 0.85em; color: #666; background: none; border: none; cursor: pointer; text-decoration: underline; padding: 0;"
|
|
1305
|
-
>
|
|
1306
|
-
🔄 ${msg("Renegotiate contract")}
|
|
1307
|
-
</button>
|
|
1308
|
-
</div>
|
|
1309
|
-
`
|
|
1310
|
-
: nothing}
|
|
1311
|
-
</div>
|
|
1312
|
-
`;
|
|
385
|
+
this.requestUpdate();
|
|
1313
386
|
}
|
|
1314
387
|
|
|
1315
|
-
|
|
1316
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Handle close modal
|
|
390
|
+
*/
|
|
391
|
+
private _closeModal() {
|
|
1317
392
|
this.dispatchEvent(new CustomEvent("close"));
|
|
1318
393
|
}
|
|
1319
394
|
|
|
1320
|
-
_renderPolicySelection(): TemplateResult {
|
|
1321
|
-
const policies = this.availablePolicies || [];
|
|
1322
|
-
|
|
1323
|
-
return html`
|
|
1324
|
-
<div class="policy-selection-modal">
|
|
1325
|
-
<div class="policy-selection-header">
|
|
1326
|
-
<h3>${msg("Select a Policy")}</h3>
|
|
1327
|
-
<p>
|
|
1328
|
-
${msg(
|
|
1329
|
-
"Multiple policies are available for this dataset. Please select one to proceed with the negotiation.",
|
|
1330
|
-
)}
|
|
1331
|
-
</p>
|
|
1332
|
-
</div>
|
|
1333
|
-
<div class="policy-selection-list">
|
|
1334
|
-
${policies.map(
|
|
1335
|
-
(policy: any, index: number) => html`
|
|
1336
|
-
<div
|
|
1337
|
-
class="policy-option"
|
|
1338
|
-
@click=${() => this._selectPolicy(index)}
|
|
1339
|
-
>
|
|
1340
|
-
<div class="policy-option-header">
|
|
1341
|
-
<strong>${msg("Policy")} ${index + 1}</strong>
|
|
1342
|
-
${policy["@id"]
|
|
1343
|
-
? html`<code>${policy["@id"]}</code>`
|
|
1344
|
-
: nothing}
|
|
1345
|
-
</div>
|
|
1346
|
-
<div class="policy-option-details">
|
|
1347
|
-
${unsafeHTML(this._formatPolicyDetails(policy))}
|
|
1348
|
-
</div>
|
|
1349
|
-
</div>
|
|
1350
|
-
`,
|
|
1351
|
-
)}
|
|
1352
|
-
</div>
|
|
1353
|
-
<div class="policy-selection-actions">
|
|
1354
|
-
<tems-button
|
|
1355
|
-
type="outline-gray"
|
|
1356
|
-
@click=${this._cancelPolicySelection}
|
|
1357
|
-
>
|
|
1358
|
-
${msg("Cancel")}
|
|
1359
|
-
</tems-button>
|
|
1360
|
-
</div>
|
|
1361
|
-
</div>
|
|
1362
|
-
`;
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
_renderNegotiationButton(): TemplateResultOrSymbol {
|
|
1366
|
-
// Check if DSP connector is configured (now passed as property)
|
|
1367
|
-
|
|
1368
|
-
const hasDspConnector =
|
|
1369
|
-
this.dspStore !== undefined && this.dspStore !== null;
|
|
1370
|
-
|
|
1371
|
-
if (!hasDspConnector) {
|
|
1372
|
-
return html`<tems-button disabled=""
|
|
1373
|
-
>${msg("Activate this service")}</tems-button
|
|
1374
|
-
>`;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// Show policy selection if needed
|
|
1378
|
-
if (this.showPolicySelection) {
|
|
1379
|
-
return this._renderPolicySelection();
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
switch (this.negotiationStatus) {
|
|
1383
|
-
case "idle":
|
|
1384
|
-
return html`<tems-button @click=${this._negotiateAccess}
|
|
1385
|
-
>${msg("Negotiate access")}</tems-button
|
|
1386
|
-
>`;
|
|
1387
|
-
|
|
1388
|
-
case "negotiating":
|
|
1389
|
-
return html`<tems-button disabled="">
|
|
1390
|
-
${msg("Negotiating...")}
|
|
1391
|
-
</tems-button>`;
|
|
1392
|
-
|
|
1393
|
-
case "pending":
|
|
1394
|
-
return html`<tems-button disabled="">
|
|
1395
|
-
${this.currentState || msg("Pending")}
|
|
1396
|
-
${this.attempt ? `(${this.attempt}/${this.maxAttempts})` : ""}
|
|
1397
|
-
</tems-button>`;
|
|
1398
|
-
|
|
1399
|
-
case "granted": {
|
|
1400
|
-
return html`
|
|
1401
|
-
<tems-button disabled="" type="success">
|
|
1402
|
-
✅ ${msg("Access Granted")}
|
|
1403
|
-
</tems-button>
|
|
1404
|
-
`;
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
case "failed":
|
|
1408
|
-
return html`
|
|
1409
|
-
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
1410
|
-
<tems-button disabled="" type="error">
|
|
1411
|
-
❌ ${msg("Failed")}:
|
|
1412
|
-
${this.negotiationError || msg("Unknown error")}
|
|
1413
|
-
</tems-button>
|
|
1414
|
-
<tems-button @click=${this._negotiateAccess} type="outline-gray">
|
|
1415
|
-
${msg("Retry")}
|
|
1416
|
-
</tems-button>
|
|
1417
|
-
</div>
|
|
1418
|
-
`;
|
|
1419
|
-
|
|
1420
|
-
default:
|
|
1421
|
-
return html`<tems-button disabled=""
|
|
1422
|
-
>${msg("Activate this service")}</tems-button
|
|
1423
|
-
>`;
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
395
|
render() {
|
|
396
|
+
const offer = this.object as DSPOffer;
|
|
397
|
+
const { policies, defaultPolicy } = dspPolicyHelpers.extractPolicies(
|
|
398
|
+
offer["odrl:hasPolicy"],
|
|
399
|
+
);
|
|
400
|
+
|
|
1428
401
|
return html`<div class="modal">
|
|
1429
402
|
<div class="topbar">
|
|
1430
403
|
<tems-button
|
|
@@ -1432,18 +405,51 @@ export class Ds4goCatalogModal extends TemsObjectHandler {
|
|
|
1432
405
|
type="outline-gray"
|
|
1433
406
|
.iconLeft=${html`<icon-material-symbols-close-rounded></icon-material-symbols-close-rounded>`}
|
|
1434
407
|
></tems-button>
|
|
1435
|
-
|
|
1436
|
-
|
|
408
|
+
${this.showPolicySelection
|
|
409
|
+
? nothing
|
|
410
|
+
: html`<catalog-modal-negotiation-button
|
|
411
|
+
.dspStore=${this.dspStore}
|
|
412
|
+
.showPolicySelection=${this.showPolicySelection}
|
|
413
|
+
.negotiationStatus=${this.negotiationStatus}
|
|
414
|
+
.currentState=${this.currentState}
|
|
415
|
+
.attempt=${this.attempt}
|
|
416
|
+
.maxAttempts=${this.maxAttempts}
|
|
417
|
+
.negotiationError=${this.negotiationError}
|
|
418
|
+
@negotiate=${this._negotiateAccess}
|
|
419
|
+
@retry=${this._negotiateAccess}
|
|
420
|
+
></catalog-modal-negotiation-button>`}
|
|
1437
421
|
</div>
|
|
1438
422
|
<div class="modal-content-wrapper">
|
|
1439
423
|
<div class="modal-box">
|
|
1440
424
|
<div class="modal-content">
|
|
1441
|
-
${this.
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
425
|
+
${this.showPolicySelection
|
|
426
|
+
? html`<catalog-modal-policy-selection
|
|
427
|
+
.policies=${policies}
|
|
428
|
+
@policy-selected=${this._handlePolicySelected}
|
|
429
|
+
@policy-cancel=${this._handlePolicyCancel}
|
|
430
|
+
></catalog-modal-policy-selection>`
|
|
431
|
+
: html`<tems-division type="h2"
|
|
432
|
+
><div>${offer.name || ""}</div></tems-division
|
|
433
|
+
>
|
|
434
|
+
${offer.description
|
|
435
|
+
? html`<tems-division type="body-m"
|
|
436
|
+
><div>${offer.description}</div></tems-division
|
|
437
|
+
>`
|
|
438
|
+
: nothing}
|
|
439
|
+
<div class="multiple-columns flex flex-row flex-1">
|
|
440
|
+
<catalog-modal-policy-display
|
|
441
|
+
.policy=${defaultPolicy}
|
|
442
|
+
.policies=${policies}
|
|
443
|
+
></catalog-modal-policy-display>
|
|
444
|
+
<catalog-modal-agreement-info
|
|
445
|
+
.offer=${offer}
|
|
446
|
+
.agreementInfo=${dspAgreementStorage.loadAgreementInfo(
|
|
447
|
+
offer,
|
|
448
|
+
)}
|
|
449
|
+
.contractId=${this.contractId}
|
|
450
|
+
@renew-contract=${this._renewContract}
|
|
451
|
+
></catalog-modal-agreement-info>
|
|
452
|
+
</div>`}
|
|
1447
453
|
</div>
|
|
1448
454
|
</div>
|
|
1449
455
|
</div>
|