@startinblox/components-ds4go 3.1.7 → 3.1.9

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,938 @@
1
+ import { localized, msg, str } from "@lit/localize";
2
+ import type {
3
+ Constraint,
4
+ Duty,
5
+ OdrlPermission,
6
+ OdrlPolicy,
7
+ PolicyDefinition,
8
+ PolicyTemplate,
9
+ Prohibition,
10
+ } from "@src/component";
11
+ import { html, LitElement, nothing, unsafeCSS } from "lit";
12
+ import { customElement, property } from "lit/decorators.js";
13
+ import ComponentStyles from "@styles/odrl/policy-composer.scss?inline";
14
+
15
+ @customElement("policy-composer")
16
+ @localized()
17
+ export class PolicyComposer extends LitElement {
18
+ static styles = [unsafeCSS(ComponentStyles)];
19
+
20
+ @property({ attribute: false })
21
+ template?: PolicyTemplate;
22
+
23
+ @property({ attribute: false })
24
+ policy?: PolicyDefinition;
25
+
26
+ _emitPolicyChange() {
27
+ if (!this.policy) return;
28
+ this.dispatchEvent(
29
+ new CustomEvent("policy-changed", {
30
+ detail: this.policy,
31
+ bubbles: true,
32
+ composed: true,
33
+ }),
34
+ );
35
+ }
36
+
37
+ _updatePolicyField<K extends keyof OdrlPolicy>(
38
+ field: K,
39
+ value: OdrlPolicy[K],
40
+ ) {
41
+ if (!this.policy) return;
42
+ this.policy = {
43
+ ...this.policy,
44
+ policy: {
45
+ ...this.policy.policy,
46
+ [field]: value,
47
+ },
48
+ };
49
+ this._emitPolicyChange();
50
+ }
51
+
52
+ _updatePolicyDefinitionField<K extends keyof PolicyDefinition>(
53
+ field: K,
54
+ value: PolicyDefinition[K],
55
+ ) {
56
+ if (!this.policy) return;
57
+ this.policy = {
58
+ ...this.policy,
59
+ [field]: value,
60
+ };
61
+ this._emitPolicyChange();
62
+ }
63
+
64
+ _addPermission() {
65
+ if (!this.policy) return;
66
+ const newPermission: OdrlPermission = {
67
+ action: "",
68
+ };
69
+ this._updatePolicyField("permission", [
70
+ ...(this.policy.policy.permission || []),
71
+ newPermission,
72
+ ]);
73
+ }
74
+
75
+ _updatePermission(index: number, field: keyof OdrlPermission, value: any) {
76
+ if (!this.policy || !this.policy.policy.permission) return;
77
+ const permissions = [...this.policy.policy.permission];
78
+ permissions[index] = { ...permissions[index], [field]: value };
79
+ this._updatePolicyField("permission", permissions);
80
+ }
81
+
82
+ _removePermission(index: number) {
83
+ if (!this.policy || !this.policy.policy.permission) return;
84
+ const permissions = this.policy.policy.permission.filter(
85
+ (_, i) => i !== index,
86
+ );
87
+ this._updatePolicyField("permission", permissions);
88
+ }
89
+
90
+ _addDutyToPermission(permissionIndex: number) {
91
+ if (!this.policy || !this.policy.policy.permission) return;
92
+ const newDuty: Duty = {
93
+ action: "",
94
+ };
95
+ const permissions = [...this.policy.policy.permission];
96
+ permissions[permissionIndex] = {
97
+ ...permissions[permissionIndex],
98
+ duty: [...(permissions[permissionIndex].duty || []), newDuty],
99
+ };
100
+ this._updatePolicyField("permission", permissions);
101
+ }
102
+
103
+ _updateDutyInPermission(
104
+ permissionIndex: number,
105
+ dutyIndex: number,
106
+ field: keyof Duty,
107
+ value: any,
108
+ ) {
109
+ if (!this.policy || !this.policy.policy.permission) return;
110
+ const permissions = [...this.policy.policy.permission];
111
+ const duties = [...(permissions[permissionIndex].duty || [])];
112
+ duties[dutyIndex] = { ...duties[dutyIndex], [field]: value };
113
+ permissions[permissionIndex] = {
114
+ ...permissions[permissionIndex],
115
+ duty: duties,
116
+ };
117
+ this._updatePolicyField("permission", permissions);
118
+ }
119
+
120
+ _removeDutyFromPermission(permissionIndex: number, dutyIndex: number) {
121
+ if (!this.policy || !this.policy.policy.permission) return;
122
+ const permissions = [...this.policy.policy.permission];
123
+ const duties = permissions[permissionIndex].duty?.filter(
124
+ (_, i) => i !== dutyIndex,
125
+ );
126
+ permissions[permissionIndex] = {
127
+ ...permissions[permissionIndex],
128
+ duty: duties,
129
+ };
130
+ this._updatePolicyField("permission", permissions);
131
+ }
132
+
133
+ _addProhibition() {
134
+ if (!this.policy) return;
135
+ const newProhibition: Prohibition = {
136
+ action: "",
137
+ };
138
+ this._updatePolicyField("prohibition", [
139
+ ...(this.policy.policy.prohibition || []),
140
+ newProhibition,
141
+ ]);
142
+ }
143
+
144
+ _updateProhibition(index: number, field: keyof Prohibition, value: any) {
145
+ if (!this.policy || !this.policy.policy.prohibition) return;
146
+ const prohibitions = [...this.policy.policy.prohibition];
147
+ prohibitions[index] = { ...prohibitions[index], [field]: value };
148
+ this._updatePolicyField("prohibition", prohibitions);
149
+ }
150
+
151
+ _removeProhibition(index: number) {
152
+ if (!this.policy || !this.policy.policy.prohibition) return;
153
+ const prohibitions = this.policy.policy.prohibition.filter(
154
+ (_, i) => i !== index,
155
+ );
156
+ this._updatePolicyField("prohibition", prohibitions);
157
+ }
158
+
159
+ _addObligation() {
160
+ if (!this.policy) return;
161
+ const newObligation: Duty = {
162
+ action: "",
163
+ };
164
+ this._updatePolicyField("obligation", [
165
+ ...(this.policy.policy.obligation || []),
166
+ newObligation,
167
+ ]);
168
+ }
169
+
170
+ _updateObligation(index: number, field: keyof Duty, value: any) {
171
+ if (!this.policy || !this.policy.policy.obligation) return;
172
+ const obligations = [...this.policy.policy.obligation];
173
+ obligations[index] = { ...obligations[index], [field]: value };
174
+ this._updatePolicyField("obligation", obligations);
175
+ }
176
+
177
+ _removeObligation(index: number) {
178
+ if (!this.policy || !this.policy.policy.obligation) return;
179
+ const obligations = this.policy.policy.obligation.filter(
180
+ (_, i) => i !== index,
181
+ );
182
+ this._updatePolicyField("obligation", obligations);
183
+ }
184
+
185
+ _cancelCustomization() {
186
+ this.dispatchEvent(
187
+ new CustomEvent("cancel-policy-customization", {
188
+ bubbles: true,
189
+ composed: true,
190
+ }),
191
+ );
192
+ }
193
+
194
+ _resetPolicy() {
195
+ if (!this.template) return;
196
+ const templateId = this.template["id"] as string;
197
+ this.policy = {
198
+ "@id": templateId,
199
+ policy: {
200
+ ...this.template.policy,
201
+ "@type": "Set",
202
+ },
203
+ };
204
+ this._emitPolicyChange();
205
+ }
206
+
207
+ _addConstraint<T extends OdrlPermission | Prohibition | Duty>(
208
+ array: T[],
209
+ index: number,
210
+ ) {
211
+ const newConstraint: Constraint = {
212
+ leftOperand: "",
213
+ operator: "eq",
214
+ rightOperand: "",
215
+ };
216
+ const newArray = [...array];
217
+ newArray[index] = {
218
+ ...newArray[index],
219
+ constraint: [...(newArray[index].constraint || []), newConstraint],
220
+ };
221
+ return newArray;
222
+ }
223
+
224
+ _updateConstraint<T extends OdrlPermission | Prohibition | Duty>(
225
+ array: T[],
226
+ itemIndex: number,
227
+ constraintIndex: number,
228
+ field: keyof Constraint,
229
+ value: any,
230
+ ) {
231
+ const newArray = [...array];
232
+ const constraints = [...(newArray[itemIndex].constraint || [])];
233
+ constraints[constraintIndex] = {
234
+ ...constraints[constraintIndex],
235
+ [field]: value,
236
+ };
237
+ newArray[itemIndex] = { ...newArray[itemIndex], constraint: constraints };
238
+ return newArray;
239
+ }
240
+
241
+ _removeConstraint<T extends OdrlPermission | Prohibition | Duty>(
242
+ array: T[],
243
+ itemIndex: number,
244
+ constraintIndex: number,
245
+ ) {
246
+ const newArray = [...array];
247
+ const constraints = newArray[itemIndex].constraint?.filter(
248
+ (_, i) => i !== constraintIndex,
249
+ );
250
+ newArray[itemIndex] = { ...newArray[itemIndex], constraint: constraints };
251
+ return newArray;
252
+ }
253
+
254
+ _renderOperatorSelect(
255
+ currentOperator: string,
256
+ onChangeHandler: (e: Event) => void,
257
+ ) {
258
+ const operators = ["eq", "neq", "gt", "gte", "lt", "lte", "isA", "in"];
259
+ return html`<select @change=${onChangeHandler}>
260
+ ${operators.map(
261
+ (op) =>
262
+ html`<option value="${op}" selected=${currentOperator === op}>
263
+ ${op}
264
+ </option>`,
265
+ )}
266
+ </select>`;
267
+ }
268
+
269
+ _renderPermission(permission: OdrlPermission, index: number) {
270
+ return html`<fieldset>
271
+ <legend>${msg(str`Permission ${index + 1}`)}</legend>
272
+ <tems-input
273
+ type="text"
274
+ label="${msg("Action")}"
275
+ .value=${permission.action || ""}
276
+ @change=${(e: Event) =>
277
+ this._updatePermission(
278
+ index,
279
+ "action",
280
+ (e as CustomEvent).detail.value,
281
+ )}
282
+ ></tems-input>
283
+ <tems-input
284
+ type="text"
285
+ label="${msg("Target")}"
286
+ .value=${permission.target || ""}
287
+ @change=${(e: Event) =>
288
+ this._updatePermission(
289
+ index,
290
+ "target",
291
+ (e as CustomEvent).detail.value,
292
+ )}
293
+ ></tems-input>
294
+ ${permission.constraint && permission.constraint.length > 0
295
+ ? html`<fieldset>
296
+ <legend>${msg("Constraints")}</legend>
297
+ ${permission.constraint.map(
298
+ (constraint, cIndex) => html`
299
+ <div class="flex flex-column spacer-auto">
300
+ <tems-input
301
+ type="text"
302
+ label="${msg("Left Operand")}"
303
+ .value=${constraint.leftOperand || ""}
304
+ @change=${(e: Event) => {
305
+ const updated = this._updateConstraint(
306
+ this.policy!.policy.permission!,
307
+ index,
308
+ cIndex,
309
+ "leftOperand",
310
+ (e as CustomEvent).detail.value,
311
+ );
312
+ this._updatePolicyField("permission", updated);
313
+ }}
314
+ ></tems-input>
315
+ <div class="select">
316
+ <tems-label label=${msg("Operator")}></tems-label>
317
+ ${this._renderOperatorSelect(
318
+ constraint.operator,
319
+ (e: Event) => {
320
+ const updated = this._updateConstraint(
321
+ this.policy!.policy.permission!,
322
+ index,
323
+ cIndex,
324
+ "operator",
325
+ (e.target as HTMLSelectElement).value,
326
+ );
327
+ this._updatePolicyField("permission", updated);
328
+ },
329
+ )}
330
+ </div>
331
+ <tems-input
332
+ type="text"
333
+ label="${msg("Right Operand")}"
334
+ .value=${String(constraint.rightOperand || "")}
335
+ @change=${(e: Event) => {
336
+ const updated = this._updateConstraint(
337
+ this.policy!.policy.permission!,
338
+ index,
339
+ cIndex,
340
+ "rightOperand",
341
+ (e as CustomEvent).detail.value,
342
+ );
343
+ this._updatePolicyField("permission", updated);
344
+ }}
345
+ ></tems-input>
346
+ <tems-button
347
+ class="spacer-top"
348
+ type="outline-gray"
349
+ size="sm"
350
+ @click=${() => {
351
+ const updated = this._removeConstraint(
352
+ this.policy!.policy.permission!,
353
+ index,
354
+ cIndex,
355
+ );
356
+ this._updatePolicyField("permission", updated);
357
+ }}
358
+ >
359
+ ${msg("Remove Constraint")}
360
+ </tems-button>
361
+ </div>
362
+ `,
363
+ )}
364
+ </fieldset>`
365
+ : nothing}
366
+ ${permission.duty && permission.duty.length > 0
367
+ ? html`<fieldset>
368
+ <legend>${msg("Duties")}</legend>
369
+ ${permission.duty.map(
370
+ (duty, dIndex) => html`
371
+ <div class="flex flex-column">
372
+ <tems-input
373
+ type="text"
374
+ label="${msg("Action")}"
375
+ .value=${duty.action || ""}
376
+ @change=${(e: Event) => {
377
+ this._updateDutyInPermission(
378
+ index,
379
+ dIndex,
380
+ "action",
381
+ (e as CustomEvent).detail.value,
382
+ );
383
+ }}
384
+ ></tems-input>
385
+ ${duty.constraint && duty.constraint.length > 0
386
+ ? html`<fieldset>
387
+ <legend>${msg("Constraints")}</legend>
388
+ ${duty.constraint.map(
389
+ (constraint, cIndex) => html`
390
+ <div class="flex flex-column spacer-auto">
391
+ <tems-input
392
+ type="text"
393
+ label="${msg("Left Operand")}"
394
+ .value=${constraint.leftOperand || ""}
395
+ @change=${(e: Event) => {
396
+ const permissions = [
397
+ ...this.policy!.policy.permission!,
398
+ ];
399
+ const duties = [...permissions[index].duty!];
400
+ const constraints = [
401
+ ...duties[dIndex].constraint!,
402
+ ];
403
+ constraints[cIndex] = {
404
+ ...constraints[cIndex],
405
+ leftOperand: (e as CustomEvent).detail
406
+ .value,
407
+ };
408
+ duties[dIndex] = {
409
+ ...duties[dIndex],
410
+ constraint: constraints,
411
+ };
412
+ permissions[index] = {
413
+ ...permissions[index],
414
+ duty: duties,
415
+ };
416
+ this._updatePolicyField(
417
+ "permission",
418
+ permissions,
419
+ );
420
+ }}
421
+ ></tems-input>
422
+ <div class="select">
423
+ <tems-label
424
+ label=${msg("Operator")}
425
+ ></tems-label>
426
+ ${this._renderOperatorSelect(
427
+ constraint.operator,
428
+ (e: Event) => {
429
+ const permissions = [
430
+ ...this.policy!.policy.permission!,
431
+ ];
432
+ const duties = [
433
+ ...permissions[index].duty!,
434
+ ];
435
+ const constraints = [
436
+ ...duties[dIndex].constraint!,
437
+ ];
438
+ constraints[cIndex] = {
439
+ ...constraints[cIndex],
440
+ operator: (e.target as HTMLSelectElement)
441
+ .value,
442
+ };
443
+ duties[dIndex] = {
444
+ ...duties[dIndex],
445
+ constraint: constraints,
446
+ };
447
+ permissions[index] = {
448
+ ...permissions[index],
449
+ duty: duties,
450
+ };
451
+ this._updatePolicyField(
452
+ "permission",
453
+ permissions,
454
+ );
455
+ },
456
+ )}
457
+ </div>
458
+ <tems-input
459
+ type="text"
460
+ label="${msg("Right Operand")}"
461
+ .value=${String(constraint.rightOperand || "")}
462
+ @change=${(e: Event) => {
463
+ const permissions = [
464
+ ...this.policy!.policy.permission!,
465
+ ];
466
+ const duties = [...permissions[index].duty!];
467
+ const constraints = [
468
+ ...duties[dIndex].constraint!,
469
+ ];
470
+ constraints[cIndex] = {
471
+ ...constraints[cIndex],
472
+ rightOperand: (e as CustomEvent).detail
473
+ .value,
474
+ };
475
+ duties[dIndex] = {
476
+ ...duties[dIndex],
477
+ constraint: constraints,
478
+ };
479
+ permissions[index] = {
480
+ ...permissions[index],
481
+ duty: duties,
482
+ };
483
+ this._updatePolicyField(
484
+ "permission",
485
+ permissions,
486
+ );
487
+ }}
488
+ ></tems-input>
489
+ <tems-button
490
+ class="spacer-top"
491
+ type="outline-gray"
492
+ size="sm"
493
+ @click=${() => {
494
+ const permissions = [
495
+ ...this.policy!.policy.permission!,
496
+ ];
497
+ const duties = [...permissions[index].duty!];
498
+ const constraints = duties[
499
+ dIndex
500
+ ].constraint?.filter((_, i) => i !== cIndex);
501
+ duties[dIndex] = {
502
+ ...duties[dIndex],
503
+ constraint: constraints,
504
+ };
505
+ permissions[index] = {
506
+ ...permissions[index],
507
+ duty: duties,
508
+ };
509
+ this._updatePolicyField(
510
+ "permission",
511
+ permissions,
512
+ );
513
+ }}
514
+ >
515
+ ${msg("Remove Constraint")}
516
+ </tems-button>
517
+ </div>
518
+ `,
519
+ )}
520
+ </fieldset>`
521
+ : nothing}
522
+ <div class="flex spacer-top">
523
+ <tems-button
524
+ type="outline-gray"
525
+ size="sm"
526
+ @click=${() => {
527
+ const permissions = [
528
+ ...this.policy!.policy.permission!,
529
+ ];
530
+ const duties = [...permissions[index].duty!];
531
+ const newConstraint: Constraint = {
532
+ leftOperand: "",
533
+ operator: "eq",
534
+ rightOperand: "",
535
+ };
536
+ duties[dIndex] = {
537
+ ...duties[dIndex],
538
+ constraint: [
539
+ ...(duties[dIndex].constraint || []),
540
+ newConstraint,
541
+ ],
542
+ };
543
+ permissions[index] = {
544
+ ...permissions[index],
545
+ duty: duties,
546
+ };
547
+ this._updatePolicyField("permission", permissions);
548
+ }}
549
+ >
550
+ ${msg("Add Constraint")}
551
+ </tems-button>
552
+ <tems-button
553
+ type="outline-gray"
554
+ size="sm"
555
+ @click=${() =>
556
+ this._removeDutyFromPermission(index, dIndex)}
557
+ >
558
+ ${msg("Remove Duty")}
559
+ </tems-button>
560
+ </div>
561
+ </div>
562
+ `,
563
+ )}
564
+ </fieldset>`
565
+ : nothing}
566
+ <div class="flex spacer-top">
567
+ <tems-button
568
+ type="outline-gray"
569
+ size="sm"
570
+ @click=${() => {
571
+ const updated = this._addConstraint(
572
+ this.policy!.policy.permission!,
573
+ index,
574
+ );
575
+ this._updatePolicyField("permission", updated);
576
+ }}
577
+ >
578
+ ${msg("Add Constraint")}
579
+ </tems-button>
580
+ <tems-button
581
+ type="outline-gray"
582
+ size="sm"
583
+ @click=${() => this._addDutyToPermission(index)}
584
+ >
585
+ ${msg("Add Duty")}
586
+ </tems-button>
587
+ <tems-button
588
+ type="outline-gray"
589
+ size="sm"
590
+ @click=${() => this._removePermission(index)}
591
+ >
592
+ ${msg("Remove Permission")}
593
+ </tems-button>
594
+ </div>
595
+ </fieldset>`;
596
+ }
597
+
598
+ _renderProhibition(prohibition: Prohibition, index: number) {
599
+ return html`<fieldset>
600
+ <legend>${msg(str`Prohibition ${index + 1}`)}</legend>
601
+ <tems-input
602
+ type="text"
603
+ label="${msg("Action")}"
604
+ .value=${prohibition.action || ""}
605
+ @change=${(e: Event) =>
606
+ this._updateProhibition(
607
+ index,
608
+ "action",
609
+ (e as CustomEvent).detail.value,
610
+ )}
611
+ ></tems-input>
612
+ <tems-input
613
+ type="text"
614
+ label="${msg("Target")}"
615
+ .value=${prohibition.target || ""}
616
+ @change=${(e: Event) =>
617
+ this._updateProhibition(
618
+ index,
619
+ "target",
620
+ (e as CustomEvent).detail.value,
621
+ )}
622
+ ></tems-input>
623
+ ${prohibition.constraint && prohibition.constraint.length > 0
624
+ ? html`<fieldset>
625
+ <legend>${msg("Constraints")}</legend>
626
+ ${prohibition.constraint.map(
627
+ (constraint, cIndex) => html`
628
+ <div class="flex flex-column spacer-auto">
629
+ <tems-input
630
+ type="text"
631
+ label="${msg("Left Operand")}"
632
+ .value=${constraint.leftOperand || ""}
633
+ @change=${(e: Event) => {
634
+ const updated = this._updateConstraint(
635
+ this.policy!.policy.prohibition!,
636
+ index,
637
+ cIndex,
638
+ "leftOperand",
639
+ (e as CustomEvent).detail.value,
640
+ );
641
+ this._updatePolicyField("prohibition", updated);
642
+ }}
643
+ ></tems-input>
644
+ <div class="select">
645
+ <tems-label label=${msg("Operator")}></tems-label>
646
+ ${this._renderOperatorSelect(
647
+ constraint.operator,
648
+ (e: Event) => {
649
+ const updated = this._updateConstraint(
650
+ this.policy!.policy.prohibition!,
651
+ index,
652
+ cIndex,
653
+ "operator",
654
+ (e.target as HTMLSelectElement).value,
655
+ );
656
+ this._updatePolicyField("prohibition", updated);
657
+ },
658
+ )}
659
+ </div>
660
+ <tems-input
661
+ type="text"
662
+ label="${msg("Right Operand")}"
663
+ .value=${String(constraint.rightOperand || "")}
664
+ @change=${(e: Event) => {
665
+ const updated = this._updateConstraint(
666
+ this.policy!.policy.prohibition!,
667
+ index,
668
+ cIndex,
669
+ "rightOperand",
670
+ (e as CustomEvent).detail.value,
671
+ );
672
+ this._updatePolicyField("prohibition", updated);
673
+ }}
674
+ ></tems-input>
675
+ <tems-button
676
+ class="spacer-top"
677
+ type="outline-gray"
678
+ size="sm"
679
+ @click=${() => {
680
+ const updated = this._removeConstraint(
681
+ this.policy!.policy.prohibition!,
682
+ index,
683
+ cIndex,
684
+ );
685
+ this._updatePolicyField("prohibition", updated);
686
+ }}
687
+ >
688
+ ${msg("Remove Constraint")}
689
+ </tems-button>
690
+ </div>
691
+ `,
692
+ )}
693
+ </fieldset>`
694
+ : nothing}
695
+ <div class="flex spacer-top">
696
+ <tems-button
697
+ type="outline-gray"
698
+ size="sm"
699
+ @click=${() => {
700
+ const updated = this._addConstraint(
701
+ this.policy!.policy.prohibition!,
702
+ index,
703
+ );
704
+ this._updatePolicyField("prohibition", updated);
705
+ }}
706
+ >
707
+ ${msg("Add Constraint")}
708
+ </tems-button>
709
+ <tems-button
710
+ type="outline-gray"
711
+ size="sm"
712
+ @click=${() => this._removeProhibition(index)}
713
+ >
714
+ ${msg("Remove Prohibition")}
715
+ </tems-button>
716
+ </div>
717
+ </fieldset>`;
718
+ }
719
+
720
+ _renderObligation(obligation: Duty, index: number) {
721
+ return html`<fieldset>
722
+ <legend>${msg(str`Obligation ${index + 1}`)}</legend>
723
+ <tems-input
724
+ type="text"
725
+ label="${msg("Action")}"
726
+ .value=${obligation.action || ""}
727
+ @change=${(e: Event) =>
728
+ this._updateObligation(
729
+ index,
730
+ "action",
731
+ (e as CustomEvent).detail.value,
732
+ )}
733
+ ></tems-input>
734
+ ${obligation.constraint && obligation.constraint.length > 0
735
+ ? html`<fieldset>
736
+ <legend>${msg("Constraints")}</legend>
737
+ ${obligation.constraint.map(
738
+ (constraint, cIndex) => html`
739
+ <div class="flex flex-column spacer-auto">
740
+ <tems-input
741
+ type="text"
742
+ label="${msg("Left Operand")}"
743
+ .value=${constraint.leftOperand || ""}
744
+ @change=${(e: Event) => {
745
+ const updated = this._updateConstraint(
746
+ this.policy!.policy.obligation!,
747
+ index,
748
+ cIndex,
749
+ "leftOperand",
750
+ (e as CustomEvent).detail.value,
751
+ );
752
+ this._updatePolicyField("obligation", updated);
753
+ }}
754
+ ></tems-input>
755
+ <div class="select">
756
+ <tems-label label=${msg("Operator")}></tems-label>
757
+ ${this._renderOperatorSelect(
758
+ constraint.operator,
759
+ (e: Event) => {
760
+ const updated = this._updateConstraint(
761
+ this.policy!.policy.obligation!,
762
+ index,
763
+ cIndex,
764
+ "operator",
765
+ (e.target as HTMLSelectElement).value,
766
+ );
767
+ this._updatePolicyField("obligation", updated);
768
+ },
769
+ )}
770
+ </div>
771
+ <tems-input
772
+ type="text"
773
+ label="${msg("Right Operand")}"
774
+ .value=${String(constraint.rightOperand || "")}
775
+ @change=${(e: Event) => {
776
+ const updated = this._updateConstraint(
777
+ this.policy!.policy.obligation!,
778
+ index,
779
+ cIndex,
780
+ "rightOperand",
781
+ (e as CustomEvent).detail.value,
782
+ );
783
+ this._updatePolicyField("obligation", updated);
784
+ }}
785
+ ></tems-input>
786
+ <tems-button
787
+ class="spacer-top"
788
+ type="outline-gray"
789
+ size="sm"
790
+ @click=${() => {
791
+ const updated = this._removeConstraint(
792
+ this.policy!.policy.obligation!,
793
+ index,
794
+ cIndex,
795
+ );
796
+ this._updatePolicyField("obligation", updated);
797
+ }}
798
+ >
799
+ ${msg("Remove Constraint")}
800
+ </tems-button>
801
+ </div>
802
+ `,
803
+ )}
804
+ </fieldset>`
805
+ : nothing}
806
+ <div class="flex spacer-top">
807
+ <tems-button
808
+ type="outline-gray"
809
+ size="sm"
810
+ @click=${() => {
811
+ const updated = this._addConstraint(
812
+ this.policy!.policy.obligation!,
813
+ index,
814
+ );
815
+ this._updatePolicyField("obligation", updated);
816
+ }}
817
+ >
818
+ ${msg("Add Constraint")}
819
+ </tems-button>
820
+ <tems-button
821
+ type="outline-gray"
822
+ size="sm"
823
+ @click=${() => this._removeObligation(index)}
824
+ >
825
+ ${msg("Remove Obligation")}
826
+ </tems-button>
827
+ </div>
828
+ </fieldset>`;
829
+ }
830
+
831
+ render() {
832
+ if (!this.template) {
833
+ return html`<p>${msg("No policy template provided")}</p>`;
834
+ }
835
+
836
+ if (!this.policy) {
837
+ this.policy = {
838
+ "@id": this.template["id"],
839
+ policy: {
840
+ ...this.template.policy,
841
+ "@type": "Set",
842
+ },
843
+ };
844
+ }
845
+
846
+ return html`<form @submit=${(e: Event) => e.preventDefault()}>
847
+ <div class="flex">
848
+ <tems-label
849
+ class="flex-1"
850
+ label=${msg(str`Custom ${this.template.name} policy`)}
851
+ ></tems-label>
852
+ <tems-button
853
+ type="outline-gray"
854
+ label=${msg("Cancel policy customization")}
855
+ size="sm"
856
+ type="outline-gray"
857
+ @click=${this._cancelCustomization}
858
+ ></tems-button>
859
+ </div>
860
+ <fieldset>
861
+ <legend>${msg("Policy Definition")}</legend>
862
+ <tems-input
863
+ type="text"
864
+ label="${msg("Policy ID")}"
865
+ .value=${this.policy["@id"] || ""}
866
+ @change=${(e: Event) =>
867
+ this._updatePolicyDefinitionField(
868
+ "@id",
869
+ (e as CustomEvent).detail.value,
870
+ )}
871
+ ></tems-input>
872
+ <tems-input
873
+ type="text"
874
+ label="${msg("Policy Type")}"
875
+ .value=${"Set"}
876
+ disabled
877
+ ></tems-input>
878
+ <tems-input
879
+ type="text"
880
+ label="${msg("Target")}"
881
+ .value=${this.policy.policy.target || ""}
882
+ @change=${(e: Event) =>
883
+ this._updatePolicyField("target", (e as CustomEvent).detail.value)}
884
+ ></tems-input>
885
+ </fieldset>
886
+
887
+ <fieldset>
888
+ <legend>${msg("Permissions")}</legend>
889
+ ${this.policy.policy.permission?.map((permission, index) =>
890
+ this._renderPermission(permission, index),
891
+ )}
892
+ <tems-button
893
+ class="spacer-top"
894
+ type="outline-gray"
895
+ size="sm"
896
+ @click=${this._addPermission}
897
+ >
898
+ ${msg("Add Permission")}
899
+ </tems-button>
900
+ </fieldset>
901
+
902
+ <fieldset>
903
+ <legend>${msg("Prohibitions")}</legend>
904
+ ${this.policy.policy.prohibition?.map((prohibition, index) =>
905
+ this._renderProhibition(prohibition, index),
906
+ )}
907
+ <tems-button
908
+ class="spacer-top"
909
+ type="outline-gray"
910
+ size="sm"
911
+ @click=${this._addProhibition}
912
+ >
913
+ ${msg("Add Prohibition")}
914
+ </tems-button>
915
+ </fieldset>
916
+
917
+ <fieldset>
918
+ <legend>${msg("Obligations")}</legend>
919
+ ${this.policy.policy.obligation?.map((obligation, index) =>
920
+ this._renderObligation(obligation, index),
921
+ )}
922
+ <tems-button
923
+ class="spacer-top"
924
+ type="outline-gray"
925
+ size="sm"
926
+ @click=${this._addObligation}
927
+ >
928
+ ${msg("Add Obligation")}
929
+ </tems-button>
930
+ </fieldset>
931
+ <div class="flex align-right spacer-top">
932
+ <tems-button type="outline-gray" size="sm" @click=${this._resetPolicy}>
933
+ ${msg(`Reset to ${this.template.name} default`)}
934
+ </tems-button>
935
+ </div>
936
+ </form>`;
937
+ }
938
+ }