added tenant filter on description listing

This commit is contained in:
Sofia Papacharalampous 2024-07-09 15:05:26 +03:00
parent 772c62a2b4
commit 2a66a69f1e
13 changed files with 108 additions and 26 deletions

View File

@ -34,6 +34,8 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
private Collection<UUID> createdByIds;
private TenantQuery tenantQuery;
private PlanDescriptionTemplateQuery planDescriptionTemplateQuery;
private PlanQuery planQuery;
@ -113,6 +115,11 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
return this;
}
public DescriptionQuery tenantSubQuery(TenantQuery subQuery) {
this.tenantQuery = subQuery;
return this;
}
public DescriptionQuery planDescriptionTemplateSubQuery(PlanDescriptionTemplateQuery subQuery) {
this.planDescriptionTemplateQuery = subQuery;
return this;
@ -302,6 +309,10 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
inClause.value(item);
predicates.add(inClause);
}
if (this.tenantQuery != null) {
QueryContext<TenantEntity, UUID> subQuery = this.applySubQuery(this.tenantQuery, queryContext, UUID.class, tenantEntityRoot -> tenantEntityRoot.get(TenantEntity._id));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._tenantId)).value(subQuery.Query));
}
if (this.createdByIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._createdById));
for (UUID item : this.createdByIds)

View File

@ -17,7 +17,6 @@ public class DescriptionLookup extends Lookup {
private String like;
private List<UUID> ids;
private List<UUID> excludedIds;
private Instant createdAfter;
private Instant createdBefore;
@ -25,6 +24,7 @@ public class DescriptionLookup extends Lookup {
private Instant finalizedBefore;
private PlanLookup planSubQuery;
private TenantLookup tenantSubQuery;
private DescriptionTemplateLookup descriptionTemplateSubQuery;
private DescriptionReferenceLookup descriptionReferenceSubQuery;
private DescriptionTagLookup descriptionTagSubQuery;
@ -79,6 +79,10 @@ public class DescriptionLookup extends Lookup {
public void setPlanSubQuery(PlanLookup planSubQuery) { this.planSubQuery = planSubQuery; }
public TenantLookup getTenantSubQuery() { return this.tenantSubQuery; }
public void setTenantSubQuery(TenantLookup tenantSubQuery) { this.tenantSubQuery = tenantSubQuery; }
public DescriptionTemplateLookup getDescriptionTemplateSubQuery() { return this.descriptionTemplateSubQuery; }
public void setDescriptionTemplateSubQuery(DescriptionTemplateLookup descriptionTemplateSubQuery) { this.descriptionTemplateSubQuery = descriptionTemplateSubQuery; }
@ -129,6 +133,7 @@ public class DescriptionLookup extends Lookup {
if (this.ids != null) query.ids(this.ids);
if (this.excludedIds != null) query.excludedIds(this.excludedIds);
if (this.planSubQuery != null) query.planSubQuery(this.planSubQuery.enrich(queryFactory));
if (this.tenantSubQuery != null) query.tenantSubQuery(this.tenantSubQuery.enrich(queryFactory));
if (this.descriptionTemplateSubQuery != null) query.descriptionTemplateSubQuery(this.descriptionTemplateSubQuery.enrich(queryFactory));
if (this.descriptionReferenceSubQuery != null) query.descriptionReferenceSubQuery(this.descriptionReferenceSubQuery.enrich(queryFactory));
if (this.descriptionTagSubQuery != null) query.descriptionTagSubQuery(this.descriptionTagSubQuery.enrich(queryFactory));

View File

@ -13,6 +13,7 @@ public class TenantLookup extends Lookup {
private String like;
private List<IsActive> isActive;
private List<UUID> ids;
private List<String> codes;
private List<UUID> excludedIds;
public String getLike() {
@ -35,9 +36,11 @@ public class TenantLookup extends Lookup {
return ids;
}
public void setIds(List<UUID> ids) {
this.ids = ids;
}
public void setIds(List<UUID> ids) { this.ids = ids; }
public List<String> getCodes() { return codes; }
public void setCodes(List<String> codes) { this.codes = codes; }
public List<UUID> getExcludedIds() {
return excludedIds;
@ -52,6 +55,7 @@ public class TenantLookup extends Lookup {
if (this.like != null) query.like(this.like);
if (this.isActive != null) query.isActive(this.isActive);
if (this.ids != null) query.ids(this.ids);
if (this.codes != null) query.codes(this.codes);
if (this.excludedIds != null) query.excludedIds(this.excludedIds);
this.enrichCommon(query);

View File

@ -3,10 +3,10 @@ import { Guid } from '@common/types/guid';
import { IsActive } from '../common/enum/is-active.enum';
import { DescriptionStatus } from '../common/enum/description-status';
import { PlanLookup } from './plan.lookup';
import { ReferenceType } from '../model/reference-type/reference-type';
import { DescriptionReferenceLookup } from './reference.lookup';
import { DescriptionTagLookup } from './tag.lookup';
import { DescriptionTemplateLookup } from './description-template.lookup';
import { TenantLookup } from './tenant.lookup';
export class DescriptionLookup extends Lookup implements DescriptionFilter {
ids: Guid[];
@ -20,6 +20,7 @@ export class DescriptionLookup extends Lookup implements DescriptionFilter {
statuses: DescriptionStatus[];
planSubQuery: PlanLookup;
tenantSubQuery: TenantLookup;
descriptionTemplateSubQuery: DescriptionTemplateLookup;
descriptionTagSubQuery: DescriptionTagLookup;
descriptionReferenceSubQuery: DescriptionReferenceLookup;

View File

@ -4,6 +4,7 @@ import { IsActive } from '../common/enum/is-active.enum';
export class TenantLookup extends Lookup implements TenantFilter {
ids: Guid[];
codes: string[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];

View File

@ -27,7 +27,7 @@
</button>
</div>
</div>
<div *ngIf="listingItems && listingItems.length > 0 || lookup.like || lookup.descriptionTemplateSubQuery || lookup.planSubQuery || lookup.descriptionTagSubQuery || lookup.descriptionReferenceSubQuery"
<div *ngIf="hasLoadedListingItems && (hasListingItems || hasFilters)"
class="filter-btn" [style.right]="dialog.getDialogById('filters') ? '446px' : '0px'" [style.width]="listingItems.length > 2 ? '57px' : '37px'" (click)="openFiltersDialog()">
<button mat-raised-button class="p-0" [matBadge]="filtersCount" [matBadgeHidden]="!hasFilters" matBadgePosition="before">
<mat-icon class="mr-4 filter-icon">filter_alt</mat-icon>
@ -81,7 +81,12 @@
</div>
<div class="col-md-12 col-sm-12 col-md-9">
<div *ngFor="let item of listingItems; let i = index">
<app-description-listing-item-component [isPublic]="isPublic" [description]="item" [showDivider]="i != (listingItems.length - 1)"></app-description-listing-item-component>
<app-description-listing-item-component
[isPublic]="isPublic"
[description]="item"
[tenants]="tenants"
[showDivider]="i != (listingItems.length - 1)"
></app-description-listing-item-component>
</div>
<div *ngIf="hasListingItems && this.lookup?.page?.offset < this.totalCount - 1 && this.pageSize < this.totalCount - 1" class="d-flex justify-content-center">
<button type="button" class="btn-load-more" (click)="loadMore()">{{'DESCRIPTION-LISTING.ACTIONS.LOAD-MORE' | translate}}</button>

View File

@ -37,6 +37,10 @@ import { QueryParamsService } from '@app/core/services/utilities/query-params.se
import { UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { Guid } from '@common/types/guid';
import { DescriptionFilterService } from './filtering/description-filter.service';
import { Tenant } from '@app/core/model/tenant/tenant';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { PrincipalService } from '@app/core/services/http/principal.service';
@Component({
selector: 'app-description-listing-component',
@ -69,6 +73,8 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
order: new UntypedFormControl()
});
tenants: Tenant[] = [];
scrollbar: boolean;
order = RecentActivityOrder;
planText: string;
@ -96,7 +102,8 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
get hasFilters(): boolean {
return (this.lookup.like != null && this.lookup.like != '') || this.lookup.statuses != null ||
this.lookup.planSubQuery != null || this.lookup.descriptionTemplateSubQuery != null ||
this.lookup.descriptionTagSubQuery != null || this.lookup.descriptionReferenceSubQuery != null;
this.lookup.descriptionTagSubQuery != null || this.lookup.descriptionReferenceSubQuery != null ||
this.lookup.tenantSubQuery != null;
}
constructor(
@ -115,6 +122,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
private analyticsService: AnalyticsService,
private fb: UntypedFormBuilder,
protected formBuilder: FormBuilder,
private principalService: PrincipalService,
) {
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
}
@ -155,6 +163,10 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
this.onPageLoad({ offset: this.lookup.page.offset / this.lookup.page.size } as PageLoadEvent);
});
this._loadUserTenants().pipe(takeUntil(this._destroyed)).subscribe( tenants => {
this.tenants = tenants;
});
this.formGroup.get('like').valueChanges
.pipe(takeUntil(this._destroyed), debounceTime(500))
.subscribe(x => this.controlModified());
@ -265,6 +277,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
panelClass: 'dialog-side-panel',
data: {
isPublic: this.isPublic ?? true,
hasSelectedTenant: this.authService.selectedTenant() != 'default',
filterForm: this._buildFormFromLookup(this.lookup, this.referenceFilters),
referencesWithTypeItems: this.referenceFilters ?? [],
}
@ -357,6 +370,15 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
this.orderByChanged();
}
private _loadUserTenants(): Observable<Array<Tenant>> {
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]
};
return this.principalService.myTenants({ params: params });
}
private _parseLookupFromParams(params: Params): DescriptionLookup {
let lookup: DescriptionLookup = JSON.parse(params['lookup']);
@ -367,6 +389,17 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
lookup.page = { size: querySize, offset: queryOffset };
lookup.project = { fields: this._lookupFields };
lookup.metadata = { countAll: true };
if (lookup.tenantSubQuery && lookup.tenantSubQuery?.codes && lookup.tenantSubQuery?.codes?.length > 0) {
const tenantFilter = lookup.tenantSubQuery?.codes[0];
if (tenantFilter != this.authService.selectedTenant()) {
lookup.tenantSubQuery = null;
this.filterChanged(lookup);
return lookup;
}
}
return lookup;
}
@ -384,6 +417,17 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
lookup.statuses = formGroup.get("status")?.value !== null ? [formGroup.get("status")?.value] : null;
// Tenants
let viewOnlyTenant = formGroup.get("viewOnlyTenant")?.value ?? false;
if (viewOnlyTenant) {
let tenant = this.tenants?.find(t => t.code && t.code?.toString() == this.authService.selectedTenant());
if (tenant && tenant?.code) {
lookup.tenantSubQuery = DescriptionFilterService.initializeTenantLookup();
lookup.tenantSubQuery.codes = [tenant.code]
}
else lookup.tenantSubQuery = null;
} else lookup.tenantSubQuery = null;
// Description Templates
let descriptionTemplates = formGroup.get("descriptionTemplates")?.value ?? null;
if (descriptionTemplates && descriptionTemplates?.length > 0) {
@ -432,6 +476,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
_buildFormFromLookup(lookup: DescriptionLookup, referenceFilters: ReferencesWithType[]): UntypedFormGroup {
return (new UntypedFormBuilder()).group({
status: [lookup.statuses?.length > 0 ? lookup.statuses[0] : null],
viewOnlyTenant: [lookup.tenantSubQuery?.codes?.length > 0 ? true : false],
role: lookup.planSubQuery?.planUserSubQuery?.userRoles ? lookup.planSubQuery?.planUserSubQuery?.userRoles[0] : [],
descriptionTemplates: lookup.descriptionTemplateSubQuery?.ids ? [lookup.descriptionTemplateSubQuery?.ids] : [],
associatedPlanIds: lookup.planSubQuery?.ids ? [lookup.planSubQuery?.ids] : [],
@ -443,6 +488,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
let count = 0;
if (lookup.statuses) count += lookup.statuses.length;
if (lookup.tenantSubQuery) count += 1;
if (lookup.descriptionTemplateSubQuery) count += lookup.descriptionTemplateSubQuery.ids?.length;
if (lookup.descriptionTagSubQuery) count += lookup.descriptionTagSubQuery.tagIds?.length;
if (lookup.planSubQuery) {

View File

@ -4,5 +4,6 @@
[filterFormGroup]="data.filterForm"
[referencesWithTypeItems]="data.referencesWithTypeItems"
[isPublic]="data.isPublic"
[hasSelectedTenant]="data.hasSelectedTenant"
(filterChanged)="onFilterChanged($event)"
></app-description-filter-component>

View File

@ -20,6 +20,7 @@ export class DescriptionFilterDialogComponent implements OnInit {
private analyticsService: AnalyticsService,
@Inject(MAT_DIALOG_DATA) public data: {
isPublic: boolean,
hasSelectedTenant: boolean,
filterForm: UntypedFormGroup,
referencesWithTypeItems: ReferencesWithType[],
}

View File

@ -21,6 +21,14 @@
</div>
<!-- End of Status Filter-->
<!-- Tenant Filter -->
<div *ngIf="!isPublic && hasSelectedTenant" class="col-10">
<h6 class="category-title">{{ 'PLAN-LISTING.FILTERS.RELATED-TENANT.NAME' | translate}}</h6>
<mat-slide-toggle [formControl]="formGroup.get('viewOnlyTenant')">{{ 'PLAN-LISTING.FILTERS.RELATED-TENANT.FILTER-BY-TENANT' | translate }}</mat-slide-toggle>
<hr>
</div>
<!-- End of Tenant Filter -->
<!-- Related Dataset Templates Filters -->
<div class="col-10">
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.RELATED-DESCRIPTION-TEMPLATES.NAME' | translate}}</h6>

View File

@ -33,6 +33,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent implements
@Input() status;
@Input() isPublic: boolean;
@Input() hasSelectedTenant: boolean;
@Input() referencesWithTypeItems: ReferencesWithType[];
@Input() filterFormGroup: UntypedFormGroup;
@Output() filterChanged: EventEmitter<any> = new EventEmitter();

View File

@ -4,11 +4,13 @@ import { Description, DescriptionReference, DescriptionTag } from "@app/core/mod
import { Plan, PlanUser } from "@app/core/model/plan/plan";
import { Reference } from "@app/core/model/reference/reference";
import { Tag } from "@app/core/model/tag/tag";
import { Tenant } from "@app/core/model/tenant/tenant";
import { DescriptionTemplateLookup } from "@app/core/query/description-template.lookup";
import { PlanUserLookup } from "@app/core/query/plan-user.lookup";
import { PlanLookup } from "@app/core/query/plan.lookup";
import { DescriptionReferenceLookup } from "@app/core/query/reference.lookup";
import { DescriptionTagLookup } from "@app/core/query/tag.lookup";
import { TenantLookup } from "@app/core/query/tenant.lookup";
import { IsActive } from "@notification-service/core/enum/is-active.enum";
import { nameof } from "ts-simple-nameof";
@ -79,4 +81,17 @@ export class DescriptionFilterService {
return lookup;
}
public static initializeTenantLookup(): TenantLookup {
const lookup = new TenantLookup();
lookup.metadata = { countAll: true };
lookup.isActive = [IsActive.Active];
lookup.project = {
fields: [
nameof<Tenant>(x => x.code),
]
};
return lookup;
}
}

View File

@ -30,10 +30,6 @@ import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { Tenant } from '@app/core/model/tenant/tenant';
import { PrincipalService } from '@app/core/services/http/principal.service';
import { Observable } from 'rxjs';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
@Component({
selector: 'app-description-listing-item-component',
@ -45,6 +41,7 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
@Input() description: Description;
@Input() showDivider: boolean = true;
@Input() isPublic: boolean = false;
@Input() tenants: Tenant[] = [];
@Output() onClick: EventEmitter<Description> = new EventEmitter();
isDraft: boolean;
@ -56,7 +53,6 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
canDelete: boolean = false;
canEdit: boolean = false;
canInvitePlanUsers: boolean = false;
tenants: Tenant[] = [];
constructor(
public routerUtils: RouterUtilsService,
@ -78,7 +74,6 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
private fb: UntypedFormBuilder,
private analyticsService: AnalyticsService,
private httpErrorHandlingService: HttpErrorHandlingService,
private principalService: PrincipalService,
) {
super();
}
@ -105,10 +100,6 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
this.canInvitePlanUsers = (this.authService.hasPermission(AppPermission.InvitePlanUsers) ||
this.description.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers)) && this.description.belongsToCurrentTenant != false;
this.loadUserTenants().pipe(takeUntil(this._destroyed)).subscribe( tenants => {
this.tenants = tenants;
});
}
public getTenantName(id: Guid): string {
@ -248,12 +239,4 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
canEditDescription(): boolean {
return (this.isDraft) && (this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription) || this.authentication.hasPermission(AppPermission.EditDescription)) && this.isPublic == false && this.description.belongsToCurrentTenant != false;
}
loadUserTenants(): Observable<Array<Tenant>> {
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]
};
return this.principalService.myTenants({ params: params });
}
}