plan status listing + editor WIP

This commit is contained in:
mchouliara 2024-08-22 16:18:17 +03:00
parent f19c8e77eb
commit bb47954068
48 changed files with 1689 additions and 70 deletions

View File

@ -401,8 +401,12 @@ const appRoutes: Routes = [
loadChildren: () => import('./ui/admin/plan-status/plan-status.module').then(m => m.PlanStatusModule),
data: {
authContext: {
permissions: [AppPermission.ViewMaintenancePage]
permissions: [AppPermission.ViewPlanStatusPage]
},
breadcrumb: true,
...BreadcrumbService.generateRouteDataConfiguration({
title: 'GENERAL.TITLES.PLAN-STATUSES'
})
}
},
{

View File

@ -239,5 +239,6 @@ export enum AppPermission {
ViewTenantConfigurationPage = "ViewTenantConfigurationPage",
ViewStatusPage = "ViewStatusPage",
ViewUsageLimitPage = "ViewUsageLimitPage",
ViewPlanStatusPage = "ViewPlanStatusPage"
}

View File

@ -1,4 +1,4 @@
export enum PlanStatus {
export enum PlanStatusEnum {
Draft = 0,
Finalized = 1
}

View File

@ -48,6 +48,7 @@ import { DefaultUserLocaleService } from './services/default-user-locale/default
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';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -115,7 +116,8 @@ export class CoreServiceModule {
StorageFileService,
TenantHandlingService,
RouterUtilsService,
UsageLimitService
UsageLimitService,
PlanStatusService
],
};
}

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status';
import { BaseEntity, BaseEntityPersist } from '@common/base/base-entity.model';
@ -26,7 +26,7 @@ export interface BasePlan extends BaseEntity {
planReferences?: PlanReference[];
entityDois?: EntityDoi[];
tenantId?: Guid;
status?: PlanStatus;
status?: PlanStatusEnum;
descriptions?: BaseDescription[];
}
export interface Plan extends BasePlan {
@ -96,7 +96,7 @@ export interface PlanDescriptionTemplate extends BaseEntity {
//
export interface PlanPersist extends BaseEntityPersist {
label: string;
status: PlanStatus;
status: PlanStatusEnum;
properties: PlanPropertiesPersist;
description: String;
language: String;

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 PlanStatusLookup extends Lookup implements PlanStatusFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
constructor() {
super();
}
}
export interface PlanStatusFilter {
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 { PlanAccessType } from '../common/enum/plan-access-type';
import { PlanStatus } from '../common/enum/plan-status';
import { PlanStatusEnum } from '../common/enum/plan-status';
import { PlanVersionStatus } from '../common/enum/plan-version-status';
import { IsActive } from '../common/enum/is-active.enum';
import { PlanDescriptionTemplateLookup } from './plan-description-template.lookup';
@ -16,7 +16,7 @@ export class PlanLookup extends Lookup implements PlanFilter {
like: string;
isActive: IsActive[];
versionStatuses: PlanVersionStatus[];
statuses: PlanStatus[];
statuses: PlanStatusEnum[];
accessTypes: PlanAccessType[];
versions: Number[];
groupIds: Guid[];
@ -38,7 +38,7 @@ export interface PlanFilter {
like: string;
isActive: IsActive[];
versionStatuses: PlanVersionStatus[];
statuses: PlanStatus[];
statuses: PlanStatusEnum[];
accessTypes: PlanAccessType[];
versions: Number[];
groupIds: Guid[];

View File

@ -45,6 +45,8 @@ export class AnalyticsService {
public static UserGuideContent: string = 'User Guide Content';
public static UserProfile: string = 'User Profile';
public static NotificationTempplateEditor: string = 'Admin: Notification Tempplates';
public static PlanStatusListing: string = 'Plan Status Listing';
public static PlanStatusEditor: string = 'Plan Status Editor';
//#endregion
//#region trackDownload

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 { PlanStatusLookup } from "@app/core/query/plan-status.lookup";
import { PlanStatus } from "@app/core/model/plan-status/plan-status";
import { QueryResult } from "@common/model/query-result";
import { catchError, Observable, throwError } from "rxjs";
import { PlanStatusPersist } from "@app/core/model/plan-status/plan-status-persist";
import { Guid } from "@common/types/guid";
@Injectable()
export class PlanStatusService {
private headers = new HttpHeaders();
constructor(
private http: BaseHttpV2Service,
private configurationService: ConfigurationService,
) {
}
private get apiBase(): string { return `${this.configurationService.server}plan-status`; }
query(q: PlanStatusLookup): Observable<QueryResult<PlanStatus>> {
const url = `${this.apiBase}/query`;
return this.http.post<QueryResult<PlanStatus>>(url, q).pipe(catchError((error: any) => throwError(error)));
}
getSingle(id: Guid, reqFields: string[] = []): Observable<PlanStatus> {
const url = `${this.apiBase}/${id}`;
const options = { params: { f: reqFields } };
return this.http
.get<PlanStatus>(url, options).pipe(
catchError((error: any) => throwError(() => error)));
}
persist(item: PlanStatusPersist): Observable<PlanStatus> {
const url = `${this.apiBase}/persist`;
return this.http
.post<PlanStatus>(url, item).pipe(
catchError((error: any) => throwError(() => error)));
}
delete(id: Guid): Observable<PlanStatus> {
const url = `${this.apiBase}/${id}`;
return this.http
.delete<PlanStatus>(url).pipe(
catchError((error: any) => throwError(() => error)));
}
}

View File

@ -1,6 +1,6 @@
import { HttpClient, HttpHeaders, HttpParamsOptions, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { PlanDescriptionTemplateLookup } from '@app/core/query/plan-description-template.lookup';
@ -261,7 +261,7 @@ export class PlanService {
valueAssign: (item: Plan) => item.id,
};
public buildAutocompleteLookup(isActive: IsActive[], like?: string, excludedIds?: Guid[], ids?: Guid[], statuses?: PlanStatus[], planDescriptionTemplateSubQuery?: PlanDescriptionTemplateLookup): PlanLookup {
public buildAutocompleteLookup(isActive: IsActive[], like?: string, excludedIds?: Guid[], ids?: Guid[], statuses?: PlanStatusEnum[], planDescriptionTemplateSubQuery?: PlanDescriptionTemplateLookup): PlanLookup {
const lookup: PlanLookup = new PlanLookup();
lookup.page = { size: 100, offset: 0 };
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }

View File

@ -27,7 +27,7 @@ import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-descript
import { TranslateService } from '@ngx-translate/core';
import { AppRole } from '../../common/enum/app-role';
import { PlanBlueprintExtraFieldDataType } from '../../common/enum/plan-blueprint-field-type';
import { PlanStatus } from '../../common/enum/plan-status';
import { PlanStatusEnum } from '../../common/enum/plan-status';
import { ValidationType } from '../../common/enum/validation-type';
import { UsageLimitTargetMetric } from '@app/core/common/enum/usage-limit-target-metric';
import { UsageLimitPeriodicityRange } from '@app/core/common/enum/usage-limit-periodicity-range';
@ -69,10 +69,10 @@ export class EnumUtils {
}
}
toPlanStatusString(status: PlanStatus): string {
toPlanStatusString(status: PlanStatusEnum): string {
switch (status) {
case PlanStatus.Draft: return this.language.instant('TYPES.PLAN.DRAFT');
case PlanStatus.Finalized: return this.language.instant('TYPES.PLAN.FINALISED');
case PlanStatusEnum.Draft: return this.language.instant('TYPES.PLAN.DRAFT');
case PlanStatusEnum.Finalized: return this.language.instant('TYPES.PLAN.FINALISED');
}
}

View File

@ -0,0 +1,47 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { PlanStatus, PlanStatusDefinition, PlanStatusDefinitionAuthorization, PlanStatusDefinitionAuthorizationItem } from "@app/core/model/plan-status/plan-status";
import { PlanStatusService } from "@app/core/services/plan/plan-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 PlanStatusEditorResolver extends BaseEditorResolver{
constructor(private planStatusService: PlanStatusService, private breadcrumbService: BreadcrumbService) {
super();
}
public static lookupFields(): string[] {
return [
nameof<PlanStatus>(x => x.id),
nameof<PlanStatus>(x => x.name),
nameof<PlanStatus>(x => x.description),
nameof<PlanStatus>(x => x.internalStatus),
[nameof<PlanStatus>(x => x.definition), nameof<PlanStatusDefinition>(x => x.authorization), nameof<PlanStatusDefinitionAuthorization>(x => x.edit), nameof<PlanStatusDefinitionAuthorizationItem>(x => x.roles)].join('.'),
[nameof<PlanStatus>(x => x.definition), nameof<PlanStatusDefinition>(x => x.authorization), nameof<PlanStatusDefinitionAuthorization>(x => x.edit), nameof<PlanStatusDefinitionAuthorizationItem>(x => x.planRoles)].join('.'),
[nameof<PlanStatus>(x => x.definition), nameof<PlanStatusDefinition>(x => x.authorization), nameof<PlanStatusDefinitionAuthorization>(x => x.edit), nameof<PlanStatusDefinitionAuthorizationItem>(x => x.allowAnonymous)].join('.'),
[nameof<PlanStatus>(x => x.definition), nameof<PlanStatusDefinition>(x => x.authorization), nameof<PlanStatusDefinitionAuthorization>(x => x.edit), nameof<PlanStatusDefinitionAuthorizationItem>(x => x.allowAuthenticated)].join('.'),
nameof<PlanStatus>(x => x.updatedAt),
nameof<PlanStatus>(x => x.createdAt),
nameof<PlanStatus>(x => x.hash),
nameof<PlanStatus>(x => x.isActive),
nameof<PlanStatus>(x => x.belongsToCurrentTenant),
]
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const fields = [
...PlanStatusEditorResolver.lookupFields()
];
const id = route.paramMap.get('id');
if (id != null) {
return this.planStatusService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.name)), takeUntil(this._destroyed));
}
}
}

View File

@ -0,0 +1,108 @@
<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="!isNew">
<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?.toString()">{{enumUtils.toPlanStatusString(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?.toString()">{{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,164 @@
import { Component, OnInit } from '@angular/core';
import { PlanStatusDefinitionAuthorizationForm, PlanStatusDefinitionAuthorizationItemForm, PlanStatusEditorModel, PlanStatusForm } from './plan-status-editor.model';
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
import { BaseEditor } from '@common/base/base-editor';
import { Guid } from '@common/types/guid';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { MatDialog } from '@angular/material/dialog';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { FormService } from '@common/forms/form-service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { TranslateService } from '@ngx-translate/core';
import { PlanStatusService } from '@app/core/services/plan/plan-status.service';
import { PlanStatusEditorResolver } from '../plan-status-editor.resolver';
import { map, takeUntil } from 'rxjs';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { PlanStatusPersist } from '@app/core/model/plan-status/plan-status-persist';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { FormGroup } from '@angular/forms';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { AppRole } from '@app/core/common/enum/app-role';
@Component({
selector: 'app-plan-status-editor',
templateUrl: './plan-status-editor.component.html',
styleUrl: './plan-status-editor.component.scss'
})
export class PlanStatusEditorComponent extends BaseEditor<PlanStatusEditorModel, PlanStatus> implements OnInit{
protected internalStatusEnum = this.enumUtils.getEnumValues<PlanStatusEnum>(PlanStatusEnum);
protected userRolesEnum = this.enumUtils.getEnumValues<AppRole>(AppRole);
protected planRolesEnum = this.enumUtils.getEnumValues<PlanUserRole>(PlanUserRole);
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 planStatusService: PlanStatusService,
private logger: LoggingService,
private routerUtils: RouterUtilsService,
){
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, route, queryParamsService, lockService, authService, configurationService);
}
formGroup: FormGroup<PlanStatusForm>;
ngOnInit(){
this.analyticsService.trackPageView(AnalyticsService.PlanStatusEditor);
super.ngOnInit();
}
get editAuthenticationForm(): FormGroup<PlanStatusDefinitionAuthorizationItemForm> {
return this.formGroup?.controls?.definition?.controls?.authorization?.controls?.edit;
}
getItem(itemId: Guid, successFunction: (item: PlanStatus) => void): void {
this.planStatusService.getSingle(itemId, PlanStatusEditorResolver.lookupFields())
.pipe(map(data => data as PlanStatus), takeUntil(this._destroyed))
.subscribe({
next: (data) => successFunction(data),
error: (error) => this.onCallbackError(error)
});
}
prepareForm(data: PlanStatus): void {
try {
this.editorModel = data ? new PlanStatusEditorModel().fromModel(data) : new PlanStatusEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
} catch (error) {
this.logger.error('Could not parse planStatus item: ' + data + error);
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
}
}
buildForm(): void {
this.formGroup = this.editorModel.buildForm({disabled: this.isDeleted || !this.authService.hasPermission(AppPermission.EditPlanStatus)});
}
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.planStatusService.delete(value.id).pipe(takeUntil(this._destroyed))
.subscribe({
complete: () => this.onCallbackDeleteSuccess(),
error: (error) => this.onCallbackError(error)
});
}
});
}
}
refreshData(): void {
this.getItem(this.editorModel.id, (data: PlanStatus) => this.prepareForm(data));
}
refreshOnNavigateToData(id?: Guid): void {
debugger
this.formGroup.markAsPristine();
if (this.isNew) {
let route = [];
route.push(this.routerUtils.generateUrl('/plan-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 PlanStatusPersist;
this.planStatusService.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 editPlanStatus = this.authService.permissionEnum.EditPlanStatus;
return !this.isDeleted && this.authService.hasPermission(editPlanStatus);
}
}

View File

@ -0,0 +1,117 @@
import { FormArray, FormControl, FormGroup, Validators } from "@angular/forms";
import { AppRole } from "@app/core/common/enum/app-role";
import { AppPermission } from "@app/core/common/enum/permission.enum";
import { PlanStatusEnum } from "@app/core/common/enum/plan-status";
import { PlanUserRole } from "@app/core/common/enum/plan-user-role";
import { PlanStatus, PlanStatusDefinition, PlanStatusDefinitionAuthorizationItem } from "@app/core/model/plan-status/plan-status";
import { PlanStatusPersist } from "@app/core/model/plan-status/plan-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 PlanStatusEditorModel extends BaseEditorModel implements PlanStatusPersist {
name: string;
description: string;
internalStatus: PlanStatusEnum;
definition: PlanStatusDefinition;
public fromModel(item: PlanStatus): PlanStatusEditorModel {
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<PlanStatusForm> {
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<PlanStatusDefinitionForm> {
const {context = this.createValidationContext(), disabled} = params;
const definitionForm = new FormGroup<PlanStatusDefinitionForm>({
authorization: new FormGroup<PlanStatusDefinitionAuthorizationForm>({
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: PlanStatusDefinitionAuthorizationItem, rootPath: string, disabled: boolean}): FormGroup<PlanStatusDefinitionAuthorizationItemForm>{
const {item, rootPath, disabled} = params;
const context = this.createAuthorizationItemContext(rootPath);
return new FormGroup<PlanStatusDefinitionAuthorizationItemForm>({
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 PlanStatusForm {
id: FormControl<Guid>;
name: FormControl<string>;
description: FormControl<string>;
internalStatus: FormControl<PlanStatusEnum>;
definition: FormGroup<PlanStatusDefinitionForm>;
}
export interface PlanStatusDefinitionForm {
authorization: FormGroup<PlanStatusDefinitionAuthorizationForm>
}
export interface PlanStatusDefinitionAuthorizationForm {
edit: FormGroup<PlanStatusDefinitionAuthorizationItemForm>;
}
export interface PlanStatusDefinitionAuthorizationItemForm {
roles: FormControl<AppRole[]>;
planRoles: FormControl<PlanUserRole[]>;
allowAuthenticated: FormControl<boolean>;
allowAnonymous: FormControl<boolean>;
}

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,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PlanStatusListingFiltersComponent } from './plan-status-listing-filters.component';
describe('PlanStatusListingFiltersComponent', () => {
let component: PlanStatusListingFiltersComponent;
let fixture: ComponentFixture<PlanStatusListingFiltersComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PlanStatusListingFiltersComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PlanStatusListingFiltersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,88 @@
import { Component, effect, EventEmitter, input, Input, Output } from '@angular/core';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { PlanStatusFilter } from '@app/core/query/plan-status.lookup';
import { BaseComponent } from '@common/base/base.component';
@Component({
selector: 'app-plan-status-listing-filters',
templateUrl: './plan-status-listing-filters.component.html',
styleUrl: './plan-status-listing-filters.component.scss'
})
export class PlanStatusListingFiltersComponent extends BaseComponent{
readonly filter = input<PlanStatusFilter>();
@Output() filterChange = new EventEmitter<PlanStatusFilter>();
internalFilters: PlanStatusListingFilters = this._getEmptyFilters();
appliedFilterCount: number = 0;
constructor(){
super();
effect(() => {
const newFilters = this.filter();
if(newFilters){
this.updateFilters();
}
})
}
private _parseToInternalFilters(inputFilter: PlanStatusFilter): PlanStatusListingFilters {
if (!inputFilter) {
return this._getEmptyFilters();
}
let { isActive, like } = inputFilter;
return {
isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length,
like: like,
}
}
private _computeAppliedFilters(filters: PlanStatusListingFilters): number {
let count = 0;
if (!filters?.isActive) {
count++
}
if(filters?.like){
count++;
}
return count;
}
private _getEmptyFilters(): PlanStatusListingFilters {
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 PlanStatusListingFilters {
isActive: boolean;
like: string;
}

View File

@ -0,0 +1,97 @@
<div class="container-fluid">
<div class="row plan-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.EditPlanStatus)" [routerLink]="routerUtils.generateUrl(['/plan-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('/plan-statuss'))"
(pageLoad)="alterPage($event)"
(columnSort)="onColumnSort($event)"
(columnsChanged)="onColumnsChanged($event)"
[listItemTemplate]="listItemTemplate"
>
<app-plan-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 #listItemTemplate let-item="item" let-isColumnSelected="isColumnSelected">
<div class="d-flex align-items-center p-3 gap-1-rem">
<div class="row">
<ng-container *ngIf="isColumnSelected('name')">
<a class="buttonLinkClass" [routerLink]="routerUtils.generateUrl('./' + item?.id)" class="col-12" (click)="$event.stopPropagation()">{{item?.name | nullifyValue}}</a>
<br />
</ng-container>
<ng-container *ngIf="isColumnSelected('internalStatus')">
<div class="col-auto">
<div>
{{enumUtils.toPlanStatusString(item.internalStatus) | nullifyValue}}
</div>
</div>
</ng-container>
<!-- <ng-container *ngIf="isColumnSelected('createdAt')">
<span class="col-12">
{{'PLAN-BLUEPRINT-LISTING.FIELDS.CREATED-AT' | translate}}:
<small>
{{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}}
</small>
</span>
<br>
</ng-container>
<ng-container *ngIf="isColumnSelected('updatedAt')">
<span class="col-12">
{{'PLAN-BLUEPRINT-LISTING.FIELDS.UPDATED-AT' | translate}}:
<small>
{{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}}
</small>
</span>
</ng-container> -->
</div>
</div>
</ng-template>
<ng-template #actions let-row="row" let-item>
<div class="row" (click)="$event.stopPropagation()">
<div class="col-auto">
<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])">
<mat-icon>edit</mat-icon>{{'PLAN-STATUS-LISTING.ACTIONS.EDIT' | translate}}
</button>
<button *ngIf="row.belongsToCurrentTenant != false" 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,10 @@
.create-btn {
border-radius: 30px;
background-color: var(--secondary-color);
padding-left: 2em;
padding-right: 2em;
.button-text {
display: inline-block;
}
}

View File

@ -0,0 +1,176 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
import { UserSettingsKey } from '@app/core/model/user-settings/user-settings.model';
import { DataTableDateTimeFormatPipe } from '@app/core/pipes/date-time-format.pipe';
import { PlanStatusLookup } from '@app/core/query/plan-status.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { PlanStatusService } from '@app/core/services/plan/plan-status.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 { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { IsActive } from '@notification-service/core/enum/is-active.enum';
import { Observable, takeUntil } from 'rxjs';
import { nameof } from 'ts-simple-nameof';
import { PlanStatusEditorResolver } from '../../editor/plan-status-editor.resolver';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-plan-status-listing',
templateUrl: './plan-status-listing.component.html',
styleUrl: './plan-status-listing.component.scss'
})
export class PlanStatusListingComponent extends BaseListingComponent<PlanStatus, PlanStatusLookup> implements OnInit{
userSettingsKey: UserSettingsKey = {key: 'PlanStatusListingUserSettings'};
publish = false;
propertiesAvailableForOrder: ColumnDefinition[];
@ViewChild('actions', { static: true }) actions: 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 planStatusService: PlanStatusService,
private analyticsService: AnalyticsService,
private language: TranslateService,
private dialog: MatDialog
){
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
this.lookup = this.initializeLookup();
}
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.PlanStatusListing);
super.ngOnInit();
}
private readonly lookupFields = PlanStatusEditorResolver.lookupFields();
protected initializeLookup(): PlanStatusLookup {
const lookup = new PlanStatusLookup();
lookup.metadata = { countAll: true };
lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE };
lookup.isActive = [IsActive.Active];
lookup.order = { items: [this.toDescSortField(nameof<PlanStatus>(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<PlanStatus>> {
return this.planStatusService.query(this.lookup);
}
protected setupColumns() {
this.gridColumns.push(...[
{
prop: nameof<PlanStatus>(x => x.name),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.NAME'
},
{
prop: nameof<PlanStatus>(x => x.description),
languageName: 'PLAN-STATUS-LISTING.FIELDS.DESCRIPTION'
},
{
prop: nameof<PlanStatus>(x => x.internalStatus),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.STATUS',
},
{
prop: nameof<PlanStatus>(x => x.createdAt),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.CREATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<PlanStatus>(x => x.updatedAt),
sortable: true,
languageName: 'PLAN-STATUS-LISTING.FIELDS.UPDATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<PlanStatus>(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.planStatusService.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();
}
changeSetting($event) {
}
}

View File

@ -2,14 +2,32 @@ import { NgModule } from "@angular/core";
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { PlanStatusRoutingModule } from "./plan-status.routing";
import { PlanStatusListingComponent } from "./listing/plan-status-listing/plan-status-listing.component";
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 { PlanStatusListingFiltersComponent } from "./listing/plan-status-listing/plan-status-listing-filters/plan-status-listing-filters.component";
import { PlanStatusEditorComponent } from "./editor/plan-status-editor/plan-status-editor.component";
import { TextFilterModule } from "@common/modules/text-filter/text-filter.module";
import { RichTextEditorModule } from "@app/library/rich-text-editor/rich-text-editor.module";
@NgModule({
imports: [
CommonUiModule,
CommonFormsModule,
PlanStatusRoutingModule,
PlanStatusRoutingModule,
ConfirmationDialogModule,
HybridListingModule,
UserSettingsModule,
CommonFormattingModule,
TextFilterModule,
RichTextEditorModule
],
declarations: [
PlanStatusListingComponent,
PlanStatusListingFiltersComponent,
PlanStatusEditorComponent
]
})
export class PlanStatusModule { }

View File

@ -4,17 +4,26 @@ 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 { PlanStatusListingComponent } from './listing/plan-status-listing/plan-status-listing.component';
import { PlanStatusEditorComponent } from './editor/plan-status-editor/plan-status-editor.component';
import { PlanStatusEditorResolver } from './editor/plan-status-editor.resolver';
const routes: Routes = [
{
path: '',
// component:,
canActivate: [AuthGuard]
component: PlanStatusListingComponent,
canActivate: [AuthGuard],
data: {
breadcrumb: true,
...BreadcrumbService.generateRouteDataConfiguration({
hideNavigationItem: true
}),
}
},
{
path: 'new',
canActivate: [AuthGuard],
// component:,
component: PlanStatusEditorComponent,
canDeactivate: [PendingChangesGuard],
data: {
authContext: {
@ -30,11 +39,11 @@ const routes: Routes = [
{
path: ':id',
canActivate: [AuthGuard],
// component:,
component: PlanStatusEditorComponent,
canDeactivate: [PendingChangesGuard],
// resolve: {
// 'entity':
// },
resolve: {
'entity': PlanStatusEditorResolver
},
data: {
authContext: {
permissions: [AppPermission.EditPlanStatus]
@ -50,5 +59,6 @@ const routes: Routes = [
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [PlanStatusEditorResolver]
})
export class PlanStatusRoutingModule { }

View File

@ -3,7 +3,7 @@ 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 { PlanStatus } from '@app/core/common/enum/plan-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';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
@ -209,7 +209,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
response.forEach(item => {
if (item.plan){
if (item.plan.descriptions) {
if (item.plan.status == PlanStatus.Finalized) {
if (item.plan.status == PlanStatusEnum.Finalized) {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatus.Canceled);

View File

@ -13,7 +13,7 @@ import { PlanDescriptionTemplateLookup } from '@app/core/query/plan-description-
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { PlanLookup } from '@app/core/query/plan.lookup';
import { Guid } from '@common/types/guid';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { nameof } from 'ts-simple-nameof';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@ -51,7 +51,7 @@ export class DescriptionCopyDialogComponent {
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
if (ids && ids.length > 0) { lookup.ids = ids; }
lookup.isActive = [IsActive.Active];
lookup.statuses = [PlanStatus.Draft];
lookup.statuses = [PlanStatusEnum.Draft];
lookup.project = {
fields: [
nameof<Plan>(x => x.id),

View File

@ -8,7 +8,7 @@ import { FileTransformerEntityType } from '@app/core/common/enum/file-transforme
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplatePage, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
import { Description, DescriptionPersist, DescriptionStatusPersist } from '@app/core/model/description/description';
import { PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
@ -551,7 +551,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
hasReversableStatus(): boolean {
if (this.item?.plan) {
return (this.item.plan.status == PlanStatus.Draft && this.isFinalized);
return (this.item.plan.status == PlanStatusEnum.Draft && this.isFinalized);
} else {
return false;
}

View File

@ -5,7 +5,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
@ -63,7 +63,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
isLocked: Boolean;
descriptionStatusEnum = DescriptionStatus;
planAccessTypeEnum = PlanAccessType;
planStatusEnum = PlanStatus;
planStatusEnum = PlanStatusEnum;
planUserRoleEnum = PlanUserRole;
fileTransformerEntityTypeEnum = FileTransformerEntityType;
@ -467,7 +467,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
}
hasReversableStatus(description: Description): boolean {
return description.plan.status == PlanStatus.Draft && description.status == DescriptionStatus.Finalized && this.canFinalize
return description.plan.status == PlanStatusEnum.Draft && description.status == DescriptionStatus.Finalized && this.canFinalize
}
reverseFinalization(description: Description) {

View File

@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
import { Plan } from '@app/core/model/plan/plan';
@ -41,13 +41,13 @@ export class StartNewDescriptionDialogComponent extends BaseComponent {
valueAssign: (item: Plan) => item.id,
};
private buildAutocompleteLookup(like?: string, excludedIds?: Guid[], ids?: Guid[], statuses?: PlanStatus[], planDescriptionTemplateSubQuery?: PlanDescriptionTemplateLookup): PlanLookup {
private buildAutocompleteLookup(like?: string, excludedIds?: Guid[], ids?: Guid[], statuses?: PlanStatusEnum[], planDescriptionTemplateSubQuery?: PlanDescriptionTemplateLookup): PlanLookup {
const lookup: PlanLookup = new PlanLookup();
lookup.page = { size: 100, offset: 0 };
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
if (ids && ids.length > 0) { lookup.ids = ids; }
lookup.isActive = [IsActive.Active];
lookup.statuses = [PlanStatus.Draft];
lookup.statuses = [PlanStatusEnum.Draft];
lookup.project = {
fields: [
nameof<Plan>(x => x.id),

View File

@ -6,7 +6,7 @@ import { TranslateService } from '@ngx-translate/core';
import { AuthService } from '@app/core/services/auth/auth.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { PlanBlueprintService } from '@app/core/services/plan/plan-blueprint.service';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
@ -35,7 +35,7 @@ export class PlanFilterComponent extends BaseCriteriaComponent implements OnInit
@Input() filterFormGroup: UntypedFormGroup;
@Output() filterChanged: EventEmitter<any> = new EventEmitter();
status = PlanStatus;
status = PlanStatusEnum;
role = PlanUserRole;
filteringGrantsAsync = false;
sizeError = false;

View File

@ -19,7 +19,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 { PlanStatus } from '../../../../core/common/enum/plan-status';
import { PlanStatusEnum } from '../../../../core/common/enum/plan-status';
import { AuthService } from '../../../../core/services/auth/auth.service';
import { ClonePlanDialogComponent } from '../../clone-dialog/plan-clone-dialog.component';
import { PlanInvitationDialogComponent } from '../../invitation/dialog/plan-invitation-dialog.component';
@ -49,7 +49,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
isDraft: boolean;
isFinalized: boolean;
isPublished: boolean;
planStatusEnum = PlanStatus;
planStatusEnum = PlanStatusEnum;
fileTransformerEntityTypeEnum = FileTransformerEntityType;
constructor(
@ -77,16 +77,16 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.PlanListingItem);
if (this.plan.status == PlanStatus.Draft) {
if (this.plan.status == PlanStatusEnum.Draft) {
this.isDraft = true;
this.isFinalized = false;
this.isPublished = false;
}
else if (this.plan.status == PlanStatus.Finalized) {
else if (this.plan.status == PlanStatusEnum.Finalized) {
this.isDraft = false;
this.isFinalized = true;
this.isPublished = false;
if (this.plan.status === PlanStatus.Finalized && this.plan.accessType === PlanAccessType.Public) { this.isPublished = true }
if (this.plan.status === PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public) { this.isPublished = true }
}
}
@ -112,7 +112,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
}
viewVersions(plan: Plan) {
if (plan.accessType == PlanAccessType.Public && plan.status == PlanStatus.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
if (plan.accessType == PlanAccessType.Public && plan.status == PlanStatusEnum.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
let url = this.router.createUrlTree(['/explore-plans/versions/', plan.groupId]);
window.open(url.toString(), '_blank');
} else {
@ -217,7 +217,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
}
isDraftPlan(activity: Plan) {
return activity.status == PlanStatus.Draft;
return activity.status == PlanStatusEnum.Draft;
}
reloadPage(): void {

View File

@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
@ -77,7 +77,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
descriptionStatusEnum = DescriptionStatus;
planAccessTypeEnum = PlanAccessType;
planStatusEnum = PlanStatus;
planStatusEnum = PlanStatusEnum;
planUserRoleEnum = PlanUserRole;
authorFocus: string;
@ -132,7 +132,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
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.status == PlanStatus.Finalized) {
if (this.plan.status == PlanStatusEnum.Finalized) {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
@ -411,15 +411,15 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
}
isDraftPlan() {
return this.plan.status == PlanStatus.Draft;
return this.plan.status == PlanStatusEnum.Draft;
}
isFinalizedPlan(plan: Plan) {
return plan.status == PlanStatus.Finalized;
return plan.status == PlanStatusEnum.Finalized;
}
isPublishedPlan() {
return (this.plan.status == PlanStatus.Finalized && this.plan.accessType === PlanAccessType.Public);
return (this.plan.status == PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public);
}
hasDoi() {

View File

@ -15,7 +15,7 @@ import { PlanBlueprintFieldCategory } from '@app/core/common/enum/plan-blueprint
import { PlanBlueprintExtraFieldDataType } from '@app/core/common/enum/plan-blueprint-field-type';
import { PlanBlueprintStatus } from '@app/core/common/enum/plan-blueprint-status';
import { PlanBlueprintSystemFieldType } from '@app/core/common/enum/plan-blueprint-system-field-type';
import { PlanStatus } from '@app/core/common/enum/plan-status';
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { PlanUserType } from '@app/core/common/enum/plan-user-type';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
@ -135,7 +135,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
}
protected get canReverseFinalize(): boolean {
return !this.isDeleted && !this.isNew && this.canEdit && this.isLockedByUser && this.item.status == PlanStatus.Finalized && (this.hasPermission(this.authService.permissionEnum.EditPlan) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditPlan));
return !this.isDeleted && !this.isNew && this.canEdit && this.isLockedByUser && this.item.status == PlanStatusEnum.Finalized && (this.hasPermission(this.authService.permissionEnum.EditPlan) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditPlan));
}
protected canEditSection(id: Guid): boolean {
@ -261,7 +261,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.editorModel = data ? new PlanEditorModel().fromModel(data) : new PlanEditorModel();
if (data) {
if (data.descriptions) {
if (data.status == PlanStatus.Finalized) {
if (data.status == PlanStatusEnum.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
@ -277,7 +277,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.selectedBlueprint = data?.blueprint;
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.isFinalized = data ? data.status === PlanStatus.Finalized : false;
this.isFinalized = data ? data.status === PlanStatusEnum.Finalized : false;
if (data && data.id) {
const descriptionSectionPermissionResolverModel: DescriptionSectionPermissionResolver = {
@ -304,9 +304,9 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.sectionToFieldsMap = this.prepareErrorIndication();
if (this.editorModel.status == PlanStatus.Finalized || this.isDeleted) {
if (this.editorModel.status == PlanStatusEnum.Finalized || this.isDeleted) {
this.viewOnly = true;
this.isFinalized = this.editorModel.status == PlanStatus.Finalized;
this.isFinalized = this.editorModel.status == PlanStatusEnum.Finalized;
this.formGroup.disable();
} else {
this.viewOnly = false;
@ -593,7 +593,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
label: this.formGroup.get('label').value,
description: this.formGroup.get('description').value,
blueprint: this.selectedBlueprint,
status: PlanStatus.Draft
status: PlanStatusEnum.Draft
}
this.prepareForm(plan);

View File

@ -2,7 +2,7 @@ import { FormArray, FormControl, UntypedFormBuilder, UntypedFormGroup, Validator
import { PlanAccessType } from "@app/core/common/enum/plan-access-type";
import { PlanBlueprintFieldCategory } from "@app/core/common/enum/plan-blueprint-field-category";
import { PlanBlueprintSystemFieldType } from "@app/core/common/enum/plan-blueprint-system-field-type";
import { PlanStatus } from "@app/core/common/enum/plan-status";
import { PlanStatusEnum } from "@app/core/common/enum/plan-status";
import { PlanUserRole } from "@app/core/common/enum/plan-user-role";
import { PlanUserType } from "@app/core/common/enum/plan-user-type";
import { IsActive } from "@app/core/common/enum/is-active.enum";
@ -18,7 +18,7 @@ import { Guid } from "@common/types/guid";
export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
label: string;
status: PlanStatus;
status: PlanStatusEnum;
properties: PlanPropertiesEditorModel = new PlanPropertiesEditorModel(this.validationErrorModel);
description: String;
language: String;

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES":"Annotation Statuses",
"SUPPORTIVE-MATERIAL":"Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",

View File

@ -179,7 +179,8 @@
"HOME": "Home",
"ANNOTATION-STATUSES": "Annotation Statuses",
"SUPPORTIVE-MATERIAL": "Supportive Material",
"USAGE-LIMITS": "Usage Limits"
"USAGE-LIMITS": "Usage Limits",
"PLAN-STATUSES": "Plan Statuses"
},
"FILE-TRANSFORMER": {
"PDF": "PDF",
@ -829,6 +830,54 @@
"UNAUTHORIZED-ORCID": "This ORCID is entered by the Plan creators, without further acknowledgement of the owner."
}
},
"PLAN-STATUS-LISTING": {
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"STATUS": "Status",
"UPDATED-AT": "Updated",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
},
"FILTER": {
"TITLE": "Filters",
"IS-ACTIVE": "Is Active",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
"ACTIONS": {
"DELETE": "Delete",
"EDIT": "Edit",
"CREATE-PLAN-STATUS": "Create Plan Status"
}
},
"PLAN-STATUS-EDITOR": {
"TITLE": {
"NEW": "New Plan Blueprint",
"EDIT": "Edit Plan Blueprint"
},
"FIELDS": {
"NAME": "Name",
"DESCRIPTION": "Description",
"DEFINITION": "Definition",
"INTERNAL-STATUS": "Internal Status",
"AUTHORIZATION": "Authorization",
"EDIT": "Edit",
"ALLOW-AUTHENTICATED": "Allow authenticated users",
"ALLOW-ANONYMOUS": "Allow anonymous users",
"ROLES": "User roles",
"PLAN-ROLES": "User plan roles"
},
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel",
"DELETE": "Delete"
},
"LOCKED-DIALOG": {
"TITLE": "Plan Status is locked",
"MESSAGE": "Somebody else is modifying the Plan Status at this moment. You may view the Plan Status but you cannot make any changes. If you would like to modify it please come back later."
}
},
"DESCRIPTION-OVERVIEW": {
"TITLE": "Description",
"PUBLIC": "Public",