@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@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 });
|
|
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
|
|
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>" }]
|
|
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
|
-
*
|
|
2572
|
-
*
|
|
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
|
-
//
|
|
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
|
-
*
|
|
2620
|
-
*
|
|
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
|
|
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
|
|
2623
|
+
return current?.key ?? current;
|
|
2639
2624
|
}
|
|
2640
2625
|
catch {
|
|
2641
2626
|
return undefined;
|
|
2642
2627
|
}
|
|
2643
2628
|
}
|
|
2644
2629
|
/**
|
|
2645
|
-
*
|
|
2646
|
-
*
|
|
2647
|
-
*
|
|
2630
|
+
* Sanitizes text:
|
|
2631
|
+
* - Removes HTML tags
|
|
2632
|
+
* - Replaces and non-breaking spaces
|
|
2633
|
+
* - Normalizes Unicode (keeps German umlauts and Serbian letters)
|
|
2648
2634
|
*/
|
|
2649
|
-
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') {
|
|
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
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
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
|
-
|
|
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
|
|
2695
|
-
* -
|
|
2696
|
-
* -
|
|
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
|
|
2702
|
-
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).
|
|
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
|
-
* -
|
|
2715
|
-
* - Translates headers via provided translation function
|
|
2716
|
-
* - 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)
|
|
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
|
|
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
|
|
2724
|
-
const
|
|
2725
|
-
const safe = v.replace(/"/g, '""');
|
|
2726
|
-
return
|
|
2727
|
-
}).join(
|
|
2728
|
-
const csv = [headers.join(
|
|
2729
|
-
|
|
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
|
|
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
|
|
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
|
}
|