add description status model, listing, editor, rename enum descriptionStatus to descriptionStatusEnum, small changes to plan status

This commit is contained in:
mchouliara 2024-08-23 15:55:45 +03:00
parent 318e269b95
commit 0cd47ede1a
52 changed files with 1314 additions and 100 deletions

View File

@ -406,7 +406,22 @@ const appRoutes: Routes = [
breadcrumb: true,
...BreadcrumbService.generateRouteDataConfiguration({
title: 'GENERAL.TITLES.PLAN-STATUSES'
})
}),
title: 'GENERAL.TITLES.PLAN-STATUSES'
}
},
{
path: 'description-statuses',
loadChildren: () => import('./ui/admin/description-status/description-status.module').then(m => m.DescriptionStatusModule),
data: {
authContext: {
permissions: [AppPermission.ViewDescriptionStatusPage]
},
breadcrumb: true,
...BreadcrumbService.generateRouteDataConfiguration({
title: 'GENERAL.TITLES.DESCRIPTION-STATUSES'
}),
title: 'GENERAL.TITLES.DESCRIPTION-STATUSES'
}
},
{

View File

@ -1,4 +1,4 @@
export enum DescriptionStatus {
export enum DescriptionStatusEnum {
Draft = 0,
Finalized = 1,
Canceled = 2

View File

@ -90,6 +90,10 @@ export enum AppPermission {
EditPlanStatus = "EditPlanStatus",
DeletePlanStatus = "DeletePlanStatus",
//DescriptionStatus
EditDescriptionStatus = "EditDescriptionStatus",
DeleteDescriptionStatus = "DeleteDescriptionStatus",
//PlanBlueprint
BrowsePlanBlueprint = "BrowsePlanBlueprint",
EditPlanBlueprint = "EditPlanBlueprint",
@ -239,6 +243,7 @@ export enum AppPermission {
ViewTenantConfigurationPage = "ViewTenantConfigurationPage",
ViewStatusPage = "ViewStatusPage",
ViewUsageLimitPage = "ViewUsageLimitPage",
ViewPlanStatusPage = "ViewPlanStatusPage"
ViewPlanStatusPage = "ViewPlanStatusPage",
ViewDescriptionStatusPage = "ViewDescriptionStatusPage",
}

View File

@ -49,6 +49,7 @@ import { TenantHandlingService } from './services/tenant/tenant-handling.service
import { RouterUtilsService } from './services/router/router-utils.service';
import { UsageLimitService } from './services/usage-limit/usage.service';
import { PlanStatusService } from './services/plan/plan-status.service';
import { DescriptionStatusService } from './services/description-status/description-status.service';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -117,7 +118,8 @@ export class CoreServiceModule {
TenantHandlingService,
RouterUtilsService,
UsageLimitService,
PlanStatusService
PlanStatusService,
DescriptionStatusService
],
};
}

View File

@ -0,0 +1,10 @@
import { DescriptionStatusEnum } from "@app/core/common/enum/description-status";
import { BaseEntityPersist } from "@common/base/base-entity.model";
import { DescriptionStatusDefinition } from "./description-status";
export interface DescriptionStatusPersist extends BaseEntityPersist {
name: string;
description: string;
internalStatus: DescriptionStatusEnum;
definition: DescriptionStatusDefinition;
}

View File

@ -0,0 +1,26 @@
import { AppRole } from "@app/core/common/enum/app-role";
import { DescriptionStatusEnum } from "@app/core/common/enum/description-status";
import { PlanUserRole } from "@app/core/common/enum/plan-user-role";
import { BaseEntity } from "@common/base/base-entity.model";
export interface DescriptionStatus extends BaseEntity{
name: string;
description: string;
internalStatus: DescriptionStatusEnum;
definition: DescriptionStatusDefinition;
}
export interface DescriptionStatusDefinition {
authorization: DescriptionStatusDefinitionAuthorization
}
export interface DescriptionStatusDefinitionAuthorization {
edit: DescriptionStatusDefinitionAuthorizationItem;
}
export interface DescriptionStatusDefinitionAuthorizationItem{
roles: AppRole[],
planRoles: PlanUserRole[],
allowAuthenticated: boolean;
allowAnonymous: boolean
}

View File

@ -1,4 +1,4 @@
import { DescriptionStatus } from "@app/core/common/enum/description-status";
import { DescriptionStatusEnum } from "@app/core/common/enum/description-status";
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
import { Guid } from "@common/types/guid";
import { DescriptionTemplate } from "../description-template/description-template";
@ -76,7 +76,7 @@ export interface DescriptionPersist extends BaseEntityPersist {
planId: Guid;
planDescriptionTemplateId: Guid;
descriptionTemplateId: Guid;
status: DescriptionStatus;
status: DescriptionStatusEnum;
description: string;
properties: DescriptionPropertyDefinitionPersist;
tags: string[];
@ -120,7 +120,7 @@ export interface DescriptionReferencePersist {
export interface DescriptionStatusPersist {
id: Guid;
status: DescriptionStatus;
status: DescriptionStatusEnum;
hash: string;
}
@ -130,7 +130,7 @@ export interface DescriptionStatusPersist {
export interface PublicDescription extends BaseDescription {
label?: string;
status?: DescriptionStatus;
status?: DescriptionStatusEnum;
description?: string;
finalizedAt?: Date;
descriptionTemplate?: PublicDescriptionTemplate;
@ -161,5 +161,5 @@ export interface UpdateDescriptionTemplatePersist {
export interface BaseDescription extends BaseEntity {
tenantId?: Guid;
status?: DescriptionStatus;
status?: DescriptionStatusEnum;
}

View File

@ -0,0 +1,21 @@
import { Lookup } from "@common/model/lookup";
import { Guid } from "@common/types/guid";
import { IsActive } from "../common/enum/is-active.enum";
export class DescriptionStatusLookup extends Lookup implements DescriptionStatusFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
constructor() {
super();
}
}
export interface DescriptionStatusFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
}

View File

@ -1,7 +1,7 @@
import { Lookup } from '@common/model/lookup';
import { Guid } from '@common/types/guid';
import { IsActive } from '../common/enum/is-active.enum';
import { DescriptionStatus } from '../common/enum/description-status';
import { DescriptionStatusEnum } from '../common/enum/description-status';
import { PlanLookup } from './plan.lookup';
import { DescriptionReferenceLookup } from './reference.lookup';
import { DescriptionTagLookup } from './tag.lookup';
@ -17,7 +17,7 @@ export class DescriptionLookup extends Lookup implements DescriptionFilter {
finalizedAfter: Date;
finalizedBefore: Date;
isActive: IsActive[];
statuses: DescriptionStatus[];
statuses: DescriptionStatusEnum[];
planSubQuery: PlanLookup;
tenantSubQuery: TenantLookup;
@ -39,7 +39,7 @@ export interface DescriptionFilter {
finalizedAfter: Date;
finalizedBefore: Date;
isActive: IsActive[];
statuses: DescriptionStatus[];
statuses: DescriptionStatusEnum[];
planSubQuery: PlanLookup;
descriptionTemplateSubQuery: DescriptionTemplateLookup;

View File

@ -0,0 +1,53 @@
import { HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { BaseHttpV2Service } from "../http/base-http-v2.service";
import { ConfigurationService } from "../configuration/configuration.service";
import { QueryResult } from "@common/model/query-result";
import { catchError, Observable, throwError } from "rxjs";
import { Guid } from "@common/types/guid";
import { DescriptionStatusLookup } from "@app/core/query/description-status.lookup";
import { DescriptionStatus } from "@app/core/model/description-status/description-status";
import { DescriptionStatusPersist } from "@app/core/model/description/description";
@Injectable()
export class DescriptionStatusService {
private headers = new HttpHeaders();
constructor(
private http: BaseHttpV2Service,
private configurationService: ConfigurationService,
) {
}
private get apiBase(): string { return `${this.configurationService.server}description-status`; }
query(q: DescriptionStatusLookup): Observable<QueryResult<DescriptionStatus>> {
const url = `${this.apiBase}/query`;
return this.http.post<QueryResult<DescriptionStatus>>(url, q).pipe(catchError((error: any) => throwError(() => error)));
}
getSingle(id: Guid, reqFields: string[] = []): Observable<DescriptionStatus> {
const url = `${this.apiBase}/${id}`;
const options = { params: { f: reqFields } };
return this.http
.get<DescriptionStatus>(url, options).pipe(
catchError((error: any) => throwError(() => error)));
}
persist(item: DescriptionStatusPersist): Observable<DescriptionStatus> {
const url = `${this.apiBase}/persist`;
return this.http
.post<DescriptionStatus>(url, item).pipe(
catchError((error: any) => throwError(() => error)));
}
delete(id: Guid): Observable<DescriptionStatus> {
const url = `${this.apiBase}/${id}`;
return this.http
.delete<DescriptionStatus>(url).pipe(
catchError((error: any) => throwError(() => error)));
}
}

View File

@ -47,6 +47,8 @@ export class AnalyticsService {
public static NotificationTempplateEditor: string = 'Admin: Notification Tempplates';
public static PlanStatusListing: string = 'Plan Status Listing';
public static PlanStatusEditor: string = 'Plan Status Editor';
public static DescriptionStatusListing: string = 'Description Status Listing';
public static DescriptionStatusEditor: string = 'Description Status Editor';
//#endregion
//#region trackDownload

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { AnnotationProtectionType } from '@app/core/common/enum/annotation-protection-type.enum';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { DescriptionTemplateExternalSelectAuthType } from '@app/core/common/enum/description-template-external-select-auth-type';
import { DescriptionTemplateExternalSelectHttpMethodType } from '@app/core/common/enum/description-template-external-select-http-method-type';
import { DescriptionTemplateFieldDataExternalDatasetType } from '@app/core/common/enum/description-template-field-data-external-dataset-type';
@ -210,11 +210,11 @@ export class EnumUtils {
}
}
toDescriptionStatusString(status: DescriptionStatus): string {
toDescriptionStatusString(status: DescriptionStatusEnum): string {
switch (status) {
case DescriptionStatus.Draft: return this.language.instant('TYPES.DESCRIPTION-STATUS.DRAFT');
case DescriptionStatus.Finalized: return this.language.instant('TYPES.DESCRIPTION-STATUS.FINALIZED');
case DescriptionStatus.Canceled: return this.language.instant('TYPES.DESCRIPTION-STATUS.CANCELED');
case DescriptionStatusEnum.Draft: return this.language.instant('TYPES.DESCRIPTION-STATUS.DRAFT');
case DescriptionStatusEnum.Finalized: return this.language.instant('TYPES.DESCRIPTION-STATUS.FINALIZED');
case DescriptionStatusEnum.Canceled: return this.language.instant('TYPES.DESCRIPTION-STATUS.CANCELED');
}
}

View File

@ -0,0 +1,33 @@
import { NgModule } from "@angular/core";
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { CommonFormattingModule } from "@common/formatting/common-formatting.module";
import { ConfirmationDialogModule } from "@common/modules/confirmation-dialog/confirmation-dialog.module";
import { HybridListingModule } from "@common/modules/hybrid-listing/hybrid-listing.module";
import { UserSettingsModule } from "@common/modules/user-settings/user-settings.module";
import { TextFilterModule } from "@common/modules/text-filter/text-filter.module";
import { RichTextEditorModule } from "@app/library/rich-text-editor/rich-text-editor.module";
import { DescriptionStatusRoutingModule } from "./description-status.routing";
import { DescriptionStatusListingComponent } from "./listing/description-status-listing/description-status-listing.component";
import { DescriptionStatusEditorComponent } from "./editor/description-status-editor/description-status-editor.component";
import { DescriptionStatusListingFiltersComponent } from "./listing/description-status-listing-filters/description-status-listing-filters.component";
@NgModule({
imports: [
CommonUiModule,
CommonFormsModule,
DescriptionStatusRoutingModule,
ConfirmationDialogModule,
HybridListingModule,
UserSettingsModule,
CommonFormattingModule,
TextFilterModule,
RichTextEditorModule
],
declarations: [
DescriptionStatusListingComponent,
DescriptionStatusListingFiltersComponent,
DescriptionStatusEditorComponent,
]
})
export class DescriptionStatusModule { }

View File

@ -0,0 +1,64 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { AuthGuard } from '@app/core/auth-guard.service';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service';
import { DescriptionStatusEditorComponent } from './editor/description-status-editor/description-status-editor.component';
import { DescriptionStatusListingComponent } from './listing/description-status-listing/description-status-listing.component';
import { DescriptionStatusEditorResolver } from './editor/description-status-editor/description-status-editor.resolver';
const routes: Routes = [
{
path: '',
component: DescriptionStatusListingComponent,
canActivate: [AuthGuard],
data: {
breadcrumb: true,
...BreadcrumbService.generateRouteDataConfiguration({
hideNavigationItem: true
}),
}
},
{
path: 'new',
canActivate: [AuthGuard],
component: DescriptionStatusEditorComponent,
canDeactivate: [PendingChangesGuard],
data: {
authContext: {
permissions: [AppPermission.EditDescriptionStatus]
},
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.NEW-LANGUAGE'
}),
getFromTitleService: true,
usePrefix: false
}
},
{
path: ':id',
canActivate: [AuthGuard],
component: DescriptionStatusEditorComponent,
canDeactivate: [PendingChangesGuard],
resolve: {
'entity': DescriptionStatusEditorResolver
},
data: {
authContext: {
permissions: [AppPermission.EditDescriptionStatus]
},
getFromTitleService: true,
usePrefix: false
}
},
{ path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [DescriptionStatusEditorResolver]
})
export class DescriptionStatusRoutingModule { }

View File

@ -0,0 +1,109 @@
<div class="container-fluid">
<div class="row">
<div class="col-md-10 offset-md-1 colums-gapped">
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
<div class="col-md col-12">
<app-navigation-breadcrumb />
</div>
<div class="col-auto">
<button mat-button class="action-btn cancel-btn" (click)="cancel()" type="button">{{'PLAN-STATUS-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col-auto" *ngIf="canDelete">
<button mat-button class="action-btn delete-btn" type="button" (click)="delete()">
<mat-icon>delete</mat-icon>
{{'PLAN-STATUS-EDITOR.ACTIONS.DELETE' | translate}}
</button>
</div>
<div class="col-auto" *ngIf="canSave">
<button mat-button class="action-btn save-btn" (click)="formSubmit()">
<mat-icon>save</mat-icon>
{{'PLAN-STATUS-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
<mat-card class="pt-3 pb-3" appearance="outlined">
<mat-card-content>
<div class="row">
<div class="col-12">
<mat-form-field class="w-100">
<mat-label>{{'PLAN-STATUS-EDITOR.FIELDS.NAME' | translate}}</mat-label>
<input matInput type="text" name="name" [formControl]="formGroup.controls.name" required>
<mat-error *ngIf="formGroup.controls.name.hasError('backendError')">{{formGroup.controls.name.getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.controls.name.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-12">
<mat-form-field class="w-100">
<mat-label>{{'PLAN-STATUS-EDITOR.FIELDS.INTERNAL-STATUS' | translate}}*</mat-label>
<mat-select [formControl]="formGroup.controls.internalStatus">
<mat-option *ngFor="let internalStatus of internalStatusEnum" [value]="internalStatus">{{enumUtils.toDescriptionStatusString(internalStatus)}}</mat-option>
</mat-select>
<mat-error *ngIf="formGroup.controls.internalStatus.hasError('backendError')">{{formGroup.controls.internalStatus.getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.controls.internalStatus.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-12">
<h3>{{'PLAN-STATUS-EDITOR.FIELDS.DESCRIPTION' | translate}}</h3>
<div class="col-12">
<rich-text-editor-component
[form]="formGroup.controls.description"
[placeholder]="'PLAN-STATUS-EDITOR.FIELDS.DESCRIPTION'"
[editable]="!formGroup.controls.description.disabled"
[wrapperClasses]="(formGroup.controls.description.touched && formGroup.controls.description.hasError('backendError')) ? 'required' : ''"
/>
@if(formGroup.controls.description.hasError('backendError')){
<div class="mat-form-field formGroup-field-subscript-wrapper">
<mat-error>{{formGroup.controls.description.getError('backendError').message}}</mat-error>
</div>
}
</div>
</div>
<div class="col-12">
<h3>
{{'PLAN-STATUS-EDITOR.FIELDS.AUTHORIZATION' | translate}}
</h3>
<div id="Edit">
<mat-card-title class="pb-2">{{'PLAN-STATUS-EDITOR.FIELDS.EDIT' | translate}}</mat-card-title>
<div class="col-12">
<mat-form-field class="w-100">
<mat-label>{{'PLAN-STATUS-EDITOR.FIELDS.ROLES' | translate}}*</mat-label>
<mat-select [formControl]="editAuthenticationForm.controls.roles" [multiple]="true">
<mat-option *ngFor="let userRole of userRolesEnum" [value]="userRole">{{enumUtils.toAppRoleString(userRole)}}</mat-option>
</mat-select>
<mat-error *ngIf="editAuthenticationForm.controls.roles.hasError('backendError')">{{"editAuthenticationForm.controls.roles.getError('backendError').message}}</mat-error>
<mat-error *ngIf="editAuthenticationForm.controls.roles.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-12">
<mat-form-field class="w-100">
<mat-label>{{'PLAN-STATUS-EDITOR.FIELDS.PLAN-ROLES' | translate}}*</mat-label>
<mat-select [formControl]="editAuthenticationForm.controls.planRoles" [multiple]="true">
<mat-option *ngFor="let planRole of planRolesEnum" [value]="planRole">{{enumUtils.toPlanUserRoleString(planRole)}}</mat-option>
</mat-select>
<mat-error *ngIf="editAuthenticationForm.controls.planRoles.hasError('backendError')">{{"editAuthenticationForm.controls.planRoles.getError('backendError').message}}</mat-error>
<mat-error *ngIf="editAuthenticationForm.controls.planRoles.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="d-flex" style="gap: 0.5rem">
<div>
<mat-checkbox [formControl]="editAuthenticationForm.controls.allowAuthenticated">{{'PLAN-STATUS-EDITOR.FIELDS.ALLOW-AUTHENTICATED' | translate}}</mat-checkbox>
<mat-error *ngIf="editAuthenticationForm.controls.allowAuthenticated.hasError('backendError')">{{editAuthenticationForm.controls.allowAuthenticated.getError('backendError').message}}</mat-error>
</div>
<div>
<mat-checkbox [formControl]="editAuthenticationForm.controls.allowAnonymous">{{'PLAN-STATUS-EDITOR.FIELDS.ALLOW-ANONYMOUS' | translate}}</mat-checkbox>
<mat-error *ngIf="editAuthenticationForm.controls.allowAnonymous.hasError('backendError')">{{editAuthenticationForm.controls.allowAnonymous.getError('backendError').message}}</mat-error>
</div>
</div>
</div>
</div>
</div>
</mat-card-content>
</mat-card>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,33 @@
::ng-deep label {
margin: 0;
}
.action-btn {
border-radius: 30px;
background-color: var(--secondary-color);
border: 1px solid transparent;
padding-left: 2em;
padding-right: 2em;
box-shadow: 0px 3px 6px #1E202029;
transition-property: background-color, color;
transition-duration: 200ms;
transition-delay: 50ms;
transition-timing-function: ease-in-out;
&:disabled{
background-color: #CBCBCB;
color: #FFF;
border: 0px;
}
}
.cancel-btn {
background: #ffffff 0% 0% no-repeat padding-box;
border: 1px solid #b5b5b5;
}
.delete-btn {
background: #ffffff;
color: #ba2c2c;
border: 1px solid #ba2c2c;
}

View File

@ -0,0 +1,169 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Router, ActivatedRoute } from '@angular/router';
import { AppRole } from '@app/core/common/enum/app-role';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { DescriptionStatusPersist } from '@app/core/model/description/description';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
import { map, takeUntil } from 'rxjs';
import { DescriptionStatusForm, DescriptionStatusDefinitionAuthorizationItemForm, DescriptionStatusEditorModel } from './description-status-editor.model';
import { DescriptionStatusEditorResolver } from './description-status-editor.resolver';
import { BaseEditor } from '@common/base/base-editor';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
@Component({
selector: 'app-description-status-editor',
templateUrl: './description-status-editor.component.html',
styleUrl: './description-status-editor.component.scss'
})
export class DescriptionStatusEditorComponent extends BaseEditor<DescriptionStatusEditorModel, DescriptionStatus> implements OnInit{
protected internalStatusEnum = this.enumUtils.getEnumValues<DescriptionStatusEnum>(DescriptionStatusEnum);
protected userRolesEnum = this.enumUtils.getEnumValues<AppRole>(AppRole);
protected planRolesEnum = this.enumUtils.getEnumValues<PlanUserRole>(PlanUserRole);
protected belongsToCurrentTenant: boolean;
constructor(
protected enumUtils: EnumUtils,
protected dialog: MatDialog,
protected language: TranslateService,
protected formService: FormService,
protected router: Router,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected filterService: FilterService,
protected route: ActivatedRoute,
protected queryParamsService: QueryParamsService,
protected lockService: LockService,
protected authService: AuthService,
protected configurationService: ConfigurationService,
private analyticsService: AnalyticsService,
private descriptionStatusService: DescriptionStatusService,
private logger: LoggingService,
private routerUtils: RouterUtilsService,
){
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, route, queryParamsService, lockService, authService, configurationService);
}
formGroup: FormGroup<DescriptionStatusForm>;
ngOnInit(){
this.analyticsService.trackPageView(AnalyticsService.DescriptionStatusEditor);
super.ngOnInit();
}
get editAuthenticationForm(): FormGroup<DescriptionStatusDefinitionAuthorizationItemForm> {
return this.formGroup?.controls?.definition?.controls?.authorization?.controls?.edit;
}
getItem(itemId: Guid, successFunction: (item: DescriptionStatus) => void): void {
this.descriptionStatusService.getSingle(itemId, DescriptionStatusEditorResolver.lookupFields())
.pipe(map(data => data as DescriptionStatus), takeUntil(this._destroyed))
.subscribe({
next: (data) => successFunction(data),
error: (error) => this.onCallbackError(error)
});
}
prepareForm(data: DescriptionStatus): void {
try {
this.editorModel = data ? new DescriptionStatusEditorModel().fromModel(data) : new DescriptionStatusEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.belongsToCurrentTenant = data?.belongsToCurrentTenant;
this.buildForm();
} catch (error) {
this.logger.error('Could not parse descriptionStatus item: ' + data + error);
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
}
}
buildForm(): void {
this.formGroup = this.editorModel.buildForm({disabled: !this.belongsToCurrentTenant || this.isDeleted || !this.authService.hasPermission(AppPermission.EditDescriptionStatus)});
}
formSubmit(): void {
this.formService.removeAllBackEndErrors(this.formGroup);
this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) {
return;
}
this.persistEntity();
}
delete(): void {
const value = this.formGroup.value;
if (value.id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.descriptionStatusService.delete(value.id).pipe(takeUntil(this._destroyed))
.subscribe({
complete: () => this.onCallbackDeleteSuccess(),
error: (error) => this.onCallbackError(error)
});
}
});
}
}
refreshData(): void {
this.getItem(this.editorModel.id, (data: DescriptionStatus) => this.prepareForm(data));
}
refreshOnNavigateToData(id?: Guid): void {
this.formGroup.markAsPristine();
if (this.isNew) {
let route = [];
route.push(this.routerUtils.generateUrl('/description-statuses/' + id));
this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route });
} else {
this.refreshData();
}
}
persistEntity(onSuccess?: (response) => void): void {
const formData = this.formGroup.value as DescriptionStatusPersist;
this.descriptionStatusService.persist(formData)
.pipe(takeUntil(this._destroyed)).subscribe({
next: (complete) => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error: (error) => this.onCallbackError(error)
});
}
protected get canSave(): boolean {
const editDescriptionStatus = this.authService.permissionEnum.EditDescriptionStatus;
return (this.isNew || (this.belongsToCurrentTenant && !this.isDeleted)) && this.authService.hasPermission(editDescriptionStatus);
}
protected get canDelete(): boolean {
const deletedDescriptionStatus = this.authService.permissionEnum.DeleteDescriptionStatus;
return this.belongsToCurrentTenant && !this.isNew && !this.isDeleted && this.authService.hasPermission(deletedDescriptionStatus);
}
}

View File

@ -0,0 +1,116 @@
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { AppRole } from "@app/core/common/enum/app-role";
import { DescriptionStatusEnum } from "@app/core/common/enum/description-status";
import { PlanUserRole } from "@app/core/common/enum/plan-user-role";
import { DescriptionStatus, DescriptionStatusDefinition, DescriptionStatusDefinitionAuthorizationItem } from "@app/core/model/description-status/description-status";
import { DescriptionStatusPersist } from "@app/core/model/description-status/description-status-persist";
import { BaseEditorModel } from "@common/base/base-form-editor-model";
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
import { Guid } from "@common/types/guid";
export class DescriptionStatusEditorModel extends BaseEditorModel implements DescriptionStatusPersist {
name: string;
description: string;
internalStatus: DescriptionStatusEnum;
definition: DescriptionStatusDefinition;
public fromModel(item: DescriptionStatus): DescriptionStatusEditorModel {
if (item) {
super.fromModel(item);
this.name = item?.name;
this.description = item?.description;
this.internalStatus = item?.internalStatus;
this.definition = item?.definition;
}
return this;
}
buildForm(params: {context?: ValidationContext, disabled?: boolean}): FormGroup<DescriptionStatusForm> {
const {context = this.createValidationContext(), disabled = false} = params;
return this.formBuilder.group({
id: [{ value: this.id, disabled }, context.getValidation('id').validators],
name: [{value: this.name, disabled}, context.getValidation('name').validators],
description: [{value: this.description, disabled}, context.getValidation('description').validators],
internalStatus: [{value: this.internalStatus, disabled}, context.getValidation('internalStatus').validators],
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators],
definition: this.buildDefinitionForm({context, disabled}),
});
}
buildDefinitionForm(params: {context: ValidationContext, disabled: boolean}): FormGroup<DescriptionStatusDefinitionForm> {
const {context = this.createValidationContext(), disabled} = params;
const definitionForm = new FormGroup<DescriptionStatusDefinitionForm>({
authorization: new FormGroup<DescriptionStatusDefinitionAuthorizationForm>({
edit: this.buildDefinitionAuthorizationItemForm({item: this.definition?.authorization?.edit, rootPath: 'edit', disabled})
})
});
definitionForm.controls.authorization.addValidators(context.getValidation('authorization').validators);
definitionForm.addValidators(context.getValidation('definition').validators);
return definitionForm;
}
buildDefinitionAuthorizationItemForm(params: {item: DescriptionStatusDefinitionAuthorizationItem, rootPath: string, disabled: boolean}): FormGroup<DescriptionStatusDefinitionAuthorizationItemForm>{
const {item, rootPath, disabled} = params;
const context = this.createAuthorizationItemContext(rootPath);
return new FormGroup<DescriptionStatusDefinitionAuthorizationItemForm>({
allowAnonymous: new FormControl({value: item?.allowAnonymous ?? false, disabled}, context.getValidation('allowAnonymous').validators),
allowAuthenticated: new FormControl({value: item?.allowAuthenticated ?? false, disabled}, context.getValidation('allowAuthenticated').validators),
roles: new FormControl({value: item?.roles ?? [], disabled}, context.getValidation('roles').validators),
planRoles: new FormControl({value: item?.planRoles ?? [], disabled}, context.getValidation('planRoles').validators),
})
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'name')] });
baseValidationArray.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] });
baseValidationArray.push({ key: 'internalStatus', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'internalStatus')] });
baseValidationArray.push({ key: 'hash', validators: [] });
baseValidationArray.push({ key: 'definition', validators: [BackendErrorValidator(this.validationErrorModel, 'definition')] });
baseValidationArray.push({ key: 'authorization', validators: [BackendErrorValidator(this.validationErrorModel, 'definition.authorization')] });
baseValidationArray.push({ key: 'edit', validators: [BackendErrorValidator(this.validationErrorModel, 'definition.authorization.edit')] });
baseContext.validation = baseValidationArray;
return baseContext;
}
createAuthorizationItemContext(rootPath?: string): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'allowAnonymous', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, `${rootPath}allowAnonymous`)] });
baseValidationArray.push({ key: 'allowAuthenticated', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, `${rootPath}allowAuthenticated`)] });
baseValidationArray.push({ key: 'planRoles', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, `${rootPath}planRoles`)] });
baseValidationArray.push({ key: 'roles', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, `${rootPath}roles`)] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}
export interface DescriptionStatusForm {
id: FormControl<Guid>;
name: FormControl<string>;
description: FormControl<string>;
internalStatus: FormControl<DescriptionStatusEnum>;
definition: FormGroup<DescriptionStatusDefinitionForm>;
}
export interface DescriptionStatusDefinitionForm {
authorization: FormGroup<DescriptionStatusDefinitionAuthorizationForm>
}
export interface DescriptionStatusDefinitionAuthorizationForm {
edit: FormGroup<DescriptionStatusDefinitionAuthorizationItemForm>;
}
export interface DescriptionStatusDefinitionAuthorizationItemForm {
roles: FormControl<AppRole[]>;
planRoles: FormControl<PlanUserRole[]>;
allowAuthenticated: FormControl<boolean>;
allowAnonymous: FormControl<boolean>;
}

View File

@ -0,0 +1,49 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { DescriptionStatus, DescriptionStatusDefinition, DescriptionStatusDefinitionAuthorization, DescriptionStatusDefinitionAuthorizationItem } from "@app/core/model/description-status/description-status";
import { DescriptionStatusService } from "@app/core/services/description-status/description-status.service";
import { BreadcrumbService } from "@app/ui/misc/breadcrumb/breadcrumb.service";
import { BaseEditorResolver } from "@common/base/base-editor.resolver";
import { Guid } from "@common/types/guid";
import { takeUntil, tap } from "rxjs";
import { nameof } from "ts-simple-nameof";
@Injectable()
export class DescriptionStatusEditorResolver extends BaseEditorResolver{
constructor(private descriptionStatusService: DescriptionStatusService, private breadcrumbService: BreadcrumbService) {
super();
}
public static lookupFields(): string[] {
return [
nameof<DescriptionStatus>(x => x.id),
nameof<DescriptionStatus>(x => x.name),
nameof<DescriptionStatus>(x => x.description),
nameof<DescriptionStatus>(x => x.internalStatus),
nameof<DescriptionStatus>(x => x.definition),
[nameof<DescriptionStatus>(x => x.definition), nameof<DescriptionStatusDefinition>(x => x.authorization)].join('.'),
[nameof<DescriptionStatus>(x => x.definition), nameof<DescriptionStatusDefinition>(x => x.authorization), nameof<DescriptionStatusDefinitionAuthorization>(x => x.edit), nameof<DescriptionStatusDefinitionAuthorizationItem>(x => x.roles)].join('.'),
[nameof<DescriptionStatus>(x => x.definition), nameof<DescriptionStatusDefinition>(x => x.authorization), nameof<DescriptionStatusDefinitionAuthorization>(x => x.edit), nameof<DescriptionStatusDefinitionAuthorizationItem>(x => x.planRoles)].join('.'),
[nameof<DescriptionStatus>(x => x.definition), nameof<DescriptionStatusDefinition>(x => x.authorization), nameof<DescriptionStatusDefinitionAuthorization>(x => x.edit), nameof<DescriptionStatusDefinitionAuthorizationItem>(x => x.allowAnonymous)].join('.'),
[nameof<DescriptionStatus>(x => x.definition), nameof<DescriptionStatusDefinition>(x => x.authorization), nameof<DescriptionStatusDefinitionAuthorization>(x => x.edit), nameof<DescriptionStatusDefinitionAuthorizationItem>(x => x.allowAuthenticated)].join('.'),
nameof<DescriptionStatus>(x => x.updatedAt),
nameof<DescriptionStatus>(x => x.createdAt),
nameof<DescriptionStatus>(x => x.hash),
nameof<DescriptionStatus>(x => x.isActive),
nameof<DescriptionStatus>(x => x.belongsToCurrentTenant),
]
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const fields = [
...DescriptionStatusEditorResolver.lookupFields()
];
const id = route.paramMap.get('id');
if (id != null) {
return this.descriptionStatusService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.name)), takeUntil(this._destroyed));
}
}
}

View File

@ -0,0 +1,48 @@
<div class="d-flex align-items-center gap-1-rem">
<button mat-flat-button [matMenuTriggerFor]="filterMenu" #filterMenuTrigger="matMenuTrigger" (click)="updateFilters()" class="filter-button">
<mat-icon aria-hidden="false" [matBadgeHidden]="!appliedFilterCount" [matBadge]="appliedFilterCount" matBadgeColor="warn" matBadgeSize="small">filter_alt</mat-icon>
{{'COMMONS.LISTING-COMPONENT.SEARCH-FILTER-BTN' | translate}}
</button>
<mat-menu #filterMenu>
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
<div class="row justify-content-between">
<div class="col-auto mt-2">
<h4>{{'PLAN-BLUEPRINT-LISTING.FILTER.TITLE' | translate}}</h4>
</div>
<div class="col-auto">
<button color="accent" mat-button (click)="clearFilters()">
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
</button>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<section class="w-100">
<mat-slide-toggle [(ngModel)]="internalFilters.isActive" labelPosition="before">
{{'PLAN-BLUEPRINT-LISTING.FILTER.IS-ACTIVE' | translate}}</mat-slide-toggle>
</section>
</div>
</div>
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
<div class="col-auto">
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
{{'PLAN-BLUEPRINT-LISTING.FILTER.CANCEL' | translate}}
</button>
</div>
<div class="col-auto">
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
{{'PLAN-BLUEPRINT-LISTING.FILTER.APPLY-FILTERS' | translate}}
</button>
</div>
</div>
</div>
</mat-menu>
<app-expandable-search-field [(value)]=internalFilters.like (valueChange)="onSearchTermChange($event)" />
</div>

View File

@ -0,0 +1,22 @@
//Maybe move these to global listing filter styles?
::ng-deep.mat-mdc-menu-panel {
max-width: 100% !important;
height: 100% !important;
}
:host::ng-deep.mat-mdc-menu-content:not(:empty) {
padding-top: 0 !important;
}
.filter-button{
padding-top: .6rem;
padding-bottom: .6rem;
}
::ng-deep .mdc-form-field {
label {
margin: 0;
}
}

View File

@ -0,0 +1,88 @@
import { Component, effect, EventEmitter, input, Output } from '@angular/core';
import { DescriptionStatusFilter } from '@app/core/query/description-status.lookup';
import { BaseComponent } from '@common/base/base.component';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
@Component({
selector: 'app-description-status-listing-filters',
templateUrl: './description-status-listing-filters.component.html',
styleUrl: './description-status-listing-filters.component.scss'
})
export class DescriptionStatusListingFiltersComponent extends BaseComponent{
readonly filter = input<DescriptionStatusFilter>();
@Output() filterChange = new EventEmitter<DescriptionStatusFilter>();
internalFilters: DescriptionStatusListingFilters = this._getEmptyFilters();
appliedFilterCount: number = 0;
constructor(){
super();
effect(() => {
const newFilters = this.filter();
if(newFilters){
this.updateFilters();
}
})
}
private _parseToInternalFilters(inputFilter: DescriptionStatusFilter): DescriptionStatusListingFilters {
if (!inputFilter) {
return this._getEmptyFilters();
}
let { isActive, like } = inputFilter;
return {
isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length,
like: like,
}
}
private _computeAppliedFilters(filters: DescriptionStatusListingFilters): number {
let count = 0;
if (!filters?.isActive) {
count++
}
if(filters?.like){
count++;
}
return count;
}
private _getEmptyFilters(): DescriptionStatusListingFilters {
return {
isActive: true,
like: null,
}
}
protected updateFilters(): void {
this.internalFilters = this._parseToInternalFilters(this.filter());
this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters);
}
protected applyFilters(): void {
const { isActive, like } = this.internalFilters ?? {}
this.filterChange.emit({
...this.filter(),
like,
isActive: isActive ? [IsActive.Active] : [IsActive.Inactive]
})
}
protected onSearchTermChange(searchTerm: string): void {
this.applyFilters();
}
protected clearFilters() {
this.internalFilters = this._getEmptyFilters();
}
}
interface DescriptionStatusListingFilters {
isActive: boolean;
like: string;
}

View File

@ -0,0 +1,67 @@
<div class="container-fluid">
<div class="row description-status-listing">
<div class="col-md-10 offset-md-1">
<div class="row mb-4 mt-4">
<div class="col">
<app-navigation-breadcrumb />
</div>
<div class="col-auto">
<button mat-raised-button class="create-btn" *ngIf="authService.hasPermission(authService.permissionEnum.EditDescriptionStatus)" [routerLink]="routerUtils.generateUrl(['/description-statuses/new'])">
<mat-icon>add</mat-icon>
{{'PLAN-STATUS-LISTING.ACTIONS.CREATE-PLAN-STATUS' | translate}}
</button>
</div>
</div>
<app-hybrid-listing
[rows]="gridRows"
[columns]="gridColumns"
[visibleColumns]="visibleColumns"
[count]="totalElements"
[offset]="currentPageNumber"
[limit]="lookup.page.size"
[defaultSort]="lookup.order?.items"
[externalSorting]="true"
(rowActivated)="onRowActivated($event, routerUtils.generateUrl('/description-statuses'))"
(pageLoad)="alterPage($event)"
(columnSort)="onColumnSort($event)"
(columnsChanged)="onColumnsChanged($event)"
>
<app-description-status-listing-filters hybrid-listing-filters [(filter)]="lookup" (filterChange)="filterChanged($event)" />
<app-user-settings-picker [key]="userSettingsKey" [userPreference]="lookup" (onSettingSelected)="changeSetting($event)" [autoSelectUserSettings]="autoSelectUserSettings" user-preference-settings />
</app-hybrid-listing>
</div>
</div>
</div>
<ng-template #status let-row="row" let-item>
<div
class="status-chip"
[class.status-chip-finalized]="row.internalStatus === DescriptionStatusEnum.Finalized"
[class.status-chip-draft]="row.internalStatus === DescriptionStatusEnum.Draft"
[class.status-chip-cancelled]="row.internalStatus === DescriptionStatusEnum.Canceled"
>
{{enumUtils.toDescriptionStatusString(row.internalStatus)}}
</div>
</ng-template>
<ng-template #actions let-row="row" let-item>
<div class="row" (click)="$event.stopPropagation()">
<div class="col-auto" *ngIf="row?.belongsToCurrentTenant && !isDeleted(row)">
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
<mat-icon>more_horiz</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
<button *ngIf="canEdit" mat-menu-item [routerLink]="routerUtils.generateUrl(['/description-statuses/', row.id])">
<mat-icon>edit</mat-icon>{{'PLAN-STATUS-LISTING.ACTIONS.EDIT' | translate}}
</button>
<button *ngIf="canDelete" mat-menu-item (click)="delete(row.id)">
<mat-icon>delete</mat-icon>
{{'PLAN-STATUS-LISTING.ACTIONS.DELETE' | translate}}
</button>
</mat-menu>
</div>
</div>
</ng-template>

View File

@ -0,0 +1,34 @@
.create-btn {
border-radius: 30px;
background-color: var(--secondary-color);
padding-left: 2em;
padding-right: 2em;
.button-text {
display: inline-block;
}
}
.status-chip {
width: fit-content;
border-radius: 20px;
padding-left: 1em;
padding-right: 1em;
padding-top: 0.2em;
font-size: .8em;
}
.status-chip-finalized {
color: #568b5a;
background: #9dd1a1 0% 0% no-repeat padding-box;
}
.status-chip-draft {
color: #00c4ff;
background: #d3f5ff 0% 0% no-repeat padding-box;
}
.status-chip-cancelled {
color: #cf1407;
background: #ffc8c5 0% 0% no-repeat padding-box;
}

View File

@ -0,0 +1,190 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router, ActivatedRoute } from '@angular/router';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { UserSettingsKey } from '@app/core/model/user-settings/user-settings.model';
import { DataTableDateTimeFormatPipe } from '@app/core/pipes/date-time-format.pipe';
import { DescriptionStatusLookup } from '@app/core/query/description-status.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseListingComponent } from '@common/base/base-listing-component';
import { PipeService } from '@common/formatting/pipe.service';
import { IsActiveTypePipe } from '@common/formatting/pipes/is-active-type.pipe';
import { QueryResult } from '@common/model/query-result';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { ColumnDefinition, HybridListingComponent, ColumnsChangedEvent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { TranslateService } from '@ngx-translate/core';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
import { Observable, takeUntil } from 'rxjs';
import { nameof } from 'ts-simple-nameof';
import { DescriptionStatusEditorResolver } from '../../editor/description-status-editor/description-status-editor.resolver';
@Component({
selector: 'app-description-status-listing',
templateUrl: './description-status-listing.component.html',
styleUrl: './description-status-listing.component.scss'
})
export class DescriptionStatusListingComponent extends BaseListingComponent<DescriptionStatus, DescriptionStatusLookup> implements OnInit{
userSettingsKey: UserSettingsKey = {key: 'DescriptionStatusListingUserSettings'};
publish = false;
propertiesAvailableForOrder: ColumnDefinition[];
DescriptionStatusEnum = DescriptionStatusEnum;
@ViewChild('actions', { static: true }) actions: TemplateRef<any>;
@ViewChild('status', { static: true }) status: TemplateRef<any>;
@ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent;
constructor(
public routerUtils: RouterUtilsService,
public authService: AuthService,
protected router: Router,
protected route: ActivatedRoute,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected queryParamsService: QueryParamsService,
protected enumUtils: EnumUtils,
private pipeService: PipeService,
private descriptionStatusService: DescriptionStatusService,
private analyticsService: AnalyticsService,
private language: TranslateService,
private dialog: MatDialog
){
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
this.lookup = this.initializeLookup();
}
ngOnInit() {
super.ngOnInit();
this.analyticsService.trackPageView(AnalyticsService.DescriptionStatusListing);
}
private readonly lookupFields = DescriptionStatusEditorResolver.lookupFields();
protected initializeLookup(): DescriptionStatusLookup {
const lookup = new DescriptionStatusLookup();
lookup.metadata = { countAll: true };
lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE };
lookup.isActive = [IsActive.Active];
lookup.order = { items: [this.toDescSortField(nameof<DescriptionStatus>(x => x.createdAt))] };
this.updateOrderUiFields(lookup.order);
lookup.project = {
fields: this.lookupFields
};
return lookup;
}
onColumnsChanged(event: ColumnsChangedEvent) {
super.onColumnsChanged(event);
this.onColumnsChangedInternal(event.properties.map(x => x.toString()));
}
private onColumnsChangedInternal(columns: string[]) {
const fields = new Set(this.lookupFields);
this.gridColumns.map(x => x.prop)
.filter(x => !columns?.includes(x as string))
.forEach(item => {
fields.delete(item as string)
});
this.lookup.project = { fields: [...fields] };
this.onPageLoad({ offset: 0 } as PageLoadEvent);
}
protected loadListing(): Observable<QueryResult<DescriptionStatus>> {
return this.descriptionStatusService.query(this.lookup);
}
protected setupColumns() {
this.gridColumns.push(...[
{
prop: nameof<DescriptionStatus>(x => x.name),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.NAME'
},
{
prop: nameof<DescriptionStatus>(x => x.description),
languageName: 'PLAN-STATUS-LISTING.FIELDS.DESCRIPTION'
},
{
prop: nameof<DescriptionStatus>(x => x.internalStatus),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.STATUS',
cellTemplate: this.status
},
{
prop: nameof<DescriptionStatus>(x => x.createdAt),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.CREATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<DescriptionStatus>(x => x.updatedAt),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.UPDATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<DescriptionStatus>(x => x.isActive),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.IS-ACTIVE',
pipe: this.pipeService.getPipe<IsActiveTypePipe>(IsActiveTypePipe)
},
{
alwaysShown: true,
cellTemplate: this.actions,
maxWidth: 120
}
]);
this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable);
}
delete(id){
if (id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
data: {
isDeleteConfirmation: true,
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.descriptionStatusService.delete(id).pipe(takeUntil(this._destroyed))
.subscribe({
complete: () => this.onCallbackSuccess(),
error: (error) => this.onCallbackError(error)
}
);
}
});
}
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
this.refresh();
}
isDeleted(row: DescriptionStatus): boolean {
return row?.isActive === IsActive.Inactive;
}
get canEdit(): boolean {
return this.authService.hasPermission(this.authService.permissionEnum.EditDescriptionStatus);
}
get canDelete(): boolean {
return this.authService.hasPermission(this.authService.permissionEnum.DeleteDescriptionStatus);
}
}

View File

@ -8,7 +8,7 @@
<div class="col-auto">
<button mat-button class="action-btn cancel-btn" (click)="cancel()" type="button">{{'PLAN-STATUS-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col-auto" *ngIf="!isNew">
<div class="col-auto" *ngIf="canDelete">
<button mat-button class="action-btn delete-btn" type="button" (click)="delete()">
<mat-icon>delete</mat-icon>
{{'PLAN-STATUS-EDITOR.ACTIONS.DELETE' | translate}}

View File

@ -40,6 +40,7 @@ export class PlanStatusEditorComponent extends BaseEditor<PlanStatusEditorModel,
protected internalStatusEnum = this.enumUtils.getEnumValues<PlanStatusEnum>(PlanStatusEnum);
protected userRolesEnum = this.enumUtils.getEnumValues<AppRole>(AppRole);
protected planRolesEnum = this.enumUtils.getEnumValues<PlanUserRole>(PlanUserRole);
protected belongsToCurrentTenant: boolean;
constructor(
protected enumUtils: EnumUtils,
@ -87,6 +88,7 @@ export class PlanStatusEditorComponent extends BaseEditor<PlanStatusEditorModel,
try {
this.editorModel = data ? new PlanStatusEditorModel().fromModel(data) : new PlanStatusEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.belongsToCurrentTenant = data?.belongsToCurrentTenant;
this.buildForm();
} catch (error) {
this.logger.error('Could not parse planStatus item: ' + data + error);
@ -95,7 +97,7 @@ export class PlanStatusEditorComponent extends BaseEditor<PlanStatusEditorModel,
}
buildForm(): void {
this.formGroup = this.editorModel.buildForm({disabled: this.isDeleted || !this.authService.hasPermission(AppPermission.EditPlanStatus)});
this.formGroup = this.editorModel.buildForm({disabled: !this.belongsToCurrentTenant || this.isDeleted || !this.authService.hasPermission(AppPermission.EditPlanStatus)});
}
formSubmit(): void {
@ -158,6 +160,11 @@ export class PlanStatusEditorComponent extends BaseEditor<PlanStatusEditorModel,
protected get canSave(): boolean {
const editPlanStatus = this.authService.permissionEnum.EditPlanStatus;
return !this.isDeleted && this.authService.hasPermission(editPlanStatus);
return (this.isNew || (this.belongsToCurrentTenant && !this.isDeleted)) && this.authService.hasPermission(editPlanStatus);
}
protected get canDelete(): boolean {
const deletedPlanStatus = this.authService.permissionEnum.DeletePlanStatus;
return this.belongsToCurrentTenant && !this.isNew && !this.isDeleted && this.authService.hasPermission(deletedPlanStatus);
}
}

View File

@ -38,21 +38,25 @@
</div>
<ng-template #status let-row="row" let-item>
<div class="status-chip" [ngClass]="{'status-chip-finalized': row.internalStatus === PlanStatusEnum.Finalized, 'status-chip-draft' : row.internalStatus === PlanStatusEnum.Draft}">
<div
class="status-chip"
[class.status-chip-finalized]="row.internalStatus === PlanStatusEnum.Finalized"
[class.status-chip-draft]="row.internalStatus === PlanStatusEnum.Draft"
>
{{enumUtils.toPlanStatusString(row.internalStatus)}}
</div>
</ng-template>
<ng-template #actions let-row="row" let-item>
<div class="row" (click)="$event.stopPropagation()">
<div class="col-auto">
<div class="col-auto" *ngIf="row?.belongsToCurrentTenant && !isDeleted(row)">
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
<mat-icon>more_horiz</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
<button *ngIf="authService.hasPermission(authService.permissionEnum.EditPlanStatus)" mat-menu-item [routerLink]="routerUtils.generateUrl(['/plan-statuses/', row.id])">
<button *ngIf="canEdit" mat-menu-item [routerLink]="routerUtils.generateUrl(['/plan-statuses/', row.id])">
<mat-icon>edit</mat-icon>{{'PLAN-STATUS-LISTING.ACTIONS.EDIT' | translate}}
</button>
<button *ngIf="row.belongsToCurrentTenant != false" mat-menu-item (click)="delete(row.id)">
<button *ngIf="canDelete" mat-menu-item (click)="delete(row.id)">
<mat-icon>delete</mat-icon>
{{'PLAN-STATUS-LISTING.ACTIONS.DELETE' | translate}}
</button>

View File

@ -175,4 +175,16 @@ export class PlanStatusListingComponent extends BaseListingComponent<PlanStatus,
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
this.refresh();
}
isDeleted(row: PlanStatus): boolean {
return row?.isActive === IsActive.Inactive;
}
get canEdit(): boolean {
return this.authService.hasPermission(this.authService.permissionEnum.EditPlanStatus);
}
get canDelete(): boolean {
return this.authService.hasPermission(this.authService.permissionEnum.DeletePlanStatus);
}
}

View File

@ -2,7 +2,7 @@ import { Location } from '@angular/common';
import { Component, Input, OnInit, Output } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@ -210,16 +210,16 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
if (item.plan){
if (item.plan.descriptions) {
if (item.plan.status == PlanStatusEnum.Finalized) {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
} else {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatus.Canceled);
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatusEnum.Canceled);
}
}
item.plan.planUsers = item.plan.planUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(item);
}
if (item.description){
if (item.description.status != DescriptionStatus.Canceled) this.listingItems.push(item);
if (item.description.status != DescriptionStatusEnum.Canceled) this.listingItems.push(item);
}
})

View File

@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
@ -46,7 +46,7 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
if (this.description?.descriptionTemplate != null) {
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
if (isPreviousVersion === true) {
if (this.description.status === DescriptionStatus.Draft) {
if (this.description.status === DescriptionStatusEnum.Draft) {
this.openDeprecatedDescriptionTemplateDialog();
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);

View File

@ -3,7 +3,7 @@ import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/fo
import { MatDialog } from '@angular/material/dialog';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
@ -245,9 +245,9 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
// this.selectedSystemFields = this.selectedSystemFieldDisabled();
this.descriptionEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
if (this.editorModel.status == DescriptionStatus.Finalized || this.isDeleted || !this.canEdit) {
if (this.editorModel.status == DescriptionStatusEnum.Finalized || this.isDeleted || !this.canEdit) {
this.viewOnly = true;
this.isFinalized = this.editorModel.status == DescriptionStatus.Finalized;
this.isFinalized = this.editorModel.status == DescriptionStatusEnum.Finalized;
this.formGroup.disable();
} else {
this.viewOnly = false;
@ -308,11 +308,11 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
complete => {
onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete);
this.descriptionIsOnceSaved = true;
if (this.formGroup.get('status').value == DescriptionStatus.Finalized) this.isFinalized = true;
if (this.formGroup.get('status').value == DescriptionStatusEnum.Finalized) this.isFinalized = true;
},
error => {
if (this.formGroup.get('status').value == DescriptionStatus.Finalized) {
this.formGroup.get('status').setValue(DescriptionStatus.Draft);
if (this.formGroup.get('status').value == DescriptionStatusEnum.Finalized) {
this.formGroup.get('status').setValue(DescriptionStatusEnum.Draft);
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-FINALIZE'), SnackBarNotificationLevel.Error);
} else {
this.onCallbackError(error);
@ -727,7 +727,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.formGroup.get('status').setValue(DescriptionStatus.Finalized);
this.formGroup.get('status').setValue(DescriptionStatusEnum.Finalized);
this.persistEntity();
}
});
@ -747,7 +747,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
if (result) {
const planUserRemovePersist: DescriptionStatusPersist = {
id: this.formGroup.get('id').value,
status: DescriptionStatus.Draft,
status: DescriptionStatusEnum.Draft,
hash: this.formGroup.get('hash').value
};
this.descriptionService.persistStatus(planUserRemovePersist, DescriptionEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed))

View File

@ -1,5 +1,5 @@
import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { DescriptionStatus } from "@app/core/common/enum/description-status";
import { DescriptionStatusEnum } from "@app/core/common/enum/description-status";
import { DescriptionTemplateFieldType } from "@app/core/common/enum/description-template-field-type";
import { DescriptionTemplateFieldValidationType } from "@app/core/common/enum/description-template-field-validation-type";
import { IsActive } from "@app/core/common/enum/is-active.enum";
@ -18,7 +18,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
planId: Guid;
planDescriptionTemplateId: Guid;
descriptionTemplateId: Guid;
status: DescriptionStatus;
status: DescriptionStatusEnum;
description: string;
properties: DescriptionPropertyDefinitionEditorModel = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel);
tags: string[] = [];
@ -36,7 +36,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
this.planId = item.plan?.id;
this.planDescriptionTemplateId = item.planDescriptionTemplate?.id;
this.descriptionTemplateId = item.descriptionTemplate?.id;
this.status = item.status ?? DescriptionStatus.Draft;
this.status = item.status ?? DescriptionStatusEnum.Draft;
this.description = item.description;
this.tags = item.descriptionTags?.filter(x => x.isActive === IsActive.Active).map(x => x.tag?.label);
this.properties = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel).fromModel(item.properties, descriptionTemplate, item.descriptionReferences);

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionReferenceData, DescriptionTag } from '@app/core/model/description/description';
@ -200,7 +200,7 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
description.id = null;
description.hash = null;
description.status = DescriptionStatus.Draft;
description.status = DescriptionStatusEnum.Draft;
description.plan = plan;
description.planDescriptionTemplate = {
id: plan.planDescriptionTemplates.filter(x => x.sectionId == Guid.parse(planSectionId) && x.descriptionTemplateGroupId == description.descriptionTemplate.groupId)[0].id,

View File

@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { AbstractControl, FormBuilder, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Reference } from '@app/core/model/reference/reference';
@ -41,7 +41,7 @@ export class DescriptionFilterComponent extends BaseCriteriaComponent implements
public criteria: any;
public filteringTagsAsync = false;
statuses = DescriptionStatus;
statuses = DescriptionStatusEnum;
planRole = PlanUserRole;
options: UntypedFormGroup;

View File

@ -22,7 +22,7 @@ import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { DescriptionStatus } from '../../../../core/common/enum/description-status';
import { DescriptionStatusEnum } from '../../../../core/common/enum/description-status';
import { DescriptionCopyDialogComponent } from '../../description-copy-dialog/description-copy-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
@ -47,7 +47,7 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
isDraft: boolean;
isDeleted: boolean;
isUserOwner: boolean;
descriptionStatusEnum = DescriptionStatus;
descriptionStatusEnum = DescriptionStatusEnum;
fileTransformerEntityTypeEnum = FileTransformerEntityType;
planAccessTypeEnum = PlanAccessType;
canDelete: boolean = false;
@ -82,7 +82,7 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
this.analyticsService.trackPageView(AnalyticsService.DescriptionListingItem);
if (this.description.isActive === IsActive.Inactive) {
this.isDeleted = true;
} else if (this.description.status === DescriptionStatus.Draft) {
} else if (this.description.status === DescriptionStatusEnum.Draft) {
this.isDraft = true;
this.isDeleted = false;
} else {

View File

@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
@ -61,7 +61,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
hasPublishButton: boolean = true;
expand = false;
isLocked: Boolean;
descriptionStatusEnum = DescriptionStatus;
descriptionStatusEnum = DescriptionStatusEnum;
planAccessTypeEnum = PlanAccessType;
planStatusEnum = PlanStatusEnum;
planUserRoleEnum = PlanUserRole;
@ -286,7 +286,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
}
isDraftDescription(description: Description) {
return description.status == DescriptionStatus.Draft;
return description.status == DescriptionStatusEnum.Draft;
}
editClicked(description: Description) {
@ -443,7 +443,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
if (result) {
const descriptionStatusPersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatus.Finalized,
status: DescriptionStatusEnum.Finalized,
hash: description.hash
};
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
@ -467,7 +467,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
}
hasReversableStatus(description: Description): boolean {
return description.plan.status == PlanStatusEnum.Draft && description.status == DescriptionStatus.Finalized && this.canFinalize
return description.plan.status == PlanStatusEnum.Draft && description.status == DescriptionStatusEnum.Finalized && this.canFinalize
}
reverseFinalization(description: Description) {
@ -484,7 +484,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
if (result) {
const planUserRemovePersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatus.Draft,
status: DescriptionStatusEnum.Draft,
hash: description.hash
};
this.descriptionService.persistStatus(planUserRemovePersist).pipe(takeUntil(this._destroyed))

View File

@ -2,7 +2,7 @@ import { Location } from '@angular/common';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
@ -75,7 +75,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
depositRepos: DepositConfiguration[] = [];
descriptionStatusEnum = DescriptionStatus;
descriptionStatusEnum = DescriptionStatusEnum;
planAccessTypeEnum = PlanAccessType;
planStatusEnum = PlanStatusEnum;
planUserRoleEnum = PlanUserRole;
@ -133,9 +133,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
if (this.plan.descriptions) {
if (this.plan.status == PlanStatusEnum.Finalized) {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
} else {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatusEnum.Canceled);
}
}
if (data.entityDois && data.entityDois.length > 0) this.plan.entityDois = data.entityDois.filter(x => x.isActive === IsActive.Active);

View File

@ -5,7 +5,7 @@ import { MatDialog } from '@angular/material/dialog';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { AnnotationEntityType } from '@app/core/common/enum/annotation-entity-type';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
@ -78,7 +78,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
annotationsPerAnchor: Map<string, number> = new Map<string, number>();
//Enums
descriptionStatusEnum = DescriptionStatus;
descriptionStatusEnum = DescriptionStatusEnum;
planBlueprintSectionFieldCategoryEnum = PlanBlueprintFieldCategory;
planBlueprintSystemFieldTypeEnum = PlanBlueprintSystemFieldType;
planBlueprintExtraFieldDataTypeEnum = PlanBlueprintExtraFieldDataType;
@ -262,9 +262,9 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
if (data) {
if (data.descriptions) {
if (data.status == PlanStatusEnum.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
} else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatusEnum.Canceled);
}
}
if (data.planDescriptionTemplates) {

View File

@ -1,7 +1,7 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { Plan } from '@app/core/model/plan/plan';
import { DescriptionService } from '@app/core/services/description/description.service';
@ -23,7 +23,7 @@ export class PlanFinalizeDialogComponent extends BaseComponent implements OnInit
isPlanValid: boolean;
planErrors: string[];
planAccessTypeEnum = PlanAccessType;
descriptionStatusEnum = DescriptionStatus;
descriptionStatusEnum = DescriptionStatusEnum;
descriptionValidationOutputEnum = DescriptionValidationOutput;
validationResults: DescriptionValidationResult[] = [];
descriptionsToBeFinalized: Guid[] = [];
@ -74,7 +74,7 @@ export class PlanFinalizeDialogComponent extends BaseComponent implements OnInit
getFinalizedDescriptions() {
if (!this.plan.descriptions) return [];
const finalizedDescriptions = this.plan.descriptions.filter(x => x.status === DescriptionStatus.Finalized);
const finalizedDescriptions = this.plan.descriptions.filter(x => x.status === DescriptionStatusEnum.Finalized);
if (finalizedDescriptions?.length > 0){
finalizedDescriptions.forEach(finalize => {
this.descriptionValidationOutputMap.set(finalize.id, DescriptionValidationOutput.Valid);
@ -88,16 +88,16 @@ export class PlanFinalizeDialogComponent extends BaseComponent implements OnInit
}
validateDescriptions(plan: Plan) {
if (!plan.descriptions?.some(x => x.status == DescriptionStatus.Draft)) return;
if (!plan.descriptions?.some(x => x.status == DescriptionStatusEnum.Draft)) return;
const draftDescriptions = this.plan.descriptions.filter(x => x.status == DescriptionStatus.Draft) || [];
const draftDescriptions = this.plan.descriptions.filter(x => x.status == DescriptionStatusEnum.Draft) || [];
if ( draftDescriptions.length > 0){
draftDescriptions.forEach(draft => {
this.descriptionValidationOutputMap.set(draft.id, DescriptionValidationOutput.Pending);
});
}
this.descriptionService.validate(plan.descriptions.filter(x => x.status == DescriptionStatus.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed),)
this.descriptionService.validate(plan.descriptions.filter(x => x.status == DescriptionStatusEnum.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed),)
.subscribe(result => {
this.validationResults = result;
},

View File

@ -114,8 +114,10 @@ export class SidebarComponent implements OnInit {
routes: [],
}
if (this.authentication.hasPermission(AppPermission.ViewPlanBlueprintPage)) this.adminItems.routes.push({ path: '/plan-blueprints', title: 'SIDE-BAR.PLAN-BLUEPRINTS', icon: 'library_books', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewPlanStatusPage)) this.adminItems.routes.push({ path: '/plan-statuses', title: 'SIDE-BAR.PLAN-STATUSES', icon: 'library_books', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewDescriptionTemplatePage)) this.adminItems.routes.push({ path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'description', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewDescriptionTemplateTypePage)) this.adminItems.routes.push({ path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'stack', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewDescriptionStatusPage)) this.adminItems.routes.push({ path: '/description-statuses', title: 'SIDE-BAR.DESCRIPTION-STATUSES', icon: 'description', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewEntityLockPage)) this.adminItems.routes.push({ path: '/entity-locks', title: 'SIDE-BAR.ENTITY-LOCKS', icon: 'lock_person', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewReferencePage)) this.adminItems.routes.push({ path: '/references', title: 'SIDE-BAR.REFERENCES', icon: 'dataset_linked', routeType: RouteType.System });
if (this.authentication.hasPermission(AppPermission.ViewReferenceTypePage)) this.adminItems.routes.push({ path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'add_link', routeType: RouteType.System });

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES":"Annotation Statuses",
"SUPPORTIVE-MATERIAL":"Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -315,7 +316,9 @@
"ADMIN": "ADMIN",
"DESCRIPTION-TEMPLATES": "Description Templates",
"DESCRIPTION-TEMPLATE-TYPES": "Description Types",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-BLUEPRINTS": "Plan Blueprints",
"PLAN-STATUSES": "Plan Statuses",
"USERS": "Users",
"TENANT-USERS": "Tenant Users",
"CO-BRANDING": "Co-Branding",

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"

View File

@ -180,7 +180,8 @@
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
"PLAN-STATUSES": "Plan Statuses",
"DESCRIPTION-STATUSES": "Description Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -333,7 +334,9 @@
"TENANT-CONFIGURATION": "Tenant Configuration",
"ENTITY-LOCKS": "Entity Locks",
"ANNOTATION-STATUSES": "Annotation Statuses",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"DESCRIPTION-STATUSES": "Description Statuses",
"PLAN-STATUSES": "Plan Statuses"
},
"DESCRIPTION-TEMPLATE-PREVIEW": {
"TEMPLATE": "Template"