diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.html b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.html index 37cc33f92a..a8aae4998b 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.html +++ b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.html @@ -12,7 +12,7 @@ -
  • +
  • {{'admin-notify-dashboard.logs' | translate}}
    diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.ts index 5adc71a497..77116d97d3 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard.component.ts @@ -14,6 +14,8 @@ import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.compo import { AdminNotifySearchConfigurationService } from './config/admin-notify-search-configuration.service'; import { AdminNotifySearchFilterService } from './config/admin-notify-filter-service'; import { AdminNotifySearchFilterConfig } from './config/admin-notify-search-filter-config'; +import { ViewMode } from "../../core/shared/view-mode.model"; +import { Router } from "@angular/router"; export const FILTER_SEARCH: InjectionToken = new InjectionToken('searchFilterService'); @@ -45,7 +47,8 @@ export class AdminNotifyDashboardComponent implements OnInit{ id: 'single-result-options', pageSize: 1 }); - constructor(private searchService: SearchService) {} + constructor(private searchService: SearchService, + private router: Router) {} ngOnInit() { const mertricsRowsConfigurations = this.metricsConfig @@ -99,4 +102,28 @@ export class AdminNotifyDashboardComponent implements OnInit{ }; }); } + + /** + * Activate Table view mode for search result rendering + */ + activateTableMode() { + this.searchService.setViewMode(ViewMode.Table, this.getSearchLinkParts()); + } + + /** + * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true + */ + public getSearchLink(): string { + return this.searchService.getSearchLink(); + } + + /** + * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces + */ + public getSearchLinkParts(): string[] { + if (this.searchService) { + return []; + } + return this.getSearchLink().split('/'); + } } diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html index 40e5cbc195..bc703576b2 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html +++ b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.html @@ -1,7 +1,34 @@
    -
    {{indexableObject.queueTimeout ?? 'n/a'}}
    -
    {{indexableObject.source ?? 'n/a'}}
    -
    {{indexableObject.target ?? 'n/a'}}
    -
    {{indexableObject.coarNotifyType ?? 'n/a'}}
    -
    {{indexableObject.queueStatusLabel ?? 'n/a'}}
    +
    + + + + + + + + + + + + + + + + + + + +
    TimestampSourceTargetTypeStatus
    +
    {{object.queueTimeout}}
    +
    +
    {{object.source}}
    +
    +
    {{object.target}}
    +
    +
    {{object.coarNotifyType}}
    +
    +
    {{object.queueStatusLabel}}
    +
    +
    diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts index 0df2159b38..4e2a4088d2 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.ts @@ -1,24 +1,26 @@ import { Component, OnInit } from '@angular/core'; -import { - listableObjectComponent -} from '../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { AdminNotifySearchResult } from '../models/admin-notify-message-search-result.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { Context } from '../../../core/shared/context.model'; -import { - SearchResultListElementComponent -} from '../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { AdminNotifyMessage } from '../models/admin-notify-message.model'; +import { + tabulatableObjectsComponent +} from "../../../shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator"; +import { + TabulatableResultListElementsComponent +} from "../../../shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component"; +import { PaginatedList } from "../../../core/data/paginated-list.model"; -@listableObjectComponent(AdminNotifySearchResult, ViewMode.ListElement, Context.CoarNotify) +@tabulatableObjectsComponent(AdminNotifySearchResult, ViewMode.Table, Context.CoarNotify) @Component({ selector: 'ds-admin-notify-search-result', templateUrl: './admin-notify-search-result.component.html', styleUrls: ['./admin-notify-search-result.component.scss'] }) -export class AdminNotifySearchResultComponent extends SearchResultListElementComponent implements OnInit{ - indexableObject: AdminNotifyMessage; +export class AdminNotifySearchResultComponent extends TabulatableResultListElementsComponent, AdminNotifyMessage> implements OnInit{ + public indexableObjects: AdminNotifyMessage[]; ngOnInit() { - this.indexableObject = this.object.indexableObject; + this.indexableObjects = this.objects.page.map(object => object.indexableObject); + console.log(this.objects.page) } } diff --git a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts index 42bdc7b3c2..be861861b4 100644 --- a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts +++ b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts @@ -56,6 +56,12 @@ export class AdminNotifyMessage extends DSpaceObject { @autoserialize queueStatus: number; + /** + * The status of the queue + */ + @autoserialize + indexableObject: AdminNotifyMessage; + @deserialize _links: { self: { diff --git a/src/app/core/shared/view-mode.model.ts b/src/app/core/shared/view-mode.model.ts index a27cb6954b..9c2f499b3d 100644 --- a/src/app/core/shared/view-mode.model.ts +++ b/src/app/core/shared/view-mode.model.ts @@ -7,4 +7,5 @@ export enum ViewMode { GridElement = 'grid', DetailedListElement = 'detailed', StandalonePage = 'standalone', + Table = 'table', } diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html index 3b0dca80ac..45b82dd859 100644 --- a/src/app/shared/object-collection/object-collection.component.html +++ b/src/app/shared/object-collection/object-collection.component.html @@ -58,3 +58,20 @@ + + diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 7a3cc42bf5..3110283ddd 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -154,7 +154,6 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges } private instantiateComponent(object: ListableObject, changes?: SimpleChanges): void { - const component = this.getComponent(object.getRenderTypes(), this.viewMode, this.context); const viewContainerRef = this.listableObjectDirective.viewContainerRef; diff --git a/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts new file mode 100644 index 0000000000..9ab9e7c53d --- /dev/null +++ b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.decorator.ts @@ -0,0 +1,194 @@ +import { ViewMode } from '../../../../core/shared/view-mode.model'; +import { Context } from '../../../../core/shared/context.model'; +import { hasNoValue, hasValue, isNotEmpty } from '../../../empty.util'; +import { GenericConstructor } from '../../../../core/shared/generic-constructor'; +import { ListableObject } from '../listable-object.model'; +import { environment } from '../../../../../environments/environment'; +import { ThemeConfig } from '../../../../../config/theme.config'; +import { InjectionToken } from '@angular/core'; + +export const DEFAULT_VIEW_MODE = ViewMode.ListElement; +export const DEFAULT_CONTEXT = Context.Any; +export const DEFAULT_THEME = '*'; + +/** + * A class used to compare two matches and their relevancy to determine which of the two gains priority over the other + * + * "level" represents the index of the first default value that was used to find the match with: + * ViewMode being index 0, Context index 1 and theme index 2. Examples: + * - If a default value was used for context, but not view-mode and theme, the "level" will be 1 + * - If a default value was used for view-mode and context, but not for theme, the "level" will be 0 + * - If no default value was used for any of the fields, the "level" will be 3 + * + * "relevancy" represents the amount of values that didn't require a default value to fall back on. Examples: + * - If a default value was used for theme, but not view-mode and context, the "relevancy" will be 2 + * - If a default value was used for view-mode and context, but not for theme, the "relevancy" will be 1 + * - If a default value was used for all fields, the "relevancy" will be 0 + * - If no default value was used for any of the fields, the "relevancy" will be 3 + * + * To determine which of two MatchRelevancies is the most relevant, we compare "level" and "relevancy" in that order. + * If any of the two is higher than the other, that match is most relevant. Examples: + * - { level: 1, relevancy: 1 } is more relevant than { level: 0, relevancy: 2 } + * - { level: 1, relevancy: 1 } is less relevant than { level: 1, relevancy: 2 } + * - { level: 1, relevancy: 1 } is more relevant than { level: 1, relevancy: 0 } + * - { level: 1, relevancy: 1 } is less relevant than { level: 2, relevancy: 0 } + * - { level: 1, relevancy: 1 } is more relevant than null + */ +class MatchRelevancy { + constructor(public match: any, + public level: number, + public relevancy: number) { + } + + isMoreRelevantThan(otherMatch: MatchRelevancy): boolean { + if (hasNoValue(otherMatch)) { + return true; + } + if (otherMatch.level > this.level) { + return false; + } + if (otherMatch.level === this.level && otherMatch.relevancy > this.relevancy) { + return false; + } + return true; + } + + isLessRelevantThan(otherMatch: MatchRelevancy): boolean { + return !this.isMoreRelevantThan(otherMatch); + } +} + +/** + * Factory to allow us to inject getThemeConfigFor so we can mock it in tests + */ +export const GET_THEME_CONFIG_FOR_FACTORY = new InjectionToken<(str) => ThemeConfig>('getThemeConfigFor', { + providedIn: 'root', + factory: () => getThemeConfigFor +}); + +const map = new Map(); + +/** + * Decorator used for rendering a listable object + * @param objectType The object type or entity type the component represents + * @param viewMode The view mode the component represents + * @param context The optional context the component represents + * @param theme The optional theme for the component + */ +export function tabulatableObjectsComponent(objectsType: string | GenericConstructor, viewMode: ViewMode, context: Context = DEFAULT_CONTEXT, theme = DEFAULT_THEME) { + return function decorator(component: any) { + if (hasNoValue(objectsType)) { + return; + } + if (hasNoValue(map.get(objectsType))) { + map.set(objectsType, new Map()); + } + if (hasNoValue(map.get(objectsType).get(viewMode))) { + map.get(objectsType).set(viewMode, new Map()); + } + if (hasNoValue(map.get(objectsType).get(viewMode).get(context))) { + map.get(objectsType).get(viewMode).set(context, new Map()); + } + map.get(objectsType).get(viewMode).get(context).set(theme, component); + }; +} + +/** + * Getter to retrieve the matching listable object component + * + * Looping over the provided types, it'll attempt to find the best match depending on the {@link MatchRelevancy} returned by getMatch() + * The most relevant match between types is kept and eventually returned + * + * @param types The types of which one should match the listable component + * @param viewMode The view mode that should match the components + * @param context The context that should match the components + * @param theme The theme that should match the components + */ +export function getTabulatableObjectsComponent(types: (string | GenericConstructor)[], viewMode: ViewMode, context: Context = DEFAULT_CONTEXT, theme: string = DEFAULT_THEME) { + let currentBestMatch: MatchRelevancy = null; + for (const type of types) { + const typeMap = map.get(type); + if (hasValue(typeMap)) { + const match = getMatch(typeMap, [viewMode, context, theme], [DEFAULT_VIEW_MODE, DEFAULT_CONTEXT, DEFAULT_THEME]); + if (hasNoValue(currentBestMatch) || currentBestMatch.isLessRelevantThan(match)) { + currentBestMatch = match; + } + } + } + return hasValue(currentBestMatch) ? currentBestMatch.match : null; +} + +/** + * Find an object within a nested map, matching the provided keys as best as possible, falling back on defaults wherever + * needed. + * + * Starting off with a Map, it loops over the provided keys, going deeper into the map until it finds a value + * If at some point, no value is found, it'll attempt to use the default value for that index instead + * If the default value exists, the index is stored in the "level" + * If no default value exists, 1 is added to "relevancy" + * See {@link MatchRelevancy} what these represent + * + * @param typeMap a multi-dimensional map + * @param keys the keys of the multi-dimensional map to loop over. Each key represents a level within the map + * @param defaults the default values to use for each level, in case no value is found for the key at that index + * @returns matchAndLevel a {@link MatchRelevancy} object containing the match and its level of relevancy + */ +function getMatch(typeMap: Map, keys: any[], defaults: any[]): MatchRelevancy { + let currentMap = typeMap; + let level = -1; + let relevancy = 0; + for (let i = 0; i < keys.length; i++) { + // If we're currently checking the theme, resolve it first to take extended themes into account + let currentMatch = defaults[i] === DEFAULT_THEME ? resolveTheme(currentMap, keys[i]) : currentMap.get(keys[i]); + if (hasNoValue(currentMatch)) { + currentMatch = currentMap.get(defaults[i]); + if (level === -1) { + level = i; + } + } else { + relevancy++; + } + if (hasValue(currentMatch)) { + if (currentMatch instanceof Map) { + currentMap = currentMatch as Map; + } else { + return new MatchRelevancy(currentMatch, level > -1 ? level : i + 1, relevancy); + } + } else { + return null; + } + } + return null; +} + +/** + * Searches for a ThemeConfig by its name; + */ +export const getThemeConfigFor = (themeName: string): ThemeConfig => { + return environment.themes.find(theme => theme.name === themeName); +}; + +/** + * Find a match in the given map for the given theme name, taking theme extension into account + * + * @param contextMap A map of theme names to components + * @param themeName The name of the theme to check + * @param checkedThemeNames The list of theme names that are already checked + */ +export const resolveTheme = (contextMap: Map, themeName: string, checkedThemeNames: string[] = []): any => { + const match = contextMap.get(themeName); + if (hasValue(match)) { + return match; + } else { + const cfg = getThemeConfigFor(themeName); + if (hasValue(cfg) && isNotEmpty(cfg.extends)) { + const nextTheme = cfg.extends; + const nextCheckedThemeNames = [...checkedThemeNames, themeName]; + if (checkedThemeNames.includes(nextTheme)) { + throw new Error('Theme extension cycle detected: ' + [...nextCheckedThemeNames, nextTheme].join(' -> ')); + } else { + return resolveTheme(contextMap, nextTheme, nextCheckedThemeNames); + } + } + } +}; diff --git a/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.directive.ts b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.directive.ts new file mode 100644 index 0000000000..88c12dfe76 --- /dev/null +++ b/src/app/shared/object-collection/shared/tabulatable-objects/tabulatable-objects.directive.ts @@ -0,0 +1,11 @@ +import { Directive, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector: '[dsTabulatableObjects]', +}) +/** + * Directive used as a hook to know where to inject the dynamic listable object component + */ +export class TabulatableObjectsDirective { + constructor(public viewContainerRef: ViewContainerRef) { } +} diff --git a/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts b/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts new file mode 100644 index 0000000000..5aa2ca84ee --- /dev/null +++ b/src/app/shared/object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component.ts @@ -0,0 +1,22 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { + AbstractTabulatableElementComponent +} from "../../../object-collection/shared/objects-collection-tabulatable/objects-collection-tabulatable.component"; +import { DSpaceObject } from "../../../../core/shared/dspace-object.model"; +import { TruncatableService } from "../../../truncatable/truncatable.service"; +import { DSONameService } from "../../../../core/breadcrumbs/dso-name.service"; +import { APP_CONFIG, AppConfig } from "../../../../../config/app-config.interface"; +import { PaginatedList } from "../../../../core/data/paginated-list.model"; + +@Component({ + selector: 'ds-search-result-list-element', + template: `` +}) +export class TabulatableResultListElementsComponent, K extends DSpaceObject> extends AbstractTabulatableElementComponent { + public constructor(protected truncatableService: TruncatableService, + public dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig?: AppConfig) { + super(dsoNameService); + } + +} diff --git a/src/app/shared/object-table/object-table.component.html b/src/app/shared/object-table/object-table.component.html new file mode 100644 index 0000000000..a4fe8cfc38 --- /dev/null +++ b/src/app/shared/object-table/object-table.component.html @@ -0,0 +1,32 @@ + +
    +
    + + +
    +
    + + +
    + + diff --git a/src/app/shared/object-table/object-table.component.scss b/src/app/shared/object-table/object-table.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-table/object-table.component.spec.ts b/src/app/shared/object-table/object-table.component.spec.ts new file mode 100644 index 0000000000..b20bed8d89 --- /dev/null +++ b/src/app/shared/object-table/object-table.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ObjectTableComponent } from './object-table.component'; + +describe('ObjectTableComponent', () => { + let component: ObjectTableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ObjectTableComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ObjectTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/object-table/object-table.component.ts b/src/app/shared/object-table/object-table.component.ts new file mode 100644 index 0000000000..c202c0b865 --- /dev/null +++ b/src/app/shared/object-table/object-table.component.ts @@ -0,0 +1,209 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { ViewMode } from "../../core/shared/view-mode.model"; +import { PaginationComponentOptions } from "../pagination/pagination-component-options.model"; +import { SortDirection, SortOptions } from "../../core/cache/models/sort-options.model"; +import { CollectionElementLinkType } from "../object-collection/collection-element-link.type"; +import { Context } from "../../core/shared/context.model"; +import { BehaviorSubject} from "rxjs"; +import { RemoteData } from "../../core/data/remote-data"; +import { PaginatedList } from "../../core/data/paginated-list.model"; +import { ListableObject } from "../object-collection/shared/listable-object.model"; +import { fadeIn } from "../animations/fade"; + + +@Component({ + changeDetection: ChangeDetectionStrategy.Default, + encapsulation: ViewEncapsulation.Emulated, + selector: 'ds-object-table', + templateUrl: './object-table.component.html', + styleUrls: ['./object-table.component.scss'], + animations: [fadeIn] +}) +export class ObjectTableComponent { + /** + * The view mode of this component + */ + viewMode = ViewMode.Table; + + /** + * The current pagination configuration + */ + @Input() config: PaginationComponentOptions; + + /** + * The current sort configuration + */ + @Input() sortConfig: SortOptions; + + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ + @Input() showPaginator = true; + + /** + * Whether to show the thumbnail preview + */ + @Input() showThumbnails; + + /** + * The whether or not the gear is hidden + */ + @Input() hideGear = false; + + /** + * Whether or not the pager is visible when there is only a single page of results + */ + @Input() hidePagerWhenSinglePage = true; + + /** + * The link type of the listable elements + */ + @Input() linkType: CollectionElementLinkType; + + /** + * The context of the listable elements + */ + @Input() context: Context; + + /** + * Option for hiding the pagination detail + */ + @Input() hidePaginationDetail = false; + + /** + * Behavior subject to output the current listable objects + */ + private _objects$: BehaviorSubject>>; + + /** + * Setter to make sure the observable is turned into an observable + * @param objects The new objects to output + */ + @Input() set objects(objects: RemoteData>) { + this._objects$.next(objects); + } + + /** + * Getter to return the current objects + */ + get objects() { + return this._objects$.getValue(); + } + + /** + * An event fired when the page is changed. + * Event's payload equals to the newly selected page. + */ + @Output() change: EventEmitter<{ + pagination: PaginationComponentOptions, + sort: SortOptions + }> = new EventEmitter<{ + pagination: PaginationComponentOptions, + sort: SortOptions + }>(); + + /** + * An event fired when the page is changed. + * Event's payload equals to the newly selected page. + */ + @Output() pageChange: EventEmitter = new EventEmitter(); + + /** + * An event fired when the page wsize is changed. + * Event's payload equals to the newly selected page size. + */ + @Output() pageSizeChange: EventEmitter = new EventEmitter(); + + /** + * An event fired when the sort direction is changed. + * Event's payload equals to the newly selected sort direction. + */ + @Output() sortDirectionChange: EventEmitter = new EventEmitter(); + + /** + * An event fired when on of the pagination parameters changes + */ + @Output() paginationChange: EventEmitter = new EventEmitter(); + + /** + * An event fired when the sort field is changed. + * Event's payload equals to the newly selected sort field. + */ + @Output() sortFieldChange: EventEmitter = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the previous button is clicked + */ + @Output() prev = new EventEmitter(); + + /** + * If showPaginator is set to true, emit when the next button is clicked + */ + @Output() next = new EventEmitter(); + + data: any = {}; + + constructor() { + this._objects$ = new BehaviorSubject(undefined); + } + + /** + * Initialize the instance variables + */ + ngOnInit(): void { + console.log('table rendered') + } + + /** + * Emits the current page when it changes + * @param event The new page + */ + onPageChange(event) { + this.pageChange.emit(event); + } + /** + * Emits the current page size when it changes + * @param event The new page size + */ + onPageSizeChange(event) { + this.pageSizeChange.emit(event); + } + /** + * Emits the current sort direction when it changes + * @param event The new sort direction + */ + onSortDirectionChange(event) { + this.sortDirectionChange.emit(event); + } + + /** + * Emits the current sort field when it changes + * @param event The new sort field + */ + onSortFieldChange(event) { + this.sortFieldChange.emit(event); + } + + /** + * Emits the current pagination when it changes + * @param event The new pagination + */ + onPaginationChange(event) { + this.paginationChange.emit(event); + } + + /** + * Go to the previous page + */ + goPrev() { + this.prev.emit(true); + } + + /** + * Go to the next page + */ + goNext() { + this.next.emit(true); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 61482fb61a..ce4cb1ed06 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -286,6 +286,17 @@ import { SplitPipe } from './utils/split.pipe'; import { ThemedUserMenuComponent } from './auth-nav-menu/user-menu/themed-user-menu.component'; import { ThemedLangSwitchComponent } from './lang-switch/themed-lang-switch.component'; import { NotificationBoxComponent } from './notification-box/notification-box.component'; +import { ObjectTableComponent } from './object-table/object-table.component'; +import { TabulatableObjectsLoaderComponent } from './object-collection/shared/tabulatable-objects/tabulatable-objects-loader.component'; +import { + TabulatableObjectsDirective +} from "./object-collection/shared/tabulatable-objects/tabulatable-objects.directive"; +import { + AbstractTabulatableElementComponent +} from "./object-collection/shared/objects-collection-tabulatable/objects-collection-tabulatable.component"; +import { + TabulatableResultListElementsComponent +} from "./object-list/search-result-list-element/tabulatable-search-result/tabulatable-result-list-elements.component"; const MODULES = [ CommonModule, @@ -349,7 +360,9 @@ const COMPONENTS = [ ThemedObjectListComponent, ObjectDetailComponent, ObjectGridComponent, + ObjectTableComponent, AbstractListableElementComponent, + AbstractTabulatableElementComponent, ObjectCollectionComponent, PaginationComponent, RSSComponent, @@ -415,6 +428,7 @@ const ENTRY_COMPONENTS = [ CollectionListElementComponent, CommunityListElementComponent, SearchResultListElementComponent, + TabulatableResultListElementsComponent, CommunitySearchResultListElementComponent, CollectionSearchResultListElementComponent, CollectionGridElementComponent, @@ -471,7 +485,8 @@ const ENTRY_COMPONENTS = [ EpersonGroupListComponent, EpersonSearchBoxComponent, GroupSearchBoxComponent, - NotificationBoxComponent + NotificationBoxComponent, + TabulatableObjectsLoaderComponent, ]; const PROVIDERS = [ @@ -490,6 +505,7 @@ const DIRECTIVES = [ RoleDirective, MetadataRepresentationDirective, ListableObjectDirective, + TabulatableObjectsDirective, ClaimedTaskActionsDirective, FileValueAccessorDirective, FileValidator,