Skip to content
2 changes: 1 addition & 1 deletion client/src/app/domain/models/comittees/committee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class Committee extends BaseModel<Committee> {
public name!: string;
public description!: string;
public external_id!: string;
public parent_id!: string;
public parent_id!: Id;
public child_ids!: Id[];

public meeting_ids!: Id[]; // (meeting/committee_id)[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ export abstract class BaseSortListService<V extends BaseViewModel>
return this.sortDefinition?.sortAscending;
}

/**
* Set the current sorting order
*
* @param ascending ascending sorting if true, descending sorting if false
*/
public set additionalInfo(additional: unknown) {
this.sortDefinition!.additionalInfo = additional;
this.updateSortDefinitions();
}

/**
* @returns wether current the sorting is ascending or descending
*/
public get additionalInfo(): unknown {
return this.sortDefinition?.additionalInfo;
}

public get hasSortOptionSelected(): boolean {
const defaultDef = this._defaultDefinitionSubject.value;
const current = this.sortDefinition;
Expand Down Expand Up @@ -192,7 +209,7 @@ export abstract class BaseSortListService<V extends BaseViewModel>
.pipe(distinctUntilChanged((prev, curr) => prev?.sortProperty === curr?.sortProperty))
.subscribe(defaultDef => {
if (this._isDefaultSorting && defaultDef) {
this.setSorting(defaultDef.sortProperty, defaultDef.sortAscending);
this.setSorting(defaultDef.sortProperty, defaultDef.sortAscending, defaultDef.additionalInfo);
} else if (defaultDef && this.sortDefinition?.sortProperty === defaultDef?.sortProperty) {
this.updateSortDefinitions();
}
Expand Down Expand Up @@ -253,12 +270,13 @@ export abstract class BaseSortListService<V extends BaseViewModel>
* @param property a sorting property of a view model
* @param ascending ascending or descending
*/
public setSorting(property: OsSortProperty<V>, ascending: boolean): void {
public setSorting(property: OsSortProperty<V>, ascending: boolean, additionalInfo?: unknown): void {
if (!this.sortDefinition) {
this.sortDefinition = { sortProperty: property, sortAscending: ascending };
this.sortDefinition = { sortProperty: property, sortAscending: ascending, additionalInfo };
} else {
this.sortDefinition!.sortProperty = property;
this.sortDefinition!.sortAscending = ascending;
this.sortDefinition!.additionalInfo = additionalInfo;
this.updateSortDefinitions();
}
this.hasLoaded.resolve(true);
Expand Down Expand Up @@ -368,31 +386,36 @@ export abstract class BaseSortListService<V extends BaseViewModel>
}

private async loadDefinition(): Promise<void> {
let [sortProperty, sortAscending]: [OsSortProperty<V>, boolean] = await Promise.all([
let [sortProperty, sortAscending, additionalInfo]: [OsSortProperty<V>, boolean, any] = await Promise.all([
this.store.get<OsSortProperty<V>>(this.calcStorageKey(`sorting_property`, this.storageKey)),
this.store.get<boolean>(this.calcStorageKey(`sorting_ascending`, this.storageKey))
this.store.get<boolean>(this.calcStorageKey(`sorting_ascending`, this.storageKey)),
this.store.get<any>(this.calcStorageKey(`sorting_additional_info`, this.storageKey))
]);

const defaultDef = await this.getDefaultDefinition();
sortAscending = sortAscending ?? defaultDef.sortAscending;
sortProperty = sortProperty ?? defaultDef.sortProperty;
additionalInfo = additionalInfo ?? defaultDef.additionalInfo;
this.sortDefinition = {
sortAscending,
sortProperty
sortProperty,
additionalInfo
};
this.updateSortDefinitions();
this.hasLoaded.resolve(true);
}

private async setSortingAfterMeetingChange(meetingId: Id): Promise<void> {
let [sortProperty, sortAscending]: [OsSortProperty<V>, boolean] = await Promise.all([
let [sortProperty, sortAscending, additionalInfo]: [OsSortProperty<V>, boolean, any] = await Promise.all([
this.store.get<OsSortProperty<V>>(`sorting_property_${this.storageKey}_${meetingId}`),
this.store.get<boolean>(`sorting_ascending_${this.storageKey}_${meetingId}`)
this.store.get<boolean>(`sorting_ascending_${this.storageKey}_${meetingId}`),
this.store.get<any>(`sorting_additional_info_${this.storageKey}_${meetingId}`)
]);
const defaultDef = await this.getDefaultDefinition();
sortProperty = sortProperty ?? defaultDef.sortProperty;
sortAscending = sortAscending ?? defaultDef.sortAscending;
this.setSorting(sortProperty, sortAscending);
additionalInfo = additionalInfo ?? defaultDef.additionalInfo;
this.setSorting(sortProperty, sortAscending, additionalInfo);
}

/**
Expand All @@ -403,7 +426,7 @@ export abstract class BaseSortListService<V extends BaseViewModel>
return Array.isArray(a) && Array.isArray(b) ? a.equals(b) : a === b;
}

private compareHelperFunction(itemA: V, itemB: V, alternativeProperty: OsSortProperty<V>): number {
protected compareHelperFunction(itemA: V, itemB: V, alternativeProperty: OsSortProperty<V>): number {
return (
this.sortItems(
itemA,
Expand All @@ -426,6 +449,10 @@ export abstract class BaseSortListService<V extends BaseViewModel>
this.store.set(this.calcStorageKey(`sorting_property`, this.storageKey), this.sortDefinition?.sortProperty);
}
this.store.set(this.calcStorageKey(`sorting_ascending`, this.storageKey), this.sortDefinition?.sortAscending);
this.store.set(
this.calcStorageKey(`sorting_additional_info`, this.storageKey),
this.sortDefinition?.additionalInfo
);
}

private calculateDefaultStatus(): void {
Expand Down
1 change: 1 addition & 0 deletions client/src/app/site/base/base-sort.service/os-sort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type SortDefinition<T> = keyof T | OsSortingDefinition<T>;
export interface OsSortingDefinition<T> {
sortProperty: OsSortProperty<T>;
sortAscending: boolean;
additionalInfo?: unknown;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { OpenSlidesTranslationModule } from 'src/app/site/modules/translations';
import { DirectivesModule } from 'src/app/ui/directives';
import { ChipComponent } from 'src/app/ui/modules/chip';
Expand Down Expand Up @@ -32,6 +33,7 @@ import { CommitteeListServiceModule } from './services/committee-list-service.mo
OpenSlidesTranslationModule.forChild(),
MatDividerModule,
MatMenuModule,
MatTooltipModule,
MatIconModule,
MatButtonModule,
DirectivesModule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ <h2>{{ 'Committees' | translate }}</h2>
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
<span>{{ selectedRows.length }}&nbsp;{{ 'selected' | translate }}</span>
</ng-container>

<div class="extra-controls-slot">
<div>
@if (selectedView === 'hierarchy') {
<button mat-button matTooltip="{{ 'List view' | translate }}" (click)="onChangeView('list')">
<mat-icon>format_align_justify</mat-icon>
</button>
} @else {
<button mat-button matTooltip="{{ 'Hierarchy view' | translate }}" (click)="onChangeView('hierarchy')">
<mat-icon>format_align_right</mat-icon>
</button>
}
</div>
</div>
</os-head-bar>

<os-list
Expand All @@ -34,7 +48,11 @@ <h2>{{ 'Committees' | translate }}</h2>
[sortService]="sortService"
[(selectedRows)]="selectedRows"
>
<div *osScrollingTableCell="'name'; row as committee" class="cell-slot fill">
<div
*osScrollingTableCell="'name'; row as committee"
class="cell-slot fill"
[style.padding-left]="(selectedView === 'hierarchy' ? committee.all_parent_ids?.length || 0 : 0) * 25 + 'px'"
>
@if (!isMultiSelect && committee.canAccess()) {
<a class="stretch-to-fill-parent" [attr.aria-label]="ariaLabel(committee)" [routerLink]="committee.id"></a>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class CommitteeListComponent extends BaseListViewComponent<ViewCommittee>
return this.translate.instant(`Agenda items are in process. Please wait ...`);
}

public selectedView = `list`;

public constructor(
protected override translate: TranslateService,
public committeeController: CommitteeControllerService,
Expand All @@ -50,6 +52,15 @@ export class CommitteeListComponent extends BaseListViewComponent<ViewCommittee>
super.setTitle(`Committees`);
this.canMultiSelect = true;
this.listStorageIndex = COMMITTEE_LIST_STORAGE_INDEX;
this.subscriptions.push(
this.sortService.hierarchySort.subscribe(active => {
this.selectedView = active ? `hierarchy` : `list`;
})
);
}

public onChangeView(type: string): void {
this.sortService.hierarchySort = type === `hierarchy`;
}

public editSingle(committee: ViewCommittee): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable, ProviderToken } from '@angular/core';
import { _ } from '@ngx-translate/core';
import { map, Observable } from 'rxjs';
import { Id } from 'src/app/domain/definitions/key-types';
import { BaseRepository } from 'src/app/gateways/repositories/base-repository';
import { CommitteeRepositoryService } from 'src/app/gateways/repositories/committee-repository.service';
import { BaseSortListService, OsSortingOption } from 'src/app/site/base/base-sort.service';
Expand All @@ -12,6 +14,16 @@ import { ViewCommittee } from '../../../../view-models';
export class CommitteeSortService extends BaseSortListService<ViewCommittee> {
protected storageKey = `CommitteeList`;

public get hierarchySort(): Observable<boolean> {
return this.sortingUpdatedObservable.pipe(map(sorting => (sorting?.additionalInfo as any)?.hierarchySort));
}

public set hierarchySort(value: boolean) {
this.additionalInfo = {
hierarchySort: value
};
}

protected repositoryToken: ProviderToken<BaseRepository<any, any>> = CommitteeRepositoryService;

private readonly staticSortOptions: OsSortingOption<ViewCommittee>[] = [
Expand All @@ -23,11 +35,42 @@ export class CommitteeSortService extends BaseSortListService<ViewCommittee> {
public constructor() {
super({
sortProperty: `name`,
sortAscending: true
sortAscending: true,
additionalInfo: {
hierarchySort: true
}
});
}

protected getSortOptions(): OsSortingOption<ViewCommittee>[] {
return this.staticSortOptions;
}

/**
* Sorts the given array according to this services sort settings and returns it.
*/
public override async sort(array: ViewCommittee[]): Promise<ViewCommittee[]> {
const additionalInfo = (await this.getDefaultDefinition()).additionalInfo;
if ((this.additionalInfo as any)?.hierarchySort ?? (additionalInfo as any)?.hierarchySort) {
const input = [...array];
return (await this.doHierarchySort(input, null)).flat(Infinity);
}

return super.sort(array);
}

private async doHierarchySort(remaining: ViewCommittee[], parentId: Id): Promise<ViewCommittee[]> {
const result = [];
let i = remaining.length;
while (i--) {
const entry = remaining[i];
if (entry && (entry.parent_id ?? null) === parentId) {
remaining.splice(i, 1);
result.push([entry, await this.doHierarchySort(remaining, entry.id)]);
}
}

const alternativeProperty = (await this.getDefaultDefinition()).sortProperty;
return result.sort((a, b) => this.compareHelperFunction(a[0], b[0], alternativeProperty));
}
}
Loading