@fuentis/phoenix-ui 0.0.9-alpha.364 → 0.0.9-alpha.366

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.
@@ -1149,7 +1149,7 @@ class TopbarComponent {
1149
1149
  : `url('assets/default-user.png')`;
1150
1150
  }
1151
1151
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1152
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TopbarComponent, isStandalone: true, selector: "pho-topbar", inputs: { homeUrl: { classPropertyName: "homeUrl", publicName: "homeUrl", isSignal: false, isRequired: false, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: false, isRequired: false, transformFunction: null }, shortModuleName: { classPropertyName: "shortModuleName", publicName: "shortModuleName", isSignal: false, isRequired: false, transformFunction: null }, hidelogo: { classPropertyName: "hidelogo", publicName: "hidelogo", isSignal: false, isRequired: false, transformFunction: null }, topbarModulesMenu: { classPropertyName: "topbarModulesMenu", publicName: "topbarModulesMenu", isSignal: false, isRequired: false, transformFunction: null }, darkModeSelector: { classPropertyName: "darkModeSelector", publicName: "darkModeSelector", isSignal: false, isRequired: false, transformFunction: null }, footerConfig: { classPropertyName: "footerConfig", publicName: "footerConfig", isSignal: false, isRequired: false, transformFunction: null }, searchConfig: { classPropertyName: "searchConfig", publicName: "searchConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDarkModeSelect: "onDarkModeSelect", onUserPopoverAction: "onUserPopoverAction" }, providers: [MessageService], ngImport: i0, template: "<div class=\"h-full px-2 justify-content-between w-full flex align-items-center shadow-1 topbar\">\n <!-- Title -->\n <div class=\"flex align-items-center justify-content-center mb-2 ml-2\">\n @if (!hidelogo) {\n <img class=\"h-2rem\" src=\"assets/logo_rose.png\" alt=\"fuentis_logo_white\" />\n }\n <p-tag [style]=\"{\n marginLeft: '5px',\n marginBottom: '8px',\n padding: '2px 6px',\n fontSize: '9px',\n border: '1px solid var(--text-color)',\n background: 'transparent',\n color: 'var(--text-color)'\n }\" severity=\"info\" [value]=\"shortModuleName\" />\n\n <!-- <span *ngIf=\"shortModuleName\" class=\"text-3xl text-300\">|</span>\n <span class=\"text-xl text-400 mt-2\">\n {{ shortModuleName }}\n </span> -->\n </div>\n <!-- children -->\n <ng-content></ng-content>\n\n <div class=\"flex align-items-center gap-2\">\n <!-- Search Bar -->\n <pho-search-bar [tabs]=\"searchConfig()\"></pho-search-bar>\n\n <!-- ToggleDarkmode -->\n <p-button data-cy=\"theme-switch-button\" (onClick)=\"onDarkModeSelectEmit()\"\n [icon]=\"darkModeSelector.mode === 'dark' ? 'pi pi-sun' : 'pi pi-moon'\" variant=\"outlined\" />\n\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"op.toggle($event)\"\n icon=\"pi pi-th-large\" variant=\"outlined\" />\n <!-- Side Panel (Opens Sidebar) -->\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"openSidebarPanel()\"\n icon=\"pi pi-question-circle\" variant=\"outlined\" />\n\n <!-- User Icon -->\n <pho-user [user]=\"user\" (actionEmmiter)=\"onUserPopoverAction.emit($event)\" />\n </div>\n</div>\n\n<!-- Sidebar Menu -->\n<p-drawer styleClass=\"w-25rem\" position=\"right\" [(visible)]=\"isSidebarVisible\" [closable]=\"false\">\n <ng-template #header>\n <div class=\"flex align-items-center justify-content-between w-full\">\n <div class=\"flex\">\n <span class=\"font-bold text-xl\">\n {{ \"ACTION.HELP_CENTER\" | translate }}\n </span>\n </div>\n\n <p-button (onClick)=\"isSidebarVisible = false\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" />\n </div>\n </ng-template>\n <div class=\"p-1\">\n <div class=\"flex flex-wrap gap-4\">\n <!-- first -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-ticket text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CREATE_TICKET\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CREATE_TICKET_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <p-button (onClick)=\"goToTicketCenter()\" [label]=\"'HELP_CENTER.CREATE_TICKET_BUTTON' | translate\"\n variant=\"outlined\" size=\"small\" />\n </div>\n </div>\n\n <!-- second -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-headphones text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CALL_US\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CALL_US_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n {{ \"HELP_CENTER.PHONE_NUMBER\" | translate }}\n </div>\n </div>\n\n <!-- third -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-envelope text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.SEND_EMAIL\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.SEND_EMAIL_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <a href=\"mailto:support&#64;fuentis.com\">\n {{ \"HELP_CENTER.SUPPORT_EMAIL\" | translate }}\n </a>\n </div>\n </div>\n\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': extIncident?.disabled }\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-file text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_TITLE\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_DESCRIPTION\" | translate }}\n </div>\n </div>\n\n <div class=\"w-full flex align-items-center gap-2\">\n <a class=\"no-underline\" [href]=\"extIncident?.url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <p-button [label]=\"'HELP_CENTER.REPORT_INCIDENT_ACTION' | translate\" variant=\"outlined\" size=\"small\"\n icon=\"pi pi-copy\" (onClick)=\"copyToClipboard($event, extIncident?.url)\"\n [disabled]=\"extIncident?.disabled\" />\n </a>\n </div>\n </div>\n </div>\n <!-- External Incident Report card -->\n\n <div class=\"mt-5 p-3\">\n <h4 class=\"\">\n {{ \"ACTION.BROWSE\" | translate }} <br />\n <a class=\"\" [href]=\"footerConfig.guideUrl\" target=\"_blank\" rel=\"noopener noreferrer\">\n <span class=\"flex align-items-center\">\n {{ \"ACTION.KNOWLEDGE_BASE\" | translate }}\n <span style=\"text-decoration: none\">\n <i style=\"font-size: 15px\" class=\"pi pi-graduation-cap p-2 no-underline\"></i>\n </span>\n\n <!-- <i\n style=\"font-size: 11px\"\n class=\"pi pi-external-link p-2 no-underline\"\n ></i> -->\n </span>\n </a>\n </h4>\n </div>\n </div>\n <ng-template #footer>\n <div class=\"text-xs text-center text-600\">\n {{ \"HELP_CENTER.SUITE_DESCRIPTION\" | translate }}\n </div>\n </ng-template>\n</p-drawer>\n\n<p-popover #op>\n <div class=\"w-22rem\">\n <ng-container *ngFor=\"let moduleGroup of topbarModulesMenu\">\n <div class=\"grid\">\n <ng-container *ngIf=\"moduleGroup.layout === 'grid'\">\n <div *ngFor=\"let module of moduleGroup.items.slice(0, -1)\"\n class=\"col-4 p-3 hover:surface-100 cursor-pointer p-0 m-0 sidebaritem\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': module?.disabled }\">\n <a class=\"text-primary flex flex-column align-items-center gap-2\" [href]=\"module.url || '#'\"\n style=\"text-decoration: none; cursor: pointer\" [attr.data-cy]=\"'side-panel-menu-item-' + module.label\">\n <div class=\"w-4rem border-round-sm flex justify-content-center align-items-center\"\n [style.backgroundColor]=\"\n moduleGroup.showIconBackground\n ? module.disabled\n ? '#ccc'\n : '#e74360'\n : 'transparent'\n \">\n <img *ngIf=\"module.icon\" [src]=\"module.icon\" class=\"w-2rem\" />\n </div>\n <span class=\"text-sm text-center\">{{\n module.label | translate\n }}</span>\n </a>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n</p-popover>\n<p-toast></p-toast>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SearchBarComponent, selector: "pho-search-bar", inputs: ["tabs"] }, { kind: "component", type: UserComponent, selector: "pho-user", inputs: ["user"], outputs: ["actionEmmiter"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i2$1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i3$3.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DividerModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ImageModule }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i5$1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i7$3.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }], encapsulation: i0.ViewEncapsulation.None });
1152
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TopbarComponent, isStandalone: true, selector: "pho-topbar", inputs: { homeUrl: { classPropertyName: "homeUrl", publicName: "homeUrl", isSignal: false, isRequired: false, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: false, isRequired: false, transformFunction: null }, shortModuleName: { classPropertyName: "shortModuleName", publicName: "shortModuleName", isSignal: false, isRequired: false, transformFunction: null }, hidelogo: { classPropertyName: "hidelogo", publicName: "hidelogo", isSignal: false, isRequired: false, transformFunction: null }, topbarModulesMenu: { classPropertyName: "topbarModulesMenu", publicName: "topbarModulesMenu", isSignal: false, isRequired: false, transformFunction: null }, darkModeSelector: { classPropertyName: "darkModeSelector", publicName: "darkModeSelector", isSignal: false, isRequired: false, transformFunction: null }, footerConfig: { classPropertyName: "footerConfig", publicName: "footerConfig", isSignal: false, isRequired: false, transformFunction: null }, searchConfig: { classPropertyName: "searchConfig", publicName: "searchConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDarkModeSelect: "onDarkModeSelect", onUserPopoverAction: "onUserPopoverAction" }, providers: [MessageService], ngImport: i0, template: "<div class=\"h-full px-2 justify-content-between w-full flex align-items-center shadow-1 topbar\">\n <!-- Title -->\n <div class=\"flex align-items-center justify-content-center mb-2 ml-2\">\n @if (!hidelogo) {\n <img class=\"h-2rem\" src=\"assets/logo_rose.png\" alt=\"fuentis_logo_white\" />\n }\n <p-tag [style]=\"{\n marginLeft: '5px',\n marginBottom: '8px',\n padding: '2px 6px',\n fontSize: '9px',\n border: '1px solid var(--text-color)',\n background: 'transparent',\n color: 'var(--text-color)'\n }\" severity=\"info\" [value]=\"shortModuleName\" />\n </div>\n <!-- children -->\n <ng-content></ng-content>\n\n <div class=\"flex align-items-center gap-2\">\n <!-- Search Bar -->\n <pho-search-bar [tabs]=\"searchConfig()\"></pho-search-bar>\n\n <!-- ToggleDarkmode -->\n <p-button data-cy=\"theme-switch-button\" (onClick)=\"onDarkModeSelectEmit()\"\n [icon]=\"darkModeSelector.mode === 'dark' ? 'pi pi-sun' : 'pi pi-moon'\" variant=\"outlined\" />\n\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"op.toggle($event)\"\n icon=\"pi pi-th-large\" variant=\"outlined\" />\n <!-- Side Panel (Opens Sidebar) -->\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"openSidebarPanel()\"\n icon=\"pi pi-question-circle\" variant=\"outlined\" />\n\n <!-- User Icon -->\n <pho-user [user]=\"user\" (actionEmmiter)=\"onUserPopoverAction.emit($event)\" />\n </div>\n</div>\n\n<!-- Sidebar Menu -->\n<p-drawer styleClass=\"w-25rem\" position=\"right\" [(visible)]=\"isSidebarVisible\" [closable]=\"false\">\n <ng-template #header>\n <div class=\"flex align-items-center justify-content-between w-full\">\n <div class=\"flex\">\n <span class=\"font-bold text-xl\">\n {{ \"ACTION.HELP_CENTER\" | translate }}\n </span>\n </div>\n\n <p-button (onClick)=\"isSidebarVisible = false\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" />\n </div>\n </ng-template>\n <div class=\"p-1\">\n <div class=\"flex flex-wrap gap-4\">\n <!-- first -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-ticket text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CREATE_TICKET\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CREATE_TICKET_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <p-button (onClick)=\"goToTicketCenter()\" [label]=\"'HELP_CENTER.CREATE_TICKET_BUTTON' | translate\"\n variant=\"outlined\" size=\"small\" />\n </div>\n </div>\n\n <!-- second -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-headphones text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CALL_US\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CALL_US_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n {{ \"HELP_CENTER.PHONE_NUMBER\" | translate }}\n </div>\n </div>\n\n <!-- third -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-envelope text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.SEND_EMAIL\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.SEND_EMAIL_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <a href=\"mailto:support&#64;fuentis.com\">\n {{ \"HELP_CENTER.SUPPORT_EMAIL\" | translate }}\n </a>\n </div>\n </div>\n\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': extIncident?.disabled }\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-file text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_TITLE\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_DESCRIPTION\" | translate }}\n </div>\n </div>\n\n <div class=\"w-full flex align-items-center gap-2\">\n <a class=\"no-underline\" [href]=\"extIncident?.url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <p-button [label]=\"'HELP_CENTER.REPORT_INCIDENT_ACTION' | translate\" variant=\"outlined\" size=\"small\"\n icon=\"pi pi-copy\" (onClick)=\"copyToClipboard($event, extIncident?.url)\"\n [disabled]=\"extIncident?.disabled\" />\n </a>\n </div>\n </div>\n </div>\n <!-- External Incident Report card -->\n\n <div class=\"mt-5 p-3\">\n <h4 class=\"\">\n {{ \"ACTION.BROWSE\" | translate }} <br />\n <a class=\"\" [href]=\"footerConfig.guideUrl\" target=\"_blank\" rel=\"noopener noreferrer\">\n <span class=\"flex align-items-center\">\n {{ \"ACTION.KNOWLEDGE_BASE\" | translate }}\n <span style=\"text-decoration: none\">\n <i style=\"font-size: 15px\" class=\"pi pi-graduation-cap p-2 no-underline\"></i>\n </span>\n\n <!-- <i\n style=\"font-size: 11px\"\n class=\"pi pi-external-link p-2 no-underline\"\n ></i> -->\n </span>\n </a>\n </h4>\n </div>\n </div>\n <ng-template #footer>\n <div class=\"text-xs text-center text-600\">\n {{ \"HELP_CENTER.SUITE_DESCRIPTION\" | translate }}\n </div>\n </ng-template>\n</p-drawer>\n\n<p-popover #op>\n <div class=\"w-22rem\">\n <ng-container *ngFor=\"let moduleGroup of topbarModulesMenu\">\n <div class=\"grid\">\n <ng-container *ngIf=\"moduleGroup.layout === 'grid'\">\n <div *ngFor=\"let module of moduleGroup.items.slice(0, -1)\"\n class=\"col-4 p-3 hover:surface-100 cursor-pointer p-0 m-0 sidebaritem\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': module?.disabled }\">\n <a class=\"text-primary flex flex-column align-items-center gap-2\" [href]=\"module.url || '#'\"\n style=\"text-decoration: none; cursor: pointer\" [attr.data-cy]=\"'side-panel-menu-item-' + module.label\">\n <div class=\"w-4rem border-round-sm flex justify-content-center align-items-center\"\n [style.backgroundColor]=\"\n moduleGroup.showIconBackground\n ? module.disabled\n ? '#ccc'\n : '#e74360'\n : 'transparent'\n \">\n <img *ngIf=\"module.icon\" [src]=\"module.icon\" class=\"w-2rem\" />\n </div>\n <span class=\"text-sm text-center\">{{\n module.label | translate\n }}</span>\n </a>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n</p-popover>\n<p-toast></p-toast>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SearchBarComponent, selector: "pho-search-bar", inputs: ["tabs"] }, { kind: "component", type: UserComponent, selector: "pho-user", inputs: ["user"], outputs: ["actionEmmiter"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i2$1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i3$3.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DividerModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ImageModule }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i5$1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i7$3.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }], encapsulation: i0.ViewEncapsulation.None });
1153
1153
  }
1154
1154
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TopbarComponent, decorators: [{
1155
1155
  type: Component,
@@ -1168,7 +1168,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
1168
1168
  TagModule,
1169
1169
  PopoverModule,
1170
1170
  ToastModule,
1171
- ], encapsulation: ViewEncapsulation.None, providers: [MessageService], template: "<div class=\"h-full px-2 justify-content-between w-full flex align-items-center shadow-1 topbar\">\n <!-- Title -->\n <div class=\"flex align-items-center justify-content-center mb-2 ml-2\">\n @if (!hidelogo) {\n <img class=\"h-2rem\" src=\"assets/logo_rose.png\" alt=\"fuentis_logo_white\" />\n }\n <p-tag [style]=\"{\n marginLeft: '5px',\n marginBottom: '8px',\n padding: '2px 6px',\n fontSize: '9px',\n border: '1px solid var(--text-color)',\n background: 'transparent',\n color: 'var(--text-color)'\n }\" severity=\"info\" [value]=\"shortModuleName\" />\n\n <!-- <span *ngIf=\"shortModuleName\" class=\"text-3xl text-300\">|</span>\n <span class=\"text-xl text-400 mt-2\">\n {{ shortModuleName }}\n </span> -->\n </div>\n <!-- children -->\n <ng-content></ng-content>\n\n <div class=\"flex align-items-center gap-2\">\n <!-- Search Bar -->\n <pho-search-bar [tabs]=\"searchConfig()\"></pho-search-bar>\n\n <!-- ToggleDarkmode -->\n <p-button data-cy=\"theme-switch-button\" (onClick)=\"onDarkModeSelectEmit()\"\n [icon]=\"darkModeSelector.mode === 'dark' ? 'pi pi-sun' : 'pi pi-moon'\" variant=\"outlined\" />\n\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"op.toggle($event)\"\n icon=\"pi pi-th-large\" variant=\"outlined\" />\n <!-- Side Panel (Opens Sidebar) -->\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"openSidebarPanel()\"\n icon=\"pi pi-question-circle\" variant=\"outlined\" />\n\n <!-- User Icon -->\n <pho-user [user]=\"user\" (actionEmmiter)=\"onUserPopoverAction.emit($event)\" />\n </div>\n</div>\n\n<!-- Sidebar Menu -->\n<p-drawer styleClass=\"w-25rem\" position=\"right\" [(visible)]=\"isSidebarVisible\" [closable]=\"false\">\n <ng-template #header>\n <div class=\"flex align-items-center justify-content-between w-full\">\n <div class=\"flex\">\n <span class=\"font-bold text-xl\">\n {{ \"ACTION.HELP_CENTER\" | translate }}\n </span>\n </div>\n\n <p-button (onClick)=\"isSidebarVisible = false\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" />\n </div>\n </ng-template>\n <div class=\"p-1\">\n <div class=\"flex flex-wrap gap-4\">\n <!-- first -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-ticket text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CREATE_TICKET\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CREATE_TICKET_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <p-button (onClick)=\"goToTicketCenter()\" [label]=\"'HELP_CENTER.CREATE_TICKET_BUTTON' | translate\"\n variant=\"outlined\" size=\"small\" />\n </div>\n </div>\n\n <!-- second -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-headphones text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CALL_US\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CALL_US_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n {{ \"HELP_CENTER.PHONE_NUMBER\" | translate }}\n </div>\n </div>\n\n <!-- third -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-envelope text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.SEND_EMAIL\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.SEND_EMAIL_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <a href=\"mailto:support&#64;fuentis.com\">\n {{ \"HELP_CENTER.SUPPORT_EMAIL\" | translate }}\n </a>\n </div>\n </div>\n\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': extIncident?.disabled }\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-file text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_TITLE\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_DESCRIPTION\" | translate }}\n </div>\n </div>\n\n <div class=\"w-full flex align-items-center gap-2\">\n <a class=\"no-underline\" [href]=\"extIncident?.url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <p-button [label]=\"'HELP_CENTER.REPORT_INCIDENT_ACTION' | translate\" variant=\"outlined\" size=\"small\"\n icon=\"pi pi-copy\" (onClick)=\"copyToClipboard($event, extIncident?.url)\"\n [disabled]=\"extIncident?.disabled\" />\n </a>\n </div>\n </div>\n </div>\n <!-- External Incident Report card -->\n\n <div class=\"mt-5 p-3\">\n <h4 class=\"\">\n {{ \"ACTION.BROWSE\" | translate }} <br />\n <a class=\"\" [href]=\"footerConfig.guideUrl\" target=\"_blank\" rel=\"noopener noreferrer\">\n <span class=\"flex align-items-center\">\n {{ \"ACTION.KNOWLEDGE_BASE\" | translate }}\n <span style=\"text-decoration: none\">\n <i style=\"font-size: 15px\" class=\"pi pi-graduation-cap p-2 no-underline\"></i>\n </span>\n\n <!-- <i\n style=\"font-size: 11px\"\n class=\"pi pi-external-link p-2 no-underline\"\n ></i> -->\n </span>\n </a>\n </h4>\n </div>\n </div>\n <ng-template #footer>\n <div class=\"text-xs text-center text-600\">\n {{ \"HELP_CENTER.SUITE_DESCRIPTION\" | translate }}\n </div>\n </ng-template>\n</p-drawer>\n\n<p-popover #op>\n <div class=\"w-22rem\">\n <ng-container *ngFor=\"let moduleGroup of topbarModulesMenu\">\n <div class=\"grid\">\n <ng-container *ngIf=\"moduleGroup.layout === 'grid'\">\n <div *ngFor=\"let module of moduleGroup.items.slice(0, -1)\"\n class=\"col-4 p-3 hover:surface-100 cursor-pointer p-0 m-0 sidebaritem\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': module?.disabled }\">\n <a class=\"text-primary flex flex-column align-items-center gap-2\" [href]=\"module.url || '#'\"\n style=\"text-decoration: none; cursor: pointer\" [attr.data-cy]=\"'side-panel-menu-item-' + module.label\">\n <div class=\"w-4rem border-round-sm flex justify-content-center align-items-center\"\n [style.backgroundColor]=\"\n moduleGroup.showIconBackground\n ? module.disabled\n ? '#ccc'\n : '#e74360'\n : 'transparent'\n \">\n <img *ngIf=\"module.icon\" [src]=\"module.icon\" class=\"w-2rem\" />\n </div>\n <span class=\"text-sm text-center\">{{\n module.label | translate\n }}</span>\n </a>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n</p-popover>\n<p-toast></p-toast>" }]
1171
+ ], encapsulation: ViewEncapsulation.None, providers: [MessageService], template: "<div class=\"h-full px-2 justify-content-between w-full flex align-items-center shadow-1 topbar\">\n <!-- Title -->\n <div class=\"flex align-items-center justify-content-center mb-2 ml-2\">\n @if (!hidelogo) {\n <img class=\"h-2rem\" src=\"assets/logo_rose.png\" alt=\"fuentis_logo_white\" />\n }\n <p-tag [style]=\"{\n marginLeft: '5px',\n marginBottom: '8px',\n padding: '2px 6px',\n fontSize: '9px',\n border: '1px solid var(--text-color)',\n background: 'transparent',\n color: 'var(--text-color)'\n }\" severity=\"info\" [value]=\"shortModuleName\" />\n </div>\n <!-- children -->\n <ng-content></ng-content>\n\n <div class=\"flex align-items-center gap-2\">\n <!-- Search Bar -->\n <pho-search-bar [tabs]=\"searchConfig()\"></pho-search-bar>\n\n <!-- ToggleDarkmode -->\n <p-button data-cy=\"theme-switch-button\" (onClick)=\"onDarkModeSelectEmit()\"\n [icon]=\"darkModeSelector.mode === 'dark' ? 'pi pi-sun' : 'pi pi-moon'\" variant=\"outlined\" />\n\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"op.toggle($event)\"\n icon=\"pi pi-th-large\" variant=\"outlined\" />\n <!-- Side Panel (Opens Sidebar) -->\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"openSidebarPanel()\"\n icon=\"pi pi-question-circle\" variant=\"outlined\" />\n\n <!-- User Icon -->\n <pho-user [user]=\"user\" (actionEmmiter)=\"onUserPopoverAction.emit($event)\" />\n </div>\n</div>\n\n<!-- Sidebar Menu -->\n<p-drawer styleClass=\"w-25rem\" position=\"right\" [(visible)]=\"isSidebarVisible\" [closable]=\"false\">\n <ng-template #header>\n <div class=\"flex align-items-center justify-content-between w-full\">\n <div class=\"flex\">\n <span class=\"font-bold text-xl\">\n {{ \"ACTION.HELP_CENTER\" | translate }}\n </span>\n </div>\n\n <p-button (onClick)=\"isSidebarVisible = false\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" />\n </div>\n </ng-template>\n <div class=\"p-1\">\n <div class=\"flex flex-wrap gap-4\">\n <!-- first -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-ticket text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CREATE_TICKET\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CREATE_TICKET_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <p-button (onClick)=\"goToTicketCenter()\" [label]=\"'HELP_CENTER.CREATE_TICKET_BUTTON' | translate\"\n variant=\"outlined\" size=\"small\" />\n </div>\n </div>\n\n <!-- second -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-headphones text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CALL_US\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CALL_US_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n {{ \"HELP_CENTER.PHONE_NUMBER\" | translate }}\n </div>\n </div>\n\n <!-- third -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-envelope text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.SEND_EMAIL\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.SEND_EMAIL_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <a href=\"mailto:support&#64;fuentis.com\">\n {{ \"HELP_CENTER.SUPPORT_EMAIL\" | translate }}\n </a>\n </div>\n </div>\n\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': extIncident?.disabled }\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-file text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_TITLE\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.REPORT_INCIDENT_DESCRIPTION\" | translate }}\n </div>\n </div>\n\n <div class=\"w-full flex align-items-center gap-2\">\n <a class=\"no-underline\" [href]=\"extIncident?.url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <p-button [label]=\"'HELP_CENTER.REPORT_INCIDENT_ACTION' | translate\" variant=\"outlined\" size=\"small\"\n icon=\"pi pi-copy\" (onClick)=\"copyToClipboard($event, extIncident?.url)\"\n [disabled]=\"extIncident?.disabled\" />\n </a>\n </div>\n </div>\n </div>\n <!-- External Incident Report card -->\n\n <div class=\"mt-5 p-3\">\n <h4 class=\"\">\n {{ \"ACTION.BROWSE\" | translate }} <br />\n <a class=\"\" [href]=\"footerConfig.guideUrl\" target=\"_blank\" rel=\"noopener noreferrer\">\n <span class=\"flex align-items-center\">\n {{ \"ACTION.KNOWLEDGE_BASE\" | translate }}\n <span style=\"text-decoration: none\">\n <i style=\"font-size: 15px\" class=\"pi pi-graduation-cap p-2 no-underline\"></i>\n </span>\n\n <!-- <i\n style=\"font-size: 11px\"\n class=\"pi pi-external-link p-2 no-underline\"\n ></i> -->\n </span>\n </a>\n </h4>\n </div>\n </div>\n <ng-template #footer>\n <div class=\"text-xs text-center text-600\">\n {{ \"HELP_CENTER.SUITE_DESCRIPTION\" | translate }}\n </div>\n </ng-template>\n</p-drawer>\n\n<p-popover #op>\n <div class=\"w-22rem\">\n <ng-container *ngFor=\"let moduleGroup of topbarModulesMenu\">\n <div class=\"grid\">\n <ng-container *ngIf=\"moduleGroup.layout === 'grid'\">\n <div *ngFor=\"let module of moduleGroup.items.slice(0, -1)\"\n class=\"col-4 p-3 hover:surface-100 cursor-pointer p-0 m-0 sidebaritem\"\n [ngClass]=\"{ 'opacity-30 pointer-events-none': module?.disabled }\">\n <a class=\"text-primary flex flex-column align-items-center gap-2\" [href]=\"module.url || '#'\"\n style=\"text-decoration: none; cursor: pointer\" [attr.data-cy]=\"'side-panel-menu-item-' + module.label\">\n <div class=\"w-4rem border-round-sm flex justify-content-center align-items-center\"\n [style.backgroundColor]=\"\n moduleGroup.showIconBackground\n ? module.disabled\n ? '#ccc'\n : '#e74360'\n : 'transparent'\n \">\n <img *ngIf=\"module.icon\" [src]=\"module.icon\" class=\"w-2rem\" />\n </div>\n <span class=\"text-sm text-center\">{{\n module.label | translate\n }}</span>\n </a>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n</p-popover>\n<p-toast></p-toast>" }]
1172
1172
  }], propDecorators: { homeUrl: [{
1173
1173
  type: Input
1174
1174
  }], user: [{
@@ -2204,6 +2204,7 @@ class TableCaptionComponent {
2204
2204
  if (!this.tableConfiguration || !this.columns)
2205
2205
  return;
2206
2206
  this.FILTER_KEY = `filters_${this.tableConfiguration.key}`;
2207
+ console.log('Table key:', this.tableConfiguration.key, 'Filter key:', this.FILTER_KEY);
2207
2208
  this.initializeSelectedColumns();
2208
2209
  if (this.tableConfiguration.hasColumns) {
2209
2210
  this.COLUMN_KEY = `columns_${this.tableConfiguration.key}`;
@@ -2566,22 +2567,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
2566
2567
  }] });
2567
2568
 
2568
2569
  /**
2569
- * @todo Add option to pass custom color to status tag ->
2570
- * modify response to match columnName + Color (e.g. actualRiskColor).
2571
- * @todo Check DATE format consistency -> there is a cell-pipe for that.
2572
- * @todo Add filter persistence in localStorage.
2573
- * @todo Fix frozen columns behavior.
2574
- *
2575
- * @todo Unauthorized API population for multi-select filters:
2576
- * - Entities
2577
- * - Catalogs
2578
- * - Statuses
2579
- * - Responsibles
2580
- * - Types
2581
- * - CIA attributes
2570
+ * Splits table actions into context-specific arrays (global, row, bulk, more).
2571
+ * This controls where buttons will appear (toolbar, row menu, etc.).
2582
2572
  */
2583
- /// Splits actions into context-specific arrays (global, row, bulk, more)
2584
- /// This controls button positioning in the table toolbar and rows.
2585
2573
  function resolveActions(value) {
2586
2574
  const transformed = {
2587
2575
  globalActions: [],
@@ -2604,24 +2592,23 @@ function resolveActions(value) {
2604
2592
  case tableButtonContext.MORE:
2605
2593
  transformed.moreActions.push(a);
2606
2594
  break;
2607
- default:
2608
- break;
2595
+ default: break;
2609
2596
  }
2610
2597
  });
2611
- // Default global filter enabled unless explicitly set
2598
+ // Enable global filter by default (unless explicitly disabled)
2612
2599
  transformed.globalFilter =
2613
2600
  transformed.globalFilter === undefined ? true : transformed.globalFilter;
2614
2601
  return transformed;
2615
2602
  }
2603
+ /* ----------------------------- Helpers ----------------------------- */
2616
2604
  /**
2617
- * Utility to safely access nested object properties (dot notation supported).
2618
- * - If the field exists directly -> return it or its `key` prop.
2619
- * - If dot notation -> traverse path until the end.
2605
+ * Safely retrieves nested object property using dot notation.
2606
+ * Example: "user.address.city" row.user.address.city
2620
2607
  */
2621
2608
  function getNestedValue(obj, path) {
2622
2609
  try {
2623
2610
  if (obj && Object.prototype.hasOwnProperty.call(obj, path)) {
2624
- return obj[path]?.key || obj[path];
2611
+ return obj[path]?.key ?? obj[path];
2625
2612
  }
2626
2613
  const keys = path.split('.');
2627
2614
  let current = obj;
@@ -2633,18 +2620,54 @@ function getNestedValue(obj, path) {
2633
2620
  return undefined;
2634
2621
  }
2635
2622
  }
2636
- return current?.key || current;
2623
+ return current?.key ?? current;
2637
2624
  }
2638
2625
  catch {
2639
2626
  return undefined;
2640
2627
  }
2641
2628
  }
2642
2629
  /**
2643
- * Converts raw row values into display-friendly strings,
2644
- * respecting column type (DATE, LIST, TAG, BOOLEAN, etc).
2645
- * Used by both PDF and CSV export functions.
2630
+ * Sanitizes text:
2631
+ * - Removes HTML tags
2632
+ * - Replaces &nbsp; and non-breaking spaces
2633
+ * - Normalizes Unicode (keeps German umlauts and Serbian letters)
2646
2634
  */
2647
- function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2635
+ function sanitizeText(v) {
2636
+ const s = (v ?? '').toString();
2637
+ const noHtml = s.replace(/<[^>]*>/g, '');
2638
+ return noHtml
2639
+ .replace(/&nbsp;/g, ' ')
2640
+ .replace(/\u00A0/g, ' ')
2641
+ .normalize('NFKC')
2642
+ .trim();
2643
+ }
2644
+ /**
2645
+ * Checks if a CSV value must be quoted (contains delimiter, quotes or newlines).
2646
+ */
2647
+ function needsQuotesCSV(value, delimiter) {
2648
+ const pattern = new RegExp(`[\"\\n${delimiter === ',' ? ',' : ';'}]`);
2649
+ return pattern.test(value);
2650
+ }
2651
+ /**
2652
+ * Formats a date value using Angular DatePipe and the provided locale.
2653
+ */
2654
+ function formatDateValue(val, pattern, locale) {
2655
+ try {
2656
+ const d = new Date(val);
2657
+ if (!isNaN(d.getTime())) {
2658
+ const dp = new DatePipe(locale || 'en-US');
2659
+ return dp.transform(d, pattern) ?? '';
2660
+ }
2661
+ }
2662
+ catch { }
2663
+ return String(val ?? '');
2664
+ }
2665
+ /* ------------------------- Display resolver ------------------------ */
2666
+ /**
2667
+ * Converts raw row values into human-readable strings for export.
2668
+ * Handles various column types: DATE, LIST, BOOLEAN, etc.
2669
+ */
2670
+ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale = 'en-US') {
2648
2671
  const field = col.field;
2649
2672
  const type = columnTypeMap[field] || col.columnType;
2650
2673
  const val = getNestedValue(row, field);
@@ -2652,19 +2675,9 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2652
2675
  return '';
2653
2676
  switch (type) {
2654
2677
  case columnTypeEnum.DATE:
2655
- case columnTypeEnum.DATE_WITHOUT_TIME: {
2656
- try {
2657
- const d = new Date(val);
2658
- if (!isNaN(d.getTime())) {
2659
- const dp = new DatePipe('en-US');
2660
- return (dp.transform(d, type === columnTypeEnum.DATE_WITHOUT_TIME
2661
- ? 'dd.MM.yyyy'
2662
- : 'dd.MM.yyyy HH:mm') ?? '');
2663
- }
2664
- }
2665
- catch { }
2666
- return String(val);
2667
- }
2678
+ return formatDateValue(val, 'dd.MM.yyyy HH:mm', locale);
2679
+ case columnTypeEnum.DATE_WITHOUT_TIME:
2680
+ return formatDateValue(val, 'dd.MM.yyyy', locale);
2668
2681
  case columnTypeEnum.TAG:
2669
2682
  return (val?.name ?? val)?.toString();
2670
2683
  case columnTypeEnum.LIST_TAG:
@@ -2674,8 +2687,12 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2674
2687
  : String(val);
2675
2688
  case columnTypeEnum.OBJ_TAG:
2676
2689
  return (val?.value ?? val)?.toString();
2677
- case columnTypeEnum.BOOLEAN:
2678
- return (val === true || val === 'true') ? '✓' : '✗';
2690
+ case columnTypeEnum.BOOLEAN: {
2691
+ // Translate YES/NO if translation function is provided
2692
+ const yes = t ? t('ACTION.YES') : 'Yes';
2693
+ const no = t ? t('ACTION.NO') : 'No';
2694
+ return (val === true || val === 'true') ? yes : no;
2695
+ }
2679
2696
  case columnTypeEnum.TEXT_AREA:
2680
2697
  case columnTypeEnum.TEXT:
2681
2698
  case columnTypeEnum.TIMEPERIOD:
@@ -2689,15 +2706,17 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2689
2706
  }
2690
2707
  /**
2691
2708
  * Exports table rows to a PDF file.
2692
- * - Uses only the currently visible columns.
2693
- * - Translates headers via provided translation function.
2694
- * - Uses jsPDF + autoTable for table rendering.
2709
+ * - Uses jsPDF + autoTable
2710
+ * - Respects only visible columns
2711
+ * - Translates headers with provided translation function
2695
2712
  */
2696
- function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf') {
2713
+ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
2697
2714
  if (!columns?.length)
2698
2715
  return;
2699
- const head = [columns.map(c => t(c.header))];
2700
- const body = (rows ?? []).map(row => columns.map(col => getDisplayValue(row, col, columnTypeMap, columnTypeEnum)));
2716
+ const locale = options.locale || 'en-US';
2717
+ const head = [columns.map(c => sanitizeText(t(c.header)))];
2718
+ const body = (rows ?? []).map(row => columns.map(col => sanitizeText(getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale))));
2719
+ // jsPDF default font supports Latin-1 (English, German, Serbian Latin characters).
2701
2720
  const doc = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'A4' });
2702
2721
  autoTable(doc, {
2703
2722
  head,
@@ -2707,24 +2726,29 @@ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
2707
2726
  });
2708
2727
  doc.save(fileName);
2709
2728
  }
2729
+ /* -------------------------- Export: CSV ---------------------------- */
2710
2730
  /**
2711
2731
  * Exports table rows to a CSV file.
2712
- * - Uses only the currently visible columns.
2713
- * - Translates headers via provided translation function.
2714
- * - Escapes values with quotes if they contain special characters.
2732
+ * - Includes only visible columns
2733
+ * - Translates headers via provided translation function
2734
+ * - Escapes quotes and adds UTF-8 BOM (so Excel reads special chars correctly)
2715
2735
  */
2716
- function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv') {
2736
+ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv', options = {}) {
2717
2737
  if (!columns?.length)
2718
2738
  return;
2719
- const headers = columns.map(c => t(c.header));
2739
+ const locale = options.locale || 'en-US';
2740
+ const delimiter = options.delimiter || ',';
2741
+ const headers = columns.map(c => sanitizeText(t(c.header)));
2720
2742
  const lines = (rows ?? []).map(row => columns.map(col => {
2721
- const v = getDisplayValue(row, col, columnTypeMap, columnTypeEnum);
2722
- const needsQuotes = /[",\n;]/.test(v);
2723
- const safe = v.replace(/"/g, '""');
2724
- return needsQuotes ? `"${safe}"` : safe;
2725
- }).join(','));
2726
- const csv = [headers.join(','), ...lines].join('\n');
2727
- const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
2743
+ const raw = getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale);
2744
+ const v = sanitizeText(raw);
2745
+ const safe = v.replace(/"/g, '""'); // escape double quotes
2746
+ return needsQuotesCSV(v, delimiter) ? `"${safe}"` : safe;
2747
+ }).join(delimiter));
2748
+ const csv = [headers.join(delimiter), ...lines].join('\n');
2749
+ // Add UTF-8 BOM Excel correctly recognizes UTF-8 (handles č, ć, š, đ, ž, ä, ö, ü, ß, etc.)
2750
+ const BOM = '\uFEFF';
2751
+ const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' });
2728
2752
  const a = document.createElement('a');
2729
2753
  const url = URL.createObjectURL(blob);
2730
2754
  a.href = url;
@@ -2734,6 +2758,20 @@ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
2734
2758
  document.body.removeChild(a);
2735
2759
  URL.revokeObjectURL(url);
2736
2760
  }
2761
+ /* --------------------------- File name ----------------------------- */
2762
+ /**
2763
+ * Builds a file name with pattern: tableKey_YYYY-MM-DD_HH-mm.ext
2764
+ * Example: risks_2025-09-10_14-32.csv
2765
+ */
2766
+ function buildFileName(baseKey, ext) {
2767
+ const now = new Date();
2768
+ const yyyy = now.getFullYear();
2769
+ const MM = String(now.getMonth() + 1).padStart(2, '0');
2770
+ const dd = String(now.getDate()).padStart(2, '0');
2771
+ const hh = String(now.getHours()).padStart(2, '0');
2772
+ const mm = String(now.getMinutes()).padStart(2, '0');
2773
+ return `${baseKey}_${yyyy}-${MM}-${dd}_${hh}-${mm}.${ext}`;
2774
+ }
2737
2775
 
2738
2776
  const DEFAULT_MORE_ACTIONS = {
2739
2777
  context: tableButtonContext.MORE,
@@ -2999,12 +3037,16 @@ class TableComponent {
2999
3037
  }
3000
3038
  handleActionClick(event) {
3001
3039
  if (event?.action?.key === 'EXPORT_PDF') {
3002
- const file = this.tableConfiguration?.key ? `${this.tableConfiguration.key}.pdf` : 'table.pdf';
3040
+ const file = this.tableConfiguration?.key
3041
+ ? buildFileName(this.tableConfiguration.key, 'pdf')
3042
+ : buildFileName('table', 'pdf');
3003
3043
  exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
3004
3044
  return;
3005
3045
  }
3006
3046
  if (event?.action?.key === 'EXPORT_CSV') {
3007
- const file = this.tableConfiguration?.key ? `${this.tableConfiguration.key}.csv` : 'table.csv';
3047
+ const file = this.tableConfiguration?.key
3048
+ ? buildFileName(this.tableConfiguration.key, 'csv')
3049
+ : buildFileName('table', 'csv');
3008
3050
  exportRowsToCsv(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
3009
3051
  return;
3010
3052
  }