Browse Source

feat(admin-ui): Add data table filter presets functionality

Michael Bromley 2 years ago
parent
commit
a656ef272a

+ 1 - 1
packages/admin-ui/src/lib/core/src/providers/data-table/data-table-filter-collection.ts

@@ -234,7 +234,7 @@ export class DataTableFilterCollection<FilterInput extends Record<string, any> =
             )
             )
             .subscribe(value => {
             .subscribe(value => {
                 this.#activeFilters = [];
                 this.#activeFilters = [];
-                if (value === '') {
+                if (value === '' || value === null) {
                     this.#valueChanges$.next(this.#activeFilters);
                     this.#valueChanges$.next(this.#activeFilters);
                     return;
                     return;
                 }
                 }

+ 2 - 2
packages/admin-ui/src/lib/core/src/providers/local-storage/local-storage.service.ts

@@ -1,8 +1,7 @@
 import { Location } from '@angular/common';
 import { Location } from '@angular/common';
-import { Injectable, Injector } from '@angular/core';
+import { Injectable } from '@angular/core';
 
 
 import { LanguageCode } from '../../common/generated-types';
 import { LanguageCode } from '../../common/generated-types';
-import { DataService } from '../../data/providers/data.service';
 import { WidgetLayoutDefinition } from '../dashboard-widget/dashboard-widget-types';
 import { WidgetLayoutDefinition } from '../dashboard-widget/dashboard-widget-types';
 
 
 export type DataTableConfig = {
 export type DataTableConfig = {
@@ -10,6 +9,7 @@ export type DataTableConfig = {
         visibility: string[];
         visibility: string[];
         order: { [id: string]: number };
         order: { [id: string]: number };
         showSearchFilterRow: boolean;
         showSearchFilterRow: boolean;
+        filterPresets: Array<{ name: string; value: string }>;
     };
     };
 };
 };
 
 

+ 57 - 4
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html

@@ -2,10 +2,31 @@
     <ng-content select="vdr-bulk-action-menu"></ng-content>
     <ng-content select="vdr-bulk-action-menu"></ng-content>
 </div>
 </div>
 <div class="table-wrapper">
 <div class="table-wrapper">
-    <table
-        class=""
-        [class.no-select]="disableSelect"
-    >
+    <div class="preset-tabs" *ngIf="filters && filterPresets.length">
+        <div
+            *ngFor="let preset of filterPresets"
+            class="preset-tab"
+            [class.active]="preset.value === serializedActiveFilters"
+        >
+            <a
+                [routerLink]="['./']"
+                [queryParams]="preset.value === serializedActiveFilters ? {} : { filters: preset.value }"
+            >
+                <div>{{ preset.name }}</div>
+            </a>
+            <vdr-dropdown>
+                <button class="icon-button" vdrDropdownTrigger>
+                    <clr-icon shape="ellipsis-vertical" size="12" />
+                </button>
+                <vdr-dropdown-menu vdrPosition="bottom-left">
+                    <button vdrDropdownItem (click)="deleteFilterPreset(preset.name)">
+                        <clr-icon shape="trash" class="is-danger"></clr-icon>{{ 'common.delete' | translate }}
+                    </button>
+                </vdr-dropdown-menu>
+            </vdr-dropdown>
+        </div>
+    </div>
+    <table class="" [class.no-select]="disableSelect">
         <thead [class.items-selected]="selectionManager?.selection.length">
         <thead [class.items-selected]="selectionManager?.selection.length">
             <tr class="heading-row">
             <tr class="heading-row">
                 <th *ngIf="selectionManager" class="selection-col">
                 <th *ngIf="selectionManager" class="selection-col">
@@ -60,6 +81,7 @@
                     <div class="filter-row-wrapper" [class.hidden]="!showSearchFilterRow">
                     <div class="filter-row-wrapper" [class.hidden]="!showSearchFilterRow">
                         <ng-container *ngTemplateOutlet="searchComponent?.template"></ng-container>
                         <ng-container *ngTemplateOutlet="searchComponent?.template"></ng-container>
                         <ng-container *ngTemplateOutlet="customSearchTemplate"></ng-container>
                         <ng-container *ngTemplateOutlet="customSearchTemplate"></ng-container>
+                        <div *ngIf="filterPresets.length"></div>
                         <ng-container *ngIf="filters">
                         <ng-container *ngIf="filters">
                             <div class="filters">
                             <div class="filters">
                                 <vdr-data-table-filters
                                 <vdr-data-table-filters
@@ -73,6 +95,37 @@
                                     [filters]="filters"
                                     [filters]="filters"
                                     class="mt-1"
                                     class="mt-1"
                                 ></vdr-data-table-filters>
                                 ></vdr-data-table-filters>
+                                <vdr-dropdown #addPresetDropdown>
+                                    <button
+                                        class="add-preset-button mt-1"
+                                        vdrDropdownTrigger
+                                        [class.visible]="filters.activeFilters.length > 0 && !selectedFilterPreset"
+                                        [disabled]="filters.activeFilters.length === 0 || !!selectedFilterPreset"
+                                    >
+                                        <clr-icon shape="floppy"/>
+                                        <div>{{ 'common.save-filter-preset' | translate }}</div>
+                                    </button>
+                                    <vdr-dropdown-menu
+                                        vdrPosition="bottom-left"
+                                        [cdkTrapFocus]="true"
+                                        [cdkTrapFocusAutoCapture]="true"
+                                    >
+                                        <form class="mx-1">
+                                            <div>
+                                                <input
+                                                    type="text"
+                                                    [placeholder]="'common.filter-preset-name' | translate"
+                                                    [formControl]="filterPresetName"
+                                                />
+                                            </div>
+                                            <div class="preset-create-row">
+                                                <button class="button mt-2" (click)="saveFilterPreset()">
+                                                    {{ 'common.create' | translate }}
+                                                </button>
+                                            </div>
+                                        </form>
+                                    </vdr-dropdown-menu>
+                                </vdr-dropdown>
                             </div>
                             </div>
                         </ng-container>
                         </ng-container>
                     </div>
                     </div>

+ 85 - 2
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.scss

@@ -1,5 +1,5 @@
 @import 'variables';
 @import 'variables';
-@import "mixins";
+@import 'mixins';
 
 
 :host {
 :host {
     display: block;
     display: block;
@@ -26,6 +26,7 @@
     top: 5px;
     top: 5px;
     height: 40px;
     height: 40px;
 }
 }
+
 .table-wrapper {
 .table-wrapper {
     display: block;
     display: block;
     overflow-y: hidden;
     overflow-y: hidden;
@@ -33,9 +34,11 @@
     width: 100%;
     width: 100%;
     max-width: var(--surface-width);
     max-width: var(--surface-width);
 }
 }
+
 table {
 table {
     width: 100%;
     width: 100%;
 }
 }
+
 table.no-select {
 table.no-select {
     user-select: none;
     user-select: none;
 }
 }
@@ -57,6 +60,7 @@ table.no-select {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     margin-inline-start: calc(var(--space-unit) * 0.5);
     margin-inline-start: calc(var(--space-unit) * 0.5);
+
     button {
     button {
         border: 0;
         border: 0;
         border-radius: var(--border-radius-lg);
         border-radius: var(--border-radius-lg);
@@ -64,10 +68,12 @@ table.no-select {
         padding: 0 2px;
         padding: 0 2px;
         cursor: pointer;
         cursor: pointer;
         background-color: transparent;
         background-color: transparent;
+
         &.active {
         &.active {
             color: var(--color-primary-700);
             color: var(--color-primary-700);
         }
         }
     }
     }
+
     .sort-label {
     .sort-label {
         margin-inline-start: calc(var(--space-unit) * 0.5);
         margin-inline-start: calc(var(--space-unit) * 0.5);
         font-size: 10px;
         font-size: 10px;
@@ -75,6 +81,7 @@ table.no-select {
         font-weight: 400;
         font-weight: 400;
     }
     }
 }
 }
+
 .toggle-search-filter-row {
 .toggle-search-filter-row {
     position: absolute;
     position: absolute;
     top: -12px;
     top: -12px;
@@ -82,12 +89,14 @@ table.no-select {
     @media screen and (min-width: $breakpoint-large) {
     @media screen and (min-width: $breakpoint-large) {
         left: 8px;
         left: 8px;
     }
     }
+
     &.active {
     &.active {
         background-color: var(--color-primary-700);
         background-color: var(--color-primary-700);
         color: var(--color-primary-100);
         color: var(--color-primary-100);
         border-color: var(--color-primary-700);
         border-color: var(--color-primary-700);
     }
     }
 }
 }
+
 th.filter-row {
 th.filter-row {
     position: relative;
     position: relative;
     font-size: var(--font-size-base);
     font-size: var(--font-size-base);
@@ -115,11 +124,76 @@ th.filter-row {
         gap: calc(var(--space-unit) * 0.5);
         gap: calc(var(--space-unit) * 0.5);
     }
     }
 }
 }
+
+.preset-tabs {
+    padding-inline-start: var(--surface-margin-left);
+    margin: var(--space-unit) 0;
+    display: flex;
+    overflow-x: auto;
+    overflow-y: hidden;
+    border-bottom: 1px solid var(--color-component-border-100);
+}
+
+.preset-tab {
+    display: flex;
+    align-items: center;
+    gap: calc(var(--space-unit) * 0.5);
+    font-size: var(--font-size-sm);
+    cursor: pointer;
+    white-space: nowrap;
+    text-transform: none;
+    padding-inline-end: calc(var(--space-unit) * 1);
+    border-bottom: 1px solid var(--color-weight-300);
+    margin-bottom: -1px;
+    cursor: pointer;
+
+    > a {
+        padding: calc(var(--space-unit) * 1) calc(var(--space-unit) * 2);
+        padding-inline-end: 0;
+        color: var(--color-weight-600);
+    }
+
+    &.active {
+        border-bottom-color: var(--color-text-active);
+
+        > a {
+            color: var(--color-text-active);
+        }
+    }
+}
+
+.preset-tab-link {
+    display: flex;
+    align-items: center;
+    gap: calc(var(--space-unit) * 0.5);
+}
+
+.add-preset-button {
+    display: flex;
+    flex-direction: row;
+    cursor: pointer;
+    gap: 6px;
+    align-items: center;
+    border: none;
+    padding: 0 var(--space-unit);
+    height: calc(var(--space-unit) * 3);
+    font-size: var(--font-size-xs);
+    border-radius: var(--border-radius-lg);
+    background-color: var(--color-button-small-bg);
+    color: var(--color-button-small-text);
+    opacity: 0;
+    transition: opacity 0.2s;
+    &.visible {
+        opacity: 1;
+    }
+}
+
 .filter-row-wrapper {
 .filter-row-wrapper {
     padding: calc(var(--space-unit) * 4);
     padding: calc(var(--space-unit) * 4);
     padding-inline-start: 0;
     padding-inline-start: 0;
     max-height: 150px;
     max-height: 150px;
     transition: max-height 0.2s, padding 0.2s, opacity 0.2s;
     transition: max-height 0.2s, padding 0.2s, opacity 0.2s;
+
     &.hidden {
     &.hidden {
         max-height: 0px;
         max-height: 0px;
         padding-top: 0;
         padding-top: 0;
@@ -129,7 +203,6 @@ th.filter-row {
     }
     }
 }
 }
 
 
-
 .cell-link {
 .cell-link {
     display: block;
     display: block;
     width: 100%;
     width: 100%;
@@ -145,12 +218,15 @@ td.active {
     align-items: center;
     align-items: center;
     line-height: var(--font-size-sm);
     line-height: var(--font-size-sm);
     color: var(--color-weight-700);
     color: var(--color-weight-700);
+
     &.left {
     &.left {
         justify-content: flex-start;
         justify-content: flex-start;
     }
     }
+
     &.center {
     &.center {
         justify-content: center;
         justify-content: center;
     }
     }
+
     &.right {
     &.right {
         justify-content: flex-end;
         justify-content: flex-end;
     }
     }
@@ -172,11 +248,18 @@ vdr-empty-placeholder {
     margin-inline-start: var(--surface-margin-left);
     margin-inline-start: var(--surface-margin-left);
     margin-inline-end: var(--space-unit);
     margin-inline-end: var(--space-unit);
 }
 }
+
 .total-items-count {
 .total-items-count {
     font-size: var(--font-size-xs);
     font-size: var(--font-size-xs);
 }
 }
+
 @container (max-width: 500px) {
 @container (max-width: 500px) {
     .total-items-count {
     .total-items-count {
         display: none;
         display: none;
     }
     }
 }
 }
+
+.preset-create-row {
+    display: flex;
+    justify-content: flex-end;
+}

+ 99 - 19
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts

@@ -6,22 +6,28 @@ import {
     ContentChild,
     ContentChild,
     ContentChildren,
     ContentChildren,
     EventEmitter,
     EventEmitter,
+    inject,
     Input,
     Input,
     OnChanges,
     OnChanges,
     OnDestroy,
     OnDestroy,
+    OnInit,
     Output,
     Output,
     QueryList,
     QueryList,
     SimpleChanges,
     SimpleChanges,
     TemplateRef,
     TemplateRef,
+    ViewChild,
 } from '@angular/core';
 } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
 import { PaginationService } from 'ngx-pagination';
 import { PaginationService } from 'ngx-pagination';
-import { Observable, Subscription } from 'rxjs';
-import { distinctUntilChanged, map } from 'rxjs/operators';
+import { Observable, Subject, Subscription } from 'rxjs';
+import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
 import { LanguageCode } from '../../../common/generated-types';
 import { LanguageCode } from '../../../common/generated-types';
 import { DataService } from '../../../data/providers/data.service';
 import { DataService } from '../../../data/providers/data.service';
 import { DataTableFilterCollection } from '../../../providers/data-table/data-table-filter-collection';
 import { DataTableFilterCollection } from '../../../providers/data-table/data-table-filter-collection';
 import { DataTableConfig, LocalStorageService } from '../../../providers/local-storage/local-storage.service';
 import { DataTableConfig, LocalStorageService } from '../../../providers/local-storage/local-storage.service';
 import { BulkActionMenuComponent } from '../bulk-action-menu/bulk-action-menu.component';
 import { BulkActionMenuComponent } from '../bulk-action-menu/bulk-action-menu.component';
+import { DropdownComponent } from '../dropdown/dropdown.component';
 
 
 import { DataTable2ColumnComponent } from './data-table-column.component';
 import { DataTable2ColumnComponent } from './data-table-column.component';
 import { DataTableCustomFieldColumnComponent } from './data-table-custom-field-column.component';
 import { DataTableCustomFieldColumnComponent } from './data-table-custom-field-column.component';
@@ -96,7 +102,7 @@ import { DataTable2SearchComponent } from './data-table-search.component';
     changeDetection: ChangeDetectionStrategy.OnPush,
     changeDetection: ChangeDetectionStrategy.OnPush,
     providers: [PaginationService],
     providers: [PaginationService],
 })
 })
-export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDestroy {
+export class DataTable2Component<T> implements OnInit, AfterContentInit, OnChanges, OnDestroy {
     @Input() id: string;
     @Input() id: string;
     @Input() items: T[];
     @Input() items: T[];
     @Input() itemsPerPage: number;
     @Input() itemsPerPage: number;
@@ -114,7 +120,11 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
     @ContentChild(DataTable2SearchComponent) searchComponent: DataTable2SearchComponent;
     @ContentChild(DataTable2SearchComponent) searchComponent: DataTable2SearchComponent;
     @ContentChild(BulkActionMenuComponent) bulkActionMenuComponent: BulkActionMenuComponent;
     @ContentChild(BulkActionMenuComponent) bulkActionMenuComponent: BulkActionMenuComponent;
     @ContentChild('vdrDt2CustomSearch') customSearchTemplate: TemplateRef<any>;
     @ContentChild('vdrDt2CustomSearch') customSearchTemplate: TemplateRef<any>;
+    @ViewChild('addPresetDropdown') addPresetDropdown: DropdownComponent;
     @ContentChildren(TemplateRef) templateRefs: QueryList<TemplateRef<any>>;
     @ContentChildren(TemplateRef) templateRefs: QueryList<TemplateRef<any>>;
+
+    route = inject(ActivatedRoute);
+
     rowTemplate: TemplateRef<any>;
     rowTemplate: TemplateRef<any>;
     currentStart: number;
     currentStart: number;
     currentEnd: number;
     currentEnd: number;
@@ -122,8 +132,12 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
     // which allows shift-click multi-row selection
     // which allows shift-click multi-row selection
     disableSelect = false;
     disableSelect = false;
     showSearchFilterRow = false;
     showSearchFilterRow = false;
+    filterPresetName = new FormControl('');
+    filterPresets: Array<{ name: string; value: string }> = [];
+    serializedActiveFilters: string;
+    selectedFilterPreset: string | undefined;
     protected uiLanguage$: Observable<LanguageCode>;
     protected uiLanguage$: Observable<LanguageCode>;
-    private subscription: Subscription | undefined;
+    protected destroy$ = new Subject<void>();
 
 
     constructor(
     constructor(
         protected changeDetectorRef: ChangeDetectorRef,
         protected changeDetectorRef: ChangeDetectorRef,
@@ -175,6 +189,19 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
         }
         }
     };
     };
 
 
+    ngOnInit() {
+        this.filterPresets = this.getFilterPreset();
+        this.route.queryParamMap
+            .pipe(
+                map(qpm => qpm.get('filters')),
+                distinctUntilChanged(),
+                takeUntil(this.destroy$),
+            )
+            .subscribe(() => {
+                this.getSerializedActiveFilters();
+            });
+    }
+
     ngOnChanges(changes: SimpleChanges) {
     ngOnChanges(changes: SimpleChanges) {
         if (changes.items) {
         if (changes.items) {
             this.currentStart = this.itemsPerPage * (this.currentPage - 1);
             this.currentStart = this.itemsPerPage * (this.currentPage - 1);
@@ -184,24 +211,22 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
     }
     }
 
 
     ngOnDestroy() {
     ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
         if (this.selectionManager) {
         if (this.selectionManager) {
             document.removeEventListener('keydown', this.shiftDownHandler);
             document.removeEventListener('keydown', this.shiftDownHandler);
             document.removeEventListener('keyup', this.shiftUpHandler);
             document.removeEventListener('keyup', this.shiftUpHandler);
         }
         }
-        this.subscription?.unsubscribe();
     }
     }
 
 
     ngAfterContentInit(): void {
     ngAfterContentInit(): void {
         this.rowTemplate = this.templateRefs.last;
         this.rowTemplate = this.templateRefs.last;
-        const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
+        const dataTableConfig = this.getDataTableConfig();
 
 
         if (!this.id) {
         if (!this.id) {
             console.warn(`No id was assigned to the data table component`);
             console.warn(`No id was assigned to the data table component`);
         }
         }
         const updateColumnVisibility = () => {
         const updateColumnVisibility = () => {
-            if (!dataTableConfig[this.id]) {
-                dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };
-            }
             dataTableConfig[this.id].visibility = this.allColumns
             dataTableConfig[this.id].visibility = this.allColumns
                 .filter(c => (c.visible && c.hiddenByDefault) || (!c.visible && !c.hiddenByDefault))
                 .filter(c => (c.visible && c.hiddenByDefault) || (!c.visible && !c.hiddenByDefault))
                 .map(c => c.id);
                 .map(c => c.id);
@@ -223,24 +248,25 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
             });
             });
             this.selectionManager.setCurrentItems(this.items);
             this.selectionManager.setCurrentItems(this.items);
         }
         }
-        this.showSearchFilterRow = dataTableConfig?.[this.id]?.showSearchFilterRow ?? false;
+        this.showSearchFilterRow =
+            !!this.filters?.activeFilters.length ||
+            (dataTableConfig?.[this.id]?.showSearchFilterRow ?? false);
         this.columns.changes.subscribe(() => {
         this.columns.changes.subscribe(() => {
             this.changeDetectorRef.markForCheck();
             this.changeDetectorRef.markForCheck();
         });
         });
 
 
-        this.subscription = this.selectionManager?.selectionChanges$.subscribe(() =>
-            this.changeDetectorRef.markForCheck(),
-        );
+        this.selectionManager?.selectionChanges$
+            .pipe(takeUntil(this.destroy$))
+            .subscribe(() => this.changeDetectorRef.markForCheck());
 
 
-        if (this.selectionManager && this.subscription) {
-            const channelSub = this.dataService.client
+        if (this.selectionManager) {
+            this.dataService.client
                 .userStatus()
                 .userStatus()
                 .mapStream(({ userStatus }) => userStatus.activeChannelId)
                 .mapStream(({ userStatus }) => userStatus.activeChannelId)
-                .pipe(distinctUntilChanged())
-                .subscribe(activeChannelId => {
+                .pipe(distinctUntilChanged(), takeUntil(this.destroy$))
+                .subscribe(() => {
                     this.selectionManager?.clearSelection();
                     this.selectionManager?.clearSelection();
                 });
                 });
-            this.subscription?.add(channelSub);
         }
         }
     }
     }
 
 
@@ -285,11 +311,65 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnDe
         this.selectionManager?.toggleSelection(item, event);
         this.selectionManager?.toggleSelection(item, event);
     }
     }
 
 
+    saveFilterPreset() {
+        const name = this.filterPresetName.value;
+        if (this.filters && name) {
+            const value = this.filters.serialize();
+            const dataTableConfig = this.getDataTableConfig();
+            if (!dataTableConfig[this.id].filterPresets) {
+                dataTableConfig[this.id].filterPresets = [];
+            }
+            const existingName = dataTableConfig[this.id].filterPresets.find(p => p.name === name);
+            if (existingName) {
+                existingName.value = value;
+            } else {
+                dataTableConfig[this.id].filterPresets.push({
+                    name,
+                    value: this.filters.serialize(),
+                });
+            }
+            this.localStorageService.set('dataTableConfig', dataTableConfig);
+            this.filterPresetName.setValue('');
+            this.filterPresets = this.getFilterPreset();
+            this.addPresetDropdown.toggleOpen();
+            this.getSerializedActiveFilters();
+        }
+    }
+
+    deleteFilterPreset(name: string) {
+        const dataTableConfig = this.getDataTableConfig();
+        dataTableConfig[this.id].filterPresets = dataTableConfig[this.id].filterPresets.filter(
+            p => p.name !== name,
+        );
+        this.localStorageService.set('dataTableConfig', dataTableConfig);
+        this.filterPresets = this.getFilterPreset();
+        this.getSerializedActiveFilters();
+    }
+
+    private getSerializedActiveFilters(): void {
+        const dataTableConfig = this.getDataTableConfig();
+        this.serializedActiveFilters = this.filters.serialize();
+        this.selectedFilterPreset = dataTableConfig[this.id].filterPresets.find(
+            p => p.value === this.serializedActiveFilters,
+        )?.name;
+        this.changeDetectorRef.markForCheck();
+    }
+
     protected getDataTableConfig(): DataTableConfig {
     protected getDataTableConfig(): DataTableConfig {
         const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
         const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
         if (!dataTableConfig[this.id]) {
         if (!dataTableConfig[this.id]) {
-            dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };
+            dataTableConfig[this.id] = {
+                visibility: [],
+                order: {},
+                showSearchFilterRow: false,
+                filterPresets: [],
+            };
         }
         }
         return dataTableConfig;
         return dataTableConfig;
     }
     }
+
+    private getFilterPreset(): Array<{ name: string; value: string }> {
+        const dataTableConfig = this.getDataTableConfig();
+        return dataTableConfig[this.id].filterPresets ?? [];
+    }
 }
 }

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/data-table-filters/data-table-filters.component.ts

@@ -106,7 +106,7 @@ export class DataTableFiltersComponent implements AfterViewInit {
                     amount: new FormControl(value?.amount ?? ''),
                     amount: new FormControl(value?.amount ?? ''),
                 },
                 },
                 control => {
                 control => {
-                    if (!control.value.amount) {
+                    if (control.value.amount == null) {
                         return { noSelection: true };
                         return { noSelection: true };
                     }
                     }
                     return null;
                     return null;

+ 0 - 1
packages/admin-ui/src/lib/core/src/shared/components/page-header-tabs/page-header-tabs.component.scss

@@ -15,7 +15,6 @@
     cursor: pointer;
     cursor: pointer;
 
 
     &.active {
     &.active {
-        font-weight: 600;
         color: var(--color-text-active);
         color: var(--color-text-active);
         border-bottom-color: var(--color-text-active);
         border-bottom-color: var(--color-text-active);
     }
     }

+ 4 - 0
packages/admin-ui/src/lib/static/styles/global/_overrides.scss

@@ -79,6 +79,10 @@ button.icon-button {
     background: none;
     background: none;
     cursor: pointer;
     cursor: pointer;
     color: var(--color-icon-button);
     color: var(--color-icon-button);
+    border-radius: var(--border-radius);
+    &:hover {
+        color: var(--color-icon-button-hover);
+    }
 }
 }
 
 
 @media screen and (min-width: $breakpoint-small) {
 @media screen and (min-width: $breakpoint-small) {

+ 1 - 0
packages/admin-ui/src/lib/static/styles/theme/dark.scss

@@ -93,6 +93,7 @@
     --color-chip-error-bg: var(--color-error-700);
     --color-chip-error-bg: var(--color-error-700);
 
 
     --color-icon-button: var(--color-grey-200);
     --color-icon-button: var(--color-grey-200);
+    --color-icon-button-hover: var(--color-primary-200);
     --color-form-input-bg: var(--color-weight-150);
     --color-form-input-bg: var(--color-weight-150);
     --color-form-input-focus: var(--color-primary-500);
     --color-form-input-focus: var(--color-primary-500);
     --color-timeline-thread: var(--color-primary-700);
     --color-timeline-thread: var(--color-primary-700);

+ 1 - 0
packages/admin-ui/src/lib/static/styles/theme/default.scss

@@ -152,6 +152,7 @@
 
 
 
 
     --color-icon-button: var(--color-grey-600);
     --color-icon-button: var(--color-grey-600);
+    --color-icon-button-hover: var(--color-primary-600);
     --color-form-input-bg: white;
     --color-form-input-bg: white;
     --color-form-input-focus: var(--color-primary-100);
     --color-form-input-focus: var(--color-primary-100);
     --color-timeline-thread: var(--color-primary-100);
     --color-timeline-thread: var(--color-primary-100);