Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Implementation of an organization unit tree frontend #52

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ import { WorkflowStepStatistics } from './statistics/models/workflow-step-statis
import { WorkflowOwnerStatisticsDataService } from './statistics/workflow-owner-statistics-data.service';
import { WorkflowOwnerStatistics } from './statistics/models/workflow-owner-statistics.model';
import { LoginStatisticsService } from './statistics/login-statistics.service';
import { OrgunittreeService } from './orgunittree/orgunittree.service';
import { LoginStatistics } from './statistics/models/login-statistics.model';
import { MachineToken } from './auth/models/machine-token.model';
import { SchemaJsonLDService } from './metadata/schema-json-ld/schema-json-ld.service';
Expand Down Expand Up @@ -375,6 +376,7 @@ const PROVIDERS = [
WorkflowStepStatisticsDataService,
WorkflowOwnerStatisticsDataService,
LoginStatisticsService,
OrgunittreeService,
];

const SCHEMA_PROVIDERS = [
Expand Down
52 changes: 52 additions & 0 deletions src/app/core/orgunittree/models/orgunittree-node.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { autoserialize, autoserializeAs, deserialize, inheritSerialization } from 'cerialize';
import { HALLink } from '../../shared/hal-link.model';
import { DSpaceObject } from '../../shared/dspace-object.model';
import { CacheableObject } from '../../cache/cacheable-object.model';
import { ORGUNITTREENODE } from './orgunittree-node.resource-type';
import { GenericConstructor } from '../../shared/generic-constructor';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { link, typedObject } from '../../cache/builders/build-decorators';
import { Item } from '../../shared/item.model';
import { Observable } from 'rxjs';
import { RemoteData } from '../../data/remote-data';
import { ITEM } from '../../shared/item.resource-type';

@typedObject
@inheritSerialization(DSpaceObject)
export class OrgunittreeNode extends DSpaceObject implements CacheableObject {
static type = ORGUNITTREENODE;

@autoserialize
public uuid: string;

public id: string;

@autoserialize
public childs: string[];

@autoserialize
public metrics: Map<string,number>;

@autoserialize
public aggregatedmetrics: Map<string,number>;

@autoserializeAs(DSpaceObject, 'item')
public item: DSpaceObject;

@link(ITEM, false)
items?: Observable<RemoteData<Item>>;

/**
* The {@link HALLink}s for OrgunittreeNode
*/
@deserialize
_links: {
self: HALLink,
items: HALLink;
};

getRenderTypes(): (string | GenericConstructor<ListableObject>)[] {
return [this.constructor as GenericConstructor<ListableObject>];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ResourceType } from '../../shared/resource-type';

/**
* The resource type for Orgunittreenode
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const ORGUNITTREENODE = new ResourceType('orgunittreenode');
72 changes: 72 additions & 0 deletions src/app/core/orgunittree/orgunittree.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { RequestService } from '../data/request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { Store } from '@ngrx/store';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../shared/operators';
import { OrgunittreeNode } from './models/orgunittree-node.model';
import { map } from 'rxjs/operators';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { FindAllDataImpl } from '../data/base/find-all-data';
import { dataService } from '../data/base/data-service.decorator';
import { ORGUNITTREENODE } from './models/orgunittree-node.resource-type';
import { followLink } from '../../shared/utils/follow-link-config.model';

/**
* Service for checking and managing the orgunittrees
*/
@Injectable()
@dataService(ORGUNITTREENODE)
export class OrgunittreeService extends IdentifiableDataService<OrgunittreeNode> {
protected linkPath = 'orgunittree';

private findAllData: FindAllDataImpl<OrgunittreeNode>;

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected store: Store<any>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<OrgunittreeNode>,
) {
super('orgunittree', requestService, rdbService, objectCache, halService);
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
}

/**
* Retrieve the Orgunittree Object
*/
find(): Observable<OrgunittreeNode[]> {
return this.findAllData.findAll(undefined, true, true, followLink('items'))
.pipe(getFirstSucceededRemoteData(),
map((remoteData: RemoteData<PaginatedList<OrgunittreeNode>>) => remoteData.payload),
map((list: PaginatedList<OrgunittreeNode>) => list.page),
);
}

findsingle(id: string): Observable<OrgunittreeNode> {
return this.findById(id, true, true, followLink('items'))
.pipe(getFirstSucceededRemoteData(),
map((remoteData: RemoteData<OrgunittreeNode>) => remoteData.payload));
}

recreate(): Observable<OrgunittreeNode[]> {
let href$: Observable<string> = this.getIDHrefObs(encodeURIComponent('recreate'));
return this.findListByHref(href$).pipe(
getFirstCompletedRemoteData(),
map((remoteData: RemoteData<PaginatedList<OrgunittreeNode>>) => remoteData.payload),
map((list: PaginatedList<OrgunittreeNode>) => list?.page)
);
}

}
3 changes: 3 additions & 0 deletions src/app/explore-page/explore-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
<ds-themed-counters-section *ngSwitchCase="'counters'"
[sectionId]="sectionId"
[countersSection]="sectionComponent"></ds-themed-counters-section>
<ds-themed-orgunit-tree-section *ngSwitchCase="'orgunittree'"
[sectionId]="sectionId"
[treeSection]="sectionComponent"></ds-themed-orgunit-tree-section>
</div>
</div>
</div>
11 changes: 10 additions & 1 deletion src/app/shared/explore/explore.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import { SearchSectionComponent } from './section-component/search-section/searc
import { ThemedSearchSectionComponent } from './section-component/search-section/themed-search-section.component';
import { TextSectionComponent } from './section-component/text-section/text-section.component';
import { ThemedTextSectionComponent } from './section-component/text-section/themed-text-section.component';
import { OrgunittreeSectionComponent } from './section-component/orgunit-tree-section/orgunit-tree-section.component';
import { ThemedOrgunittreeSectionComponent } from './section-component/orgunit-tree-section/themed-orgunit-tree-section.component';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { OrgunitTreeNodeComponent } from './section-component/orgunit-tree-section/orgunit-tree-node/orgunit-tree-node.component';

import { SharedModule } from '../shared.module';

const COMPONENTS = [
Expand All @@ -36,6 +41,9 @@ const COMPONENTS = [
ThemedTextSectionComponent,
TopSectionComponent,
ThemedTopSectionComponent,
OrgunittreeSectionComponent,
OrgunitTreeNodeComponent,
ThemedOrgunittreeSectionComponent
];

@NgModule({
Expand All @@ -44,7 +52,8 @@ const COMPONENTS = [
],
imports: [
CommonModule,
SharedModule
SharedModule,
NgbAccordionModule
],
exports: [
...COMPONENTS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<ng-template #orgunittreemetrics>
<!-- Print metrics -->

<div *ngIf="node.metrics != undefined && (!isParent() || printnochilds)">
<ng-container *ngFor="let rendering of metricsrendering.renderings">
<ng-container *ngIf="hasMetrics(rendering.key)">
<ng-container *ngVar="getMetricsValue(rendering.key) as metricvalue">
<span class="badge badge-secondary metrics-icon"
*ngIf="rendering.hidezero === undefined || (rendering?.hidezero && rendering?.hidezero === true && metricvalue > 0)">
<i class="fa {{rendering.icon}}" [ngbTooltip]="(rendering.tooltip) ? (rendering.tooltip | translate) : null" [placement]="'right'">
{{metricvalue}}
</i>&nbsp;
</span>
</ng-container>
</ng-container>
</ng-container>
</div>
<div *ngIf="node.aggregatedmetrics != undefined && isParent() && !printnochilds">
<ng-container *ngFor="let rendering of metricsrendering.renderings">
<ng-container *ngIf="hasAggregatedMetrics(rendering.key)">
<ng-container *ngVar="getAggregatedMetricsValue(rendering.key) as metricvalue">
<span class="badge badge-secondary metrics-icon"
*ngIf="rendering.hidezero === undefined || (rendering?.hidezero && rendering?.hidezero === true && metricvalue > 0)">
<i class="fa {{rendering.icon}}" [ngbTooltip]="(rendering.tooltip) ? (rendering.tooltip | translate) : null" [placement]="'right'">
{{metricvalue}}
</i>&nbsp;
</span>
</ng-container>
</ng-container>
</ng-container>
</div>
</ng-template>
<div *ngIf="isLoading$ | async">
<ds-loading message="{{'orgunittree.loading.node' | translate}}"></ds-loading>
</div>
<div *ngIf="node$ | async">
<ngb-accordion #acc="ngbAccordion">
<ngb-panel #panel>
<ng-template ngbPanelTitle>
<!-- Got some item - Print title -->
<div class="treenode-title"
*ngIf="hasItem() || ((node.items | async)?.payload != undefined)">
<div *ngIf="(isParent() && !printnochilds)">
<span *ngIf="acc.panels.first && acc.isExpanded(acc.panels.first.id)">
<i class="fa fa-chevron-down pr-1"></i>
</span>
<span *ngIf="!acc.isExpanded(acc.panels.first.id)">
<i class="fa fa-chevron-right pr-1"></i>
</span>
{{printTitle() | async}}
<ng-container *ngTemplateOutlet="orgunittreemetrics"></ng-container>
</div>
<a *ngIf="(!isParent() || printnochilds)"
rel="noopener noreferrer" [routerLink]="[path]">
<span *ngIf="node.item && hasEntityIcon()" class="pl-1">
<i class="fa {{getEntityIcon()}}"></i>
</span>
{{printTitle() | async}}
<ng-container *ngTemplateOutlet="orgunittreemetrics"></ng-container>
</a>
</div>
</ng-template>
<ng-template ngbPanelContent *ngIf="isParent() && !printnochilds">
<!-- repeat the actual parent node one time inside the body -->
<ds-orgunit-tree-node
[inputnode]="node"
[depth]="depth+1"
[printnochilds]="true"
[metricsrendering]="metricsrendering">
</ds-orgunit-tree-node>
<ds-orgunit-tree-node
*ngFor="let childsuuid of node.childs"
[uuid]="childsuuid"
[depth]="depth+1"
[metricsrendering]="metricsrendering">
</ds-orgunit-tree-node>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.metrics-icon {
margin-left:3px;
}

.treenode-title {
text-align: left;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { OrgunitTreeNodeComponent } from './orgunit-tree-node.component';

describe('OrgunitTreeNodeComponent', () => {
let component: OrgunitTreeNodeComponent;
let fixture: ComponentFixture<OrgunitTreeNodeComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ OrgunitTreeNodeComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(OrgunitTreeNodeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});