oip-common 0.0.11 → 0.0.13

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.
@@ -1431,6 +1431,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1431
1431
  type: Input
1432
1432
  }] } });
1433
1433
 
1434
+ class SecurityService {
1435
+ }
1434
1436
  /**
1435
1437
  * SecurityService extends OidcSecurityService to manage authentication,
1436
1438
  * token handling, and user role access in an Angular application.
@@ -1438,7 +1440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1438
1440
  * It provides helper methods for checking authentication, managing tokens,
1439
1441
  * determining user roles, and performing logout and refresh operations.
1440
1442
  */
1441
- class SecurityService extends OidcSecurityService {
1443
+ class KeycloakSecurityService extends OidcSecurityService {
1442
1444
  /**
1443
1445
  * Initializes service and subscribes to authentication events.
1444
1446
  * When a 'NewAuthenticationResult' event is received, the `auth` method is called.
@@ -1464,6 +1466,18 @@ class SecurityService extends OidcSecurityService {
1464
1466
  this.auth();
1465
1467
  });
1466
1468
  }
1469
+ refreshToken() {
1470
+ throw new Error("Method not implemented.");
1471
+ }
1472
+ hasRole(role) {
1473
+ throw new Error("Method not implemented.");
1474
+ }
1475
+ getUserRoles() {
1476
+ throw new Error("Method not implemented.");
1477
+ }
1478
+ getCurrentUser() {
1479
+ return this.userData;
1480
+ }
1467
1481
  /**
1468
1482
  * Returns the ID token for the sign-in.
1469
1483
  * @returns A string with the id token.
@@ -1476,7 +1490,7 @@ class SecurityService extends OidcSecurityService {
1476
1490
  *
1477
1491
  * @returns {boolean} True if the user is an admin, false otherwise.
1478
1492
  */
1479
- get isAdmin() {
1493
+ isAdmin() {
1480
1494
  return this.payload.getValue()?.realm_access?.roles?.includes('admin');
1481
1495
  }
1482
1496
  /**
@@ -1518,10 +1532,10 @@ class SecurityService extends OidcSecurityService {
1518
1532
  return payload.exp < Math.floor(Date.now() / 1000);
1519
1533
  }));
1520
1534
  }
1521
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1522
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SecurityService }); }
1535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: KeycloakSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1536
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: KeycloakSecurityService }); }
1523
1537
  }
1524
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SecurityService, decorators: [{
1538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: KeycloakSecurityService, decorators: [{
1525
1539
  type: Injectable
1526
1540
  }], ctorParameters: () => [] });
1527
1541
 
@@ -1548,11 +1562,11 @@ class UserService {
1548
1562
  * Typically used for avatar display when a photo is unavailable.
1549
1563
  */
1550
1564
  get shortLabel() {
1551
- const data = this.securityService.userData;
1552
- return data.given_name[0] + data.family_name[0];
1565
+ const data = this.securityService.getCurrentUser();
1566
+ return (data) ? data.given_name[0] + data.family_name[0] : "";
1553
1567
  }
1554
1568
  get userName() {
1555
- const data = this.securityService.userData;
1569
+ const data = this.securityService.getCurrentUser();
1556
1570
  return `${data.given_name} ${data.family_name}`;
1557
1571
  }
1558
1572
  /**
@@ -1560,7 +1574,7 @@ class UserService {
1560
1574
  * and updates the `photo` and `photoLoaded` properties accordingly.
1561
1575
  */
1562
1576
  getUserPhoto() {
1563
- const url = `${this.baseDataService.baseUrl}api/user-profile/get-user-photo?email=${this.securityService.userData.email}`;
1577
+ const url = `${this.baseDataService.baseUrl}api/user-profile/get-user-photo?email=${this.securityService.getCurrentUser().email}`;
1564
1578
  this.baseDataService.getBlob(url).then((data) => {
1565
1579
  this.createImageFromBlob(data);
1566
1580
  this.photoLoaded = true;
@@ -1608,101 +1622,102 @@ class AppTopbar {
1608
1622
  }
1609
1623
  }
1610
1624
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AppTopbar, deps: [{ token: LayoutService }], target: i0.ɵɵFactoryTarget.Component }); }
1611
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: AppTopbar, isStandalone: true, selector: "app-topbar", ngImport: i0, template: ` <div class="layout-topbar">
1612
- <div class="layout-topbar-logo-container">
1613
- <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
1614
- <i class="pi pi-bars"></i>
1615
- </button>
1616
- <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
1617
- <logo [height]="36" [width]="36"></logo>
1618
- <span>OIP</span>
1619
- </a>
1620
- </div>
1625
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: AppTopbar, isStandalone: true, selector: "app-topbar", ngImport: i0, template: `
1626
+ <div class="layout-topbar">
1627
+ <div class="layout-topbar-logo-container">
1628
+ <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
1629
+ <i class="pi pi-bars"></i>
1630
+ </button>
1631
+ <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
1632
+ <logo [height]="36" [width]="36"></logo>
1633
+ <span>OIP</span>
1634
+ </a>
1635
+ </div>
1621
1636
 
1622
- @if (securityService.isAdmin && topBarService.topBarItems.length > 0) {
1623
- <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
1624
- <p-tablist>
1625
- @for (tab of topBarService.availableTopBarItems; track tab.id) {
1626
- <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
1627
- <i class="pi {{ tab.icon }}"></i>
1628
- <span class="ml-2">{{ tab.caption }}</span>
1629
- </p-tab>
1630
- }
1631
- </p-tablist>
1632
- </p-tabs>
1633
- }
1634
- <div class="layout-topbar-actions">
1635
- <div class="layout-config-menu">
1636
- <p-button
1637
- class="layout-topbar-action"
1638
- id="oip-app-topbar-theme-button"
1639
- severity="secondary"
1640
- type="button"
1641
- [rounded]="true"
1642
- [text]="true"
1643
- (click)="toggleDarkMode()">
1644
- <i
1645
- class="pi"
1646
- [ngClass]="{
1637
+ @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
1638
+ <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
1639
+ <p-tablist>
1640
+ @for (tab of topBarService.availableTopBarItems; track tab.id) {
1641
+ <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
1642
+ <i class="pi {{ tab.icon }}"></i>
1643
+ <span class="ml-2">{{ tab.caption }}</span>
1644
+ </p-tab>
1645
+ }
1646
+ </p-tablist>
1647
+ </p-tabs>
1648
+ }
1649
+ <div class="layout-topbar-actions">
1650
+ <div class="layout-config-menu">
1651
+ <p-button
1652
+ class="layout-topbar-action"
1653
+ id="oip-app-topbar-theme-button"
1654
+ severity="secondary"
1655
+ type="button"
1656
+ [rounded]="true"
1657
+ [text]="true"
1658
+ (click)="toggleDarkMode()">
1659
+ <i
1660
+ class="pi"
1661
+ [ngClass]="{
1647
1662
  'pi-moon': layoutService.isDarkTheme(),
1648
1663
  'pi-sun': !layoutService.isDarkTheme()
1649
1664
  }"></i>
1650
- </p-button>
1651
- <div class="relative">
1652
- <p-button
1653
- class="layout-topbar-action layout-topbar-action-highlight"
1654
- enterActiveClass="animate-scalein"
1655
- enterFromClass="hidden"
1656
- id="oip-app-topbar-palette-button"
1657
- leaveActiveClass="animate-fadeout"
1658
- leaveToClass="hidden"
1659
- pStyleClass="@next"
1660
- [hideOnOutsideClick]="true"
1661
- [rounded]="true">
1662
- <i class="pi pi-palette"></i>
1663
1665
  </p-button>
1664
- <app-configurator />
1666
+ <div class="relative">
1667
+ <p-button
1668
+ class="layout-topbar-action layout-topbar-action-highlight"
1669
+ enterActiveClass="animate-scalein"
1670
+ enterFromClass="hidden"
1671
+ id="oip-app-topbar-palette-button"
1672
+ leaveActiveClass="animate-fadeout"
1673
+ leaveToClass="hidden"
1674
+ pStyleClass="@next"
1675
+ [hideOnOutsideClick]="true"
1676
+ [rounded]="true">
1677
+ <i class="pi pi-palette"></i>
1678
+ </p-button>
1679
+ <app-configurator/>
1680
+ </div>
1665
1681
  </div>
1666
- </div>
1667
1682
 
1668
- <button
1669
- class="layout-topbar-menu-button layout-topbar-action"
1670
- enterActiveClass="animate-scalein"
1671
- enterFromClass="hidden"
1672
- id="oip-app-topbar-menu-expand-button"
1673
- leaveActiveClass="animate-fadeout"
1674
- leaveToClass="hidden"
1675
- pStyleClass="@next"
1676
- [hideOnOutsideClick]="true">
1677
- <i class="pi pi-ellipsis-v"></i>
1678
- </button>
1683
+ <button
1684
+ class="layout-topbar-menu-button layout-topbar-action"
1685
+ enterActiveClass="animate-scalein"
1686
+ enterFromClass="hidden"
1687
+ id="oip-app-topbar-menu-expand-button"
1688
+ leaveActiveClass="animate-fadeout"
1689
+ leaveToClass="hidden"
1690
+ pStyleClass="@next"
1691
+ [hideOnOutsideClick]="true">
1692
+ <i class="pi pi-ellipsis-v"></i>
1693
+ </button>
1679
1694
 
1680
- <div class="layout-topbar-menu hidden lg:block">
1681
- <div class="layout-topbar-menu-content">
1682
- <button
1683
- class="layout-topbar-action"
1684
- id="oip-app-topbar-logout-button"
1685
- type="button"
1686
- (click)="securityService.logout()"
1687
- (keydown)="logoutKeyDown($event)">
1688
- <i class="pi pi-sign-out"></i>
1689
- <span>Logout</span>
1690
- </button>
1691
- <button class="layout-topbar-action" routerLink="config">
1692
- <p-avatar
1693
- class="p-link flex align-items-center"
1694
- id="oip-app-topbar-user-avatar"
1695
- shape="circle"
1696
- size="normal"
1697
- [image]="userService.photoLoaded ? userService.photo : null"
1695
+ <div class="layout-topbar-menu hidden lg:block">
1696
+ <div class="layout-topbar-menu-content">
1697
+ <button
1698
+ class="layout-topbar-action"
1699
+ id="oip-app-topbar-logout-button"
1700
+ type="button"
1701
+ (click)="securityService.logout()"
1702
+ (keydown)="logoutKeyDown($event)">
1703
+ <i class="pi pi-sign-out"></i>
1704
+ <span>Logout</span>
1705
+ </button>
1706
+ <button class="layout-topbar-action" routerLink="config">
1707
+ <p-avatar
1708
+ class="p-link flex align-items-center"
1709
+ id="oip-app-topbar-user-avatar"
1710
+ shape="circle"
1711
+ size="normal"
1712
+ [image]="userService.photoLoaded ? userService.photo : null"
1698
1713
  >{{ !userService.photoLoaded ? userService.shortLabel : null }}
1699
- </p-avatar>
1700
- <span class="ml-2">Profile</span>
1701
- </button>
1714
+ </p-avatar>
1715
+ <span class="ml-2">Profile</span>
1716
+ </button>
1717
+ </div>
1702
1718
  </div>
1703
1719
  </div>
1704
- </div>
1705
- </div>`, isInline: true, styles: [""], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i4.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfiguratorComponent, selector: "app-configurator" }, { kind: "component", type: LogoComponent, selector: "logo", inputs: ["width", "height"] }, { kind: "component", type: Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: TabList, selector: "p-tablist" }, { kind: "component", type: Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i5$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
1720
+ </div>`, isInline: true, styles: [""], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i4.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfiguratorComponent, selector: "app-configurator" }, { kind: "component", type: LogoComponent, selector: "logo", inputs: ["width", "height"] }, { kind: "component", type: Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: TabList, selector: "p-tablist" }, { kind: "component", type: Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i5$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
1706
1721
  }
1707
1722
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AppTopbar, decorators: [{
1708
1723
  type: Component,
@@ -1717,101 +1732,102 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1717
1732
  Tab,
1718
1733
  AvatarModule,
1719
1734
  ButtonModule
1720
- ], template: ` <div class="layout-topbar">
1721
- <div class="layout-topbar-logo-container">
1722
- <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
1723
- <i class="pi pi-bars"></i>
1724
- </button>
1725
- <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
1726
- <logo [height]="36" [width]="36"></logo>
1727
- <span>OIP</span>
1728
- </a>
1729
- </div>
1735
+ ], template: `
1736
+ <div class="layout-topbar">
1737
+ <div class="layout-topbar-logo-container">
1738
+ <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
1739
+ <i class="pi pi-bars"></i>
1740
+ </button>
1741
+ <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
1742
+ <logo [height]="36" [width]="36"></logo>
1743
+ <span>OIP</span>
1744
+ </a>
1745
+ </div>
1730
1746
 
1731
- @if (securityService.isAdmin && topBarService.topBarItems.length > 0) {
1732
- <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
1733
- <p-tablist>
1734
- @for (tab of topBarService.availableTopBarItems; track tab.id) {
1735
- <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
1736
- <i class="pi {{ tab.icon }}"></i>
1737
- <span class="ml-2">{{ tab.caption }}</span>
1738
- </p-tab>
1739
- }
1740
- </p-tablist>
1741
- </p-tabs>
1742
- }
1743
- <div class="layout-topbar-actions">
1744
- <div class="layout-config-menu">
1745
- <p-button
1746
- class="layout-topbar-action"
1747
- id="oip-app-topbar-theme-button"
1748
- severity="secondary"
1749
- type="button"
1750
- [rounded]="true"
1751
- [text]="true"
1752
- (click)="toggleDarkMode()">
1753
- <i
1754
- class="pi"
1755
- [ngClass]="{
1747
+ @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
1748
+ <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
1749
+ <p-tablist>
1750
+ @for (tab of topBarService.availableTopBarItems; track tab.id) {
1751
+ <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
1752
+ <i class="pi {{ tab.icon }}"></i>
1753
+ <span class="ml-2">{{ tab.caption }}</span>
1754
+ </p-tab>
1755
+ }
1756
+ </p-tablist>
1757
+ </p-tabs>
1758
+ }
1759
+ <div class="layout-topbar-actions">
1760
+ <div class="layout-config-menu">
1761
+ <p-button
1762
+ class="layout-topbar-action"
1763
+ id="oip-app-topbar-theme-button"
1764
+ severity="secondary"
1765
+ type="button"
1766
+ [rounded]="true"
1767
+ [text]="true"
1768
+ (click)="toggleDarkMode()">
1769
+ <i
1770
+ class="pi"
1771
+ [ngClass]="{
1756
1772
  'pi-moon': layoutService.isDarkTheme(),
1757
1773
  'pi-sun': !layoutService.isDarkTheme()
1758
1774
  }"></i>
1759
- </p-button>
1760
- <div class="relative">
1761
- <p-button
1762
- class="layout-topbar-action layout-topbar-action-highlight"
1763
- enterActiveClass="animate-scalein"
1764
- enterFromClass="hidden"
1765
- id="oip-app-topbar-palette-button"
1766
- leaveActiveClass="animate-fadeout"
1767
- leaveToClass="hidden"
1768
- pStyleClass="@next"
1769
- [hideOnOutsideClick]="true"
1770
- [rounded]="true">
1771
- <i class="pi pi-palette"></i>
1772
1775
  </p-button>
1773
- <app-configurator />
1776
+ <div class="relative">
1777
+ <p-button
1778
+ class="layout-topbar-action layout-topbar-action-highlight"
1779
+ enterActiveClass="animate-scalein"
1780
+ enterFromClass="hidden"
1781
+ id="oip-app-topbar-palette-button"
1782
+ leaveActiveClass="animate-fadeout"
1783
+ leaveToClass="hidden"
1784
+ pStyleClass="@next"
1785
+ [hideOnOutsideClick]="true"
1786
+ [rounded]="true">
1787
+ <i class="pi pi-palette"></i>
1788
+ </p-button>
1789
+ <app-configurator/>
1790
+ </div>
1774
1791
  </div>
1775
- </div>
1776
1792
 
1777
- <button
1778
- class="layout-topbar-menu-button layout-topbar-action"
1779
- enterActiveClass="animate-scalein"
1780
- enterFromClass="hidden"
1781
- id="oip-app-topbar-menu-expand-button"
1782
- leaveActiveClass="animate-fadeout"
1783
- leaveToClass="hidden"
1784
- pStyleClass="@next"
1785
- [hideOnOutsideClick]="true">
1786
- <i class="pi pi-ellipsis-v"></i>
1787
- </button>
1793
+ <button
1794
+ class="layout-topbar-menu-button layout-topbar-action"
1795
+ enterActiveClass="animate-scalein"
1796
+ enterFromClass="hidden"
1797
+ id="oip-app-topbar-menu-expand-button"
1798
+ leaveActiveClass="animate-fadeout"
1799
+ leaveToClass="hidden"
1800
+ pStyleClass="@next"
1801
+ [hideOnOutsideClick]="true">
1802
+ <i class="pi pi-ellipsis-v"></i>
1803
+ </button>
1788
1804
 
1789
- <div class="layout-topbar-menu hidden lg:block">
1790
- <div class="layout-topbar-menu-content">
1791
- <button
1792
- class="layout-topbar-action"
1793
- id="oip-app-topbar-logout-button"
1794
- type="button"
1795
- (click)="securityService.logout()"
1796
- (keydown)="logoutKeyDown($event)">
1797
- <i class="pi pi-sign-out"></i>
1798
- <span>Logout</span>
1799
- </button>
1800
- <button class="layout-topbar-action" routerLink="config">
1801
- <p-avatar
1802
- class="p-link flex align-items-center"
1803
- id="oip-app-topbar-user-avatar"
1804
- shape="circle"
1805
- size="normal"
1806
- [image]="userService.photoLoaded ? userService.photo : null"
1805
+ <div class="layout-topbar-menu hidden lg:block">
1806
+ <div class="layout-topbar-menu-content">
1807
+ <button
1808
+ class="layout-topbar-action"
1809
+ id="oip-app-topbar-logout-button"
1810
+ type="button"
1811
+ (click)="securityService.logout()"
1812
+ (keydown)="logoutKeyDown($event)">
1813
+ <i class="pi pi-sign-out"></i>
1814
+ <span>Logout</span>
1815
+ </button>
1816
+ <button class="layout-topbar-action" routerLink="config">
1817
+ <p-avatar
1818
+ class="p-link flex align-items-center"
1819
+ id="oip-app-topbar-user-avatar"
1820
+ shape="circle"
1821
+ size="normal"
1822
+ [image]="userService.photoLoaded ? userService.photo : null"
1807
1823
  >{{ !userService.photoLoaded ? userService.shortLabel : null }}
1808
- </p-avatar>
1809
- <span class="ml-2">Profile</span>
1810
- </button>
1824
+ </p-avatar>
1825
+ <span class="ml-2">Profile</span>
1826
+ </button>
1827
+ </div>
1811
1828
  </div>
1812
1829
  </div>
1813
- </div>
1814
- </div>` }]
1830
+ </div>` }]
1815
1831
  }], ctorParameters: () => [{ type: LayoutService }] });
1816
1832
 
1817
1833
  class FooterComponent {
@@ -3566,7 +3582,7 @@ class ConfigComponent {
3566
3582
  </div>
3567
3583
  </div>
3568
3584
  </div>
3569
- @if (securityService.isAdmin) {
3585
+ @if (securityService.isAdmin()) {
3570
3586
  <div class="md:w-1/2">
3571
3587
  <div class="card flex flex-col gap-4">
3572
3588
  <div class="font-semibold text-xl">{{ l10n.menu }}</div>
@@ -3629,7 +3645,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
3629
3645
  </div>
3630
3646
  </div>
3631
3647
  </div>
3632
- @if (securityService.isAdmin) {
3648
+ @if (securityService.isAdmin()) {
3633
3649
  <div class="md:w-1/2">
3634
3650
  <div class="card flex flex-col gap-4">
3635
3651
  <div class="font-semibold text-xl">{{ l10n.menu }}</div>