@fuentis/phoenix-ui 0.0.9-alpha.364 → 0.0.9-alpha.366
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1149,7 +1149,7 @@ class TopbarComponent {
|
|
|
1149
1149
|
: `url('assets/default-user.png')`;
|
|
1150
1150
|
}
|
|
1151
1151
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1152
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: TopbarComponent, isStandalone: true, selector: "pho-topbar", inputs: { homeUrl: { classPropertyName: "homeUrl", publicName: "homeUrl", isSignal: false, isRequired: false, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: false, isRequired: false, transformFunction: null }, shortModuleName: { classPropertyName: "shortModuleName", publicName: "shortModuleName", isSignal: false, isRequired: false, transformFunction: null }, hidelogo: { classPropertyName: "hidelogo", publicName: "hidelogo", isSignal: false, isRequired: false, transformFunction: null }, topbarModulesMenu: { classPropertyName: "topbarModulesMenu", publicName: "topbarModulesMenu", isSignal: false, isRequired: false, transformFunction: null }, darkModeSelector: { classPropertyName: "darkModeSelector", publicName: "darkModeSelector", isSignal: false, isRequired: false, transformFunction: null }, footerConfig: { classPropertyName: "footerConfig", publicName: "footerConfig", isSignal: false, isRequired: false, transformFunction: null }, searchConfig: { classPropertyName: "searchConfig", publicName: "searchConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onDarkModeSelect: "onDarkModeSelect", onUserPopoverAction: "onUserPopoverAction" }, providers: [MessageService], ngImport: i0, template: "<div class=\"h-full px-2 justify-content-between w-full flex align-items-center shadow-1 topbar\">\n <!-- Title -->\n <div class=\"flex align-items-center justify-content-center mb-2 ml-2\">\n @if (!hidelogo) {\n <img class=\"h-2rem\" src=\"assets/logo_rose.png\" alt=\"fuentis_logo_white\" />\n }\n <p-tag [style]=\"{\n marginLeft: '5px',\n marginBottom: '8px',\n padding: '2px 6px',\n fontSize: '9px',\n border: '1px solid var(--text-color)',\n background: 'transparent',\n color: 'var(--text-color)'\n }\" severity=\"info\" [value]=\"shortModuleName\" />\n\n <!-- <span *ngIf=\"shortModuleName\" class=\"text-3xl text-300\">|</span>\n <span class=\"text-xl text-400 mt-2\">\n {{ shortModuleName }}\n </span> -->\n </div>\n <!-- children -->\n <ng-content></ng-content>\n\n <div class=\"flex align-items-center gap-2\">\n <!-- Search Bar -->\n <pho-search-bar [tabs]=\"searchConfig()\"></pho-search-bar>\n\n <!-- ToggleDarkmode -->\n <p-button data-cy=\"theme-switch-button\" (onClick)=\"onDarkModeSelectEmit()\"\n [icon]=\"darkModeSelector.mode === 'dark' ? 'pi pi-sun' : 'pi pi-moon'\" variant=\"outlined\" />\n\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"op.toggle($event)\"\n icon=\"pi pi-th-large\" variant=\"outlined\" />\n <!-- Side Panel (Opens Sidebar) -->\n <p-button data-cy=\"side-panel-togg-button\" data-cy=\"side-panel-togg-button\" (onClick)=\"openSidebarPanel()\"\n icon=\"pi pi-question-circle\" variant=\"outlined\" />\n\n <!-- User Icon -->\n <pho-user [user]=\"user\" (actionEmmiter)=\"onUserPopoverAction.emit($event)\" />\n </div>\n</div>\n\n<!-- Sidebar Menu -->\n<p-drawer styleClass=\"w-25rem\" position=\"right\" [(visible)]=\"isSidebarVisible\" [closable]=\"false\">\n <ng-template #header>\n <div class=\"flex align-items-center justify-content-between w-full\">\n <div class=\"flex\">\n <span class=\"font-bold text-xl\">\n {{ \"ACTION.HELP_CENTER\" | translate }}\n </span>\n </div>\n\n <p-button (onClick)=\"isSidebarVisible = false\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" />\n </div>\n </ng-template>\n <div class=\"p-1\">\n <div class=\"flex flex-wrap gap-4\">\n <!-- first -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-ticket text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CREATE_TICKET\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CREATE_TICKET_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <p-button (onClick)=\"goToTicketCenter()\" [label]=\"'HELP_CENTER.CREATE_TICKET_BUTTON' | translate\"\n variant=\"outlined\" size=\"small\" />\n </div>\n </div>\n\n <!-- second -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-headphones text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.CALL_US\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.CALL_US_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n {{ \"HELP_CENTER.PHONE_NUMBER\" | translate }}\n </div>\n </div>\n\n <!-- third -->\n <div class=\"w-22rem border-round-md border-300 surface-100 pho-reverse p-3\">\n <div style=\"width: 30px; height: 30px\"\n class=\"m-2 border-round-md bg-primary font-bold flex align-items-center justify-content-center\">\n <i class=\"pi pi-envelope text-white\" style=\"font-size: 1.2rem\"></i>\n </div>\n <div>\n <div class=\"text-xl font-semibold mb-1\">\n {{ \"HELP_CENTER.SEND_EMAIL\" | translate }}\n </div>\n <div class=\"text-sm mb-2\">\n {{ \"HELP_CENTER.SEND_EMAIL_DESCRIPTION\" | translate }}\n </div>\n </div>\n <div class=\"w-full\">\n <a href=\"mailto:support@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)
|
|
2646
2634
|
*/
|
|
2647
|
-
function
|
|
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.
|
|
2669
|
+
*/
|
|
2670
|
+
function getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale = 'en-US') {
|
|
2648
2671
|
const field = col.field;
|
|
2649
2672
|
const type = columnTypeMap[field] || col.columnType;
|
|
2650
2673
|
const val = getNestedValue(row, field);
|
|
@@ -2652,19 +2675,9 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
|
|
|
2652
2675
|
return '';
|
|
2653
2676
|
switch (type) {
|
|
2654
2677
|
case columnTypeEnum.DATE:
|
|
2655
|
-
|
|
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:
|
|
@@ -2689,15 +2706,17 @@ function getDisplayValue(row, col, columnTypeMap, columnTypeEnum) {
|
|
|
2689
2706
|
}
|
|
2690
2707
|
/**
|
|
2691
2708
|
* Exports table rows to a PDF file.
|
|
2692
|
-
* - Uses
|
|
2693
|
-
* -
|
|
2694
|
-
* -
|
|
2709
|
+
* - Uses jsPDF + autoTable
|
|
2710
|
+
* - Respects only visible columns
|
|
2711
|
+
* - Translates headers with provided translation function
|
|
2695
2712
|
*/
|
|
2696
|
-
function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf') {
|
|
2713
|
+
function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
|
|
2697
2714
|
if (!columns?.length)
|
|
2698
2715
|
return;
|
|
2699
|
-
const
|
|
2700
|
-
const
|
|
2716
|
+
const locale = options.locale || 'en-US';
|
|
2717
|
+
const head = [columns.map(c => sanitizeText(t(c.header)))];
|
|
2718
|
+
const body = (rows ?? []).map(row => columns.map(col => sanitizeText(getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale))));
|
|
2719
|
+
// jsPDF default font supports Latin-1 (English, German, Serbian Latin characters).
|
|
2701
2720
|
const doc = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'A4' });
|
|
2702
2721
|
autoTable(doc, {
|
|
2703
2722
|
head,
|
|
@@ -2707,24 +2726,29 @@ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
|
|
|
2707
2726
|
});
|
|
2708
2727
|
doc.save(fileName);
|
|
2709
2728
|
}
|
|
2729
|
+
/* -------------------------- Export: CSV ---------------------------- */
|
|
2710
2730
|
/**
|
|
2711
2731
|
* Exports table rows to a CSV file.
|
|
2712
|
-
* -
|
|
2713
|
-
* - Translates headers via provided translation function
|
|
2714
|
-
* - Escapes
|
|
2732
|
+
* - Includes only visible columns
|
|
2733
|
+
* - Translates headers via provided translation function
|
|
2734
|
+
* - Escapes quotes and adds UTF-8 BOM (so Excel reads special chars correctly)
|
|
2715
2735
|
*/
|
|
2716
|
-
function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv') {
|
|
2736
|
+
function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.csv', options = {}) {
|
|
2717
2737
|
if (!columns?.length)
|
|
2718
2738
|
return;
|
|
2719
|
-
const
|
|
2739
|
+
const locale = options.locale || 'en-US';
|
|
2740
|
+
const delimiter = options.delimiter || ',';
|
|
2741
|
+
const headers = columns.map(c => sanitizeText(t(c.header)));
|
|
2720
2742
|
const lines = (rows ?? []).map(row => columns.map(col => {
|
|
2721
|
-
const
|
|
2722
|
-
const
|
|
2723
|
-
const safe = v.replace(/"/g, '""');
|
|
2724
|
-
return
|
|
2725
|
-
}).join(
|
|
2726
|
-
const csv = [headers.join(
|
|
2727
|
-
|
|
2743
|
+
const raw = getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale);
|
|
2744
|
+
const v = sanitizeText(raw);
|
|
2745
|
+
const safe = v.replace(/"/g, '""'); // escape double quotes
|
|
2746
|
+
return needsQuotesCSV(v, delimiter) ? `"${safe}"` : safe;
|
|
2747
|
+
}).join(delimiter));
|
|
2748
|
+
const csv = [headers.join(delimiter), ...lines].join('\n');
|
|
2749
|
+
// Add UTF-8 BOM → Excel correctly recognizes UTF-8 (handles č, ć, š, đ, ž, ä, ö, ü, ß, etc.)
|
|
2750
|
+
const BOM = '\uFEFF';
|
|
2751
|
+
const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' });
|
|
2728
2752
|
const a = document.createElement('a');
|
|
2729
2753
|
const url = URL.createObjectURL(blob);
|
|
2730
2754
|
a.href = url;
|
|
@@ -2734,6 +2758,20 @@ function exportRowsToCsv(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
|
|
|
2734
2758
|
document.body.removeChild(a);
|
|
2735
2759
|
URL.revokeObjectURL(url);
|
|
2736
2760
|
}
|
|
2761
|
+
/* --------------------------- File name ----------------------------- */
|
|
2762
|
+
/**
|
|
2763
|
+
* Builds a file name with pattern: tableKey_YYYY-MM-DD_HH-mm.ext
|
|
2764
|
+
* Example: risks_2025-09-10_14-32.csv
|
|
2765
|
+
*/
|
|
2766
|
+
function buildFileName(baseKey, ext) {
|
|
2767
|
+
const now = new Date();
|
|
2768
|
+
const yyyy = now.getFullYear();
|
|
2769
|
+
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
|
2770
|
+
const dd = String(now.getDate()).padStart(2, '0');
|
|
2771
|
+
const hh = String(now.getHours()).padStart(2, '0');
|
|
2772
|
+
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
2773
|
+
return `${baseKey}_${yyyy}-${MM}-${dd}_${hh}-${mm}.${ext}`;
|
|
2774
|
+
}
|
|
2737
2775
|
|
|
2738
2776
|
const DEFAULT_MORE_ACTIONS = {
|
|
2739
2777
|
context: tableButtonContext.MORE,
|
|
@@ -2999,12 +3037,16 @@ class TableComponent {
|
|
|
2999
3037
|
}
|
|
3000
3038
|
handleActionClick(event) {
|
|
3001
3039
|
if (event?.action?.key === 'EXPORT_PDF') {
|
|
3002
|
-
const file = this.tableConfiguration?.key
|
|
3040
|
+
const file = this.tableConfiguration?.key
|
|
3041
|
+
? buildFileName(this.tableConfiguration.key, 'pdf')
|
|
3042
|
+
: buildFileName('table', 'pdf');
|
|
3003
3043
|
exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
|
|
3004
3044
|
return;
|
|
3005
3045
|
}
|
|
3006
3046
|
if (event?.action?.key === 'EXPORT_CSV') {
|
|
3007
|
-
const file = this.tableConfiguration?.key
|
|
3047
|
+
const file = this.tableConfiguration?.key
|
|
3048
|
+
? buildFileName(this.tableConfiguration.key, 'csv')
|
|
3049
|
+
: buildFileName('table', 'csv');
|
|
3008
3050
|
exportRowsToCsv(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file);
|
|
3009
3051
|
return;
|
|
3010
3052
|
}
|