@startinblox/components-ds4go 3.3.8 â 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 +2702 -3362
- 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 -1105
- 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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { localized, msg } from "@lit/localize";
|
|
2
|
+
import { html, nothing, type TemplateResult } from "lit";
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { LitElement } from "lit";
|
|
5
|
+
import type { DSPOffer, AgreementInfo } from "@src/ds4go";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Agreement info component for displaying contract agreement details
|
|
9
|
+
*/
|
|
10
|
+
@customElement("catalog-modal-agreement-info")
|
|
11
|
+
@localized()
|
|
12
|
+
export class CatalogModalAgreementInfo extends LitElement {
|
|
13
|
+
@property({ attribute: false })
|
|
14
|
+
offer?: DSPOffer;
|
|
15
|
+
|
|
16
|
+
@property({ attribute: false })
|
|
17
|
+
agreementInfo?: AgreementInfo;
|
|
18
|
+
|
|
19
|
+
@property({ attribute: false })
|
|
20
|
+
contractId?: string;
|
|
21
|
+
|
|
22
|
+
private _handleRenewContract(): void {
|
|
23
|
+
this.dispatchEvent(
|
|
24
|
+
new CustomEvent("renew-contract", {
|
|
25
|
+
bubbles: true,
|
|
26
|
+
composed: true,
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render(): TemplateResult | typeof nothing {
|
|
32
|
+
// Only show if we have agreement info
|
|
33
|
+
if (!this.contractId || !this.agreementInfo) {
|
|
34
|
+
return nothing;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const agreementDate = this.agreementInfo.timestamp
|
|
38
|
+
? new Date(this.agreementInfo.timestamp).toLocaleString()
|
|
39
|
+
: null;
|
|
40
|
+
|
|
41
|
+
// Get endpoint URL from asset
|
|
42
|
+
const endpointUrl = this.offer?.["dcat:endpointUrl"];
|
|
43
|
+
|
|
44
|
+
return html`<div
|
|
45
|
+
style="margin-top: 24px; padding: 16px; background: #e8f5e9; border-radius: 8px;"
|
|
46
|
+
>
|
|
47
|
+
<tems-division type="h4"
|
|
48
|
+
><div>${msg("Contract Agreement")}</div></tems-division
|
|
49
|
+
>
|
|
50
|
+
<div style="font-size: 0.9em; margin-top: 12px;">
|
|
51
|
+
<div style="margin-bottom: 8px;">
|
|
52
|
+
<strong>â
${msg("Agreement ID:")}</strong>
|
|
53
|
+
<div
|
|
54
|
+
style="font-family: monospace; background: white; padding: 8px; border-radius: 4px; margin-top: 4px; word-break: break-all;"
|
|
55
|
+
>
|
|
56
|
+
${this.contractId}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
${endpointUrl
|
|
61
|
+
? html`
|
|
62
|
+
<div style="margin-bottom: 8px;">
|
|
63
|
+
<strong>đ ${msg("Endpoint URL:")}</strong>
|
|
64
|
+
<div
|
|
65
|
+
style="font-family: monospace; background: white; padding: 8px; border-radius: 4px; margin-top: 4px; word-break: break-all;"
|
|
66
|
+
>
|
|
67
|
+
${endpointUrl}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
`
|
|
71
|
+
: nothing}
|
|
72
|
+
${agreementDate
|
|
73
|
+
? html`
|
|
74
|
+
<div style="opacity: 0.8; font-size: 0.85em;">
|
|
75
|
+
<strong>${msg("Agreed on:")}</strong> ${agreementDate}
|
|
76
|
+
</div>
|
|
77
|
+
`
|
|
78
|
+
: nothing}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div
|
|
82
|
+
style="margin-top: 12px; padding: 12px; background: rgba(0,0,0,0.05); border-radius: 4px; font-size: 0.85em;"
|
|
83
|
+
>
|
|
84
|
+
<div style="margin-bottom: 4px;">
|
|
85
|
+
<strong>âšī¸ ${msg("Note:")}</strong>
|
|
86
|
+
</div>
|
|
87
|
+
<div>
|
|
88
|
+
${msg(
|
|
89
|
+
"You can now use this agreement ID to access the service through the provider's API or data gateway.",
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
${this.agreementInfo
|
|
95
|
+
? html`
|
|
96
|
+
<div
|
|
97
|
+
style="margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(0,0,0,0.1);"
|
|
98
|
+
>
|
|
99
|
+
<button
|
|
100
|
+
@click=${this._handleRenewContract}
|
|
101
|
+
style="font-size: 0.85em; color: #666; background: none; border: none; cursor: pointer; text-decoration: underline; padding: 0;"
|
|
102
|
+
>
|
|
103
|
+
đ ${msg("Renegotiate contract")}
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
`
|
|
107
|
+
: nothing}
|
|
108
|
+
</div>`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { localized, msg } from "@lit/localize";
|
|
2
|
+
import { html, type TemplateResult } from "lit";
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { LitElement } from "lit";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Negotiation button component for handling DSP contract negotiation UI
|
|
8
|
+
*/
|
|
9
|
+
@customElement("catalog-modal-negotiation-button")
|
|
10
|
+
@localized()
|
|
11
|
+
export class CatalogModalNegotiationButton extends LitElement {
|
|
12
|
+
@property({ attribute: false })
|
|
13
|
+
dspStore?: any;
|
|
14
|
+
|
|
15
|
+
@property({ attribute: false })
|
|
16
|
+
showPolicySelection = false;
|
|
17
|
+
|
|
18
|
+
@property()
|
|
19
|
+
negotiationStatus:
|
|
20
|
+
| "idle"
|
|
21
|
+
| "negotiating"
|
|
22
|
+
| "pending"
|
|
23
|
+
| "granted"
|
|
24
|
+
| "failed"
|
|
25
|
+
| "transferring" = "idle";
|
|
26
|
+
|
|
27
|
+
@property()
|
|
28
|
+
currentState?: string;
|
|
29
|
+
|
|
30
|
+
@property()
|
|
31
|
+
attempt?: number;
|
|
32
|
+
|
|
33
|
+
@property()
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
|
|
36
|
+
@property()
|
|
37
|
+
negotiationError?: string;
|
|
38
|
+
|
|
39
|
+
private _handleNegotiate(): void {
|
|
40
|
+
this.dispatchEvent(
|
|
41
|
+
new CustomEvent("negotiate", {
|
|
42
|
+
bubbles: true,
|
|
43
|
+
composed: true,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _handleRetry(): void {
|
|
49
|
+
this.dispatchEvent(
|
|
50
|
+
new CustomEvent("retry", {
|
|
51
|
+
bubbles: true,
|
|
52
|
+
composed: true,
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
render(): TemplateResult {
|
|
58
|
+
const hasDspConnector =
|
|
59
|
+
this.dspStore !== undefined && this.dspStore !== null;
|
|
60
|
+
|
|
61
|
+
if (!hasDspConnector) {
|
|
62
|
+
return html`<tems-button disabled=""
|
|
63
|
+
>${msg("Activate this service")}</tems-button
|
|
64
|
+
>`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (this.negotiationStatus) {
|
|
68
|
+
case "idle":
|
|
69
|
+
return html`<tems-button @click=${this._handleNegotiate}
|
|
70
|
+
>${msg("Negotiate access")}</tems-button
|
|
71
|
+
>`;
|
|
72
|
+
|
|
73
|
+
case "negotiating":
|
|
74
|
+
return html`<tems-button disabled="">
|
|
75
|
+
${msg("Negotiating...")}
|
|
76
|
+
</tems-button>`;
|
|
77
|
+
|
|
78
|
+
case "pending":
|
|
79
|
+
return html`<tems-button disabled="">
|
|
80
|
+
${this.currentState || msg("Pending")}
|
|
81
|
+
${this.attempt ? `(${this.attempt}/${this.maxAttempts})` : ""}
|
|
82
|
+
</tems-button>`;
|
|
83
|
+
|
|
84
|
+
case "granted": {
|
|
85
|
+
return html`
|
|
86
|
+
<tems-button disabled="" type="success">
|
|
87
|
+
â
${msg("Access Granted")}
|
|
88
|
+
</tems-button>
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case "failed":
|
|
93
|
+
return html`<div
|
|
94
|
+
style="display: flex; flex-direction: column; gap: 8px;"
|
|
95
|
+
>
|
|
96
|
+
<tems-button disabled="" type="error">
|
|
97
|
+
â ${msg("Failed")}:
|
|
98
|
+
${this.negotiationError || msg("Unknown error")}
|
|
99
|
+
</tems-button>
|
|
100
|
+
<tems-button @click=${this._handleRetry} type="outline-gray">
|
|
101
|
+
${msg("Retry")}
|
|
102
|
+
</tems-button>
|
|
103
|
+
</div>`;
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
return html`<tems-button disabled=""
|
|
107
|
+
>${msg("Activate this service")}</tems-button
|
|
108
|
+
>`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { localized, msg } from "@lit/localize";
|
|
2
|
+
import { html, nothing, type TemplateResult } from "lit";
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { LitElement } from "lit";
|
|
5
|
+
import { hasMultiplePolicies } from "@helpers/dsp/policyHelpers";
|
|
6
|
+
import type { ODRLPolicy } from "@src/ds4go";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Policy display component for showing access policy information
|
|
10
|
+
*/
|
|
11
|
+
@customElement("catalog-modal-policy-display")
|
|
12
|
+
@localized()
|
|
13
|
+
export class CatalogModalPolicyDisplay extends LitElement {
|
|
14
|
+
@property({ attribute: false })
|
|
15
|
+
policy?: ODRLPolicy;
|
|
16
|
+
|
|
17
|
+
@property({ attribute: false })
|
|
18
|
+
policies?: ODRLPolicy[];
|
|
19
|
+
|
|
20
|
+
render(): TemplateResult | typeof nothing {
|
|
21
|
+
const hasMultiple = hasMultiplePolicies(this.policies || []);
|
|
22
|
+
const policies = this.policies || [];
|
|
23
|
+
|
|
24
|
+
// Only show if there's a policy
|
|
25
|
+
if (!this.policy && (!this.policies || this.policies.length === 0)) {
|
|
26
|
+
return nothing;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return html`<div
|
|
30
|
+
style="margin-top: 24px; padding: 16px; background: #f0f7ff; border-radius: 8px; border: 1px solid #d0e7ff;"
|
|
31
|
+
>
|
|
32
|
+
${hasMultiple
|
|
33
|
+
? html`<tems-division type="h4"
|
|
34
|
+
><div>${msg("Access Policies")}</div></tems-division
|
|
35
|
+
>
|
|
36
|
+
<div style="margin-bottom: 12px; color: #0066cc; font-size: 0.9em;">
|
|
37
|
+
${msg("Multiple contract policies available for this asset")}
|
|
38
|
+
(${policies.length})
|
|
39
|
+
</div>
|
|
40
|
+
${policies.map(
|
|
41
|
+
(p: ODRLPolicy, index: number) =>
|
|
42
|
+
html`<div
|
|
43
|
+
style="margin-bottom: 16px; padding: 12px; background: white; border-radius: 6px; border-left: 4px solid #0066cc;"
|
|
44
|
+
>
|
|
45
|
+
<div
|
|
46
|
+
style="font-weight: 600; margin-bottom: 8px; color: #333;"
|
|
47
|
+
>
|
|
48
|
+
${msg("Policy")} ${index + 1}
|
|
49
|
+
${p["@id"]
|
|
50
|
+
? html`<span
|
|
51
|
+
style="font-weight: normal; font-size: 0.85em; color: #666; display: block; margin-top: 4px; word-break: break-all; font-family: monospace;"
|
|
52
|
+
>
|
|
53
|
+
${p["@id"]}
|
|
54
|
+
</span>`
|
|
55
|
+
: nothing}
|
|
56
|
+
</div>
|
|
57
|
+
<odrl-policy-viewer .policy=${p}></odrl-policy-viewer>
|
|
58
|
+
</div>`,
|
|
59
|
+
)}`
|
|
60
|
+
: html`<tems-division type="h4"
|
|
61
|
+
><div>${msg("Access Policy")}</div></tems-division
|
|
62
|
+
>
|
|
63
|
+
<odrl-policy-viewer .policy=${this.policy}></odrl-policy-viewer>`}
|
|
64
|
+
</div>`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { localized, msg } from "@lit/localize";
|
|
2
|
+
import { html, nothing, type TemplateResult } from "lit";
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
5
|
+
import { LitElement } from "lit";
|
|
6
|
+
import { formatPolicyDetails } from "@helpers/dsp/policyHelpers";
|
|
7
|
+
import type { ODRLPolicy } from "@src/ds4go";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Policy selection component for choosing from multiple available policies
|
|
11
|
+
*/
|
|
12
|
+
@customElement("catalog-modal-policy-selection")
|
|
13
|
+
@localized()
|
|
14
|
+
export class CatalogModalPolicySelection extends LitElement {
|
|
15
|
+
@property({ attribute: false })
|
|
16
|
+
policies: ODRLPolicy[] = [];
|
|
17
|
+
|
|
18
|
+
private _handlePolicySelect(index: number): void {
|
|
19
|
+
this.dispatchEvent(
|
|
20
|
+
new CustomEvent("policy-selected", {
|
|
21
|
+
detail: { index },
|
|
22
|
+
bubbles: true,
|
|
23
|
+
composed: true,
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private _handleCancel(): void {
|
|
29
|
+
this.dispatchEvent(
|
|
30
|
+
new CustomEvent("policy-cancel", {
|
|
31
|
+
bubbles: true,
|
|
32
|
+
composed: true,
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
render(): TemplateResult {
|
|
38
|
+
return html`<div class="policy-selection-modal">
|
|
39
|
+
<div class="policy-selection-header">
|
|
40
|
+
<h3>${msg("Select a Policy")}</h3>
|
|
41
|
+
<p>
|
|
42
|
+
${msg(
|
|
43
|
+
"Multiple policies are available for this dataset. Please select one to proceed with the negotiation.",
|
|
44
|
+
)}
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="policy-selection-list">
|
|
48
|
+
${this.policies.map(
|
|
49
|
+
(policy: ODRLPolicy, index: number) =>
|
|
50
|
+
html`<div
|
|
51
|
+
class="policy-option"
|
|
52
|
+
@click=${() => this._handlePolicySelect(index)}
|
|
53
|
+
>
|
|
54
|
+
<div class="policy-option-header">
|
|
55
|
+
<strong>${msg("Policy")} ${index + 1}</strong>
|
|
56
|
+
${policy["@id"] ? html`<code>${policy["@id"]}</code>` : nothing}
|
|
57
|
+
</div>
|
|
58
|
+
<div class="policy-option-details">
|
|
59
|
+
${formatPolicyDetails(policy)}
|
|
60
|
+
</div>
|
|
61
|
+
</div>`,
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
<div class="policy-selection-actions">
|
|
65
|
+
<tems-button type="outline-gray" @click=${this._handleCancel}>
|
|
66
|
+
${msg("Cancel")}
|
|
67
|
+
</tems-button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>`;
|
|
70
|
+
}
|
|
71
|
+
}
|