Merge pull request #4924 from tsiegleauq/amendment-lists
Add amendment list
This commit is contained in:
commit
eaf4d8180d
@ -6,7 +6,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
import { ProgressSnackBarComponent } from 'app/shared/components/progress-snack-bar/progress-snack-bar.component';
|
import { ProgressSnackBarComponent } from 'app/shared/components/progress-snack-bar/progress-snack-bar.component';
|
||||||
import { ExportFormData } from 'app/site/motions/modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
import { MotionExportInfo } from 'app/site/motions/services/motion-export.service';
|
||||||
import { ConfigService } from '../ui-services/config.service';
|
import { ConfigService } from '../ui-services/config.service';
|
||||||
import { HttpService } from '../core-services/http.service';
|
import { HttpService } from '../core-services/http.service';
|
||||||
import { ProgressService } from '../ui-services/progress.service';
|
import { ProgressService } from '../ui-services/progress.service';
|
||||||
@ -163,7 +163,7 @@ export class PdfDocumentService {
|
|||||||
private async getStandardPaper(
|
private async getStandardPaper(
|
||||||
documentContent: object,
|
documentContent: object,
|
||||||
metadata?: object,
|
metadata?: object,
|
||||||
exportInfo?: ExportFormData,
|
exportInfo?: MotionExportInfo,
|
||||||
imageUrls?: string[],
|
imageUrls?: string[],
|
||||||
customMargins?: [number, number, number, number],
|
customMargins?: [number, number, number, number],
|
||||||
landscape?: boolean
|
landscape?: boolean
|
||||||
@ -308,7 +308,7 @@ export class PdfDocumentService {
|
|||||||
* @param lrMargin optionally overriding the margins
|
* @param lrMargin optionally overriding the margins
|
||||||
* @returns the footer doc definition
|
* @returns the footer doc definition
|
||||||
*/
|
*/
|
||||||
private getFooter(lrMargin?: [number, number], exportInfo?: ExportFormData): object {
|
private getFooter(lrMargin?: [number, number], exportInfo?: MotionExportInfo): object {
|
||||||
const columns = [];
|
const columns = [];
|
||||||
const showPageNr = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
|
const showPageNr = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
|
||||||
const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false;
|
const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false;
|
||||||
@ -399,7 +399,7 @@ export class PdfDocumentService {
|
|||||||
* @param filename the name of the file to use
|
* @param filename the name of the file to use
|
||||||
* @param metadata
|
* @param metadata
|
||||||
*/
|
*/
|
||||||
public download(docDefinition: object, filename: string, metadata?: object, exportInfo?: ExportFormData): void {
|
public download(docDefinition: object, filename: string, metadata?: object, exportInfo?: MotionExportInfo): void {
|
||||||
this.getStandardPaper(docDefinition, metadata, exportInfo).then(doc => {
|
this.getStandardPaper(docDefinition, metadata, exportInfo).then(doc => {
|
||||||
this.createPdf(doc, filename);
|
this.createPdf(doc, filename);
|
||||||
});
|
});
|
||||||
|
@ -497,6 +497,13 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns all amendments
|
||||||
|
*/
|
||||||
|
public getAllAmendmentsInstantly(): ViewMotion[] {
|
||||||
|
return this.getViewModelList().filter(motion => !!motion.parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amendments to a given motion
|
* Returns the amendments to a given motion
|
||||||
*
|
*
|
||||||
|
@ -146,17 +146,21 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
return this._filterStack;
|
return this._filterStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key to access stored valued
|
||||||
|
*/
|
||||||
|
private storageKey: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param name the name of the filter service
|
* @param name the name of the filter service
|
||||||
* @param store storage service, to read saved filter variables
|
* @param store storage service, to read saved filter variables
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(private store: StorageService, private OSStatus: OpenSlidesStatusService) {
|
||||||
protected name: string,
|
this.storageKey = this.constructor.name;
|
||||||
private store: StorageService,
|
console.log('storage-key: ', this.storageKey);
|
||||||
private OSStatus: OpenSlidesStatusService
|
}
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the filterService.
|
* Initializes the filterService.
|
||||||
@ -166,7 +170,7 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
public async initFilters(inputData: Observable<V[]>): Promise<void> {
|
public async initFilters(inputData: Observable<V[]>): Promise<void> {
|
||||||
let storedFilter: OsFilter[] = null;
|
let storedFilter: OsFilter[] = null;
|
||||||
if (!this.OSStatus.isInHistoryMode) {
|
if (!this.OSStatus.isInHistoryMode) {
|
||||||
storedFilter = await this.store.get<OsFilter[]>('filter_' + this.name);
|
storedFilter = await this.store.get<OsFilter[]>('filter_' + this.storageKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storedFilter && this.isOsFilter(storedFilter)) {
|
if (storedFilter && this.isOsFilter(storedFilter)) {
|
||||||
@ -237,7 +241,7 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
|
|
||||||
let storedFilter = null;
|
let storedFilter = null;
|
||||||
if (!this.OSStatus.isInHistoryMode) {
|
if (!this.OSStatus.isInHistoryMode) {
|
||||||
storedFilter = await this.store.get<OsFilter[]>('filter_' + this.name);
|
storedFilter = await this.store.get<OsFilter[]>('filter_' + this.storageKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!storedFilter) {
|
if (!!storedFilter) {
|
||||||
@ -276,21 +280,19 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
* @param repo repository to create dynamic filters from
|
* @param repo repository to create dynamic filters from
|
||||||
* @param filter the OSFilter for the filter property
|
* @param filter the OSFilter for the filter property
|
||||||
* @param noneOptionLabel The label of the non option, if set
|
* @param noneOptionLabel The label of the non option, if set
|
||||||
* @param exexcludeIds Set if certain ID's should be excluded from filtering
|
* @param filterFn custom filter function if required
|
||||||
*/
|
*/
|
||||||
protected updateFilterForRepo(
|
protected updateFilterForRepo(
|
||||||
repo: BaseRepository<BaseViewModel, BaseModel, TitleInformation>,
|
repo: BaseRepository<BaseViewModel, BaseModel, TitleInformation>,
|
||||||
filter: OsFilter,
|
filter: OsFilter,
|
||||||
noneOptionLabel?: string,
|
noneOptionLabel?: string,
|
||||||
excludeIds?: number[]
|
filterFn?: (filter: BaseViewModel<any>) => boolean
|
||||||
): void {
|
): void {
|
||||||
repo.getViewModelListObservable().subscribe(viewModel => {
|
repo.getViewModelListObservable().subscribe(viewModel => {
|
||||||
if (viewModel && viewModel.length) {
|
if (viewModel && viewModel.length) {
|
||||||
let filterProperties: (OsFilterOption | string)[];
|
let filterProperties: (OsFilterOption | string)[];
|
||||||
|
|
||||||
filterProperties = viewModel
|
filterProperties = viewModel.filter(filterFn ? filterFn : () => true).map((model: HierarchyModel) => {
|
||||||
.filter(model => (excludeIds && excludeIds.length ? !excludeIds.includes(model.id) : true))
|
|
||||||
.map((model: HierarchyModel) => {
|
|
||||||
return {
|
return {
|
||||||
condition: model.id,
|
condition: model.id,
|
||||||
label: model.getTitle(),
|
label: model.getTitle(),
|
||||||
@ -307,11 +309,13 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!!noneOptionLabel) {
|
||||||
filterProperties.push('-');
|
filterProperties.push('-');
|
||||||
filterProperties.push({
|
filterProperties.push({
|
||||||
condition: null,
|
condition: null,
|
||||||
label: noneOptionLabel
|
label: noneOptionLabel
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
filter.options = filterProperties;
|
filter.options = filterProperties;
|
||||||
this.setFilterDefinitions();
|
this.setFilterDefinitions();
|
||||||
@ -325,7 +329,7 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
|||||||
public storeActiveFilters(): void {
|
public storeActiveFilters(): void {
|
||||||
this.updateFilteredData();
|
this.updateFilteredData();
|
||||||
if (!this.OSStatus.isInHistoryMode) {
|
if (!this.OSStatus.isInHistoryMode) {
|
||||||
this.store.set('filter_' + this.name, this.filterDefinitions);
|
this.store.set('filter_' + this.storageKey, this.filterDefinitions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ export class SearchValueSelectorComponent implements OnDestroy {
|
|||||||
* Placeholder of the List
|
* Placeholder of the List
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public listname: String;
|
public listname: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the Form
|
* Name of the Form
|
||||||
|
@ -22,7 +22,7 @@ export class AgendaFilterListService extends BaseFilterListService<ViewItem> {
|
|||||||
* @param translate Translation service
|
* @param translate Translation service
|
||||||
*/
|
*/
|
||||||
public constructor(store: StorageService, OSStatus: OpenSlidesStatusService, private translate: TranslateService) {
|
public constructor(store: StorageService, OSStatus: OpenSlidesStatusService, private translate: TranslateService) {
|
||||||
super('Agenda', store, OSStatus);
|
super(store, OSStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +19,7 @@ export class AssignmentFilterListService extends BaseFilterListService<ViewAssig
|
|||||||
* @param translate translate service
|
* @param translate translate service
|
||||||
*/
|
*/
|
||||||
public constructor(store: StorageService, OSStatus: OpenSlidesStatusService) {
|
public constructor(store: StorageService, OSStatus: OpenSlidesStatusService) {
|
||||||
super('Assignments', store, OSStatus);
|
super(store, OSStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { AmendmentListComponent } from './amendment-list.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AmendmentListComponent,
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{ path: ':id', component: AmendmentListComponent }
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AmendmentListRoutingModule {}
|
@ -0,0 +1,107 @@
|
|||||||
|
<os-head-bar [nav]="false" [multiSelectMode]="isMultiSelect" goBack="true">
|
||||||
|
<!-- Title -->
|
||||||
|
<div class="title-slot"><h2 translate>Amendments</h2></div>
|
||||||
|
|
||||||
|
<!-- Menu -->
|
||||||
|
<div class="menu-slot">
|
||||||
|
<button type="button" mat-icon-button [matMenuTriggerFor]="amendmentListMenu">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Multiselect info -->
|
||||||
|
<div class="central-info-slot">
|
||||||
|
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
|
||||||
|
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||||
|
</div>
|
||||||
|
</os-head-bar>
|
||||||
|
|
||||||
|
<os-list-view-table
|
||||||
|
[repo]="motionRepo"
|
||||||
|
[sortService]="motionSortService"
|
||||||
|
[filterService]="amendmentFilterService"
|
||||||
|
[columns]="tableColumnDefinition"
|
||||||
|
[filterProps]="filterProps"
|
||||||
|
[multiSelect]="isMultiSelect"
|
||||||
|
listStorageKey="amendments"
|
||||||
|
[(selectedRows)]="selectedRows"
|
||||||
|
(dataSourceChange)="onDataSourceChange($event)"
|
||||||
|
>
|
||||||
|
<!-- Meta -->
|
||||||
|
<div *pblNgridCellDef="'meta'; row as motion" class="cell-slot fill">
|
||||||
|
<a class="detail-link" [routerLink]="motion.getDetailStateURL()"></a>
|
||||||
|
<div class="column-identifier innerTable">
|
||||||
|
<!-- Identifier and line -->
|
||||||
|
<div class="title-line">
|
||||||
|
{{ motion.identifier }}
|
||||||
|
(<span translate>Line</span> <span>{{ getChangeLines(motion) }}</span
|
||||||
|
>)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submitter -->
|
||||||
|
<div class="submitters-line">
|
||||||
|
<span *ngIf="motion.submitters.length">
|
||||||
|
<span translate>by</span>
|
||||||
|
{{ motion.submitters }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span *ngIf="motion.submitters.length">
|
||||||
|
·
|
||||||
|
</span>
|
||||||
|
<span translate>Sequential number</span>
|
||||||
|
{{ motion.id }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- State -->
|
||||||
|
<div>
|
||||||
|
<mat-basic-chip *ngIf="motion.state" [ngClass]="motion.stateCssColor" [disabled]="true">
|
||||||
|
{{ motionRepo.getExtendedStateLabel(motion) }}
|
||||||
|
</mat-basic-chip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Reco -->
|
||||||
|
<div class="spacer-top-3" *ngIf="motion.recommendation && motion.state.next_states_id.length > 0">
|
||||||
|
<mat-basic-chip class="bluegrey" [disabled]="true">
|
||||||
|
{{ this.motionRepo.getExtendedRecommendationLabel(motion) }}
|
||||||
|
</mat-basic-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Summary -->
|
||||||
|
<div *pblNgridCellDef="'summary'; row as motion" class="cell-slot fill">
|
||||||
|
<div class="innerTable">
|
||||||
|
<div class="motion-text" [innerHtml]="sanitizeText(getAmendmentSummary(motion))"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- List of Speakers -->
|
||||||
|
<div *pblNgridCellDef="'speakers'; row as motion" class="cell-slot fill">
|
||||||
|
<os-speaker-button [object]="motion"></os-speaker-button>
|
||||||
|
</div>
|
||||||
|
</os-list-view-table>
|
||||||
|
|
||||||
|
<mat-menu #amendmentListMenu="matMenu">
|
||||||
|
<div *ngIf="!isMultiSelect">
|
||||||
|
<div *osPerms="'motions.can_manage'">
|
||||||
|
<button mat-menu-item (click)="toggleMultiSelect()">
|
||||||
|
<mat-icon>library_add</mat-icon>
|
||||||
|
<span translate>Multiselect</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button mat-menu-item (click)="openExportDialog()">
|
||||||
|
<mat-icon>archive</mat-icon>
|
||||||
|
<span translate>Export</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isMultiSelect">
|
||||||
|
<button mat-menu-item (click)="selectAll()">
|
||||||
|
<mat-icon>done_all</mat-icon>
|
||||||
|
<span translate>Select all</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item [disabled]="!selectedRows.length" (click)="deselectAll()">
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
<span translate>Deselect all</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
@ -0,0 +1,2 @@
|
|||||||
|
@import '~assets/styles/motion-styles-common';
|
||||||
|
@import 'app/site/motions/styles/motion-list-styles.scss';
|
@ -0,0 +1,27 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { AmendmentListComponent } from './amendment-list.component';
|
||||||
|
|
||||||
|
describe('AmendmentListComponent', () => {
|
||||||
|
let component: AmendmentListComponent;
|
||||||
|
let fixture: ComponentFixture<AmendmentListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
declarations: [AmendmentListComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AmendmentListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,189 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { MatDialog, MatSnackBar } from '@angular/material';
|
||||||
|
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||||
|
|
||||||
|
import { AmendmentFilterListService } from '../../services/amendment-filter-list.service';
|
||||||
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
|
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { DiffLinesInParagraph } from 'app/core/ui-services/diff.service';
|
||||||
|
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
|
||||||
|
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
|
import { largeDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
|
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||||
|
import { MotionExportDialogComponent } from '../shared-motion/motion-export-dialog/motion-export-dialog.component';
|
||||||
|
import { MotionExportInfo, MotionExportService } from '../../services/motion-export.service';
|
||||||
|
import { MotionSortListService } from '../../services/motion-sort-list.service';
|
||||||
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows all the amendments in the NGrid table
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'os-amendment-list',
|
||||||
|
templateUrl: './amendment-list.component.html',
|
||||||
|
styleUrls: ['./amendment-list.component.scss']
|
||||||
|
})
|
||||||
|
export class AmendmentListComponent extends BaseListViewComponent<ViewMotion> implements OnInit {
|
||||||
|
private parentMotionId: number;
|
||||||
|
/**
|
||||||
|
* Hold item visibility
|
||||||
|
*/
|
||||||
|
public itemVisibility = ItemVisibilityChoices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To hold the motions line length
|
||||||
|
*/
|
||||||
|
private motionLineLength: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column defintiion
|
||||||
|
*/
|
||||||
|
public tableColumnDefinition: PblColumnDefinition[] = [
|
||||||
|
{
|
||||||
|
prop: 'meta',
|
||||||
|
minWidth: 250,
|
||||||
|
width: '15%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'summary',
|
||||||
|
width: 'auto'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'speakers',
|
||||||
|
width: this.singleButtonWidth
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To filter stuff
|
||||||
|
*/
|
||||||
|
public filterProps = ['submitters', 'title', 'identifier'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param titleService set the title
|
||||||
|
* @param translate translate stuff
|
||||||
|
* @param matSnackBar show errors
|
||||||
|
* @param storage store and recall
|
||||||
|
* @param motionRepo get the motions
|
||||||
|
* @param motionSortService the default motion sorter
|
||||||
|
*
|
||||||
|
* @param configService get config vars
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
titleService: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
storage: StorageService,
|
||||||
|
route: ActivatedRoute,
|
||||||
|
public motionRepo: MotionRepositoryService,
|
||||||
|
public motionSortService: MotionSortListService,
|
||||||
|
public amendmentFilterService: AmendmentFilterListService,
|
||||||
|
private sanitizer: DomSanitizer,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private motionExport: MotionExportService,
|
||||||
|
private linenumberingService: LinenumberingService
|
||||||
|
) {
|
||||||
|
super(titleService, translate, matSnackBar, storage);
|
||||||
|
super.setTitle('Amendments');
|
||||||
|
this.canMultiSelect = true;
|
||||||
|
this.parentMotionId = parseInt(route.snapshot.params.id, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe the line length
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.configService.get<number>('motions_line_length').subscribe(lineLength => {
|
||||||
|
this.motionLineLength = lineLength;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!this.parentMotionId) {
|
||||||
|
this.amendmentFilterService.clearAllFilters();
|
||||||
|
this.amendmentFilterService.parentMotionId = this.parentMotionId;
|
||||||
|
} else {
|
||||||
|
this.amendmentFilterService.parentMotionId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get amendment paragraphs of a given motion
|
||||||
|
*
|
||||||
|
* @param amendment the get the paragraphs from
|
||||||
|
* @returns DiffLinesInParagraph-List
|
||||||
|
*/
|
||||||
|
private getDiffLines(amendment: ViewMotion): DiffLinesInParagraph[] {
|
||||||
|
if (amendment.isParagraphBasedAmendment()) {
|
||||||
|
return this.motionRepo.getAmendmentParagraphs(amendment, this.motionLineLength, false);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the lines of the amendments
|
||||||
|
* If an amendments has multiple changes, they will be printed like an array of strings
|
||||||
|
*
|
||||||
|
* @param amendment the motion to create the amendment to
|
||||||
|
* @return The lines of the amendment
|
||||||
|
*/
|
||||||
|
public getChangeLines(amendment: ViewMotion): string {
|
||||||
|
const diffLines = this.getDiffLines(amendment);
|
||||||
|
|
||||||
|
if (!!diffLines) {
|
||||||
|
return diffLines
|
||||||
|
.map(diffLine => {
|
||||||
|
if (diffLine.diffLineTo === diffLine.diffLineFrom + 1) {
|
||||||
|
return '' + diffLine.diffLineFrom;
|
||||||
|
} else {
|
||||||
|
return `${diffLine.diffLineFrom} - ${diffLine.diffLineTo - 1}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formulate the amendment summary
|
||||||
|
*
|
||||||
|
* @param amendment the motion to create the amendment to
|
||||||
|
* @returns the amendments as string, if they are multiple they gonna be separated by `[...]`
|
||||||
|
*/
|
||||||
|
public getAmendmentSummary(amendment: ViewMotion): string {
|
||||||
|
const diffLines = this.getDiffLines(amendment);
|
||||||
|
if (!!diffLines) {
|
||||||
|
return diffLines
|
||||||
|
.map(diffLine => {
|
||||||
|
return this.linenumberingService.stripLineNumbers(diffLine.text);
|
||||||
|
})
|
||||||
|
.join('[...]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo put in own file
|
||||||
|
public openExportDialog(): void {
|
||||||
|
const exportDialogRef = this.dialog.open(MotionExportDialogComponent, {
|
||||||
|
...largeDialogSettings,
|
||||||
|
data: this.dataSource
|
||||||
|
});
|
||||||
|
|
||||||
|
exportDialogRef
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((exportInfo: MotionExportInfo) =>
|
||||||
|
this.motionExport.evaluateExportRequest(
|
||||||
|
exportInfo,
|
||||||
|
this.isMultiSelect ? this.selectedRows : this.dataSource.filteredData
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sanitizeText(text: string): SafeHtml {
|
||||||
|
return this.sanitizer.bypassSecurityTrustHtml(text);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AmendmentListRoutingModule } from './amendment-list-routing.module';
|
||||||
|
import { AmendmentListComponent } from './amendment-list.component';
|
||||||
|
import { SharedModule } from 'app/shared/shared.module';
|
||||||
|
import { SharedMotionModule } from '../shared-motion/shared-motion.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AmendmentListComponent],
|
||||||
|
imports: [CommonModule, AmendmentListRoutingModule, SharedModule, SharedMotionModule]
|
||||||
|
})
|
||||||
|
export class AmendmentListModule {}
|
@ -452,9 +452,12 @@
|
|||||||
<!-- Ammendments -->
|
<!-- Ammendments -->
|
||||||
<div *ngIf="!editMotion && amendments && amendments.length > 0">
|
<div *ngIf="!editMotion && amendments && amendments.length > 0">
|
||||||
<h4 translate>Amendments</h4>
|
<h4 translate>Amendments</h4>
|
||||||
<div *ngFor="let amendment of amendments">
|
<a *ngIf="amendments.length === 1" [routerLink]="['/motions/amendments', motion.id]">
|
||||||
<a [routerLink]="amendment.getDetailStateURL()">{{ amendment.identifierOrTitle }}</a>
|
{{ amendments.length }} <span translate>Amendment</span>
|
||||||
</div>
|
</a>
|
||||||
|
<a *ngIf="amendments.length > 1" [routerLink]="['/motions/amendments', motion.id]">
|
||||||
|
{{ amendments.length }} <span translate>Amendments</span></a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- motion polls -->
|
<!-- motion polls -->
|
||||||
|
@ -600,6 +600,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
this.repo.amendmentsTo(motionId).subscribe((amendments: ViewMotion[]): void => {
|
this.repo.amendmentsTo(motionId).subscribe((amendments: ViewMotion[]): void => {
|
||||||
this.amendments = amendments;
|
this.amendments = amendments;
|
||||||
this.recalcUnifiedChanges();
|
this.recalcUnifiedChanges();
|
||||||
|
@ -104,10 +104,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- TODO: These two now appear twice. Might be an own component -->
|
||||||
<!-- Workflow state -->
|
<!-- Workflow state -->
|
||||||
<div class="ellipsis-overflow white">
|
<div class="ellipsis-overflow white">
|
||||||
<mat-basic-chip *ngIf="motion.state" [ngClass]="motion.stateCssColor" [disabled]="true">
|
<mat-basic-chip *ngIf="motion.state" [ngClass]="motion.stateCssColor" [disabled]="true">
|
||||||
{{ getStateLabel(motion) }}
|
{{ this.motionRepo.getExtendedStateLabel(motion) }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -117,7 +118,7 @@
|
|||||||
class="ellipsis-overflow white spacer-top-3"
|
class="ellipsis-overflow white spacer-top-3"
|
||||||
>
|
>
|
||||||
<mat-basic-chip class="bluegrey" [disabled]="true">
|
<mat-basic-chip class="bluegrey" [disabled]="true">
|
||||||
{{ getRecommendationLabel(motion) }}
|
{{ motionRepo.getExtendedRecommendationLabel(motion) }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -219,6 +220,14 @@
|
|||||||
<span translate>Multiselect</span>
|
<span translate>Multiselect</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="perms.isAllowed('manage') || hasAmendments()">
|
||||||
|
<button mat-menu-item routerLink="amendments">
|
||||||
|
<!-- color_lens -->
|
||||||
|
<!-- format_paint -->
|
||||||
|
<mat-icon>color_lens</mat-icon>
|
||||||
|
<span translate>Amendments</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div *ngIf="perms.isAllowed('manage')">
|
<div *ngIf="perms.isAllowed('manage')">
|
||||||
<button mat-menu-item routerLink="call-list">
|
<button mat-menu-item routerLink="call-list">
|
||||||
<mat-icon>sort</mat-icon>
|
<mat-icon>sort</mat-icon>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@import '~assets/styles/tables.scss';
|
@import '~assets/styles/tables.scss';
|
||||||
|
@import '~app/site/motions/styles/motion-list-styles.scss';
|
||||||
|
|
||||||
// Determine the distance between the top edge to the start of the table content
|
// Determine the distance between the top edge to the start of the table content
|
||||||
$text-margin-top: 10px;
|
$text-margin-top: 10px;
|
||||||
@ -27,9 +28,6 @@ $text-margin-top: 10px;
|
|||||||
margin-top: $text-margin-top;
|
margin-top: $text-margin-top;
|
||||||
|
|
||||||
.title-line {
|
.title-line {
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
|
|
||||||
.attached-files {
|
.attached-files {
|
||||||
.mat-icon {
|
.mat-icon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -45,10 +43,6 @@ $text-margin-top: 10px;
|
|||||||
padding-right: 3px;
|
padding-right: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submitters-line {
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-state {
|
.column-state {
|
||||||
|
@ -8,7 +8,6 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { PblColumnDefinition } from '@pebula/ngrid';
|
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||||
|
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { PdfError } from 'app/core/pdf-services/pdf-document.service';
|
|
||||||
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||||
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
|
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
|
||||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
@ -24,18 +23,12 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
|
|||||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||||
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
|
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
|
||||||
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
|
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
|
||||||
import { MotionCsvExportService } from 'app/site/motions/services/motion-csv-export.service';
|
import { MotionExportInfo, MotionExportService } from 'app/site/motions/services/motion-export.service';
|
||||||
import { MotionFilterListService } from 'app/site/motions/services/motion-filter-list.service';
|
import { MotionFilterListService } from 'app/site/motions/services/motion-filter-list.service';
|
||||||
import { MotionMultiselectService } from 'app/site/motions/services/motion-multiselect.service';
|
import { MotionMultiselectService } from 'app/site/motions/services/motion-multiselect.service';
|
||||||
import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-export.service';
|
|
||||||
import { MotionSortListService } from 'app/site/motions/services/motion-sort-list.service';
|
import { MotionSortListService } from 'app/site/motions/services/motion-sort-list.service';
|
||||||
import { MotionXlsxExportService } from 'app/site/motions/services/motion-xlsx-export.service';
|
|
||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
import {
|
import { MotionExportDialogComponent } from '../../../shared-motion/motion-export-dialog/motion-export-dialog.component';
|
||||||
ExportFormData,
|
|
||||||
FileFormat,
|
|
||||||
MotionExportDialogComponent
|
|
||||||
} from '../motion-export-dialog/motion-export-dialog.component';
|
|
||||||
|
|
||||||
interface TileCategoryInformation {
|
interface TileCategoryInformation {
|
||||||
filter: string;
|
filter: string;
|
||||||
@ -208,12 +201,10 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
private categoryRepo: CategoryRepositoryService,
|
private categoryRepo: CategoryRepositoryService,
|
||||||
private workflowRepo: WorkflowRepositoryService,
|
private workflowRepo: WorkflowRepositoryService,
|
||||||
public motionRepo: MotionRepositoryService,
|
public motionRepo: MotionRepositoryService,
|
||||||
private motionCsvExport: MotionCsvExportService,
|
|
||||||
private pdfExport: MotionPdfExportService,
|
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public multiselectService: MotionMultiselectService,
|
public multiselectService: MotionMultiselectService,
|
||||||
public perms: LocalPermissionsService,
|
public perms: LocalPermissionsService,
|
||||||
private motionXlsxExport: MotionXlsxExportService
|
private motionExport: MotionExportService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar, storage);
|
super(titleService, translate, matSnackBar, storage);
|
||||||
this.canMultiSelect = true;
|
this.canMultiSelect = true;
|
||||||
@ -358,37 +349,14 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
data: this.dataSource
|
data: this.dataSource
|
||||||
});
|
});
|
||||||
|
|
||||||
exportDialogRef.afterClosed().subscribe((exportInfo: ExportFormData) => {
|
exportDialogRef
|
||||||
if (exportInfo && exportInfo.format) {
|
.afterClosed()
|
||||||
const data = this.isMultiSelect ? this.selectedRows : this.dataSource.filteredData;
|
.subscribe((exportInfo: MotionExportInfo) =>
|
||||||
if (exportInfo.format === FileFormat.PDF) {
|
this.motionExport.evaluateExportRequest(
|
||||||
try {
|
exportInfo,
|
||||||
this.pdfExport.exportMotionCatalog(data, exportInfo);
|
this.isMultiSelect ? this.selectedRows : this.dataSource.filteredData
|
||||||
} catch (err) {
|
)
|
||||||
if (err instanceof PdfError) {
|
);
|
||||||
this.raiseError(err.message);
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (exportInfo.format === FileFormat.CSV) {
|
|
||||||
const content = [];
|
|
||||||
const comments = [];
|
|
||||||
if (exportInfo.content) {
|
|
||||||
content.push(...exportInfo.content);
|
|
||||||
}
|
|
||||||
if (exportInfo.metaInfo) {
|
|
||||||
content.push(...exportInfo.metaInfo);
|
|
||||||
}
|
|
||||||
if (exportInfo.comments) {
|
|
||||||
comments.push(...exportInfo.comments);
|
|
||||||
}
|
|
||||||
this.motionCsvExport.exportMotionList(data, content, comments, exportInfo.crMode);
|
|
||||||
} else if (exportInfo.format === FileFormat.XLSX) {
|
|
||||||
this.motionXlsxExport.exportMotionList(data, exportInfo.metaInfo, exportInfo.comments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -404,26 +372,6 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a motion's current recommendation label
|
|
||||||
*
|
|
||||||
* @param motion
|
|
||||||
* @returns the current recommendation label (with extension)
|
|
||||||
*/
|
|
||||||
public getRecommendationLabel(motion: ViewMotion): string {
|
|
||||||
return this.motionRepo.getExtendedRecommendationLabel(motion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a motion's current state label
|
|
||||||
*
|
|
||||||
* @param motion
|
|
||||||
* @returns the current state label (with extension)
|
|
||||||
*/
|
|
||||||
public getStateLabel(motion: ViewMotion): string {
|
|
||||||
return this.motionRepo.getExtendedStateLabel(motion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function saves the selected view by changes.
|
* This function saves the selected view by changes.
|
||||||
*
|
*
|
||||||
@ -495,6 +443,13 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns if there are amendments or not
|
||||||
|
*/
|
||||||
|
public hasAmendments(): boolean {
|
||||||
|
return !!this.motionRepo.getAllAmendmentsInstantly().length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if categories are available.
|
* Checks if categories are available.
|
||||||
*
|
*
|
||||||
|
@ -4,7 +4,6 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
||||||
|
|
||||||
const routes: Routes = [{ path: '', component: MotionListComponent, pathMatch: 'full' }];
|
const routes: Routes = [{ path: '', component: MotionListComponent, pathMatch: 'full' }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
|
@ -2,13 +2,12 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { SharedModule } from 'app/shared/shared.module';
|
import { SharedModule } from 'app/shared/shared.module';
|
||||||
import { MotionExportDialogComponent } from './components/motion-export-dialog/motion-export-dialog.component';
|
|
||||||
import { MotionListRoutingModule } from './motion-list-routing.module';
|
import { MotionListRoutingModule } from './motion-list-routing.module';
|
||||||
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
||||||
|
import { SharedMotionModule } from '../shared-motion/shared-motion.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, MotionListRoutingModule, SharedModule],
|
imports: [CommonModule, MotionListRoutingModule, SharedModule, SharedMotionModule],
|
||||||
declarations: [MotionListComponent, MotionExportDialogComponent],
|
declarations: [MotionListComponent]
|
||||||
entryComponents: [MotionExportDialogComponent]
|
|
||||||
})
|
})
|
||||||
export class MotionListModule {}
|
export class MotionListModule {}
|
||||||
|
@ -11,29 +11,7 @@ import { ConfigService } from 'app/core/ui-services/config.service';
|
|||||||
import { ChangeRecoMode, LineNumberingMode } from 'app/site/motions/models/view-motion';
|
import { ChangeRecoMode, LineNumberingMode } from 'app/site/motions/models/view-motion';
|
||||||
import { ViewMotionCommentSection } from 'app/site/motions/models/view-motion-comment-section';
|
import { ViewMotionCommentSection } from 'app/site/motions/models/view-motion-comment-section';
|
||||||
import { motionImportExportHeaderOrder, noMetaData } from 'app/site/motions/motion-import-export-order';
|
import { motionImportExportHeaderOrder, noMetaData } from 'app/site/motions/motion-import-export-order';
|
||||||
import { InfoToExport } from 'app/site/motions/services/motion-pdf.service';
|
import { ExportFileFormat, MotionExportInfo } from 'app/site/motions/services/motion-export.service';
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the possible file format
|
|
||||||
*/
|
|
||||||
export enum FileFormat {
|
|
||||||
PDF = 1,
|
|
||||||
CSV,
|
|
||||||
XLSX
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shape the structure of the dialog data
|
|
||||||
*/
|
|
||||||
export interface ExportFormData {
|
|
||||||
format?: FileFormat;
|
|
||||||
lnMode?: LineNumberingMode;
|
|
||||||
crMode?: ChangeRecoMode;
|
|
||||||
content?: string[];
|
|
||||||
metaInfo?: InfoToExport[];
|
|
||||||
pdfOptions?: string[];
|
|
||||||
comments?: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog component to determine exporting.
|
* Dialog component to determine exporting.
|
||||||
@ -57,7 +35,7 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* to use the format in the template
|
* to use the format in the template
|
||||||
*/
|
*/
|
||||||
public fileFormat = FileFormat;
|
public fileFormat = ExportFileFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form that contains the export information.
|
* The form that contains the export information.
|
||||||
@ -67,8 +45,8 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The default export values in contrast to the restored values
|
* The default export values in contrast to the restored values
|
||||||
*/
|
*/
|
||||||
private defaults: ExportFormData = {
|
private defaults: MotionExportInfo = {
|
||||||
format: FileFormat.PDF,
|
format: ExportFileFormat.PDF,
|
||||||
content: ['text', 'reason'],
|
content: ['text', 'reason'],
|
||||||
pdfOptions: ['toc', 'page'],
|
pdfOptions: ['toc', 'page'],
|
||||||
metaInfo: ['submitters', 'state', 'recommendation', 'category', 'origin', 'tags', 'motion_block', 'polls', 'id']
|
metaInfo: ['submitters', 'state', 'recommendation', 'category', 'origin', 'tags', 'motion_block', 'polls', 'id']
|
||||||
@ -130,26 +108,26 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
* Observes the form for changes to react dynamically
|
* Observes the form for changes to react dynamically
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.exportForm.valueChanges.pipe(auditTime(500)).subscribe((value: ExportFormData) => {
|
this.exportForm.valueChanges.pipe(auditTime(500)).subscribe((value: MotionExportInfo) => {
|
||||||
this.store.set('motion_export_selection', value);
|
this.store.set('motion_export_selection', value);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.exportForm.get('format').valueChanges.subscribe((value: FileFormat) => this.onFormatChange(value));
|
this.exportForm.get('format').valueChanges.subscribe((value: ExportFileFormat) => this.onFormatChange(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React to changes on the file format
|
* React to changes on the file format
|
||||||
* @param format
|
* @param format
|
||||||
*/
|
*/
|
||||||
private onFormatChange(format: FileFormat): void {
|
private onFormatChange(format: ExportFileFormat): void {
|
||||||
// XLSX cannot have "content"
|
// XLSX cannot have "content"
|
||||||
if (format === FileFormat.XLSX) {
|
if (format === ExportFileFormat.XLSX) {
|
||||||
this.disableControl('content');
|
this.disableControl('content');
|
||||||
} else {
|
} else {
|
||||||
this.enableControl('content');
|
this.enableControl('content');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format === FileFormat.CSV || format === FileFormat.XLSX) {
|
if (format === ExportFileFormat.CSV || format === ExportFileFormat.XLSX) {
|
||||||
this.disableControl('lnMode');
|
this.disableControl('lnMode');
|
||||||
this.disableControl('crMode');
|
this.disableControl('crMode');
|
||||||
this.disableControl('pdfOptions');
|
this.disableControl('pdfOptions');
|
||||||
@ -165,7 +143,7 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
this.votingResultButton.disabled = true;
|
this.votingResultButton.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format === FileFormat.PDF) {
|
if (format === ExportFileFormat.PDF) {
|
||||||
this.enableControl('lnMode');
|
this.enableControl('lnMode');
|
||||||
this.enableControl('crMode');
|
this.enableControl('crMode');
|
||||||
this.enableControl('pdfOptions');
|
this.enableControl('pdfOptions');
|
||||||
@ -222,7 +200,7 @@ export class MotionExportDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// restore selection or set default
|
// restore selection or set default
|
||||||
this.store.get<ExportFormData>('motion_export_selection').then(restored => {
|
this.store.get<MotionExportInfo>('motion_export_selection').then(restored => {
|
||||||
if (!!restored) {
|
if (!!restored) {
|
||||||
this.exportForm.patchValue(restored);
|
this.exportForm.patchValue(restored);
|
||||||
} else {
|
} else {
|
@ -0,0 +1,12 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { SharedModule } from 'app/shared/shared.module';
|
||||||
|
import { MotionExportDialogComponent } from './motion-export-dialog/motion-export-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, SharedModule],
|
||||||
|
declarations: [MotionExportDialogComponent],
|
||||||
|
entryComponents: [MotionExportDialogComponent]
|
||||||
|
})
|
||||||
|
export class SharedMotionModule {}
|
@ -52,6 +52,11 @@ const routes: Routes = [
|
|||||||
loadChildren: () => import('./modules/motion-detail/motion-detail.module').then(m => m.MotionDetailModule),
|
loadChildren: () => import('./modules/motion-detail/motion-detail.module').then(m => m.MotionDetailModule),
|
||||||
data: { basePerm: 'motions.can_create' }
|
data: { basePerm: 'motions.can_create' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'amendments',
|
||||||
|
loadChildren: () => import('./modules/amendment-list/amendment-list.module').then(m => m.AmendmentListModule),
|
||||||
|
data: { basePerm: 'motions.can_see' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
loadChildren: () => import('./modules/motion-detail/motion-detail.module').then(m => m.MotionDetailModule),
|
loadChildren: () => import('./modules/motion-detail/motion-detail.module').then(m => m.MotionDetailModule),
|
||||||
|
@ -2,9 +2,8 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { MotionsRoutingModule } from './motions-routing.module';
|
import { MotionsRoutingModule } from './motions-routing.module';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, MotionsRoutingModule, SharedModule]
|
imports: [CommonModule, MotionsRoutingModule]
|
||||||
})
|
})
|
||||||
export class MotionsModule {}
|
export class MotionsModule {}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { AmendmentFilterListService } from './amendment-filter-list.service';
|
||||||
|
|
||||||
|
describe('AmendmentFilterService', () => {
|
||||||
|
beforeEach(() =>
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: AmendmentFilterListService = TestBed.get(AmendmentFilterListService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,96 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
|
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||||
|
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
|
||||||
|
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
|
||||||
|
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
|
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
|
||||||
|
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
|
||||||
|
import { OsFilter } from 'app/core/ui-services/base-filter-list.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { MotionFilterListService } from './motion-filter-list.service';
|
||||||
|
import { ViewMotion } from '../models/view-motion';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list of Amendments
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AmendmentFilterListService extends MotionFilterListService {
|
||||||
|
/**
|
||||||
|
* Private acessor for an amendment id
|
||||||
|
*/
|
||||||
|
private _parentMotionId: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* publicly get an amendment id
|
||||||
|
*/
|
||||||
|
public set parentMotionId(id: number) {
|
||||||
|
this._parentMotionId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private motionFilterOptions: OsFilter = {
|
||||||
|
property: 'parent_id',
|
||||||
|
label: 'Motion',
|
||||||
|
options: []
|
||||||
|
};
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
store: StorageService,
|
||||||
|
OSStatus: OpenSlidesStatusService,
|
||||||
|
categoryRepo: CategoryRepositoryService,
|
||||||
|
motionBlockRepo: MotionBlockRepositoryService,
|
||||||
|
commentRepo: MotionCommentSectionRepositoryService,
|
||||||
|
tagRepo: TagRepositoryService,
|
||||||
|
workflowRepo: WorkflowRepositoryService,
|
||||||
|
translate: TranslateService,
|
||||||
|
operator: OperatorService,
|
||||||
|
configService: ConfigService,
|
||||||
|
motionRepo: MotionRepositoryService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
store,
|
||||||
|
OSStatus,
|
||||||
|
categoryRepo,
|
||||||
|
motionBlockRepo,
|
||||||
|
commentRepo,
|
||||||
|
tagRepo,
|
||||||
|
workflowRepo,
|
||||||
|
translate,
|
||||||
|
operator,
|
||||||
|
configService
|
||||||
|
);
|
||||||
|
|
||||||
|
this.updateFilterForRepo(motionRepo, this.motionFilterOptions, null, (model: ViewMotion) =>
|
||||||
|
motionRepo.hasAmendments(model)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override from base filter list service
|
||||||
|
*
|
||||||
|
* @returns the list of Motions which only contains view motions
|
||||||
|
*/
|
||||||
|
protected preFilter(motions: ViewMotion[]): ViewMotion[] {
|
||||||
|
return motions.filter(motion => {
|
||||||
|
if (!!this._parentMotionId) {
|
||||||
|
return motion.parent_id === this._parentMotionId;
|
||||||
|
} else {
|
||||||
|
return !!motion.parent_id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently, no filters for the amendment list, except the pre-filter
|
||||||
|
*/
|
||||||
|
protected getFilterDefinitions(): OsFilter[] {
|
||||||
|
return [this.motionFilterOptions].concat(super.getFilterDefinitions());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { MotionExportService } from './motion-export.service';
|
||||||
|
|
||||||
|
describe('MotionExportService', () => {
|
||||||
|
beforeEach(() =>
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: MotionExportService = TestBed.get(MotionExportService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,80 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { PdfError } from 'app/core/pdf-services/pdf-document.service';
|
||||||
|
import { MotionCsvExportService } from './motion-csv-export.service';
|
||||||
|
import { MotionPdfExportService } from './motion-pdf-export.service';
|
||||||
|
import { InfoToExport } from './motion-pdf.service';
|
||||||
|
import { MotionXlsxExportService } from './motion-xlsx-export.service';
|
||||||
|
import { ChangeRecoMode, LineNumberingMode, ViewMotion } from '../models/view-motion';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the possible file format
|
||||||
|
*/
|
||||||
|
export enum ExportFileFormat {
|
||||||
|
PDF = 1,
|
||||||
|
CSV,
|
||||||
|
XLSX
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shape the structure of the dialog data
|
||||||
|
*/
|
||||||
|
export interface MotionExportInfo {
|
||||||
|
format?: ExportFileFormat;
|
||||||
|
lnMode?: LineNumberingMode;
|
||||||
|
crMode?: ChangeRecoMode;
|
||||||
|
content?: string[];
|
||||||
|
metaInfo?: InfoToExport[];
|
||||||
|
pdfOptions?: string[];
|
||||||
|
comments?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic layer to unify any motion export
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class MotionExportService {
|
||||||
|
public constructor(
|
||||||
|
private pdfExport: MotionPdfExportService,
|
||||||
|
private csvExport: MotionCsvExportService,
|
||||||
|
private xlsxExport: MotionXlsxExportService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public evaluateExportRequest(exportInfo: MotionExportInfo, data: ViewMotion[]): void {
|
||||||
|
if (!!exportInfo.format) {
|
||||||
|
if (exportInfo.format === ExportFileFormat.PDF) {
|
||||||
|
try {
|
||||||
|
this.pdfExport.exportMotionCatalog(data, exportInfo);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof PdfError) {
|
||||||
|
console.error('PDFError: ', err);
|
||||||
|
/**
|
||||||
|
* TODO: Has been this.raiseError(err.message) before. Central error treatment
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (exportInfo.format === ExportFileFormat.CSV) {
|
||||||
|
const content = [];
|
||||||
|
const comments = [];
|
||||||
|
if (exportInfo.content) {
|
||||||
|
content.push(...exportInfo.content);
|
||||||
|
}
|
||||||
|
if (exportInfo.metaInfo) {
|
||||||
|
content.push(...exportInfo.metaInfo);
|
||||||
|
}
|
||||||
|
if (exportInfo.comments) {
|
||||||
|
comments.push(...exportInfo.comments);
|
||||||
|
}
|
||||||
|
this.csvExport.exportMotionList(data, content, comments, exportInfo.crMode);
|
||||||
|
} else if (exportInfo.format === ExportFileFormat.XLSX) {
|
||||||
|
this.xlsxExport.exportMotionList(data, exportInfo.metaInfo, exportInfo.comments);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('No export format was provided');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -134,7 +134,7 @@ export class MotionFilterListService extends BaseFilterListService<ViewMotion> {
|
|||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private config: ConfigService
|
private config: ConfigService
|
||||||
) {
|
) {
|
||||||
super('Motion', store, OSStatus);
|
super(store, OSStatus);
|
||||||
this.getWorkflowConfig();
|
this.getWorkflowConfig();
|
||||||
|
|
||||||
this.updateFilterForRepo(categoryRepo, this.categoryFilterOptions, this.translate.instant('No category set'));
|
this.updateFilterForRepo(categoryRepo, this.categoryFilterOptions, this.translate.instant('No category set'));
|
||||||
|
@ -7,7 +7,7 @@ import { BorderType, PdfDocumentService, PdfError, StyleType } from 'app/core/pd
|
|||||||
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
import { MotionExportInfo } from './motion-export.service';
|
||||||
import { MotionPdfService } from './motion-pdf.service';
|
import { MotionPdfService } from './motion-pdf.service';
|
||||||
import { ViewCategory } from '../models/view-category';
|
import { ViewCategory } from '../models/view-category';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
@ -56,7 +56,7 @@ export class MotionPdfCatalogService {
|
|||||||
* @param commentsToExport
|
* @param commentsToExport
|
||||||
* @returns pdfmake doc definition as object
|
* @returns pdfmake doc definition as object
|
||||||
*/
|
*/
|
||||||
public motionListToDocDef(motions: ViewMotion[], exportInfo: ExportFormData): object {
|
public motionListToDocDef(motions: ViewMotion[], exportInfo: MotionExportInfo): object {
|
||||||
let doc = [];
|
let doc = [];
|
||||||
const motionDocList = [];
|
const motionDocList = [];
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||||
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
import { MotionExportInfo } from './motion-export.service';
|
||||||
import { MotionPdfCatalogService } from './motion-pdf-catalog.service';
|
import { MotionPdfCatalogService } from './motion-pdf-catalog.service';
|
||||||
import { MotionPdfService } from './motion-pdf.service';
|
import { MotionPdfService } from './motion-pdf.service';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
@ -41,7 +41,7 @@ export class MotionPdfExportService {
|
|||||||
* @param lnMode the desired line numbering mode
|
* @param lnMode the desired line numbering mode
|
||||||
* @param crMode the desired change recomendation mode
|
* @param crMode the desired change recomendation mode
|
||||||
*/
|
*/
|
||||||
public exportSingleMotion(motion: ViewMotion, exportInfo?: ExportFormData): void {
|
public exportSingleMotion(motion: ViewMotion, exportInfo?: MotionExportInfo): void {
|
||||||
const doc = this.motionPdfService.motionToDocDef(motion, exportInfo);
|
const doc = this.motionPdfService.motionToDocDef(motion, exportInfo);
|
||||||
const filename = `${this.translate.instant('Motion')} ${motion.identifierOrTitle}`;
|
const filename = `${this.translate.instant('Motion')} ${motion.identifierOrTitle}`;
|
||||||
const metadata = {
|
const metadata = {
|
||||||
@ -60,7 +60,7 @@ export class MotionPdfExportService {
|
|||||||
* @param infoToExport Determine the meta info to export
|
* @param infoToExport Determine the meta info to export
|
||||||
* @param commentsToExport Comments (by id) to export
|
* @param commentsToExport Comments (by id) to export
|
||||||
*/
|
*/
|
||||||
public exportMotionCatalog(motions: ViewMotion[], exportInfo: ExportFormData): void {
|
public exportMotionCatalog(motions: ViewMotion[], exportInfo: MotionExportInfo): void {
|
||||||
const doc = this.pdfCatalogService.motionListToDocDef(motions, exportInfo);
|
const doc = this.pdfCatalogService.motionListToDocDef(motions, exportInfo);
|
||||||
const filename = this.translate.instant(this.configService.instant<string>('motions_export_title'));
|
const filename = this.translate.instant(this.configService.instant<string>('motions_export_title'));
|
||||||
const metadata = {
|
const metadata = {
|
||||||
|
@ -13,7 +13,7 @@ import { LinenumberingService } from 'app/core/ui-services/linenumbering.service
|
|||||||
import { CalculablePollKey } from 'app/core/ui-services/poll.service';
|
import { CalculablePollKey } from 'app/core/ui-services/poll.service';
|
||||||
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
|
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
|
||||||
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
|
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
|
||||||
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
import { MotionExportInfo } from './motion-export.service';
|
||||||
import { MotionPollService } from './motion-poll.service';
|
import { MotionPollService } from './motion-poll.service';
|
||||||
import { ChangeRecoMode, LineNumberingMode, ViewMotion } from '../models/view-motion';
|
import { ChangeRecoMode, LineNumberingMode, ViewMotion } from '../models/view-motion';
|
||||||
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
||||||
@ -92,7 +92,7 @@ export class MotionPdfService {
|
|||||||
* @param commentsToExport comments to chose for export. If 'allcomments' is set in infoToExport, this selection will be ignored and all comments exported
|
* @param commentsToExport comments to chose for export. If 'allcomments' is set in infoToExport, this selection will be ignored and all comments exported
|
||||||
* @returns doc def for the motion
|
* @returns doc def for the motion
|
||||||
*/
|
*/
|
||||||
public motionToDocDef(motion: ViewMotion, exportInfo?: ExportFormData): object {
|
public motionToDocDef(motion: ViewMotion, exportInfo?: MotionExportInfo): object {
|
||||||
let lnMode = exportInfo && exportInfo.lnMode ? exportInfo.lnMode : null;
|
let lnMode = exportInfo && exportInfo.lnMode ? exportInfo.lnMode : null;
|
||||||
let crMode = exportInfo && exportInfo.crMode ? exportInfo.crMode : null;
|
let crMode = exportInfo && exportInfo.crMode ? exportInfo.crMode : null;
|
||||||
const infoToExport = exportInfo ? exportInfo.metaInfo : null;
|
const infoToExport = exportInfo ? exportInfo.metaInfo : null;
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
.title-line {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitters-line {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
@ -35,8 +35,13 @@ export class UserFilterListService extends BaseFilterListService<ViewUser> {
|
|||||||
groupRepo: GroupRepositoryService,
|
groupRepo: GroupRepositoryService,
|
||||||
private translate: TranslateService
|
private translate: TranslateService
|
||||||
) {
|
) {
|
||||||
super('User', store, OSStatus);
|
super(store, OSStatus);
|
||||||
this.updateFilterForRepo(groupRepo, this.userGroupFilterOptions, this.translate.instant('Default'), [1]);
|
this.updateFilterForRepo(
|
||||||
|
groupRepo,
|
||||||
|
this.userGroupFilterOptions,
|
||||||
|
this.translate.instant('Default'),
|
||||||
|
(model: ViewUser) => model.id !== 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user