Skip to content

Commit d683f9d

Browse files
committed
Display nested attribute values in the presentation results
1 parent ea9be97 commit d683f9d

5 files changed

Lines changed: 142 additions & 52 deletions

File tree

src/app/core/constants/attestation-definitions.ts

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -195,48 +195,48 @@ export const PDA1_ATTESTATION: AttestationDefinition = {
195195
],
196196
},
197197
{
198-
identifier: "EmploymentDetail",
199-
attribute: "Employment detail",
198+
identifier: "employment_details",
199+
attribute: "Employment details",
200200
nested: [
201-
{ identifier: "EmploymentDetail.type", attribute: "Type of employment" },
202-
{ identifier: "EmploymentDetail.name", attribute: "Name" },
203-
{ identifier: "EmploymentDetail.employer_id", attribute: "Employer ID" },
204-
{ identifier: "EmploymentDetail.type_id", attribute: "Type of ID" },
205-
],
206-
},
207-
{
208-
identifier: "address",
209-
attribute: "Address",
210-
nested: [
211-
{ identifier: "address.street", attribute: "Street" },
212-
{ identifier: "address.town", attribute: "Town" },
213-
{ identifier: "address.postal_code", attribute: "Postal code" },
214-
{ identifier: "address.country_code", attribute: "Country code" },
201+
{ identifier: "employment_details.type", attribute: "Type of employment" },
202+
{ identifier: "employment_details.name", attribute: "Name" },
203+
{ identifier: "employment_details.employer_id", attribute: "Employer ID" },
204+
{ identifier: "employment_details.type_id", attribute: "Type of ID" },
205+
{
206+
identifier: "address",
207+
attribute: "Address",
208+
nested: [
209+
{ identifier: "address.street", attribute: "Street" },
210+
{ identifier: "address.town", attribute: "Town" },
211+
{ identifier: "address.postal_code", attribute: "Postal code" },
212+
{ identifier: "address.country_code", attribute: "Country code" },
213+
],
214+
},
215215
],
216216
},
217217
{
218-
identifier: 'PlacesOfWork',
218+
identifier: 'places_of_work',
219219
attribute: 'Places of work',
220220
nested: [
221221
{
222-
identifier: "NoFixedPlace",
222+
identifier: "no_fixed_place",
223223
attribute: "No fixed place of work",
224224
nested: [
225-
{ identifier: "NoFixedPlace.country_code", attribute: "Country code" },
225+
{ identifier: "no_fixed_place.country_code", attribute: "Country code" },
226226
],
227227
},
228228
{
229-
identifier: "PlaceOfWork",
229+
identifier: "place_of_work",
230230
attribute: "Place of work",
231231
nested: [
232-
{ identifier: "PlaceOfWork.company", attribute: "Company" },
233-
{ identifier: "PlaceOfWork.flag_base_home_state", attribute: "Flag base home state" },
234-
{ identifier: "PlaceOfWork.company_id", attribute: "Company ID" },
235-
{ identifier: "PlaceOfWork.id_type", attribute: "Type of ID" },
236-
{ identifier: "PlaceOfWork.street", attribute: "Street" },
237-
{ identifier: "PlaceOfWork.town", attribute: "Town" },
238-
{ identifier: "PlaceOfWork.postal_code", attribute: "Postal code" },
239-
{ identifier: "PlaceOfWork.country_code", attribute: "Country code" },
232+
{ identifier: "place_of_work.company", attribute: "Company" },
233+
{ identifier: "place_of_work.flag_base_home_state", attribute: "Flag base home state" },
234+
{ identifier: "place_of_work.company_id", attribute: "Company ID" },
235+
{ identifier: "place_of_work.id_type", attribute: "Type of ID" },
236+
{ identifier: "place_of_work.street", attribute: "Street" },
237+
{ identifier: "place_of_work.town", attribute: "Town" },
238+
{ identifier: "place_of_work.postal_code", attribute: "Postal code" },
239+
{ identifier: "place_of_work.country_code", attribute: "Country code" },
240240
],
241241
},
242242
]
@@ -257,12 +257,13 @@ export const PDA1_ATTESTATION: AttestationDefinition = {
257257
identifier: "validity_period",
258258
attribute: "Validity period",
259259
nested: [
260-
{ identifier: "validity_period.starting_date", attribute: "Starintg date" },
260+
{ identifier: "validity_period.starting_date", attribute: "Staring date" },
261261
{ identifier: "validity_period.ending_date", attribute: "Ending date" },
262262
],
263263
},
264264
]
265265
},
266+
{ identifier: 'status_confirmation', attribute: 'Status confirmation'},
266267
{ identifier: 'document_id', attribute: 'Document Identifier'},
267268
{
268269
identifier: "competentInstitution",

src/app/features/invoke-wallet/components/presentations-results/presentations-results.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class PresentationsResultsComponent implements OnInit {
7575
data: {
7676
attestation: attestation
7777
},
78-
height: '40%',
78+
height: '70%',
7979
width: '60%',
8080
});
8181
}

src/app/features/invoke-wallet/components/view-attestation/view-attestation.component.html

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,50 @@
1-
21
<h2 mat-dialog-title>{{attestation.name}}</h2>
32

43
<mat-dialog-content class="mat-typography">
54
<mat-tab-group>
65
<mat-tab label="Attributes">
76
<mat-list>
8-
<mat-list-item *ngFor="let option of attestation.attributes">
7+
<mat-list-item *ngFor="let option of attestation.attributes; trackBy: trackByFn">
98
<span matListItemTitle>
109
<strong>{{option.key}}</strong>
1110
</span>
1211
<span matListItemLine>
13-
<span [innerHTML]="option.value"></span>
12+
<ng-container *ngIf="isJson(option.value); else plainText">
13+
<!-- Recursive template for nested JSON -->
14+
<ng-container *ngTemplateOutlet="recursiveJson; context:{ $implicit: parseJson(option.value), level: 0 }"></ng-container>
15+
</ng-container>
16+
<ng-template #plainText>
17+
<span>{{option.value}}</span>
18+
</ng-template>
1419
</span>
1520
</mat-list-item>
1621
</mat-list>
22+
23+
<!-- Recursive template definition -->
24+
<ng-template #recursiveJson let-value let-level="level">
25+
<ng-container *ngIf="isObject(value)">
26+
<div *ngFor="let item of objectToKeyValue(value); trackBy: trackByFn" class="nested-attributes">
27+
<span><strong>{{item.key}}</strong>:</span>
28+
<ng-container *ngIf="isObject(item.value) || isArray(item.value)">
29+
<ng-container *ngTemplateOutlet="recursiveJson; context:{ $implicit: item.value, level: level + 1 }"></ng-container>
30+
</ng-container>
31+
<ng-container *ngIf="!isObject(item.value) && !isArray(item.value)">
32+
{{item.value}}
33+
</ng-container>
34+
</div>
35+
</ng-container>
36+
<ng-container *ngIf="isArray(value)">
37+
<div *ngFor="let item of value; let i = index; trackBy: trackByFn" class="nested-attributes">
38+
<strong>[{{i}}]</strong>:
39+
<ng-container *ngIf="isObject(item) || isArray(item)">
40+
<ng-container *ngTemplateOutlet="recursiveJson; context:{ $implicit: item, level: level + 1 }"></ng-container>
41+
</ng-container>
42+
<ng-container *ngIf="!isObject(item) && !isArray(item)">
43+
{{item}}
44+
</ng-container>
45+
</div>
46+
</ng-container>
47+
</ng-template>
1748
</mat-tab>
1849

1950
<mat-tab *ngIf="attestation.metadata && attestation.metadata.length > 0" label="Metadata">
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@use '/src/template' as temp;
2+
3+
:host {
4+
.nested-attributes {
5+
padding-left: 1em;
6+
}
7+
}
Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,79 @@
1-
import {ChangeDetectionStrategy, Component, inject, OnInit} from "@angular/core";
2-
import {CommonModule} from "@angular/common";
3-
import {SharedModule} from "@shared/shared.module";
4-
import {JWTService} from "@core/services/jwt.service";
5-
import {Single} from "@core/models/presentation/PresentedAttestation";
6-
import {MatExpansionModule} from "@angular/material/expansion";
7-
import {MatListModule} from "@angular/material/list";
8-
import {MAT_DIALOG_DATA, MatDialogModule} from "@angular/material/dialog";
9-
import {DialogData} from "@features/invoke-wallet/components/view-attestation/model/DialogData";
10-
import {MatButtonModule} from "@angular/material/button";
11-
import {MatTabsModule} from "@angular/material/tabs";
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
inject,
5+
OnInit,
6+
} from '@angular/core';
7+
import { CommonModule } from '@angular/common';
8+
import { SharedModule } from '@shared/shared.module';
9+
import { JWTService } from '@core/services/jwt.service';
10+
import { Single } from '@core/models/presentation/PresentedAttestation';
11+
import { MatExpansionModule } from '@angular/material/expansion';
12+
import { MatListModule } from '@angular/material/list';
13+
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
14+
import { DialogData } from '@features/invoke-wallet/components/view-attestation/model/DialogData';
15+
import { MatButtonModule } from '@angular/material/button';
16+
import { MatTabsModule } from '@angular/material/tabs';
1217

1318
@Component({
14-
selector: 'vc-view-attestation',
15-
imports: [CommonModule, SharedModule, MatExpansionModule, MatListModule, MatDialogModule, MatButtonModule, MatTabsModule],
16-
templateUrl: './view-attestation.component.html',
17-
providers: [JWTService],
18-
changeDetection: ChangeDetectionStrategy.OnPush
19+
selector: 'vc-view-attestation',
20+
imports: [
21+
CommonModule,
22+
SharedModule,
23+
MatExpansionModule,
24+
MatListModule,
25+
MatDialogModule,
26+
MatButtonModule,
27+
MatTabsModule,
28+
],
29+
templateUrl: './view-attestation.component.html',
30+
styleUrls: ['./view-attestation.component.scss'],
31+
providers: [JWTService],
32+
changeDetection: ChangeDetectionStrategy.OnPush,
1933
})
20-
export class ViewAttestationComponent implements OnInit{
21-
34+
export class ViewAttestationComponent implements OnInit {
2235
readonly data = inject<DialogData>(MAT_DIALOG_DATA);
2336
attestation!: Single;
2437

25-
ngOnInit (): void {
38+
ngOnInit(): void {
2639
this.attestation = this.data.attestation;
2740
}
41+
42+
isJson(str: string): boolean {
43+
// try to parse the string as JSON
44+
if (!str.trim().startsWith('{') && !str.trim().startsWith('[')) {
45+
return false;
46+
}
47+
try {
48+
JSON.parse(str);
49+
return true;
50+
} catch (e) {
51+
return false;
52+
}
53+
}
54+
55+
parseJson(value: string): any {
56+
try {
57+
return JSON.parse(value);
58+
} catch (e) {
59+
console.error('Invalid JSON string:', value);
60+
return {};
61+
}
62+
}
63+
64+
isObject(value: any): boolean {
65+
return value !== null && typeof value === 'object' && !Array.isArray(value);
66+
}
67+
68+
isArray(value: any): boolean {
69+
return Array.isArray(value);
70+
}
71+
72+
objectToKeyValue(obj: any): {key: string, value: any}[] {
73+
return Object.keys(obj).map(key => ({key, value: obj[key]}));
74+
}
75+
76+
trackByFn(index: number, data: any) {
77+
return data.key + index;
78+
}
2879
}

0 commit comments

Comments
 (0)