feat(client+home): single-instance family relations + DSC verified badge

- Family member dialog: disable Father / Mother / Spouse in the Relationship
  dropdown when one is already added to the client. Son, daughter, brother
  etc. stay unrestricted. Editing a member keeps its own current relation
  selectable. Hint added below the field.
- Home welcome banner: render a small green "DSC Verified" pill next to the
  username when GET /v1/dsc/me returns at least one ACTIVE certificate.
  Tooltip surfaces the masked serial. Silent when no DSC is mapped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-12 18:16:20 +05:30
co-authored by Claude Opus 4.7
parent d175354f88
commit 6714897688
7 changed files with 174 additions and 4 deletions
+29
View File
@@ -25,11 +25,13 @@ import { AuthenticationService } from '../core/authentication/authentication.ser
import { PopoverService } from '../configuration-wizard/popover/popover.service';
import { ConfigurationWizardService } from '../configuration-wizard/configuration-wizard.service';
import { SettingsService } from 'app/settings/settings.service';
import { DscService } from 'app/users/dsc/dsc.service';
/** Custom Components */
import { NextStepDialogComponent } from '../configuration-wizard/next-step-dialog/next-step-dialog.component';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatAutocompleteTrigger, MatAutocomplete, MatOption } from '@angular/material/autocomplete';
import { MatTooltip } from '@angular/material/tooltip';
import { AsyncPipe, DatePipe, TitleCasePipe } from '@angular/common';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -45,6 +47,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
FaIconComponent,
MatAutocompleteTrigger,
MatAutocomplete,
MatTooltip,
AsyncPipe,
DatePipe,
TitleCasePipe
@@ -58,6 +61,7 @@ export class HomeComponent implements OnInit, AfterViewInit {
private configurationWizardService = inject(ConfigurationWizardService);
private popoverService = inject(PopoverService);
private settingsService = inject(SettingsService);
private dscService = inject(DscService);
/** Username of authenticated user. */
username: string;
@@ -65,6 +69,10 @@ export class HomeComponent implements OnInit, AfterViewInit {
tenant: string;
/** Today's date */
today = new Date();
/** True when the signed-in user has at least one ACTIVE DSC certificate mapped. */
dscMapped = false;
/** Masked serial of the ACTIVE DSC, surfaced in the welcome badge tooltip. */
dscSerial = '';
/** Activity Form. */
activityForm: any;
/** Search Text. */
@@ -95,6 +103,7 @@ export class HomeComponent implements OnInit, AfterViewInit {
this.username = credentials.username;
this.tenant = this.tenantIdentifier();
this.setFilteredActivities();
this.loadDscStatus();
if (!this.authenticationService.hasDialogBeenShown()) {
this.dialog.open(WarningDialogComponent, {
disableClose: false,
@@ -104,6 +113,26 @@ export class HomeComponent implements OnInit, AfterViewInit {
}
}
/**
* Surface a small "DSC verified" badge in the welcome banner when the
* signed-in user has at least one ACTIVE DSC certificate. Silent on
* error / when nothing is mapped — the badge simply doesn't render.
*/
private loadDscStatus(): void {
this.dscService.listMyDscs().subscribe({
next: (dscs) => {
const active = (dscs || []).find((d) => (d?.certStatus || '').toUpperCase() === 'ACTIVE');
if (active) {
this.dscMapped = true;
this.dscSerial = active.dscSerialNoMasked || active.dscSerialNoFull || '';
}
},
error: () => {
// Silent — endpoint may be unavailable; we just don't show the badge.
}
});
}
/**
* Sets filtered activities for autocomplete.
*/