@fuentis/phoenix-ui 0.0.9-alpha.363 → 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.
@@ -208,7 +208,6 @@ class SidebarItemComponent {
208
208
  severity="secondary"
209
209
  styleClass="w-full p-2"
210
210
  (click)="onSliderItemsClick({ type: 'CREATE' })"
211
- [disabled]="true"
212
211
  />
213
212
  </ng-template>
214
213
  </p-menu>
@@ -338,7 +337,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
338
337
  severity="secondary"
339
338
  styleClass="w-full p-2"
340
339
  (click)="onSliderItemsClick({ type: 'CREATE' })"
341
- [disabled]="true"
342
340
  />
343
341
  </ng-template>
344
342
  </p-menu>
@@ -1151,7 +1149,7 @@ class TopbarComponent {
1151
1149
  : `url('assets/default-user.png')`;
1152
1150
  }
1153
1151
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1154
- 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 });
1155
1153
  }
1156
1154
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TopbarComponent, decorators: [{
1157
1155
  type: Component,
@@ -1170,7 +1168,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
1170
1168
  TagModule,
1171
1169
  PopoverModule,
1172
1170
  ToastModule,
1173
- ], 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>" }]
1174
1172
  }], propDecorators: { homeUrl: [{
1175
1173
  type: Input
1176
1174
  }], user: [{
@@ -2206,6 +2204,7 @@ class TableCaptionComponent {
2206
2204
  if (!this.tableConfiguration || !this.columns)
2207
2205
  return;
2208
2206
  this.FILTER_KEY = `filters_${this.tableConfiguration.key}`;
2207
+ console.log('Table key:', this.tableConfiguration.key, 'Filter key:', this.FILTER_KEY);
2209
2208
  this.initializeSelectedColumns();
2210
2209
  if (this.tableConfiguration.hasColumns) {
2211
2210
  this.COLUMN_KEY = `columns_${this.tableConfiguration.key}`;
@@ -2568,22 +2567,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
2568
2567
  }] });
2569
2568
 
2570
2569
  /**
2571
- * @todo Add option to pass custom color to status tag ->
2572
- * modify response to match columnName + Color (e.g. actualRiskColor).
2573
- * @todo Check DATE format consistency -> there is a cell-pipe for that.
2574
- * @todo Add filter persistence in localStorage.
2575
- * @todo Fix frozen columns behavior.
2576
- *
2577
- * @todo Unauthorized API population for multi-select filters:
2578
- * - Entities
2579
- * - Catalogs
2580
- * - Statuses
2581
- * - Responsibles
2582
- * - Types
2583
- * - 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.).
2584
2572
  */
2585
- /// Splits actions into context-specific arrays (global, row, bulk, more)
2586
- /// This controls button positioning in the table toolbar and rows.
2587
2573
  function resolveActions(value) {
2588
2574
  const transformed = {
2589
2575
  globalActions: [],
@@ -2606,24 +2592,23 @@ function resolveActions(value) {
2606
2592
  case tableButtonContext.MORE:
2607
2593
  transformed.moreActions.push(a);
2608
2594
  break;
2609
- default:
2610
- break;
2595
+ default: break;
2611
2596
  }
2612
2597
  });
2613
- // Default global filter enabled unless explicitly set
2598
+ // Enable global filter by default (unless explicitly disabled)
2614
2599
  transformed.globalFilter =
2615
2600
  transformed.globalFilter === undefined ? true : transformed.globalFilter;
2616
2601
  return transformed;
2617
2602
  }
2603
+ /* ----------------------------- Helpers ----------------------------- */
2618
2604
  /**
2619
- * Utility to safely access nested object properties (dot notation supported).
2620
- * - If the field exists directly -> return it or its `key` prop.
2621
- * - 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
2622
2607
  */
2623
2608
  function getNestedValue(obj, path) {
2624
2609
  try {
2625
2610
  if (obj && Object.prototype.hasOwnProperty.call(obj, path)) {
2626
- return obj[path]?.key || obj[path];
2611
+ return obj[path]?.key ?? obj[path];
2627
2612
  }
2628
2613
  const keys = path.split('.');
2629
2614
  let current = obj;
@@ -2635,18 +2620,54 @@ function getNestedValue(obj, path) {
2635
2620
  return undefined;
2636
2621
  }
2637
2622
  }
2638
- return current?.key || current;
2623
+ return current?.key ?? current;
2639
2624
  }
2640
2625
  catch {
2641
2626
  return undefined;
2642
2627
  }
2643
2628
  }
2644
2629
  /**
2645
- * Converts raw row values into display-friendly strings,
2646
- * respecting column type (DATE, LIST, TAG, BOOLEAN, etc).
2647
- * 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)
2648
2634
  */
2649
- 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') {
2650
2671
  const field = col.field;
2651
2672
  const type = columnTypeMap[field] || col.columnType;
2652
2673
  const val = getNestedValue(row, field);
@@ -2654,19 +2675,9 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2654
2675
  return '';
2655
2676
  switch (type) {
2656
2677
  case columnTypeEnum.DATE:
2657
- case columnTypeEnum.DATE_WITHOUT_TIME: {
2658
- try {
2659
- const d = new Date(val);
2660
- if (!isNaN(d.getTime())) {
2661
- const dp = new DatePipe('en-US');
2662
- return (dp.transform(d, type === columnTypeEnum.DATE_WITHOUT_TIME
2663
- ? 'dd.MM.yyyy'
2664
- : 'dd.MM.yyyy HH:mm') ?? '');
2665
- }
2666
- }
2667
- catch { }
2668
- return String(val);
2669
- }
2678
+ return formatDateValue(val, 'dd.MM.yyyy HH:mm', locale);
2679
+ case columnTypeEnum.DATE_WITHOUT_TIME:
2680
+ return formatDateValue(val, 'dd.MM.yyyy', locale);
2670
2681
  case columnTypeEnum.TAG:
2671
2682
  return (val?.name ?? val)?.toString();
2672
2683
  case columnTypeEnum.LIST_TAG:
@@ -2676,8 +2687,12 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2676
2687
  : String(val);
2677
2688
  case columnTypeEnum.OBJ_TAG:
2678
2689
  return (val?.value ?? val)?.toString();
2679
- case columnTypeEnum.BOOLEAN:
2680
- 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
+ }
2681
2696
  case columnTypeEnum.TEXT_AREA:
2682
2697
  case columnTypeEnum.TEXT:
2683
2698
  case columnTypeEnum.TIMEPERIOD:
@@ -2691,15 +2706,17 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
2691
2706
  }
2692
2707
  /**
2693
2708
  * Exports table rows to a PDF file.
2694
- * - Uses only the currently visible columns.
2695
- * - Translates headers via provided translation function.
2696
- * - Uses jsPDF + autoTable for table rendering.
2709
+ * - Uses jsPDF + autoTable
2710
+ * - Respects only visible columns
2711
+ * - Translates headers with provided translation function
2697
2712
  */
2698
- function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf') {
2713
+ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
2699
2714
  if (!columns?.length)
2700
2715
  return;
2701
- const head = [columns.map(c => t(c.header))];
2702
- 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).
2703
2720
  const doc = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'A4' });
2704
2721
  autoTable(doc, {
2705
2722
  head,
@@ -2709,24 +2726,29 @@ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
2709
2726
  });
2710
2727
  doc.save(fileName);
2711
2728
  }
2729
+ /* -------------------------- Export: CSV ---------------------------- */
2712
2730
  /**
2713
2731
  * Exports table rows to a CSV file.
2714
- * - Uses only the currently visible columns.
2715
- * - Translates headers via provided translation function.
2716
- * - 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)
2717
2735
  */
2718
- function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv') {
2736
+ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv', options = {}) {
2719
2737
  if (!columns?.length)
2720
2738
  return;
2721
- 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)));
2722
2742
  const lines = (rows ?? []).map(row => columns.map(col => {
2723
- const v = getDisplayValue(row, col, columnTypeMap, columnTypeEnum);
2724
- const needsQuotes = /[",\n;]/.test(v);
2725
- const safe = v.replace(/"/g, '""');
2726
- return needsQuotes ? `"${safe}"` : safe;
2727
- }).join(','));
2728
- const csv = [headers.join(','), ...lines].join('\n');
2729
- 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;' });
2730
2752
  const a = document.createElement('a');
2731
2753
  const url = URL.createObjectURL(blob);
2732
2754
  a.href = url;
@@ -2736,6 +2758,20 @@ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
2736
2758
  document.body.removeChild(a);
2737
2759
  URL.revokeObjectURL(url);
2738
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
+ }
2739
2775
 
2740
2776
  const DEFAULT_MORE_ACTIONS = {
2741
2777
  context: tableButtonContext.MORE,
@@ -3001,12 +3037,16 @@ class TableComponent {
3001
3037
  }
3002
3038
  handleActionClick(event) {
3003
3039
  if (event?.action?.key === 'EXPORT_PDF') {
3004
- 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');
3005
3043
  exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
3006
3044
  return;
3007
3045
  }
3008
3046
  if (event?.action?.key === 'EXPORT_CSV') {
3009
- 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');
3010
3050
  exportRowsToCsv(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
3011
3051
  return;
3012
3052
  }