@fuentis/phoenix-ui 0.0.9-alpha.364 → 0.0.9-alpha.367
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@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@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
|
|
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@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
|
-
*
|
|
2570
|
-
*
|
|
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
|
-
//
|
|
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
|
-
*
|
|
2618
|
-
*
|
|
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
|
|
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
|
|
2623
|
+
return current?.key ?? current;
|
|
2637
2624
|
}
|
|
2638
2625
|
catch {
|
|
2639
2626
|
return undefined;
|
|
2640
2627
|
}
|
|
2641
2628
|
}
|
|
2642
2629
|
/**
|
|
2643
|
-
*
|
|
2644
|
-
*
|
|
2645
|
-
*
|
|
2630
|
+
* Sanitizes text:
|
|
2631
|
+
* - Removes HTML tags
|
|
2632
|
+
* - Replaces and non-breaking spaces
|
|
2633
|
+
* - Normalizes Unicode (keeps German umlauts and Serbian letters)
|
|
2634
|
+
*/
|
|
2635
|
+
function sanitizeText(v) {
|
|
2636
|
+
const s = (v ?? '').toString();
|
|
2637
|
+
const noHtml = s.replace(/<[^>]*>/g, '');
|
|
2638
|
+
return noHtml
|
|
2639
|
+
.replace(/ /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.
|
|
2646
2669
|
*/
|
|
2647
|
-
function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
|
|
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
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
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
|
-
|
|
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:
|
|
@@ -2687,17 +2704,25 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
|
|
|
2687
2704
|
return String(val);
|
|
2688
2705
|
}
|
|
2689
2706
|
}
|
|
2707
|
+
function trimExcelLineBreaks(s) {
|
|
2708
|
+
return s
|
|
2709
|
+
.replace(/^[\r\n]+/, '') // remove leading line breaks
|
|
2710
|
+
.replace(/[\r\n]+$/, '') // remove trailing line breaks
|
|
2711
|
+
.replace(/(\r?\n){2,}/g, '\n'); // collapse multiple to a single
|
|
2712
|
+
}
|
|
2690
2713
|
/**
|
|
2691
2714
|
* Exports table rows to a PDF file.
|
|
2692
|
-
* - Uses
|
|
2693
|
-
* -
|
|
2694
|
-
* -
|
|
2715
|
+
* - Uses jsPDF + autoTable
|
|
2716
|
+
* - Respects only visible columns
|
|
2717
|
+
* - Translates headers with provided translation function
|
|
2695
2718
|
*/
|
|
2696
|
-
function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf') {
|
|
2719
|
+
function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
|
|
2697
2720
|
if (!columns?.length)
|
|
2698
2721
|
return;
|
|
2699
|
-
const
|
|
2700
|
-
const
|
|
2722
|
+
const locale = options.locale || 'en-US';
|
|
2723
|
+
const head = [columns.map(c => sanitizeText(t(c.header)))];
|
|
2724
|
+
const body = (rows ?? []).map(row => columns.map(col => sanitizeText(getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale))));
|
|
2725
|
+
// jsPDF default font supports Latin-1 (English, German, Serbian Latin characters).
|
|
2701
2726
|
const doc = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'A4' });
|
|
2702
2727
|
autoTable(doc, {
|
|
2703
2728
|
head,
|
|
@@ -2707,24 +2732,30 @@ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
|
|
|
2707
2732
|
});
|
|
2708
2733
|
doc.save(fileName);
|
|
2709
2734
|
}
|
|
2735
|
+
/* -------------------------- Export: CSV ---------------------------- */
|
|
2710
2736
|
/**
|
|
2711
2737
|
* Exports table rows to a CSV file.
|
|
2712
|
-
* -
|
|
2713
|
-
* - Translates headers via provided translation function
|
|
2714
|
-
* - Escapes
|
|
2738
|
+
* - Includes only visible columns
|
|
2739
|
+
* - Translates headers via provided translation function
|
|
2740
|
+
* - Escapes quotes and adds UTF-8 BOM (so Excel reads special chars correctly)
|
|
2715
2741
|
*/
|
|
2716
|
-
function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv') {
|
|
2742
|
+
function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv', options = {}) {
|
|
2717
2743
|
if (!columns?.length)
|
|
2718
2744
|
return;
|
|
2719
|
-
const
|
|
2745
|
+
const locale = options.locale || 'en-US';
|
|
2746
|
+
const delimiter = options.delimiter || ',';
|
|
2747
|
+
const headers = columns.map(c => sanitizeText(t(c.header)));
|
|
2720
2748
|
const lines = (rows ?? []).map(row => columns.map(col => {
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2723
|
-
const
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
const
|
|
2749
|
+
const raw = getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale);
|
|
2750
|
+
// keep multiline, but remove useless blank line at start/end
|
|
2751
|
+
const v = trimExcelLineBreaks(sanitizeText(raw));
|
|
2752
|
+
const safe = v.replace(/"/g, '""'); // escape double quotes
|
|
2753
|
+
return needsQuotesCSV(v, delimiter) ? `"${safe}"` : safe;
|
|
2754
|
+
}).join(delimiter));
|
|
2755
|
+
const csv = [headers.join(delimiter), ...lines].join('\n');
|
|
2756
|
+
// Add UTF-8 BOM → Excel correctly recognizes UTF-8 (handles č, ć, š, đ, ž, ä, ö, ü, ß, etc.)
|
|
2757
|
+
const BOM = '\uFEFF';
|
|
2758
|
+
const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' });
|
|
2728
2759
|
const a = document.createElement('a');
|
|
2729
2760
|
const url = URL.createObjectURL(blob);
|
|
2730
2761
|
a.href = url;
|
|
@@ -2734,6 +2765,20 @@ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
|
|
|
2734
2765
|
document.body.removeChild(a);
|
|
2735
2766
|
URL.revokeObjectURL(url);
|
|
2736
2767
|
}
|
|
2768
|
+
/* --------------------------- File name ----------------------------- */
|
|
2769
|
+
/**
|
|
2770
|
+
* Builds a file name with pattern: tableKey_YYYY-MM-DD_HH-mm.ext
|
|
2771
|
+
* Example: risks_2025-09-10_14-32.csv
|
|
2772
|
+
*/
|
|
2773
|
+
function buildFileName(baseKey, ext) {
|
|
2774
|
+
const now = new Date();
|
|
2775
|
+
const yyyy = now.getFullYear();
|
|
2776
|
+
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
|
2777
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
2778
|
+
const hh = String(now.getHours()).padStart(2, '0');
|
|
2779
|
+
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
2780
|
+
return `${baseKey}_${yyyy}-${MM}-${dd}_${hh}-${mm}.${ext}`;
|
|
2781
|
+
}
|
|
2737
2782
|
|
|
2738
2783
|
const DEFAULT_MORE_ACTIONS = {
|
|
2739
2784
|
context: tableButtonContext.MORE,
|
|
@@ -2999,12 +3044,16 @@ class TableComponent {
|
|
|
2999
3044
|
}
|
|
3000
3045
|
handleActionClick(event) {
|
|
3001
3046
|
if (event?.action?.key === 'EXPORT_PDF') {
|
|
3002
|
-
const file = this.tableConfiguration?.key
|
|
3047
|
+
const file = this.tableConfiguration?.key
|
|
3048
|
+
? buildFileName(this.tableConfiguration.key, 'pdf')
|
|
3049
|
+
: buildFileName('table', 'pdf');
|
|
3003
3050
|
exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
|
|
3004
3051
|
return;
|
|
3005
3052
|
}
|
|
3006
3053
|
if (event?.action?.key === 'EXPORT_CSV') {
|
|
3007
|
-
const file = this.tableConfiguration?.key
|
|
3054
|
+
const file = this.tableConfiguration?.key
|
|
3055
|
+
? buildFileName(this.tableConfiguration.key, 'csv')
|
|
3056
|
+
: buildFileName('table', 'csv');
|
|
3008
3057
|
exportRowsToCsv(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
|
|
3009
3058
|
return;
|
|
3010
3059
|
}
|