add inactive filter and plan description ui changes

This commit is contained in:
CITE\amentis 2024-09-26 11:27:46 +03:00
parent 3bbe71d218
commit e97de92b3a
34 changed files with 117 additions and 40 deletions

View File

@ -108,8 +108,7 @@ public class QueryUtilsServiceImpl implements QueryUtilsService {
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(PlanUserEntity._planId))
.filterFunc((subQueryRoot, cb) ->
userId != null ? cb.and(
cb.equal(subQueryRoot.get(PlanUserEntity._userId), userId),
cb.equal(subQueryRoot.get(PlanUserEntity._isActive), IsActive.Active)
cb.equal(subQueryRoot.get(PlanUserEntity._userId), userId)
) : cb.or() //Creates a false query
)
));

View File

@ -1298,6 +1298,7 @@ public class PlanServiceImpl implements PlanService {
PlanEntity data = this.entityManager.find(PlanEntity.class, model.getPlanId(), true);
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (data.getIsActive().equals(IsActive.Inactive)) throw new MyApplicationException("Plan is not Active");
List<PlanUserEntity> existingUsers = this.queryFactory.query(PlanUserQuery.class)
.planIds(model.getPlanId()).ids(model.getId()).userRoles(model.getRole())
.collect();
@ -1668,7 +1669,7 @@ public class PlanServiceImpl implements PlanService {
} else if (currentStatusEntity != null && currentStatusEntity.getInternalStatus()!= null && !currentStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) {
// description to be canceled
description.setStatusId(canceledStatusEntity.getId());
this.deleterFactory.deleter(DescriptionDeleter.class).delete(List.of(description), true);
this.deleterFactory.deleter(DescriptionDeleter.class).delete(List.of(description), false);
}
}
}

View File

@ -276,11 +276,15 @@ export class PlanService {
//
//
getCurrentUserRolesInPlan(planUsers: PlanUser[]): PlanUserRole[] {
getCurrentUserRolesInPlan(planUsers: PlanUser[], isDeletedPlan: boolean = false): PlanUserRole[] {
const principalId: Guid = this.authService.userId();
let planUserRoles: PlanUserRole[] = null;
if (principalId) {
planUserRoles = planUsers.filter(element => element.isActive == IsActive.Active && element?.user?.id === principalId).map(x => x.role);
if (isDeletedPlan) {
planUserRoles = planUsers.filter(element => element?.user?.id === principalId).map(x => x.role);
} else {
planUserRoles = planUsers.filter(element => element.isActive == IsActive.Active && element?.user?.id === principalId).map(x => x.role);
}
}
return planUserRoles;
}

View File

@ -27,7 +27,7 @@
</div>
</div>
<div class="col-auto">
<div class="row h-100">
<div *ngIf="!isDeleted" class="row h-100">
<div class="col-auto d-flex align-items-center">
<button *ngIf="formGroup.get('id').value && canExport" [disabled]="isDirty()" [matTooltipDisabled]="!isDirty()" mat-button class="rounded-btn neutral" type="button" [matMenuTriggerFor]="exportMenu" (click)="$event.stopPropagation();" [matTooltip]="'DESCRIPTION-EDITOR.ACTIONS.EXPORT.CAN-NOT-EXPORT' | translate">

View File

@ -100,7 +100,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
return (this.lookup.like != null && this.lookup.like != '') || this.lookup.statusIds != null ||
this.lookup.planSubQuery != null || this.lookup.descriptionTemplateSubQuery != null ||
this.lookup.descriptionTagSubQuery != null || this.lookup.descriptionReferenceSubQuery != null ||
this.lookup.tenantSubQuery != null;
this.lookup.tenantSubQuery != null || this.lookup.isActive != null;
}
constructor(
@ -416,6 +416,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
_patchLookupFromFilters(filters: DescriptionListingFilters): DescriptionLookup {
this.lookup.statusIds = filters?.statusId != null ? [filters?.statusId] : null;
this.lookup.isActive = filters?.isActive ? [IsActive.Active] : [IsActive.Inactive];
// Tenants
let viewOnlyTenant = filters?.viewOnlyTenant ?? false;
@ -475,6 +476,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
let count = 0;
if (lookup.statusIds) count += lookup.statusIds.length;
if (lookup.isActive[0] == IsActive.Inactive) count += lookup.isActive.length;
if (lookup.tenantSubQuery) count += 1;
if (lookup.descriptionTemplateSubQuery) count += lookup.descriptionTemplateSubQuery.ids?.length;
if (lookup.descriptionTagSubQuery) count += lookup.descriptionTagSubQuery.tagIds?.length;
@ -492,6 +494,7 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
nameof<Description>(x => x.id),
nameof<Description>(x => x.tenantId),
nameof<Description>(x => x.label),
nameof<Description>(x => x.isActive),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),

View File

@ -3,6 +3,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { DescriptionFilterComponent, DescriptionListingFilters } from '../description-filter.component';
import { DescriptionLookup, ReferencesWithType } from '@app/core/query/description.lookup';
import { IsActive } from '@app/core/common/enum/is-active.enum';
@Component({
selector: 'description-filter-dialog-component',
@ -48,6 +49,7 @@ export class DescriptionFilterDialogComponent implements OnInit {
private _buildDescriptionFilters(lookup: DescriptionLookup, references: ReferencesWithType[]): DescriptionListingFilters {
return {
statusId: lookup.statusIds?.[0] ?? null,
isActive: lookup.isActive?.[0] == IsActive.Active ?? false,
viewOnlyTenant: lookup.tenantSubQuery?.codes?.length > 0,
role: lookup.planSubQuery?.planUserSubQuery?.userRoles?.[0] ?? null,
descriptionTemplates: lookup.descriptionTemplateSubQuery?.ids ?? [],

View File

@ -19,6 +19,12 @@
</div>
<!-- End of Status Filter-->
<!-- IsActive Filter -->
<div class="col-10">
<mat-slide-toggle [formControl]="formGroup.get('isActive')">{{ 'DESCRIPTION-LISTING.FILTERS.IS-ACTIVE' | translate }}</mat-slide-toggle>
<hr>
</div>
<!-- Tenant Filter -->
<div *ngIf="!isPublic && hasSelectedTenant" class="col-10">
<h6 class="category-title">{{ 'DESCRIPTION-LISTING.FILTERS.RELATED-TENANT.NAME' | translate}}</h6>

View File

@ -81,6 +81,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent<Descriptio
buildForm(filters: DescriptionListingFilters) {
this.formGroup = new FormGroup<DescriptionListingFilterForm>({
statusId: new FormControl(filters?.statusId),
isActive: new FormControl(filters?.isActive),
viewOnlyTenant: new FormControl(filters.viewOnlyTenant),
role: new FormControl(filters.role),
descriptionTemplates: new FormControl(filters.descriptionTemplates),
@ -114,6 +115,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent<Descriptio
this.formGroup.reset();
this.formGroup.patchValue({
statusId: null,
isActive: true,
viewOnlyTenant: null,
role: null,
descriptionTemplates: null,
@ -213,6 +215,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent<Descriptio
export interface DescriptionListingFilters {
statusId: Guid,
isActive: boolean,
viewOnlyTenant: boolean,
role: Guid,
descriptionTemplates: Guid[],
@ -223,6 +226,7 @@ export interface DescriptionListingFilters {
interface DescriptionListingFilterForm {
statusId: FormControl<Guid>,
isActive: FormControl<boolean>,
viewOnlyTenant: FormControl<boolean>,
role: FormControl<Guid>,
descriptionTemplates: FormControl<Guid[]>,

View File

@ -18,7 +18,7 @@
<div *ngIf="description.status?.internalStatus === descriptionStatusEnum.Canceled" class="col-auto description-title-draft">{{description.label}}</div>
<div *ngIf="description.status?.internalStatus === null" class="col-auto description-title-draft">{{description.label}}</div>
<div class="description-subtitle">
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(description?.plan?.planUsers)) }}</span>
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(description?.plan?.planUsers, isDeleted)) }}</span>
<span *ngIf="isUserPlanRelated()">.</span>
<span class="col-auto" *ngIf="description.status?.internalStatus === descriptionStatusEnum.Finalized && description.plan.accessType === planAccessTypeEnum.Public"><span class="material-icons icon-align">public</span>{{'DESCRIPTION-LISTING.STATES.PUBLIC' | translate}}</span>
<span *ngIf="description.status?.internalStatus === descriptionStatusEnum.Finalized && description.plan.accessType != planAccessTypeEnum.Public; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ description.status.name }}</span>
@ -38,7 +38,7 @@
<div class="description-card-actions">
<a class="col-auto border-right pointer" *ngIf="canExport && fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Description) && fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Description).length > 0" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DESCRIPTION-LISTING.ACTIONS.EXPORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="canInvitePlanUsers" (click)="openShareDialog()"><span class="material-icons icon-align pr-2">group_add</span>{{'DESCRIPTION-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="copyToPlan(description)"><span class="material-icons icon-align pr-2">file_copy</span>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isAuthenticated() && !isDeleted" (click)="copyToPlan(description)"><span class="material-icons icon-align pr-2">file_copy</span>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="canDelete" (click)="deleteClicked(description.id)"><span class="material-icons icon-align pr-2">delete</span>{{ 'DESCRIPTION-LISTING.ACTIONS.DELETE' | translate }}</a>
</div>
<mat-menu #actionsMenu="matMenu">

View File

@ -93,15 +93,15 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
}
this.canDelete = !this.isPublic && (this.authService.hasPermission(AppPermission.DeleteDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription)) && this.description.belongsToCurrentTenant != false;
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription)) && !this.isDeleted &&this.description.belongsToCurrentTenant != false;
this.canEdit = !this.isPublic && (this.authService.hasPermission(AppPermission.EditDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription)) && this.description.belongsToCurrentTenant != false;
this.canInvitePlanUsers = !this.isPublic && (this.authService.hasPermission(AppPermission.InvitePlanUsers) ||
this.description.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers)) && this.description.belongsToCurrentTenant != false;
this.description.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers)) && !this.isDeleted && this.description.belongsToCurrentTenant != false;
this.canExport = this.description.authorizationFlags?.some(x => x === AppPermission.ExportDescription) || this.authentication.hasPermission(AppPermission.ExportDescription) &&
this.canExport = this.description.authorizationFlags?.some(x => x === AppPermission.ExportDescription) || this.authentication.hasPermission(AppPermission.ExportDescription) && !this.isDeleted &&
this.description.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0;
}
@ -111,7 +111,7 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
isUserPlanRelated() {
const principalId: Guid = this.authService.userId();
return this.description.plan.planUsers?.some(x => (x.user.id === principalId));
return this.description.plan?.planUsers?.some(x => (x.user.id === principalId));
}
public isAuthenticated(): boolean {

View File

@ -130,7 +130,7 @@
</div>
</div>
<div class="col-12 col-lg-4">
<ng-container *ngIf="isAuthenticated()">
<ng-container *ngIf="isAuthenticated() && isActive">
<div class="row">
<div class="col-12">
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">

View File

@ -78,10 +78,10 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
canAnnotate = false;
canInvitePlanUsers = false;
availableStatusesTransitions: DescriptionStatus[];
canAssignPlanUsers(): boolean {
get canAssignPlanUsers(): boolean {
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) &&
!this.isPublicView && this.description?.belongsToCurrentTenant && this.description?.plan?.status?.internalStatus != PlanStatusEnum.Finalized;
return (authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) &&
!this.isPublicView && this.description?.belongsToCurrentTenant && this.isActive && (this.description?.plan?.status?.internalStatus == null || this.description?.plan?.status?.internalStatus != PlanStatusEnum.Finalized);
}
authorFocus: string;
@ -142,7 +142,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
this.description = data;
this.getAvailableStatuses(this.description.id);
this.description.plan.planUsers = this.isActive ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
this.description.plan.planUsers = this.isActive || this.description.plan.isActive === IsActive.Active ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
this.checkLockStatus(this.description.id);
this.canDelete = this.isActive && (this.authService.hasPermission(AppPermission.DeleteDescription) ||
@ -587,6 +587,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.id)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.label)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.accessType)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.isActive)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),

View File

@ -5,6 +5,7 @@ import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { PlanFilterComponent, PlanListingFilters } from '../plan-filter.component';
import { ReferencesWithType } from '@app/core/query/description.lookup';
import { PlanLookup } from '@app/core/query/plan.lookup';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
@Component({
selector: 'plan-filter-dialog-component',
@ -36,6 +37,7 @@ export class PlanFilterDialogComponent implements OnInit {
private _buildPlanFilters(lookup: PlanLookup, references: ReferencesWithType[]): PlanListingFilters {
return {
statusId: lookup.statusIds?.[0] ?? null,
isActive: lookup.isActive?.[0] == IsActive.Active ?? false,
viewOnlyTenant: lookup.tenantSubQuery?.codes?.length > 0,
descriptionTemplates: lookup.planDescriptionTemplateSubQuery?.descriptionTemplateGroupIds ? lookup.planDescriptionTemplateSubQuery?.descriptionTemplateGroupIds : [],
planBlueprints: lookup.planBlueprintSubQuery?.ids ?? [],

View File

@ -17,6 +17,12 @@
</div>
<!-- End of Visibility Filter-->
<!-- IsActive Filter -->
<div class="col-10">
<mat-slide-toggle [formControl]="formGroup.get('isActive')">{{ 'PLAN-LISTING.FILTERS.IS-ACTIVE' | translate }}</mat-slide-toggle>
<hr>
</div>
<!-- Tenant Filter -->
<div *ngIf="!isPublic && hasSelectedTenant" class="col-10">
<h6 class="category-title">{{ 'PLAN-LISTING.FILTERS.RELATED-TENANT.NAME' | translate}}</h6>

View File

@ -79,6 +79,7 @@ export class PlanFilterComponent extends BaseCriteriaComponent<PlanListingFilter
buildForm(filters: PlanListingFilters){
this.formGroup = new FormGroup<PlanListingFilterForm>({
statusId: new FormControl(filters?.statusId),
isActive: new FormControl(filters?.isActive),
descriptionTemplates: new FormControl(filters?.descriptionTemplates),
planBlueprints: new FormControl(filters?.planBlueprints),
role: new FormControl(filters?.role),
@ -110,6 +111,7 @@ export class PlanFilterComponent extends BaseCriteriaComponent<PlanListingFilter
this.formGroup.reset();
this.formGroup.patchValue({
descriptionTemplates: null,
isActive: true,
planBlueprints: null,
references: null,
role: null,
@ -212,6 +214,7 @@ export class PlanFilterComponent extends BaseCriteriaComponent<PlanListingFilter
export interface PlanListingFilters {
statusId: Guid,
isActive: boolean,
viewOnlyTenant: boolean,
descriptionTemplates: Guid[],
planBlueprints: Guid[],
@ -221,6 +224,7 @@ export interface PlanListingFilters {
interface PlanListingFilterForm {
statusId: FormControl<Guid>,
isActive: FormControl<boolean>,
viewOnlyTenant: FormControl<boolean>,
descriptionTemplates: FormControl<Guid[]>,
planBlueprints: FormControl<Guid[]>,

View File

@ -15,7 +15,7 @@
</div>
<div class="col-auto" [ngClass]="{'plan-title': !isDraft, 'plan-title-draft': isDraft}">{{plan.label}}</div>
<div class="plan-subtitle">
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(plan?.planUsers)) }}</span>
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(plan?.planUsers, isDeleted)) }}</span>
<span *ngIf="isUserPlanRelated()">.</span>
<span class="col-auto" *ngIf="plan.status.internalStatus === planStatusEnum.Finalized && isPublic"><span class="material-icons icon-align">public</span>{{'TYPES.PLAN-VISIBILITY.PUBLIC' | translate}}</span>
<span *ngIf="plan.status.internalStatus === planStatusEnum.Finalized && !isPublic; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ plan.status.name }}</span>

View File

@ -30,6 +30,7 @@ import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { Tenant } from '@app/core/model/tenant/tenant';
import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
@Component({
selector: 'app-plan-listing-item-component',
@ -45,6 +46,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
@Input() tenants: Tenant[] = [];
@Output() onClick: EventEmitter<Plan> = new EventEmitter();
isDeleted: boolean;
isDraft: boolean;
isFinalized: boolean;
isPublished: boolean;
@ -52,15 +54,15 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
fileTransformerEntityTypeEnum = FileTransformerEntityType;
get canEditPlan(): boolean {
return (this.isDraft) && (this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan) || this.authentication.hasPermission(AppPermission.EditPlan)) && !this.isPublic && this.plan.belongsToCurrentTenant != false;
return (this.isDraft) && (this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan) || this.authentication.hasPermission(AppPermission.EditPlan)) && !this.isDeleted && !this.isPublic && this.plan.belongsToCurrentTenant != false;
}
get canCreateNewVersion(): boolean {
return (this.plan.authorizationFlags?.some(x => x === AppPermission.CreateNewVersionPlan) || this.authentication.hasPermission(AppPermission.CreateNewVersionPlan)) && this.plan.versionStatus === PlanVersionStatus.Current && !this.isPublic && this.plan.belongsToCurrentTenant != false;
return (this.plan.authorizationFlags?.some(x => x === AppPermission.CreateNewVersionPlan) || this.authentication.hasPermission(AppPermission.CreateNewVersionPlan)) && !this.isDeleted && this.plan.versionStatus === PlanVersionStatus.Current && !this.isPublic && this.plan.belongsToCurrentTenant != false;
}
get canDeletePlan(): boolean {
return (this.plan.authorizationFlags?.some(x => x === AppPermission.DeletePlan) || this.authentication.hasPermission(AppPermission.DeletePlan)) && !this.isPublic && this.plan.belongsToCurrentTenant != false && this.isDraftPlan;
return (this.plan.authorizationFlags?.some(x => x === AppPermission.DeletePlan) || this.authentication.hasPermission(AppPermission.DeletePlan)) && !this.isPublic && !this.isDeleted &&this.plan.belongsToCurrentTenant != false && this.isNotFinalizedPlan;
}
get canClonePlan(): boolean {
@ -68,28 +70,28 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
}
get canFinalizePlan(): boolean {
return (this.plan.authorizationFlags?.some(x => x === AppPermission.FinalizePlan) || this.authentication.hasPermission(AppPermission.FinalizePlan)) && !this.isPublic && this.plan.belongsToCurrentTenant != false;
return (this.plan.authorizationFlags?.some(x => x === AppPermission.FinalizePlan) || this.authentication.hasPermission(AppPermission.FinalizePlan)) && !this.isDeleted && !this.isPublic && this.plan.belongsToCurrentTenant != false;
}
get canExportPlan(): boolean {
return this.plan.authorizationFlags?.some(x => x === AppPermission.ExportPlan) || this.authentication.hasPermission(AppPermission.ExportPlan) &&
return (this.plan.authorizationFlags?.some(x => x === AppPermission.ExportPlan) || this.authentication.hasPermission(AppPermission.ExportPlan)) && this.isPublic &&
this.plan.status?.definition?.availableActions?.filter(x => x === PlanStatusAvailableActionType.Export).length > 0;;
}
get canInvitePlanUsers(): boolean {
return (this.plan.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) && !this.isPublic && this.plan.belongsToCurrentTenant != false;
return (this.plan.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) && !this.isDeleted && !this.isPublic && this.plan.belongsToCurrentTenant != false;
}
get canAssignPlanUsers(): boolean {
return (this.plan.authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) && !this.isPublic && this.plan.belongsToCurrentTenant != false;
return (this.plan.authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) && !this.isDeleted && !this.isPublic && this.plan.belongsToCurrentTenant != false;
}
get showActionsMenu(): boolean {
return this.isAuthenticated() && (this.canCreateNewVersion || this.showAllVersionsAction || this.canDeletePlan)
return this.isAuthenticated() && (this.canCreateNewVersion || this.showAllVersionsAction || this.canDeletePlan) && !this.isDeleted;
}
get isDraftPlan(): boolean {
return this.plan.status?.internalStatus == PlanStatusEnum.Draft;
get isNotFinalizedPlan(): boolean {
return this.plan.status?.internalStatus == null || this.plan.status?.internalStatus != PlanStatusEnum.Finalized;
}
constructor(
@ -125,6 +127,9 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
this.isPublished = false;
if (this.plan.status.internalStatus === PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public) { this.isPublished = true }
}
if (this.plan.isActive != IsActive.Active) {
this.isDeleted = true;
}
}
public isAuthenticated(): boolean {

View File

@ -219,7 +219,12 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
if (!result) { return []; }
this.totalCount = result.count;
if (this.lookup?.page?.offset === 0) this.listingItems = [];
const plans = this._filterPlan([...result.items]);
let plans: any[];
if (this.lookup?.isActive[0] == IsActive.Inactive) {
plans = result.items;
} else {
plans = this._filterPlan([...result.items]);
}
this.listingItems.push(...plans);
}));
}
@ -417,6 +422,8 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
this.lookup.statusIds = filters?.statusId != null ? [filters.statusId] : null;
this.lookup.isActive = filters?.isActive ? [IsActive.Active] : [IsActive.Inactive];
// Tenants
let viewOnlyTenant = filters?.viewOnlyTenant ?? false;
if (viewOnlyTenant) {
@ -473,6 +480,7 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
let count = 0;
if (lookup.statusIds) count += lookup.statusIds.length;
if (lookup.isActive[0] == IsActive.Inactive) count += lookup.isActive.length;
if (lookup.tenantSubQuery) count += 1;
if (lookup.planDescriptionTemplateSubQuery) count += lookup.planDescriptionTemplateSubQuery.descriptionTemplateGroupIds?.length;
if (lookup.planBlueprintSubQuery) count += lookup.planBlueprintSubQuery.ids?.length;
@ -494,6 +502,7 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
nameof<Plan>(x => x.id),
nameof<Plan>(x => x.label),
nameof<Plan>(x => x.description),
nameof<Plan>(x => x.isActive),
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),

View File

@ -19,7 +19,7 @@
<div class="col-auto pr-0 d-flex"><span class="plan-label">{{ plan.label }}</span></div>
<div class="col-auto pr-0 mb-1"><span style="font-weight: 700;">.</span></div>
<div class="col-auto d-flex">
<button mat-button [disabled]="!otherPlanVersions?.length" [matMenuTriggerFor]="versionsMenu" style="color: #000 !important; border-radius: 100px; background-color: #eaeaea;">
<button mat-button [disabled]="!otherPlanVersions?.length || !isActive" [matMenuTriggerFor]="versionsMenu" style="color: #000 !important; border-radius: 100px; background-color: #eaeaea;">
<div class="pl-2 pr-1 d-flex align-items-center">{{'PLAN-OVERVIEW.VERSION' | translate}} {{selectedPlanVersion}}
@if(otherPlanVersions?.length){
<mat-icon class="ml-1">arrow_drop_down</mat-icon>
@ -177,7 +177,7 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="isAuthenticated()">
<ng-container *ngIf="isAuthenticated() && isActive">
<div class="row">
<div class="col-12">
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">
@ -247,7 +247,7 @@
<app-plan-authors
[planUsers]="plan.planUsers"
[username]="userName"
[removeUser]="canAssignPlanUsers(plan) && (plan.status.internalStatus == null || plan.status.internalStatus === planStatusEnum.Finalized)"
[removeUser]="canAssignPlanUsers(plan)"
(deleteAuthor)="removeUserFromPlan($event)"
/>
<div *ngIf="canInvitePlanUsers()" class="col-12 d-flex align-items-center justify-content-center mt-2">

View File

@ -139,7 +139,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
this.getAvailableStatuses(this.plan.id);
this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers;
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
if (this.plan.descriptions) {
if (this.plan.descriptions && this.isActive) {
if (this.plan.status?.internalStatus == PlanStatusEnum.Finalized) {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus === DescriptionStatusEnum.Finalized);
} else {
@ -327,7 +327,8 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
canAssignPlanUsers(): boolean {
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) && this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) && this.isPublicView == false && this.plan.belongsToCurrentTenant != false &&
(this.plan.status.internalStatus == null || this.plan.status.internalStatus != PlanStatusEnum.Finalized);
}
canDepositPlan(): boolean {

View File

@ -9,7 +9,7 @@
<div class="title">{{(canEdit ? 'PLAN-EDITOR.TITLE-EDIT' : 'PLAN-EDITOR.TITLE-PREVIEW') | translate}}</div>
<div class="subtitle">{{ formGroup.get('label').value }} <span *ngIf="isDirty()" class="changes">({{'PLAN-EDITOR.UNSAVED-CHANGES' | translate}})</span></div>
</div>
<div *ngIf="step > 0" class="ml-auto d-flex flex-row">
<div *ngIf="step > 0 && !isDeleted" class="ml-auto d-flex flex-row">
<div *ngIf="formGroup.get('id').value && canExport" class="col-auto d-flex align-items-center">
<button [disabled]="isDirty()" [matTooltipDisabled]="!isDirty()" mat-button class="rounded-btn primary-inverted" type="button" [matMenuTriggerFor]="exportMenu" (click)="$event.stopPropagation();" [matTooltip]="'PLAN-EDITOR.ACTIONS.EXPORT.CAN-NOT-EXPORT' | translate">

View File

@ -272,16 +272,17 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
}
}
this.editorModel = data ? new PlanEditorModel().fromModel(data) : new PlanEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
if (data) {
if (data.id) this.getAvailableStatuses(data.id);
if (data.descriptions) {
if (data.descriptions && !this.isDeleted) {
if (data.status?.internalStatus == PlanStatusEnum.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus === DescriptionStatusEnum.Finalized);
} else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus !== DescriptionStatusEnum.Canceled);
}
}
if (data.planDescriptionTemplates) {
if (data.planDescriptionTemplates && !this.isDeleted) {
data.planDescriptionTemplates = data.planDescriptionTemplates.filter(x => x.isActive === IsActive.Active);
}
if (data.entityDois && data.entityDois.length > 0) data.entityDois = data.entityDois.filter(x => x.isActive === IsActive.Active);
@ -290,7 +291,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.item = data;
this.selectedBlueprint = data?.blueprint;
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
if (data && data.id) {
const descriptionSectionPermissionResolverModel: DescriptionSectionPermissionResolver = {
@ -715,6 +715,9 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
//
//
public descriptionsInSection(sectionId: Guid) {
if (this.isDeleted) {
return this.item?.descriptions?.filter(x => x?.planDescriptionTemplate?.sectionId === sectionId) || [];
}
return this.item?.descriptions?.filter(x => x.isActive == IsActive.Active && x?.planDescriptionTemplate?.sectionId === sectionId && x.planDescriptionTemplate.isActive == IsActive.Active) || [];
}

View File

@ -48,7 +48,12 @@ export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
item?.blueprint?.definition?.sections?.forEach(section => {
if (section.hasTemplates) {
const isNew = (item.id == null);
const sectionTemplatesFromPlan = item.planDescriptionTemplates?.filter(x => x.sectionId == section.id && x.isActive == IsActive.Active) || [];
let sectionTemplatesFromPlan: PlanDescriptionTemplate[];
if (item.isActive != IsActive.Active) {
sectionTemplatesFromPlan = item.planDescriptionTemplates?.filter(x => x.sectionId == section.id) || [];
} else {
sectionTemplatesFromPlan = item.planDescriptionTemplates?.filter(x => x.sectionId == section.id && x.isActive == IsActive.Active) || [];
}
if (sectionTemplatesFromPlan.length > 0) {
sectionTemplatesFromPlan?.filter(x => x.sectionId == section.id).forEach(planDescriptionTemplate => {

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -776,6 +776,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1074,6 +1075,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"

View File

@ -778,6 +778,7 @@
"DRAFT": "Draft"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"
@ -1076,6 +1077,7 @@
"CANCELED": "Canceled"
}
},
"IS-ACTIVE": "Active Filter",
"RELATED-TENANT": {
"NAME": "Related Tenant",
"FILTER-BY-TENANT": "Filter by tenant"