Application Shell
Every authenticated route renders inside BaseComponent
(src/app/views/theme/base/). The shell owns the navigation, the page title, the
global banners and the session-wide subscriptions — a feature page only supplies
the router-outlet content.
Composition
|
|
|
|
|
Top bar, plus |
|
Page title, description and breadcrumbs. |
|
Footer with the build version. |
|
Logo and white-label branding. |
|
Writes layout CSS classes onto |
|
The bootstrap loading screen. |
BaseComponent
Two things happen in the constructor, before any lifecycle hook:
constructor(/* … */) {
super();
this.baseService.loadConfigs(); (1)
this.globalSubscriptionsService.init(); (2)
}
| 1 | Loads LayoutConfig, MenuConfig and PageConfig into their services and
applies the resulting body classes. |
| 2 | Starts every session-scoped background subscription. Idempotent — see Architecture Overview. |
|
The shell is where the session actually starts
|
ngAfterViewInit then performs the post-render work:
|
Drops dashboard-message dismissals older than seven days. |
|
Fetches and shows the server-driven announcement banner. |
|
Applies |
|
Re-runs |
sidebarFixer() reaches into two private Material fields:
(this.sidenavContainer as any)._ngZone.onMicrotaskEmpty
.pipe(debounceTime(10))
.subscribe(() => {
this.sidenavContainer.updateContentMargins();
(this.sidenavContainer as any)._changeDetectorRef.markForCheck();
});
|
|
Top bar versus banners
The onboarding status bar and the global banners are mutually exclusive:
this.topBarService.details$.subscribe(details => {
if (!details) {
this.canBannerBeVisible = true;
return;
}
this.hasTopBar = details.showStatusBar;
this.canBannerBeVisible = !details.showStatusBar;
this.cd.detectChanges();
});
A supplier still in onboarding sees the progress bar; an activated supplier sees banners. See Onboarding.
Dashboard announcement banners
BaseComponentService reads DashboardMessageService.getSupplierDashboardMessage()
and shows anything whose config_key starts with supplier_dashboard_message, as
a BannerTypes.ANNOUNCEMENT banner. Dismissal is recorded in localStorage under
the message key, and messageClosedWeekAgo() expires those records after seven
days.
|
One message is deliberately un-dismissable
The exact key |
Configuration objects
Three plain classes in core/_config/ describe the shell. They are loaded into
matching services by BaseComponentService.loadConfigs().
| Config | Service | Describes |
|---|---|---|
|
|
Skins, fixed/fluid widths, logos, the splash-screen loader, and a JS colour palette. |
|
|
The navigation trees. |
|
|
Per-route title, description and breadcrumb text. |
HtmlClassService.setConfig() turns the layout config into <body> classes.
Three of them are hardcoded rather than derived:
private initSubheader() {
document.body.classList.add('kt-subheader--overlaped');
document.body.classList.add('kt-subheader--primary');
}
private initSkins() {
document.body.classList.add('pyb-aside-dark');
}
Changing subheader.style or aside.self.skin in LayoutConfig therefore has no
effect on the initial render — only SubheaderService.changeStyle() /
changeColor(), which swap the classes at runtime, do.
Menu configuration
MenuConfig defines two trees, header and aside. Each item is:
{
title: 'Seller Card',
root: true,
alignment: 'left',
page: URLS_ROUTER.SELLER_CARD.ROOT,
translate: 'MENU.SELLER_CARD',
permission: 'SELLER_CARD',
icon: 'pyb-credit-card',
}
|
Route path, always taken from |
|
i18n key for the label. |
|
Dotted |
|
|
|
Nested items, filtered independently. |
|
Renders a divider instead of a link. |
Permission filtering is done in the template, not in the service:
<ng-container *ngIf="!item.permission" [ngTemplateOutlet]="menuItemInnerTemplate" …></ng-container>
<ng-template *ngIf="item.permission" ngxPermissionsOnly="{{ item.permission }}">
<ng-container [ngTemplateOutlet]="menuItemInnerTemplate" …></ng-container>
</ng-template>
An item without permission is always visible; an item with one is wrapped in
*ngxPermissionsOnly. See Permissions System.
|
The header menu tree is configured but not rendered
The only live consumer is |
Product Offerings is filtered in code, not by permission
AsideLeftComponent removes the Product Offerings entry for suppliers who are not
yet activated:
private getMenuWithOrWithoutProductOfferings(list: any[], isActivated: boolean): any[] {
if (isActivated) {
return list;
}
const newList = [...list];
const productOfferingsIndex = newList.findIndex(o => o.page === 'product-offerings');
newList.splice(productOfferingsIndex, 1);
return newList;
}
The item is matched by the literal string 'product-offerings', so renaming the
route in URLS_ROUTER silently breaks the filter: findIndex returns -1 and
splice(-1, 1) removes the last menu item instead. Activation state comes from
TopBarService.details$.isActivated, not from a permission.
SubheaderService
Owns everything above the page content. It re-derives its state on every
NavigationEnd.
|
|
|
Resolved breadcrumb trail. |
|
Whole-subheader visibility. |
|
Compact variant, via |
|
Extra bottom padding — |
Titles resolve in this order:
-
a manual title previously set for this exact URL by
setTitle(), -
otherwise
PageConfigfor the cleaned URL, -
breadcrumbs from the header tree, falling back to the aside tree.
A single breadcrumb whose title matches the page title is dropped as redundant.
setTitle() is per-URL and permanent
The |
Responsive behaviour
MenuAsideService owns the drawer, driven by BreakpointService:
get sidenavMode() {
return this.breakPointService.isMobileOrTablet() ? 'over' : 'side';
}
On every breakpoint change the drawer is closed for mobile/tablet and opened for
desktop. BaseComponent.onMenuOptionClick() also closes it after a navigation on
small screens, so the menu does not stay over the page the user just opened.
Splash screen
The markup lives in src/index.html so it paints before Angular boots.
SplashScreenService.hide() fades it out over 800 ms after a 300 ms delay and
then removes the element, guarding with a stopped flag so a second call is a
no-op. The logo and message come from LayoutConfig.loader.
Adding a page to the shell
-
Add a
PageConfigentry for the route key so the subheader has a title. -
Add a
MenuConfigentry underaside.items, withpagefromURLS_ROUTER, atranslatekey, anicon, and apermissionif the page is gated. -
Add the translation keys to
src/assets/i18n/en.ts. Internationalization
See Adding a Feature for the full checklist.