common-header-lib
v0.20.14
Published
Angular 20 shared header library for the Arcon platform. Provides the application header bar, LOB selector, user sidebar, notification panel, mailbox panel, waffle (9-dots) app panel, general configuration drawer, and all supporting services.
Readme
common-header-lib
Angular 20 shared header library for the Arcon platform. Provides the application header bar, LOB selector, user sidebar, notification panel, mailbox panel, waffle (9-dots) app panel, general configuration drawer, and all supporting services.
Current published version: 0.20.14
What's New
0.20.14
- Icons for new modules (CI-27194) — two new SVG icons in
assets/images/for newly added modules. The 9-dots app panel loads module icons by file name, so the tiles for these modules now show their own icon. No API, configuration or behaviour change; host apps only need the usualnode_modules/common-header-lib/assetsasset entry inangular.json.
0.20.13
- Change Password encryption via XKEDSync (CI-27005) — General Configuration → Change Password now encrypts the same way as the Workspace UI. Opening the screen fetches the public key from
xaccess/api/SecureAccess/XKEDSync({ type: 0 }, stored inLoggedInUserInfo.privateKey); the old and new password sent toSetNewPassword, and the new password sent to the liveValidatePasswordpolicy check, are encrypted with it throughEncrpdataService.getEncrpytedData()(previously AES with the001-SAC-GECsession key). If the key has not arrived, submit shows "Session Token Error!" and does not call the API, and the live check is skipped (the server still validates on submit). Theenchtheader and every other encrypted call are unchanged. - Change Password — WCAG focus — the three password fields now show the focus ring (the
.p-inputtext:enabled:focusoverride had hidden it), and each show-password (eye) toggle draws the ring on its icon. Tab order is now field → its eye toggle (the toggle used to come first). Newlib-change-password-formclass on the form. - User sidebar close button — WCAG focus — shows the blue ring as an inset shadow; the sidebar's
border: nonehad left only the pale glow. The button and icon do not move. - LOB dropdown overlay width — the overlay root gets the new
lib-ui-lob-ddl-overlayclass (throughoverlayOptions.styleClass) withwidth: 140px !important, so a host-app.p-overlay { width: 250px }no longer widens the LOB list.
0.20.12
- Proxy Access Token — Download button — outlined in the library's primary colour at rest (white background; primary text, border and icon), filled primary with white text and icon on hover (was always filled). New
lib-download-btnclass; the icon is a mask that follows the text colour. - Oracle proxy certificate (CI-23566) — Oracle added to the SSL/TLS certificate list. The component keeps its own "failed to download" toast: unlike the Workspace UI, the library has no app-wide error handler that would show the API's message instead.
- General Configuration — sticky left menu — the menu (
.left-card) stays in view while a long screen such as the certificate list scrolls the dialog. - No "Required field" tooltip — the native tooltip on the red
*of required fields is removed in every General Configuration screen (Preference Path, Change Password, Preferred Timezone, Landing Page). The*keeps itsaria-label, so screen readers still announce "required". - LOB dropdown focus — the selected LOB text no longer shifts 1px when the dropdown gets the keyboard focus ring (the ring is drawn as an inset shadow, not a border). The dropdown's size now comes from SCSS (
height: 30px, width 140px) instead of an inline[style].
0.20.11
- Session-end routing (
SessionEndService, CI-26608) — new exported service that decides where the user goes when a session ends, so the header and every host app follow one rule.- The header's Logout button calls the logout API, then
SessionEndService.redirect(result). This replaces the oldidentitycheck, which sent default-identity-provider users to the login page (signing them straight back in) and Windows users to a/logoutrouter path most host apps do not have. - Host apps should call
SessionEndService.endSession()from their HTTP interceptor (302 / 401 / 419 or a missing token) and from their 403 page. It callsxaccess/api/SecureAccess/cookiesUpdationonce (parallel failures are deduplicated), clears local and session storage, and redirects. - Destination:
loginMethod1 → login page (origin/#/login), 2 →origin/#/logout?mode=windowsAuth, 3 →origin/#/logout?mode=defaultIdp. Without a validloginMethod— the logout API does not send it, onlycookiesUpdationdoes —identity, thenssoDefaultLogindecide; otherwise the login page. - Also exported:
readSessionEndAction,loginPageUrl,logoutScreenUrland theSessionEndActiontype. - Host-app usage and examples: see Session end in host applications.
- The header's Logout button calls the logout API, then
- SSO / MFA logout via
spRoute(CI-15352, CSP-safe per CI-25594) — when the logout response carriesspRoute(the identity provider's auto-submit logout page), the header replays its form in the same window without executing the response HTML (new internalExternalFormPostService;javascript:/data:form actions are refused), so it also works underscript-src 'self'. The session-end redirect is skipped in that case: from a host app it navigates to another page, which would cancel the form submission. WithoutspRoute, logout is unchanged. - Mail table header stays pinned, with its borders — PrimeNG 20 puts an inline
overflow: autoon the table container, which made the sticky header scroll away with the rows;.mail-tablesets it back tooverflow: visible. The table also moved fromborder-collapse: collapsetoseparate, with the frame and row lines drawn on the cells: collapsed borders do not move with a sticky header, so its top border scrolled away and row text showed above it. Mail table only. - Notification panel spacing matches the Workspace UI — header height (48px), title, close-button and content padding, and the hint position now match the Workspace notification sidebar; the close icon is the same
close.svg. The ticket icon keeps its 40px circle when an event name wraps to two lines. Notification panel only. - Class rename:
changingContent→lib-changing-content— the mailbox overlay wrapper now follows the library'slib-class prefix. Host apps that styled.changingContentmust switch to.lib-changing-content. - Focus ring corners — the
.lib-ui :focus-visiblering now hasborder-radius: 4pxon every focusable element inside the header.
0.20.10
- Subject filter "Contains" dropdown padding fixed — in the mail table's Subject column-filter popover, the match-mode dropdown's label lost its
0 10pxpadding and13pxfont size to competing PrimeNG/host styles, so the text sat against the control's edge. Both values are now!important. The rule stays scoped to.lib-ui.mail-filter-overlay, so no host-app dropdown is affected.
0.20.9
Merge commit 9ae222452a0190e8197898c217b703d746c8d957 (MR !18, from 742084b).
- Mail table column-resize line is blue — the vertical guide shown while dragging a mail-table column edge (PrimeNG's
.p-datatable-column-resize-indicator) used the host theme's primary color (green in some hosts). It now uses the mail UI accent#0073EA. Scoped to.mail-tablewith a compile-time SCSS value — no CSS variable or PrimeNG theme token is changed, so host-app tables keep their own color.
0.20.8
showLobChangeConfirmationinput (defaultfalse) — gates whether picking a new LOB from the dropdown shows a confirmation dialog. Previously this was always shown; now it's opt-in via[showLobChangeConfirmation]="true". Default behavior commits the LOB change immediately on selection.setCustomLobId()is unaffected — it never showed a dialog regardless of this input.- Badges clickable — the mailbox and notification unread-count badges now open the same panel as their icon buttons.
- Notification redirect (CI-24494) — each notification row opens that event's pending approvals in Identity Hub in a new tab (
{baseURL}/xaccess/api/WorkspaceGeneric/workflownotify?eventName=…). Rows are keyboard-operable (Enter/Space) with a translatedaria-label. Only events withPendingCount > 0are listed; a hint line sits above the list and an empty-state message shows when nothing is pending. - Vaulted View Password (CI-24670) — if the
OpenServicePwdresponse carriesisVaulted: true, the dialog shows the backend's message (fromsshKeyPassPhrase), an "Acknowledge" header and a single OK button instead of the password, Copy and Vault controls. A missing/falseisVaultedkeeps the previous behavior. - Mailbox Asset/DI details (CI-26604) — the Subject column splits at
(UTC)into a bold title line and a secondary asset/DI line. The table also gets a sticky header, resizable columns, and a server-side Subject "contains" filter (sent asfilters: [{ field: 'subject', operator: 'contains', value }]in theMailBoxrequest body, only when a value is entered). - Mail UI accent — the mail table and its filter popover use
#0073EA(resize handle hover, filter states, Apply button, focus borders, 3px read-row bar) via local SCSS variables.--primary-coloris not changed, so the rest of the header and host apps are unaffected. The filter popover is appended to<body>by PrimeNG and is scoped with its ownlib-ui mail-filter-overlayclass (PrimeNGptAPI), so its styles never reach host-app tables. - Fixes — a failed
MailBoxrequest now shows the "something went wrong" page instead of a misleading "No mails match the applied filters"; the filter icon no longer gets clipped at the column edge; View Password button is re-checked in code so removingdisabledin dev tools can't open it; eye icon no longer overlaps the password or Copy button; certificate Download button always visible; 9-dots focus race fixed. - i18n — 7 new
Generic_libkeys (New,EventRequests,NoPendingEventRequests,NotificationRedirectHint,Ok,Acknowledge,NoMatchingMails) in all 8 languages. de/es/fr/hi/ja/ko text should get native-speaker review.
0.20.7
- LOB dropdown truncation: long LOB names now truncate with an ellipsis instead of overflowing the 140px trigger untruncated. Also fixes a vertical-centering regression the ellipsis fix introduced (PrimeNG's own un-overridden
.p-select-labelpadding was fighting the newline-height-based centering) and addsmargin-left: 8pxfor spacing. - Mail pagination dropdown regression fixed: the rows-per-page dropdown inside the mailbox's paginator was falling back to raw, unstyled PrimeNG defaults instead of this library's theme. Root cause:
UserMailboxComponent's.mail-contentwrapper never received thelib-uimarker class added by the 0.20.4 dialog-scoping pass — a gap in that original commit, not a new leak. - Comprehensive Jasmine/Karma test suite added across the library and the test app (previously the Karma harness didn't execute any tests at all — a test-infrastructure-only fix, no production code affected). Found and fixed 4 real defects along the way:
LandingpgConfigComponent.ngOnInit()ordering, a missing null-guard inViewPasswordPopupComponentthat could throw if dialog data was malformed, a leaked polling interval inEventService.checkMFA(), and a no-op call inDateDiffPipe. Removed one confirmed-dead method (LoggedInUserInfo.setPrivateKeyFromBigInts). library-integration-guide.md: documented a known issue where the Workspace API's/workflow/api/Workflow/Workflow/endpoint returns 503, with an interceptor workaround to prevent it from being misclassified as a session/auth error.
0.20.6
- 9-dots app panel keyboard navigation: full WAI-ARIA APG roving-focus grid — Arrow keys + Home/End move between app tiles,
aria-haspopup="dialog"on the trigger. Tab past the last tile continues the user's forward tab traversal to the next page element instead of looping back onto the trigger; Shift+Tab out the front still returns to the trigger. - General Configuration tab buttons now carry
aria-pressed. - New
_accessibility.scss: the library's WCAG 2.4.7 focus-visible ring is now properly scoped to.lib-ui— previously an unscoped rule incommon-header-lib.component.scssleaked its focus styling onto every focusable element on the host page (ViewEncapsulation.None). - Dropdown focus ring fixed: PrimeNG's
p-selectputs keyboard-focus handling on an inner label span, not the outer box containing the dropdown chevron, so the ring previously stopped short of the chevron. Redirected via:has(.p-select-label:focus-visible)so the ring wraps the whole control; also removed a "double border" artifact from a pre-existing.p-focusrule. lib-uiadded toSidePopupComponentandConfirmDialogComponent— both were missing this marker class, so their contents fell back to the browser's raw default focus outline instead of this library's own ring.- Dialogs now render inside
<lib-app-header>instead of<body>: the LOB confirmation dialog, General Configuration, and Password/View Password dialogs previously escaped to<body>, so a consuming app could not target/style them with a scoped CSS selector. NewDialogHostServicegives them a reference to<lib-app-header>'s own host element to append into instead.SidePopupComponentalready rendered inline and is unaffected. SliceToTwoPipefix: hyphens are now treated as a name separator alongside spaces and underscores —"Amit-Wagh"previously produced"AM"instead of the correct"AW".
0.20.5
- Dialog dragging disabled — the LOB change confirmation dialog and both
DialogService-opened dialogs (General Configuration, Password/View Password) now have dragging explicitly disabled. PrimeNG defaultsConfirmDialogtodraggable=true, andDynamicDialogConfig's class-field default ofdraggable=falseis never applied byDialogService.open()(it forwards the caller's plain config object, and PrimeNG's own template checks!== false, soundefinedresolved to draggable).SidePopupComponentwas already non-draggable. - Password/View Password dialog border-radius consolidated to a single rule on the dialog root (
overflow: hidden), replacing separate top/bottom-corner overrides that could go out of sync. - Toast title:
Generic_lib.Successfulnow reads "Success" in English (other 7 languages unchanged; word choice already matched "Success" there). - Mailbox table: removed the icon column (mail/flag/lock icons served no function) — table is now 4 columns.
- i18n key rename:
ArconLearningCenter→ArconCIResourceCenter, displayed as "ARCON CI Resource Center" (English) across all 8 language files. The sidebar menu item's icon and click behavior are unchanged — only the label. - 9-dots app panel accessibility: the waffle popover now moves focus to the first app tile on open, traps Tab/Shift+Tab within the tiles while open, and restores focus to the 9-dots button on close.
- Mailbox "read" row: recolored to background
#CCE5FFwith a#0073EAleft border (previously border-only). _mailbox.scssscoping fix:.p-datatable-tableand.p-paginator-page.p-paginator-page-selectedare now scoped under.mail-content, closing a global-leak gap the 0.20.4 dialog-scoping pass did not cover.- Deferred:
appendTo="body"forSidePopupComponentwas requested but held back pending the parent project's specific DOM-placement symptom — no change shipped this release.
0.20.4
- Dialog/PrimeNG style scoping (breaking-leak fix) —
_dialogs.scssand_primeng.scssused to override PrimeNG's own global classes (.p-dialog,.p-confirmdialog,.p-select,.p-inputtext,.p-multiselect,.p-checkbox,.p-tooltip, etc.) with no library-specific scoping, so a host application's own unrelated PrimeNG dialogs/controls inherited this library's styling. Every one of those overrides is now scoped behind one of two marker classes the library applies to its own elements:lib-dialog— applied to every dialog-family root the library renders (SidePopupComponent,ConfirmDialogComponent, and theDialogServiceconfigs for General Configuration / View Password).lib-ui— applied to the header's own root and the same dialog roots, covering everything else (selects, inputs, tooltips, etc.).- Host applications are unaffected unless they happen to use those exact marker classes themselves.
- LOB change confirmation dialog gets its own class — the confirm-change-of-LOB dialog now carries
lib-lob-change-confirm-dialog, so a parent project can target it directly even though it renders outside the component's own DOM (a plain ancestor selector wouldn't otherwise reach it). The header shows by default; a host application that wants it hidden adds its own.lib-lob-change-confirm-dialog .p-dialog-header { display: none !important; }. Its close (X) button has a transparent background/border by default, scoped to this dialog only. - New input:
lobReadOnly(boolean, defaultfalse) — makes the LOB dropdown non-interactive without hiding it (independent ofEnableLOB, which controls visibility). Enforced at every layer that can mutate the LOB — the dropdown, the confirm dialog,setCustomLobId(), and the internal persistence service — not just a disabled attribute, so it holds up against dev-tools tampering including editinglocalStorageand reloading the page. Seelibrary-integration-guide.mdfor the full behavior and its documented limits. SidePopupComponent/ConfirmDialogComponenteach gained an optionalstyleClassinput so consumers can layer additional classes onto the underlyingp-dialog/p-confirmDialogroot (backward compatible).- Demo app (
src/app/app.component.tsin this workspace) now includes a working example of thesetCustomLobId()public method via a@ViewChild('libAppHeaderRef')reference, matching the pattern documented inlibrary-integration-guide.md.
0.20.3
- Added the Proxy Access Token feature as a new tab inside
GeneralConfigurationComponent(ProxyAccessTokenConfigurationComponent) — generate/regenerate a proxy access token, copy it, and download SSL/TLS certificates per database type.
Architecture
Entry point
src/public-api.ts — exports the module, all components, services, pipes, and models consumed by host applications.
Module
CommonHeaderLibModule — declares and exports all components. Import this in your host AppModule.
Main component
CommonHeaderLibComponent — selector lib-app-header. Thin coordinator that composes all child components and wires the two component-scoped services.
<lib-app-header
[EnableLOB]="true"
[showAllLOB]="false"
(lobSelected)="onLobSelected($event)"
(timezoneDetails)="onTimezone($event)"
(getBgImage)="onBgImage($event)"
></lib-app-header>Child components
| Component | Selector | Responsibility |
|---|---|---|
| AppPanelComponent | app-app-panel | 9-dots waffle popover, app list, per-app pre-connection validation |
| NotificationPanelComponent | app-notification-panel | Notification sidebar, event-wise count, SignalR sync |
| UserSidebarComponent | app-user-sidebar | User profile drawer, password change, logout |
| UserMailboxComponent | app-user-mailbox | Mail inbox with data table and pagination |
| GeneralConfigurationComponent | app-general-configuration | Settings drawer — timezone, landing page, language |
| ViewPasswordPopupComponent | app-viewpassword-popup | View stored password dialog |
| SidePopupComponent | app-side-popup | Reusable PrimeNG drawer wrapper |
| ConfirmDialogComponent | app-confirm-dialog | Reusable confirmation dialog wrapper |
| NoServiceMyAccessPgComponent | app-no-service-my-access-pg | Empty / error state placeholder |
| NgTranslationComponent | app-ng-translation | Language switcher |
| GridPaginationComponent | app-grid-pagination | Reusable paginator |
Component-scoped services
These are provided directly on CommonHeaderLibComponent (providers: [HeaderDataService, LobService]) — they live and die with the component instance.
| Service | Responsibility |
|---|---|
| HeaderDataService | forkJoin data loading (logo, user info, timezone, notification count, email count), SignalR / timezone / email / translate listeners, all header state as Angular signals |
| LobService | LOB list fetch, localStorage pre-selection restore, confirm / revert / validate LOB change |
Other services (singleton, provided in root or module)
HttpCommonService, HttpGeneralConfigService, RestLoginFormService, RestSignalrService, EventService, ToastMsgService, StorageService, CookieStorageManagerService, EncrpDataService, EncryptDecryptRsaService, AsyncPasswordcheckService, VaptValidationService, SessionEndService (exported for host apps — see 0.20.11), ExternalFormPostService (internal, spRoute logout)
Inputs & Outputs
CommonHeaderLibComponent
| Name | Type | Direction | Description |
|---|---|---|---|
| EnableLOB | boolean | Input | Show/hide the LOB dropdown |
| showAllLOB | boolean | Input | Include "All LOBs" option in the list |
| lobReadOnly | boolean | Input | (new in 0.20.4) Make the LOB dropdown non-interactive without hiding it — see library-integration-guide.md |
| lobSelected | LobSelectedEvent | Output | Fired when the user confirms a LOB change |
| timezoneDetails | TimezoneEvent | Output | Fired when the user saves a timezone |
| getBgImage | any | Output | Fired when a background image is configured |
Session end in host applications (SessionEndService)
The header handles its own Logout button. When the session expires, though, it is the host application that notices: its API calls fail in its own HTTP interceptor and never reach the header. In those places call SessionEndService.endSession() instead of redirecting yourself, so every module sends the user to the same place.
When to call it
| Where in the host app | When | |---|---| | HTTP interceptor | A response means the session has ended: 302, 401 or 419 (or the token is missing) | | 403 error page | After its countdown, instead of going to the login page |
Do not call it for the header's Logout button — the header already does that through the same service.
What it does
POST {page origin}/xaccess/api/SecureAccess/cookiesUpdationwith{ actiontype: 'd', keys: ['-100'] }(delete all server-side cookies) — the same call host apps made before.On a
successresponse it clearslocalStorageandsessionStorage, then sends the user whereresultsays:|
result| Destination | |---|---| |loginMethod: 1| Login page —{origin}/#/login| |loginMethod: 2| Logout screen —{origin}/#/logout?mode=windowsAuth| |loginMethod: 3| Logout screen —{origin}/#/logout?mode=defaultIdp| | no validloginMethod|identity: true→ Windows logout screen; elsessoDefaultLogin: true→ default-IDP logout screen; else the login page |A response that is not
success→ storage cleared, login page. If the call itself fails → login page, storage left as is.
The navigation is a full page load to the site root (the Workspace UI's login / logout pages), not a router navigation inside the host app.
It runs once per page: several requests failing together (e.g. five parallel calls getting 401) cause one cookiesUpdation call and one redirect. Later calls do nothing — including the one your interceptor makes if cookiesUpdation itself gets a 401, so there is no loop.
Requirements
HttpClientprovided in the host app (provideHttpClient(...)). ThecookiesUpdationcall goes through the host's own interceptors.- The call uses the page's origin, not the
_WAUWeb API URL.
Example — HTTP interceptor (Angular 20, functional)
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, throwError } from 'rxjs';
import { SessionEndService } from 'common-header-lib';
/** Responses that mean the session has ended. */
const SESSION_ENDED_STATUSES = [302, 401, 419];
export const sessionEndInterceptor: HttpInterceptorFn = (req, next) => {
const sessionEnd = inject(SessionEndService);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (SESSION_ENDED_STATUSES.includes(error.status)) {
sessionEnd.endSession();
}
return throwError(() => error);
}),
);
};// app.config.ts (standalone) — next to the library's own interceptor
provideHttpClient(withInterceptors([httpErrorInterceptor, sessionEndInterceptor])),If the host already has an interceptor with this check, keep its status check and replace its own redirect (the old if (identity) … else … block) with sessionEnd.endSession(). In an NgModule host with a class-based interceptor (HTTP_INTERCEPTORS), inject SessionEndService the same way and call the same method.
Example — 403 page
import { inject } from '@angular/core';
import { SessionEndService } from 'common-header-lib';
export class ForbiddenPageComponent {
private readonly sessionEnd = inject(SessionEndService);
// When the countdown reaches zero (instead of navigating to the login page):
private onCountdownFinished(): void {
this.sessionEnd.endSession();
}
}Lower-level exports
For a host that needs the decision without the navigation:
| Export | Purpose |
|---|---|
| readSessionEndAction(result) | Returns 'login', 'windowsAuth' or 'defaultIdp' for a cookiesUpdation / logout result |
| loginPageUrl(origin) / logoutScreenUrl(origin, mode) | The destination URLs above |
| SessionEndService.redirect(result) | Navigates for a result you already have (what the header's Logout uses) |
Angular patterns used
input()/output()/model()signal-based bindings (Angular 17+)viewChild.required()signal queriesinject()for all dependency injection (no constructor injection)effect()+untracked()for reactive side effectstakeUntilDestroyed(DestroyRef)for subscription teardownChangeDetectionStrategy.OnPushon all componentsViewEncapsulation.Noneon all components (styles are global, scoped by class names)forkJoinfor parallel API calls on header init- Bridge-signal pattern: services expose
signal<T>that the component'seffect()watches to emitoutput()events
SCSS structure
All global styles are in src/scss/. The main component imports styles.scss which forwards all partials.
| File | Contents |
|---|---|
| styles.scss | Orchestrator — @use of all partials |
| common-color.scss | CSS custom properties (design tokens) |
| _primeng.scss | PrimeNG component overrides |
| _dialogs.scss | Modal and dialog overrides |
| _sidebar.scss | Sidebar layout |
| _mailbox.scss | Mail table and pagination |
| _toast.scss | ngx-toastr styles |
| _utilities.scss | Shared utility classes |
Component-specific styles live alongside each component in their own .component.scss file (ViewEncapsulation.None).
Security notes
- The HTTP interceptor (
http-error-interceptor.service.ts) rejects all outbound requests when the001-SAC-GECkey is absent from localStorage and throwsUnauthorized. This is intentional security design — do not remove or bypass it. - The AES encryption key stored in localStorage is provided by the host platform and is required to run the library. This is an accepted design decision.
lobReadOnly(0.20.4) is a defense-in-depth client-side guard, not an absolute one — seelibrary-integration-guide.mdfor exactly what it does and does not protect against.
Build
ng build common-header-lib
# Output: dist/common-header-libPublish
ng build common-header-lib
cd dist/common-header-lib
npm publishGenerate a new component
ng generate component component-name --project common-header-libUnit tests
ng test common-header-lib