Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring
This commit is contained in:
commit
d3d57e8838
|
@ -1,6 +1,8 @@
|
||||||
package eu.eudat.model.mapper.publicapi;
|
package eu.eudat.model.mapper.publicapi;
|
||||||
|
|
||||||
import eu.eudat.model.Dmp;
|
import eu.eudat.model.Dmp;
|
||||||
|
import eu.eudat.model.publicapi.grant.GrantPublicOverviewModel;
|
||||||
|
import eu.eudat.model.publicapi.organisation.OrganizationPublicModel;
|
||||||
import eu.eudat.model.publicapi.overviewmodels.DataManagementPlanPublicModel;
|
import eu.eudat.model.publicapi.overviewmodels.DataManagementPlanPublicModel;
|
||||||
import eu.eudat.model.publicapi.researcher.ResearcherPublicModel;
|
import eu.eudat.model.publicapi.researcher.ResearcherPublicModel;
|
||||||
import eu.eudat.model.publicapi.user.UserInfoPublicModel;
|
import eu.eudat.model.publicapi.user.UserInfoPublicModel;
|
||||||
|
@ -22,6 +24,8 @@ public class DmpToPublicApiDmpMapper {
|
||||||
|
|
||||||
model.setUsers(dmp.getDmpUsers().stream().map(UserInfoPublicModel::fromDmpUser).toList());
|
model.setUsers(dmp.getDmpUsers().stream().map(UserInfoPublicModel::fromDmpUser).toList());
|
||||||
model.setResearchers(dmp.getDmpReferences().stream().map(ResearcherPublicModel::fromDmpReference).filter(Objects::isNull).toList());
|
model.setResearchers(dmp.getDmpReferences().stream().map(ResearcherPublicModel::fromDmpReference).filter(Objects::isNull).toList());
|
||||||
|
model.setGrant(GrantPublicOverviewModel.fromDmpReferences(dmp.getDmpReferences()));
|
||||||
|
model.setOrganisations(dmp.getDmpReferences().stream().map(OrganizationPublicModel::fromDmpReference).filter(Objects::isNull).toList());
|
||||||
|
|
||||||
model.setCreatedAt(Date.from(dmp.getCreatedAt()));
|
model.setCreatedAt(Date.from(dmp.getCreatedAt()));
|
||||||
model.setModifiedAt(Date.from(dmp.getUpdatedAt()));
|
model.setModifiedAt(Date.from(dmp.getUpdatedAt()));
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
package eu.eudat.model.publicapi.grant;
|
package eu.eudat.model.publicapi.grant;
|
||||||
|
|
||||||
|
import eu.eudat.commons.enums.ReferenceType;
|
||||||
import eu.eudat.data.old.Grant;
|
import eu.eudat.data.old.Grant;
|
||||||
|
import eu.eudat.model.DmpReference;
|
||||||
|
import eu.eudat.model.Reference;
|
||||||
import eu.eudat.model.publicapi.funder.FunderPublicOverviewModel;
|
import eu.eudat.model.publicapi.funder.FunderPublicOverviewModel;
|
||||||
|
import eu.eudat.model.referencedefinition.Field;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class GrantPublicOverviewModel {
|
public class GrantPublicOverviewModel {
|
||||||
|
@ -72,6 +78,37 @@ public class GrantPublicOverviewModel {
|
||||||
this.funder = funder;
|
this.funder = funder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GrantPublicOverviewModel fromDmpReferences(List<DmpReference> dmpReferences) {
|
||||||
|
FunderPublicOverviewModel funder = null;
|
||||||
|
for (DmpReference dmpReference : dmpReferences) {
|
||||||
|
if (dmpReference.getReference().getType() == ReferenceType.Funder) {
|
||||||
|
funder = new FunderPublicOverviewModel();
|
||||||
|
Reference reference = dmpReference.getReference();
|
||||||
|
funder.setId(reference.getId());
|
||||||
|
funder.setLabel(reference.getLabel());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dmpReference.getReference().getType() != ReferenceType.Grants)
|
||||||
|
continue;
|
||||||
|
GrantPublicOverviewModel model = new GrantPublicOverviewModel();
|
||||||
|
Reference reference = dmpReference.getReference();
|
||||||
|
model.setId(reference.getId());
|
||||||
|
model.setDescription(reference.getDescription());
|
||||||
|
model.setAbbreviation(reference.getAbbreviation());
|
||||||
|
model.setLabel(reference.getLabel());
|
||||||
|
model.setFunder(funder);
|
||||||
|
Field startDate = reference.getDefinition().getFields().stream().filter(x -> x.getCode().equals("startDate")).toList().get(0);
|
||||||
|
if (startDate != null) model.setStartDate(Date.from(Instant.parse(startDate.getValue())));
|
||||||
|
Field endDate = reference.getDefinition().getFields().stream().filter(x -> x.getCode().equals("endDate")).toList().get(0);
|
||||||
|
if (startDate != null) model.setEndDate(Date.from(Instant.parse(endDate.getValue())));
|
||||||
|
Field uri = reference.getDefinition().getFields().stream().filter(x -> x.getCode().equals("uri")).toList().get(0);
|
||||||
|
if (uri != null) model.setUri(uri.getValue());
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public GrantPublicOverviewModel fromDataModel(Grant entity) {
|
public GrantPublicOverviewModel fromDataModel(Grant entity) {
|
||||||
this.id = entity.getId();
|
this.id = entity.getId();
|
||||||
this.label = entity.getLabel();
|
this.label = entity.getLabel();
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package eu.eudat.model.publicapi.organisation;
|
package eu.eudat.model.publicapi.organisation;
|
||||||
|
|
||||||
|
import eu.eudat.commons.enums.ReferenceType;
|
||||||
import eu.eudat.data.old.Organisation;
|
import eu.eudat.data.old.Organisation;
|
||||||
|
import eu.eudat.model.DmpReference;
|
||||||
|
import eu.eudat.model.Reference;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -64,55 +67,18 @@ public class OrganizationPublicModel {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrganizationPublicModel fromDataModel(Organisation entity) {
|
public static OrganizationPublicModel fromDmpReference(DmpReference dmpReference) {
|
||||||
this.id = entity.getId().toString();
|
if (dmpReference.getReference().getType() != ReferenceType.Organizations)
|
||||||
this.name = entity.getLabel();
|
return null;
|
||||||
this.label = entity.getUri();
|
|
||||||
if (entity.getReference() != null) {
|
|
||||||
this.reference = entity.getReference();
|
|
||||||
this.key = entity.getReference().substring(0, entity.getReference().indexOf(":"));
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Organisation toDataModel() {
|
|
||||||
Organisation organisationEntity = new Organisation();
|
|
||||||
if (this.key != null && this.reference != null) {
|
|
||||||
if (this.reference.startsWith(this.key + ":")) {
|
|
||||||
organisationEntity.setReference(this.reference);
|
|
||||||
} else {
|
|
||||||
organisationEntity.setReference(this.key + ":" + this.reference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.id != null) {
|
|
||||||
organisationEntity.setId(UUID.fromString(this.id));
|
|
||||||
}
|
|
||||||
organisationEntity.setLabel(this.name);
|
|
||||||
organisationEntity.setUri(this.label);
|
|
||||||
organisationEntity.setCreated(new Date());
|
|
||||||
organisationEntity.setStatus((short) this.status);
|
|
||||||
return organisationEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrganizationPublicModel fromMap(HashMap<String, Object> map) {
|
|
||||||
OrganizationPublicModel model = new OrganizationPublicModel();
|
OrganizationPublicModel model = new OrganizationPublicModel();
|
||||||
if (map != null) {
|
Reference reference = dmpReference.getReference();
|
||||||
model.id = (String) map.get("id");
|
model.setId(reference.getId().toString());
|
||||||
model.key = (String) map.get("key");
|
model.setReference(reference.getReference());
|
||||||
model.label = (String) map.get("label");
|
model.setLabel(reference.getLabel());
|
||||||
model.name = (String) map.get("name");
|
model.setName(reference.getLabel());
|
||||||
model.reference = (String) map.get("reference");
|
if (reference.getReference() != null)
|
||||||
model.status = (int) map.get("status");
|
model.setKey(reference.getReference().substring(0, reference.getReference().indexOf(":")));
|
||||||
model.tag = (String) map.get("tag");
|
|
||||||
}
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateLabel() {
|
|
||||||
return this.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHint() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,11 @@ package eu.eudat.model.publicapi.researcher;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import eu.eudat.commons.enums.ReferenceType;
|
import eu.eudat.commons.enums.ReferenceType;
|
||||||
import eu.eudat.data.old.Researcher;
|
|
||||||
import eu.eudat.model.DmpReference;
|
import eu.eudat.model.DmpReference;
|
||||||
|
import eu.eudat.model.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class ResearcherPublicModel {
|
public class ResearcherPublicModel {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ResearcherPublicModel.class);
|
private static final Logger logger = LoggerFactory.getLogger(ResearcherPublicModel.class);
|
||||||
|
@ -74,10 +71,11 @@ public class ResearcherPublicModel {
|
||||||
if (dmpReference.getReference().getType() != ReferenceType.Researcher)
|
if (dmpReference.getReference().getType() != ReferenceType.Researcher)
|
||||||
return null;
|
return null;
|
||||||
ResearcherPublicModel model = new ResearcherPublicModel();
|
ResearcherPublicModel model = new ResearcherPublicModel();
|
||||||
model.setId(dmpReference.getReference().getId().toString());
|
Reference reference = dmpReference.getReference();
|
||||||
model.setReference(dmpReference.getReference().getReference());
|
model.setId(reference.getId().toString());
|
||||||
model.setLabel(dmpReference.getReference().getLabel());
|
model.setReference(reference.getReference());
|
||||||
model.setName(dmpReference.getReference().getLabel());
|
model.setLabel(reference.getLabel());
|
||||||
|
model.setName(reference.getLabel());
|
||||||
String[] refParts = dmpReference.getReference().getReference().split(":");
|
String[] refParts = dmpReference.getReference().getReference().split(":");
|
||||||
String source = refParts[0];
|
String source = refParts[0];
|
||||||
if (source.equals("dmp"))
|
if (source.equals("dmp"))
|
||||||
|
|
|
@ -161,6 +161,9 @@ public class PublicDmpsDocumentation extends BaseController {
|
||||||
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._id)),
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._id)),
|
||||||
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._reference)),
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._reference)),
|
||||||
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._label)),
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._label)),
|
||||||
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._abbreviation)),
|
||||||
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._description)),
|
||||||
|
String.join(".", Dmp._dmpReferences, String.join(".", DmpReference._reference, Reference._definition)),
|
||||||
Dmp._createdAt,
|
Dmp._createdAt,
|
||||||
Dmp._updatedAt,
|
Dmp._updatedAt,
|
||||||
Dmp._finalizedAt
|
Dmp._finalizedAt
|
||||||
|
|
|
@ -217,7 +217,7 @@ const appRoutes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
loadChildren: () => import('./ui/admin/user/user.module').then(m => m.UserModule),
|
loadChildren: () => import('./ui/admin/user/user.module').then(m => m.UsersModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
title: 'GENERAL.TITLES.USERS'
|
title: 'GENERAL.TITLES.USERS'
|
||||||
|
|
|
@ -34,6 +34,12 @@ export enum AppPermission {
|
||||||
EditTenant = "EditTenant",
|
EditTenant = "EditTenant",
|
||||||
DeleteTenant = "DeleteTenant",
|
DeleteTenant = "DeleteTenant",
|
||||||
|
|
||||||
|
//User
|
||||||
|
BrowseUser = "BrowseUser",
|
||||||
|
EditUser = "EditUser",
|
||||||
|
DeleteUser = "DeleteUser",
|
||||||
|
ExportUsers = "ExportUsers",
|
||||||
|
|
||||||
//Reference
|
//Reference
|
||||||
BrowseReference = "BrowseReference",
|
BrowseReference = "BrowseReference",
|
||||||
EditReference = "EditReference",
|
EditReference = "EditReference",
|
||||||
|
|
|
@ -29,14 +29,20 @@ import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type
|
||||||
export class EnumUtils {
|
export class EnumUtils {
|
||||||
constructor(private language: TranslateService) { }
|
constructor(private language: TranslateService) { }
|
||||||
|
|
||||||
public getEnumValues(T): Array<any> {
|
public getEnumValues<T>(T): Array<T> {
|
||||||
let keys = Object.keys(T);
|
|
||||||
keys = keys.slice(0, keys.length / 2);
|
//getting all numeric values
|
||||||
const values: Array<Number> = keys.map(Number);
|
const numericValues: T[] = Object.keys(T).map(key => T[key]).filter(value => typeof (value) === 'number');
|
||||||
return values;
|
if (numericValues.length > 0) { return numericValues; }
|
||||||
|
|
||||||
|
//getting all string values
|
||||||
|
const stringValues: T[] = Object.keys(T).map(key => T[key]).filter(value => typeof (value) === 'string');
|
||||||
|
if (stringValues.length > 0) { return stringValues; }
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
convertFromPrincipalAppRole(status: AppRole): string {
|
toAppRoleString(status: AppRole): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN');
|
case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN');
|
||||||
case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER');
|
case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER');
|
||||||
|
|
|
@ -57,9 +57,9 @@ export class DmpBlueprintEditorComponent extends BaseEditor<DmpBlueprintEditorMo
|
||||||
selectedSystemFields: Array<DmpBlueprintSystemFieldType> = [];
|
selectedSystemFields: Array<DmpBlueprintSystemFieldType> = [];
|
||||||
dmpBlueprintSectionFieldCategory = DmpBlueprintSectionFieldCategory;
|
dmpBlueprintSectionFieldCategory = DmpBlueprintSectionFieldCategory;
|
||||||
dmpBlueprintSystemFieldType = DmpBlueprintSystemFieldType;
|
dmpBlueprintSystemFieldType = DmpBlueprintSystemFieldType;
|
||||||
public dmpBlueprintSystemFieldTypeEnum = this.enumUtils.getEnumValues(DmpBlueprintSystemFieldType);
|
public dmpBlueprintSystemFieldTypeEnum = this.enumUtils.getEnumValues<DmpBlueprintSystemFieldType>(DmpBlueprintSystemFieldType);
|
||||||
dmpBlueprintExtraFieldDataType = DmpBlueprintExtraFieldDataType;
|
dmpBlueprintExtraFieldDataType = DmpBlueprintExtraFieldDataType;
|
||||||
public dmpBlueprintExtraFieldDataTypeEnum = this.enumUtils.getEnumValues(DmpBlueprintExtraFieldDataType);
|
public dmpBlueprintExtraFieldDataTypeEnum = this.enumUtils.getEnumValues<DmpBlueprintExtraFieldDataType>(DmpBlueprintExtraFieldDataType);
|
||||||
|
|
||||||
blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
||||||
filterFn: this.filterDescriptionTempaltes.bind(this),
|
filterFn: this.filterDescriptionTempaltes.bind(this),
|
||||||
|
|
|
@ -3,15 +3,17 @@ import { Component, OnInit } from '@angular/core';
|
||||||
import { FormArray, UntypedFormGroup } from '@angular/forms';
|
import { FormArray, UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
|
||||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
|
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
|
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
|
||||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
import { ReferenceType, ReferenceTypePersist, ResultFieldsMappingConfiguration } from '@app/core/model/reference-type/reference-type';
|
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
|
||||||
|
import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/reference-type-external-api-http-method-type';
|
||||||
|
import { ReferenceTypeSourceType } from '@app/core/common/enum/reference-type-source-type';
|
||||||
|
import { ReferenceType, ReferenceTypePersist } from '@app/core/model/reference-type/reference-type';
|
||||||
import { AuthService } from '@app/core/services/auth/auth.service';
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
import { LoggingService } from '@app/core/services/logging/logging-service';
|
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
|
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
|
||||||
|
@ -23,12 +25,9 @@ import { FilterService } from '@common/modules/text-filter/filter-service';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { map, takeUntil } from 'rxjs/operators';
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import { DependencyPropertyEditorModel, QueryConfigEditorModel, ReferenceTypeEditorModel, ReferenceTypeFieldEditorModel, ReferenceTypeSourceBaseConfigurationEditorModel, ReferenceTypeSourceBaseDependencyEditorModel, ReferenceTypeStaticOptionEditorModel, ResultFieldsMappingConfigurationEditorModel } from './reference-type-editor.model';
|
||||||
import { ReferenceTypeEditorResolver } from './reference-type-editor.resolver';
|
import { ReferenceTypeEditorResolver } from './reference-type-editor.resolver';
|
||||||
import { ReferenceTypeEditorService } from './reference-type-editor.service';
|
import { ReferenceTypeEditorService } from './reference-type-editor.service';
|
||||||
import { DependencyPropertyEditorModel, QueryConfigEditorModel, ReferenceTypeEditorModel, ReferenceTypeFieldEditorModel, ReferenceTypeSourceBaseConfigurationEditorModel, ReferenceTypeSourceBaseDependencyEditorModel, ReferenceTypeStaticOptionEditorModel, ResultFieldsMappingConfigurationEditorModel } from './reference-type-editor.model';
|
|
||||||
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
|
|
||||||
import { ReferenceTypeSourceType } from '@app/core/common/enum/reference-type-source-type';
|
|
||||||
import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/reference-type-external-api-http-method-type';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -45,9 +44,9 @@ export class ReferenceTypeEditorComponent extends BaseEditor<ReferenceTypeEditor
|
||||||
showInactiveDetails = false;
|
showInactiveDetails = false;
|
||||||
referenceTypeSourceType = ReferenceTypeSourceType;
|
referenceTypeSourceType = ReferenceTypeSourceType;
|
||||||
referenceTypeExternalApiHTTPMethodType = ReferenceTypeExternalApiHTTPMethodType;
|
referenceTypeExternalApiHTTPMethodType = ReferenceTypeExternalApiHTTPMethodType;
|
||||||
public referenceTypeSourceTypeEnum = this.enumUtils.getEnumValues(ReferenceTypeSourceType);
|
public referenceTypeSourceTypeEnum = this.enumUtils.getEnumValues<ReferenceTypeSourceType>(ReferenceTypeSourceType);
|
||||||
public referenceFieldDataTypeEnum = this.enumUtils.getEnumValues(ReferenceFieldDataType);
|
public referenceFieldDataTypeEnum = this.enumUtils.getEnumValues<ReferenceFieldDataType>(ReferenceFieldDataType);
|
||||||
public referenceTypeExternalApiHTTPMethodTypeEnum = this.enumUtils.getEnumValues(ReferenceTypeExternalApiHTTPMethodType);
|
public referenceTypeExternalApiHTTPMethodTypeEnum = this.enumUtils.getEnumValues<ReferenceTypeExternalApiHTTPMethodType>(ReferenceTypeExternalApiHTTPMethodType);
|
||||||
referenceTypes: ReferenceType[] = null;
|
referenceTypes: ReferenceType[] = null;
|
||||||
sourceKeys: string[] = [];
|
sourceKeys: string[] = [];
|
||||||
sourceKeysMap: Map<string, string[]> = new Map<string, string[]>();
|
sourceKeysMap: Map<string, string[]> = new Map<string, string[]>();
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<form class="user-roles-criteria" [formGroup]="formGroup">
|
|
||||||
<div class="container-fluid p-0">
|
|
||||||
<div class="row">
|
|
||||||
<span class="d-flex col-auto align-items-center pr-0 show">{{'CRITERIA.USERS.SHOW' | translate}}:</span>
|
|
||||||
<mat-form-field appearance="outline" class="sort-form col-auto">
|
|
||||||
<mat-select placeholder="{{'CRITERIA.USERS.ROLE' | translate}}" formControlName="appRoles" multiple>
|
|
||||||
<mat-option *ngFor="let role of rolesEnum" [value]="role">{{enumUtils.convertFromPrincipalAppRole(role)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="search-form ml-auto col-auto" floatLabel="never">
|
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
|
||||||
<input matInput placeholder="{{'CRITERIA.USERS.LABEL'| translate}}" formControlName="like">
|
|
||||||
<!-- <mat-error *ngIf="formGroup.get('like').hasError('backendError')">{{formGroup.get('like').getError('backendError').message}}</mat-error> -->
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -1,33 +0,0 @@
|
||||||
.user-roles-criteria {
|
|
||||||
|
|
||||||
.container-fluid {
|
|
||||||
margin-top: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show {
|
|
||||||
color: #A8A8A8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
|
||||||
text-align: left;
|
|
||||||
width: 20rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form mat-icon {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .search-form .mat-form-field-wrapper {
|
|
||||||
background-color: white !important;
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .sort-form .mat-form-field-wrapper {
|
|
||||||
background-color: white !important;
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
|
|
||||||
padding: 0.3rem 0rem 0.6rem 0rem !important;
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
|
||||||
import { AppRole } from '@app/core/common/enum/app-role';
|
|
||||||
import { UserCriteria } from '@app/core/query/user/user-criteria';
|
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
|
||||||
import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component';
|
|
||||||
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
|
||||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-user-criteria-component',
|
|
||||||
templateUrl: './user-criteria.component.html',
|
|
||||||
styleUrls: ['./user-criteria.component.scss']
|
|
||||||
})
|
|
||||||
export class UserCriteriaComponent extends BaseCriteriaComponent implements OnInit {
|
|
||||||
|
|
||||||
public rolesEnum = this.enumUtils.getEnumValues(AppRole);
|
|
||||||
public criteria: UserCriteria = new UserCriteria();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private formBuilder: UntypedFormBuilder,
|
|
||||||
public enumUtils: EnumUtils
|
|
||||||
) {
|
|
||||||
super(new ValidationErrorModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
super.ngOnInit();
|
|
||||||
if (this.criteria == null) { this.criteria = new UserCriteria(); }
|
|
||||||
if (this.formGroup == null) { this.formGroup = this.buildForm(); }
|
|
||||||
this.registerListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
setCriteria(criteria: UserCriteria): void {
|
|
||||||
this.criteria = criteria;
|
|
||||||
this.formGroup = this.buildForm();
|
|
||||||
this.registerListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
registerListeners() {
|
|
||||||
this.formGroup.valueChanges
|
|
||||||
.pipe(takeUntil(this._destroyed), debounceTime(300))
|
|
||||||
.subscribe(x => this.controlModified());
|
|
||||||
}
|
|
||||||
public fromJSONObject(item: any): UserCriteria {
|
|
||||||
this.criteria = new UserCriteria();
|
|
||||||
this.criteria.label = item.Label;
|
|
||||||
this.criteria.appRoles = item.appRoles;
|
|
||||||
return this.criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildForm(): UntypedFormGroup {
|
|
||||||
return this.formBuilder.group({
|
|
||||||
like: [this.criteria.label, []],
|
|
||||||
appRoles: [this.criteria.appRoles, []],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<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="p-3" (click)="$event?.stopPropagation?.()">
|
||||||
|
<div class="search-listing-filters-container">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<h4>{{'USER-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'USER-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||||
|
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
|
{{'USER-LISTING.FILTER.CANCEL' | translate}}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
|
{{'USER-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<app-expandable-search-field [(value)]=internalFilters.like (valueChange)="onSearchTermChange($event)" />
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
.user-listing-filters {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::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;
|
||||||
|
// .mat-icon{
|
||||||
|
// font-size: 1.5em;
|
||||||
|
// width: 1.2em;
|
||||||
|
// height: 1.2em;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||||
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
|
import { UserFilter } from '@app/core/query/user.lookup';
|
||||||
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-user-listing-filters',
|
||||||
|
templateUrl: './user-listing-filters.component.html',
|
||||||
|
styleUrls: ['./user-listing-filters.component.scss']
|
||||||
|
})
|
||||||
|
export class UserListingFiltersComponent extends BaseComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
|
@Input() readonly filter: UserFilter;
|
||||||
|
@Output() filterChange = new EventEmitter<UserFilter>();
|
||||||
|
|
||||||
|
// * State
|
||||||
|
internalFilters: UserListingFilters = this._getEmptyFilters();
|
||||||
|
|
||||||
|
protected appliedFilterCount: number = 0;
|
||||||
|
constructor(
|
||||||
|
public enumUtils: EnumUtils,
|
||||||
|
) { super(); }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const filterChange = changes[nameof<UserListingFiltersComponent>(x => x.filter)]?.currentValue as UserFilter;
|
||||||
|
if (filterChange) {
|
||||||
|
this.updateFilters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSearchTermChange(searchTerm: string): void {
|
||||||
|
this.applyFilters()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _parseToInternalFilters(inputFilter: UserFilter): UserListingFilters {
|
||||||
|
if (!inputFilter) {
|
||||||
|
return this._getEmptyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
let { excludedIds, ids, isActive, like } = inputFilter;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length,
|
||||||
|
like: like
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getEmptyFilters(): UserListingFilters {
|
||||||
|
return {
|
||||||
|
isActive: true,
|
||||||
|
like: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeAppliedFilters(filters: UserListingFilters): number {
|
||||||
|
let count = 0;
|
||||||
|
if (filters?.isActive) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFilters() {
|
||||||
|
this.internalFilters = this._getEmptyFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserListingFilters {
|
||||||
|
isActive: boolean;
|
||||||
|
like: string;
|
||||||
|
}
|
|
@ -1,28 +1,27 @@
|
||||||
<form class="row user-role-editor" [formGroup]="formGroup" (ngSubmit)="formSubmit()">
|
<form class="row user-role-editor" *ngIf="formGroup" [formGroup]="formGroup" (ngSubmit)="formSubmit()">
|
||||||
<div *ngIf="!this.nowEditing"class="roles col">
|
<div *ngIf="!this.nowEditing"class="roles col">
|
||||||
<ng-container *ngFor="let role of this.formGroup.get('appRoles').value">
|
<ng-container *ngFor="let role of this.formGroup.get('roles').value">
|
||||||
<div>
|
<div>
|
||||||
<span class="user-role" [ngClass]="{'user': role == 0, 'manager': role == 1, 'admin': role == 2, 'dataset-template-editor': role == 3}">
|
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'manager': role == appRole.Manager, 'admin': role == appRole.Admin, 'dataset-template-editor': role == appRole.DatasetTemplateEditor}">
|
||||||
{{getPrincipalAppRoleWithLanguage(role)}}
|
{{enumUtils.toAppRoleString(role)}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<mat-form-field *ngIf="this.nowEditing" class="select-role roles-width-180 col-auto">
|
<mat-form-field *ngIf="this.nowEditing" class="select-role roles-width-180 col-auto">
|
||||||
<mat-select formControlName="appRoles" multiple required>
|
<mat-select formControlName="roles" multiple required>
|
||||||
<ng-container *ngFor="let role of getPrincipalAppRoleValues()">
|
<ng-container *ngFor="let role of appRoleEnumValues">
|
||||||
<mat-option [value]="role">{{getPrincipalAppRoleWithLanguage(role)}}</mat-option>
|
<mat-option [value]="role">{{enumUtils.toAppRoleString(role)}}</mat-option>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
<mat-error *ngIf="getFormControl('appRoles').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="formGroup.get('roles').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<button *ngIf="!this.nowEditing" class="col-auto" mat-icon-button color="primary" type="button" (click)="editItem()">
|
<button *ngIf="!this.nowEditing" class="col-auto" mat-icon-button color="primary" type="button" (click)="editItem()">
|
||||||
<!-- <mat-icon class="mat-24">edit</mat-icon> -->
|
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.EDIT' | translate}}">edit</mat-icon>
|
||||||
<span class="row-action">{{'USERS.ACTIONS.EDIT' | translate}}</span>
|
<span class="row-action"></span>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="this.nowEditing" class="col-auto" mat-icon-button color="primary" type="submit">
|
<button *ngIf="this.nowEditing" class="col-auto save-button" mat-icon-button color="primary" type="submit">
|
||||||
<!-- <mat-icon class="mat-24">save</mat-icon> -->
|
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.SAVE' | translate}}">save</mat-icon>
|
||||||
<span class="row-action">{{'USERS.ACTIONS.SAVE' | translate}}</span>
|
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -102,6 +102,10 @@
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.save-button {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep .mat-form-field-wrapper {
|
:host ::ng-deep .mat-form-field-wrapper {
|
||||||
|
|
|
@ -1,68 +1,89 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, UntypedFormGroup } from '@angular/forms';
|
||||||
import { AppRole } from '@app/core/common/enum/app-role';
|
import { User, UserRolePatchPersist } from '@app/core/model/user/user';
|
||||||
import { UserListingModel } from '@app/core/model/user/user-listing';
|
|
||||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
import { UserServiceOld } from '@app/core/services/user/user.service-old';
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
|
import { BaseEditor } from '@common/base/base-editor';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
|
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { UserRolePatchEditorModel } from './user-role-editor.model';
|
||||||
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
|
import { DescriptionTemplateTypeEditorService } from '@app/ui/admin/description-types/editor/description-template-type-editor.service';
|
||||||
|
import { UserRoleEditorService } from './user-role-editor.service';
|
||||||
|
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
|
import { FormService } from '@common/forms/form-service';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { AppRole } from '@app/core/common/enum/app-role';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-role-editor-component',
|
selector: 'app-user-role-editor-component',
|
||||||
templateUrl: './user-role-editor.component.html',
|
templateUrl: './user-role-editor.component.html',
|
||||||
styleUrls: ['./user-role-editor.component.scss']
|
styleUrls: ['./user-role-editor.component.scss'],
|
||||||
|
providers: [UserRoleEditorService]
|
||||||
})
|
})
|
||||||
export class UserRoleEditorComponent extends BaseComponent implements OnInit {
|
export class UserRoleEditorComponent extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
@Input() public item: UserListingModel;
|
@Input() public item: User;
|
||||||
public formGroup: UntypedFormGroup = null;
|
public formGroup: UntypedFormGroup = null;
|
||||||
public nowEditing = false;
|
public nowEditing = false;
|
||||||
|
lookupParams: any;
|
||||||
|
editorModel: UserRolePatchEditorModel;
|
||||||
|
appRole = AppRole;
|
||||||
|
public appRoleEnumValues = this.enumUtils.getEnumValues<AppRole>(AppRole);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private language: TranslateService,
|
private language: TranslateService,
|
||||||
private userService: UserServiceOld,
|
private userService: UserService,
|
||||||
private formBuilder: UntypedFormBuilder,
|
private formService: FormService,
|
||||||
|
private logger: LoggingService,
|
||||||
private enumUtils: EnumUtils,
|
private enumUtils: EnumUtils,
|
||||||
private uiNotificationService: UiNotificationService
|
private uiNotificationService: UiNotificationService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private userRoleEditorService: UserRoleEditorService,
|
||||||
|
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
) { super(); }
|
) { super(); }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.formGroup == null) { this.formGroup = this.buildForm(); }
|
if (this.formGroup == null) { this.prepareForm(this.item); }
|
||||||
}
|
}
|
||||||
|
|
||||||
buildForm(): UntypedFormGroup {
|
prepareForm(data: User) {
|
||||||
const context: ValidationContext = this.createValidationContext();
|
try {
|
||||||
|
this.editorModel = data ? new UserRolePatchEditorModel().fromModel(data) : new UserRolePatchEditorModel();
|
||||||
return this.formBuilder.group({
|
this.buildForm();
|
||||||
appRoles: new UntypedFormControl({ value: this.item.appRoles, disabled: true }, context.getValidation('appRoles').validators)
|
} catch (error) {
|
||||||
});
|
this.logger.error('Could not parse user role item: ' + data + error);
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidationContext(): ValidationContext {
|
buildForm() {
|
||||||
const validationContext: ValidationContext = new ValidationContext();
|
this.formGroup = this.editorModel.buildForm(null, this.authService.hasPermission(AppPermission.EditUser));
|
||||||
const validationArray: Validation[] = new Array<Validation>();
|
this.userRoleEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
|
||||||
|
}
|
||||||
|
|
||||||
validationArray.push({ key: 'appRoles' });
|
persistEntity(onSuccess?: (response) => void): void {
|
||||||
|
const formData = this.formService.getValue(this.formGroup.value) as UserRolePatchPersist;
|
||||||
|
|
||||||
validationContext.validation = validationArray;
|
this.userService.persistRoles(formData)
|
||||||
return validationContext;
|
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
|
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
formSubmit(): void {
|
formSubmit(): void {
|
||||||
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
|
if (!this.isFormValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const modifiedItem = this.item;
|
this.persistEntity();
|
||||||
modifiedItem.appRoles = this.getFormControl('appRoles').value;
|
|
||||||
|
|
||||||
if (!this.isFormValid()) { return; }
|
|
||||||
this.userService.updateRoles(modifiedItem)
|
|
||||||
.pipe(takeUntil(this._destroyed))
|
|
||||||
.subscribe(
|
|
||||||
(res) => this.onCallbackSuccess(),
|
|
||||||
(error) => this.onCallbackError(error)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editItem(): void {
|
editItem(): void {
|
||||||
|
@ -70,75 +91,28 @@ export class UserRoleEditorComponent extends BaseComponent implements OnInit {
|
||||||
this.nowEditing = true;
|
this.nowEditing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFormValid(): boolean {
|
public isFormValid() {
|
||||||
this.touchAllFormFields(this.formGroup);
|
|
||||||
this.validateAllFormFields(this.formGroup);
|
|
||||||
return this.formGroup.valid;
|
return this.formGroup.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormData(): any {
|
|
||||||
return this.formGroup.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFormControl(controlName: string): AbstractControl {
|
|
||||||
return this.formGroup.get(controlName);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateAllFormFields(formControl: AbstractControl) {
|
|
||||||
if (formControl instanceof UntypedFormControl) {
|
|
||||||
formControl.updateValueAndValidity({ emitEvent: false });
|
|
||||||
} else if (formControl instanceof UntypedFormGroup) {
|
|
||||||
Object.keys(formControl.controls).forEach(item => {
|
|
||||||
const control = formControl.get(item);
|
|
||||||
this.validateAllFormFields(control);
|
|
||||||
});
|
|
||||||
} else if (formControl instanceof UntypedFormArray) {
|
|
||||||
formControl.controls.forEach(item => {
|
|
||||||
this.validateAllFormFields(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
touchAllFormFields(formControl: AbstractControl) {
|
|
||||||
if (formControl instanceof UntypedFormControl) {
|
|
||||||
formControl.markAsTouched();
|
|
||||||
} else if (formControl instanceof UntypedFormGroup) {
|
|
||||||
Object.keys(formControl.controls).forEach(item => {
|
|
||||||
const control = formControl.get(item);
|
|
||||||
this.touchAllFormFields(control);
|
|
||||||
});
|
|
||||||
} else if (formControl instanceof UntypedFormArray) {
|
|
||||||
formControl.controls.forEach(item => {
|
|
||||||
this.touchAllFormFields(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCallbackSuccess() {
|
onCallbackSuccess() {
|
||||||
this.nowEditing = false;
|
this.nowEditing = false;
|
||||||
this.formGroup.disable();
|
this.formGroup.disable();
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCallbackError(error: any) {
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
this.validateAllFormFields(this.formGroup);
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Error);
|
if (error.statusCode === 400) {
|
||||||
|
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
} else {
|
||||||
|
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPrincipalAppRoleValues(): Number[] {
|
clearErrorModel() {
|
||||||
let keys: string[] = Object.keys(AppRole);
|
this.editorModel.validationErrorModel.clear();
|
||||||
keys = keys.slice(0, keys.length / 2);
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
const values: Number[] = keys.map(Number);
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrincipalAppRoleWithLanguage(role: AppRole): string {
|
|
||||||
let result = '';
|
|
||||||
this.language.get(this.enumUtils.convertFromPrincipalAppRole(role))
|
|
||||||
.pipe(takeUntil(this._destroyed))
|
|
||||||
.subscribe((value: string) => {
|
|
||||||
result = value;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup } from "@angular/forms";
|
||||||
|
import { User, UserRolePatchPersist } from "@app/core/model/user/user";
|
||||||
|
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
|
||||||
|
import { Guid } from "@common/types/guid";
|
||||||
|
|
||||||
|
export class UserRolePatchEditorModel implements UserRolePatchPersist {
|
||||||
|
id: Guid;
|
||||||
|
roles: String[] = [];
|
||||||
|
hash: string;
|
||||||
|
|
||||||
|
permissions: string[];
|
||||||
|
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
public fromModel(item: User): UserRolePatchEditorModel {
|
||||||
|
if (item) {
|
||||||
|
this.id = item.id;
|
||||||
|
if (item.roles != null)this.roles = item.roles.map(x => x.role);
|
||||||
|
this.hash = item.hash;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||||
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||||
|
roles: [{ value: this.roles, disabled: disabled }, context.getValidation('roles').validators],
|
||||||
|
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').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: 'roles', validators: [BackendErrorValidator(this.validationErrorModel, 'roles')] });
|
||||||
|
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserRoleEditorService {
|
||||||
|
private validationErrorModel: ValidationErrorModel;
|
||||||
|
|
||||||
|
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
|
||||||
|
this.validationErrorModel = validationErrorModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValidationErrorModel(): ValidationErrorModel {
|
||||||
|
return this.validationErrorModel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +1,77 @@
|
||||||
<div class="main-content">
|
<div class="row user-listing">
|
||||||
<div class="container-fluid user-listing">
|
<div class="col-md-8 offset-md-2">
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<h3>{{'USERS.LISTING.TITLE' | translate}}</h3>
|
<div class="row mb-4 mt-3">
|
||||||
<div class="col-auto ml-auto">
|
<div class="col">
|
||||||
<button mat-raised-button type="button" class="export-btn" (click)="exportUsers()">{{'USERS.LISTING.EXPORT' | translate}}</button>
|
<h4>{{'USER-LISTING.TITLE' | translate}}</h4>
|
||||||
|
<app-navigation-breadcrumb />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button mat-raised-button class="create-btn" (click)="export()" *ngIf="authService.hasPermission(authService.permissionEnum.ExportUsers)">
|
||||||
|
<mat-icon>download</mat-icon>
|
||||||
|
{{'USER-LISTING.ACTIONS.EXPORT' | translate}}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-user-criteria-component></app-user-criteria-component>
|
|
||||||
<!-- <mat-card class="mat-card"> -->
|
|
||||||
<div class="mat-elevation-z6">
|
|
||||||
<mat-table [dataSource]="dataSource" matSort>
|
|
||||||
|
|
||||||
<!-- Column Definition: Label -->
|
<app-hybrid-listing [rows]="gridRows" [columns]="gridColumns" [visibleColumns]="visibleColumns" [count]="totalElements" [offset]="currentPageNumber" [limit]="lookup.page.size" [defaultSort]="lookup.order?.items" [externalSorting]="true" (rowActivated)="onUserRowActivated($event)" (pageLoad)="alterPage($event)" (columnSort)="onColumnSort($event)" (columnsChanged)="onColumnsChanged($event)" [listItemTemplate]="listItemTemplate">
|
||||||
<ng-container cdkColumnDef="avatar">
|
|
||||||
<mat-header-cell *matHeaderCellDef></mat-header-cell>
|
<app-user-listing-filters hybrid-listing-filters [(filter)]="lookup" (filterChange)="filterChanged($event)" />
|
||||||
<mat-cell *matCellDef="let row">
|
|
||||||
<img mat-card-avatar *ngIf="row.avatarUrl" class="my-mat-card-avatar" [src]="row.avatarUrl" (error)="this.setDefaultAvatar($event)">
|
<app-user-settings-picker [key]="userSettingsKey" [userPreference]="lookup" (onSettingSelected)="changeSetting($event)" [autoSelectUserSettings]="autoSelectUserSettings" user-preference-settings />
|
||||||
<img mat-card-avatar *ngIf="!row.avatarUrl" class="my-mat-card-avatar" [src]="'assets/images/profile-placeholder.png'">
|
</app-hybrid-listing>
|
||||||
</mat-cell>
|
|
||||||
|
</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]="'./' + item?.id" class="col-12" (click)="$event.stopPropagation()">{{item?.name | nullifyValue}}</a>
|
||||||
|
<br />
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container cdkColumnDef="name">
|
<ng-container *ngIf="isColumnSelected('createdAt')">
|
||||||
<mat-header-cell *matHeaderCellDef>{{'USERS.LISTING.NAME' | translate}}</mat-header-cell>
|
<span class="col-12">
|
||||||
<mat-cell *matCellDef="let row">
|
{{'USER-LISTING.FIELDS.CREATED-AT' | translate}}:
|
||||||
<!-- <img mat-card-avatar *ngIf="row.avatarUrl" class="my-mat-card-avatar" [src]="row.avatarUrl"> -->
|
<small>
|
||||||
{{row.name}}
|
{{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}}
|
||||||
</mat-cell>
|
</small>
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="isColumnSelected('updatedAt')">
|
||||||
<ng-container cdkColumnDef="email">
|
<span class="col-12">
|
||||||
<mat-header-cell *matHeaderCellDef>{{'USERS.LISTING.EMAIL' | translate}}</mat-header-cell>
|
{{'USER-LISTING.FIELDS.UPDATED-AT' | translate}}:
|
||||||
<mat-cell *matCellDef="let row">{{row.email}}</mat-cell>
|
<small>
|
||||||
|
{{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}}
|
||||||
|
</small>
|
||||||
|
</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<ng-container cdkColumnDef="lastloggedin">
|
<ng-template #roleCellTemplate let-row="row" let-item>
|
||||||
<mat-header-cell *matHeaderCellDef>{{'USERS.LISTING.LAST-LOGGED-IN' | translate}}</mat-header-cell>
|
<div class="row">
|
||||||
<mat-cell *matCellDef="let row">{{row.lastloggedin | date:'shortDate'}}</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- Column Definition: Roles -->
|
|
||||||
<ng-container cdkColumnDef="roles">
|
|
||||||
<mat-header-cell *matHeaderCellDef>{{'USERS.LISTING.ROLES' | translate}}</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let row">
|
|
||||||
<app-user-role-editor-component style="width: 100%;" [item]="row"></app-user-role-editor-component>
|
<app-user-role-editor-component style="width: 100%;" [item]="row"></app-user-role-editor-component>
|
||||||
</mat-cell>
|
<!-- <div class="col-auto status-chip"
|
||||||
</ng-container>
|
[ngClass]="{'status-chip-finalized': row.status === userStatuses.Finalized, 'status-chip-draft' : row.status === userStatuses.Draft}">
|
||||||
|
{{enumUtils.toUserStatusString(row.status) | nullifyValue}}
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<ng-template #nameCellTemplate let-row="row" let-item>
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
<div class="row align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<img *ngIf="row?.additionalInfo?.avatarUrl" class="user-avatar" [src]="row?.additionalInfo?.avatarUrl" (error)="this.setDefaultAvatar($event)">
|
||||||
|
<img *ngIf="!row?.additionalInfo?.avatarUrl" class="user-avatar" [src]="'assets/images/profile-placeholder.png'">
|
||||||
|
</div>
|
||||||
|
<div class="col">{{row.name}}</div>
|
||||||
|
|
||||||
</mat-table>
|
|
||||||
<mat-paginator #paginator [length]="dataSource?.totalCount" [pageSizeOptions]="[10, 25, 100]"></mat-paginator>
|
|
||||||
<!-- </mat-card> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</ng-template>
|
|
@ -1,91 +1,68 @@
|
||||||
.mat-table {
|
.user-listing {
|
||||||
margin-top: 47px;
|
margin-top: 1.3rem;
|
||||||
margin-bottom: 20px;
|
margin-left: 1rem;
|
||||||
background: #fafafa 0% 0% no-repeat padding-box;
|
margin-right: 2rem;
|
||||||
// box-shadow: 0px 5px 12px #00000038;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
.mat-header-row{
|
.mat-header-row{
|
||||||
background: #f3f5f8;
|
background: #f3f5f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-header-cell:first-of-type {
|
|
||||||
padding-left: 46px;
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-cell:first-of-type {
|
|
||||||
padding-left: 46px;
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-mat-card-avatar {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 2.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-listing {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
margin-right: 2rem;
|
|
||||||
|
|
||||||
.mat-card {
|
.mat-card {
|
||||||
margin: 1em 0;
|
margin: 16px 0;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-row {
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 4.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-row:hover {
|
mat-row:hover {
|
||||||
background-color: #eef5f6;
|
background-color: #eef5f6;
|
||||||
}
|
}
|
||||||
|
.mat-fab-bottom-right {
|
||||||
mat-row:nth-child(odd) {
|
float: right;
|
||||||
// background-color: #0c748914;
|
z-index: 5;
|
||||||
// background-color: #eef0fb;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.export-btn {
|
.create-btn {
|
||||||
background: #ffffff 0% 0% no-repeat padding-box;
|
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
width: 145px;
|
background-color: var(--secondary-color);
|
||||||
color: var(--primary-color);
|
padding-left: 2em;
|
||||||
border: 1px solid var(--primary-color);
|
padding-right: 2em;
|
||||||
}
|
// color: #000;
|
||||||
|
|
||||||
.export-icon {
|
.button-text{
|
||||||
font-size: 20px;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep .mat-paginator-container {
|
.dlt-btn {
|
||||||
// flex-direction: row-reverse !important;
|
color: rgba(0, 0, 0, 0.54);
|
||||||
// justify-content: space-between !important;
|
|
||||||
// height: 30px;
|
|
||||||
// min-height: 30px !important;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ::ng-deep .mat-paginator-page-size {
|
.status-chip{
|
||||||
// height: 43px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ::ng-deep .mat-paginator-range-label {
|
border-radius: 20px;
|
||||||
// margin: 15px 32px 0 24px !important;
|
padding-left: 1em;
|
||||||
// }
|
padding-right: 1em;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
font-size: .8em;
|
||||||
|
}
|
||||||
|
|
||||||
// ::ng-deep .mat-paginator-range-actions {
|
.status-chip-finalized{
|
||||||
// width: 55% !important;
|
color: #568b5a;
|
||||||
// min-height: 43px !important;
|
background: #9dd1a1 0% 0% no-repeat padding-box;
|
||||||
// justify-content: space-between;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// ::ng-deep .mat-paginator-navigation-previous {
|
.status-chip-draft{
|
||||||
// margin-left: auto !important;
|
color: #00c4ff;
|
||||||
// }
|
background: #d3f5ff 0% 0% no-repeat padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
// ::ng-deep .mat-icon-button {
|
.user-avatar {
|
||||||
// height: 30px !important;
|
height: 40px;
|
||||||
// font-size: 12px !important;
|
width: 40px;
|
||||||
// }
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
|
@ -1,155 +1,195 @@
|
||||||
|
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||||
import { Observable, merge as observableMerge, of as observableOf } from 'rxjs';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { DataSource } from '@angular/cdk/table';
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { User, UserAdditionalInfo, UserContactInfo, UserRole } from '@app/core/model/user/user';
|
||||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
import { UserLookup } from '@app/core/query/user.lookup';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
import { MatSort } from '@angular/material/sort';
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { FileUtils } from '@app/core/services/utilities/file-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 { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.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, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent, RowActivateEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
|
||||||
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import * as FileSaver from 'file-saver';
|
import * as FileSaver from 'file-saver';
|
||||||
import { catchError, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
|
import { Observable } from 'rxjs';
|
||||||
import { DataTableRequest } from '../../../../core/model/data-table/data-table-request';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { UserListingModel } from '../../../../core/model/user/user-listing';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
import { UserCriteria } from '../../../../core/query/user/user-criteria';
|
|
||||||
import { UserServiceOld } from '../../../../core/services/user/user.service-old';
|
|
||||||
import { SnackBarNotificationComponent } from '../../../../library/notification/snack-bar/snack-bar-notification.component';
|
|
||||||
// import { BreadcrumbItem } from '../../../misc/breadcrumb/definition/breadcrumb-item';
|
|
||||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
|
||||||
import { UserCriteriaComponent } from './criteria/user-criteria.component';
|
|
||||||
|
|
||||||
export class UsersDataSource extends DataSource<UserListingModel> {
|
|
||||||
|
|
||||||
totalCount = 0;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private _service: UserServiceOld,
|
|
||||||
private _paginator: MatPaginator,
|
|
||||||
private _sort: MatSort,
|
|
||||||
private _languageService: TranslateService,
|
|
||||||
private _snackBar: MatSnackBar,
|
|
||||||
private _criteria: UserCriteriaComponent
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
//this._paginator.page.pipe(takeUntil(this._destroyed)).subscribe((pageEvent: PageEvent) => {
|
|
||||||
// this.store.dispatch(new LoadPhotosRequestAction(pageEvent.pageIndex, pageEvent.pageSize))
|
|
||||||
//})
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(): Observable<UserListingModel[]> {
|
|
||||||
const displayDataChanges = [
|
|
||||||
this._paginator.page
|
|
||||||
//this._sort.matSortChange
|
|
||||||
];
|
|
||||||
|
|
||||||
// If the user changes the sort order, reset back to the first page.
|
|
||||||
//this._sort.matSortChange.pipe(takeUntil(this._destroyed)).subscribe(() => {
|
|
||||||
// this._paginator.pageIndex = 0;
|
|
||||||
//})
|
|
||||||
|
|
||||||
return observableMerge(...displayDataChanges).pipe(
|
|
||||||
startWith(null),
|
|
||||||
switchMap(() => {
|
|
||||||
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
|
|
||||||
let fields: Array<string> = new Array();
|
|
||||||
if (this._sort.active) { fields = this._sort.direction === 'asc' ? ['+' + this._sort.active] : ['-' + this._sort.active]; }
|
|
||||||
const request = new DataTableRequest<UserCriteria>(startIndex, this._paginator.pageSize, { fields: fields });
|
|
||||||
request.criteria = this._criteria.getFormData();
|
|
||||||
return this._service.getPaged(request);
|
|
||||||
}),
|
|
||||||
catchError((error: any) => {
|
|
||||||
this._snackBar.openFromComponent(SnackBarNotificationComponent, {
|
|
||||||
data: { message: 'GENERAL.SNACK-BAR.FORMS-BAD-REQUEST', language: this._languageService },
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
this._criteria.onCallbackError(error);
|
|
||||||
return observableOf(null);
|
|
||||||
}),
|
|
||||||
map(result => {
|
|
||||||
return result;
|
|
||||||
}),
|
|
||||||
map(result => {
|
|
||||||
if (!result) { return []; }
|
|
||||||
if (this._paginator.pageIndex === 0) { this.totalCount = result.totalCount; }
|
|
||||||
//result.data.forEach((element: any) => {
|
|
||||||
// const roles: String[] = [];
|
|
||||||
// element.roles.forEach((role: any) => {
|
|
||||||
// this._languageService.get(this._utilities.convertFromPrincipalAppRole(role)).pipe(takeUntil(this._destroyed)).subscribe(
|
|
||||||
// value => roles.push(value)
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
// element.roles = roles;
|
|
||||||
//});
|
|
||||||
return result.data;
|
|
||||||
}),);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-listing-component',
|
|
||||||
templateUrl: './user-listing.component.html',
|
templateUrl: './user-listing.component.html',
|
||||||
styleUrls: ['./user-listing.component.scss']
|
styleUrls: ['./user-listing.component.scss']
|
||||||
})
|
})
|
||||||
export class UserListingComponent extends BaseComponent implements OnInit, AfterViewInit {
|
export class UserListingComponent extends BaseListingComponent<User, UserLookup> implements OnInit {
|
||||||
|
publish = false;
|
||||||
|
userSettingsKey = { key: 'UserListingUserSettings' };
|
||||||
|
propertiesAvailableForOrder: ColumnDefinition[];
|
||||||
|
|
||||||
@ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator;
|
@ViewChild('roleCellTemplate', { static: true }) roleCellTemplate?: TemplateRef<any>;
|
||||||
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
@ViewChild('nameCellTemplate', { static: true }) nameCellTemplate?: TemplateRef<any>;
|
||||||
@ViewChild(UserCriteriaComponent, { static: true }) criteria: UserCriteriaComponent;
|
@ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent;
|
||||||
|
|
||||||
// breadCrumbs: Observable<BreadcrumbItem[]>;
|
private readonly lookupFields: string[] = [
|
||||||
dataSource: UsersDataSource | null;
|
nameof<User>(x => x.id),
|
||||||
displayedColumns: String[] = ['avatar', 'name', 'email', 'lastloggedin', 'roles'];
|
nameof<User>(x => x.name),
|
||||||
|
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.id)].join('.'),
|
||||||
|
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.type)].join('.'),
|
||||||
|
[nameof<User>(x => x.contacts), nameof<UserContactInfo>(x => x.value)].join('.'),
|
||||||
|
[nameof<User>(x => x.roles), nameof<UserRole>(x => x.id)].join('.'),
|
||||||
|
[nameof<User>(x => x.roles), nameof<UserRole>(x => x.role)].join('.'),
|
||||||
|
[nameof<User>(x => x.additionalInfo), nameof<UserAdditionalInfo>(x => x.avatarUrl)].join('.'),
|
||||||
|
nameof<User>(x => x.updatedAt),
|
||||||
|
nameof<User>(x => x.createdAt),
|
||||||
|
nameof<User>(x => x.hash),
|
||||||
|
nameof<User>(x => x.isActive)
|
||||||
|
];
|
||||||
|
|
||||||
|
rowIdentity = x => x.id;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private userService: UserServiceOld,
|
protected router: Router,
|
||||||
private languageService: TranslateService,
|
protected route: ActivatedRoute,
|
||||||
public snackBar: MatSnackBar,
|
protected uiNotificationService: UiNotificationService,
|
||||||
private httpClient: HttpClient,
|
protected httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
private matomoService: MatomoService,
|
protected queryParamsService: QueryParamsService,
|
||||||
|
private userService: UserService,
|
||||||
|
public authService: AuthService,
|
||||||
|
private pipeService: PipeService,
|
||||||
|
public enumUtils: EnumUtils,
|
||||||
|
private language: TranslateService,
|
||||||
|
private dialog: MatDialog,
|
||||||
private fileUtils: FileUtils
|
private fileUtils: FileUtils
|
||||||
) {
|
) {
|
||||||
super();
|
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
|
||||||
|
// Lookup setup
|
||||||
|
// Default lookup values are defined in the user settings class.
|
||||||
|
this.lookup = this.initializeLookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.matomoService.trackPageView('Admin: Users');
|
super.ngOnInit();
|
||||||
// this.breadCrumbs = observableOf([{
|
|
||||||
// parentComponentName: null,
|
|
||||||
// label: this.languageService.instant('NAV-BAR.USERS-BREADCRUMB'),
|
|
||||||
// url: "/users"
|
|
||||||
// }]);
|
|
||||||
//this.refresh(); //called on ngAfterViewInit with default criteria
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
protected initializeLookup(): UserLookup {
|
||||||
setTimeout(() => {
|
const lookup = new UserLookup();
|
||||||
this.criteria.setRefreshCallback(() => this.refresh());
|
lookup.metadata = { countAll: true };
|
||||||
this.criteria.setCriteria(this.getDefaultCriteria());
|
lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE };
|
||||||
this.criteria.controlModified();
|
lookup.isActive = [IsActive.Active];
|
||||||
|
lookup.order = { items: [this.toDescSortField(nameof<User>(x => x.createdAt))] };
|
||||||
|
this.updateOrderUiFields(lookup.order);
|
||||||
|
|
||||||
|
lookup.project = {
|
||||||
|
fields: this.lookupFields
|
||||||
|
};
|
||||||
|
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setupColumns() {
|
||||||
|
this.gridColumns.push(...[{
|
||||||
|
prop: nameof<User>(x => x.name),
|
||||||
|
sortable: true,
|
||||||
|
languageName: 'USER-LISTING.FIELDS.NAME',
|
||||||
|
cellTemplate: this.nameCellTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: nameof<User>(x => x.contacts),
|
||||||
|
sortable: true,
|
||||||
|
languageName: 'USER-LISTING.FIELDS.CONTACT-INFO',
|
||||||
|
valueFunction: (item: User) => (item?.contacts ?? []).map(x => x.value).join(', ')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: nameof<User>(x => x.createdAt),
|
||||||
|
sortable: true,
|
||||||
|
languageName: 'USER-LISTING.FIELDS.CREATED-AT',
|
||||||
|
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: nameof<User>(x => x.updatedAt),
|
||||||
|
sortable: true,
|
||||||
|
languageName: 'USER-LISTING.FIELDS.UPDATED-AT',
|
||||||
|
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: nameof<User>(x => x.roles),
|
||||||
|
languageName: 'USER-LISTING.FIELDS.ROLES',
|
||||||
|
alwaysShown: true,
|
||||||
|
maxWidth: 300,
|
||||||
|
cellTemplate: this.roleCellTemplate
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listing Component functions
|
||||||
|
//
|
||||||
|
onColumnsChanged(event: ColumnsChangedEvent) {
|
||||||
|
super.onColumnsChanged(event);
|
||||||
|
this.onColumnsChangedInternal(event.properties.map(x => x.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private onColumnsChangedInternal(columns: string[]) {
|
||||||
|
// Here are defined the projection fields that always requested from the api.
|
||||||
|
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<User>> {
|
||||||
|
return this.userService.query(this.lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteType(id: Guid) {
|
||||||
|
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.userService.delete(id).pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
complete => this.onCallbackSuccess(),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this._paginator.pageSize = 10;
|
|
||||||
this._paginator.pageIndex = 0;
|
|
||||||
this.dataSource = new UsersDataSource(this.userService, this._paginator, this.sort, this.languageService, this.snackBar, this.criteria);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultCriteria(): UserCriteria {
|
onCallbackSuccess(): void {
|
||||||
const defaultCriteria = new UserCriteria();
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
|
||||||
return defaultCriteria;
|
this.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export user mails
|
onUserRowActivated(event: RowActivateEvent, baseRoute: string = null) {
|
||||||
exportUsers() {
|
// Override default event to prevent click action
|
||||||
this.userService.downloadCSV()
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Export
|
||||||
|
//
|
||||||
|
|
||||||
|
export() { //TODO: send lookup to backend to export only filtered
|
||||||
|
this.userService.exportCSV()
|
||||||
.pipe(takeUntil(this._destroyed))
|
.pipe(takeUntil(this._destroyed))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
const blob = new Blob([response.body], { type: 'application/csv' });
|
const blob = new Blob([response.body], { type: 'application/csv' });
|
||||||
|
@ -158,6 +198,9 @@ export class UserListingComponent extends BaseComponent implements OnInit, After
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Avatar
|
||||||
|
//
|
||||||
public setDefaultAvatar(ev: Event) {
|
public setDefaultAvatar(ev: Event) {
|
||||||
(ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png';
|
(ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { CommonModule } from '@angular/common';
|
||||||
import { FormattingModule } from '@app/core/formatting.module';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonFormattingModule } from '@common/formatting/common-formatting.module';
|
||||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||||
|
import { HybridListingModule } from '@common/modules/hybrid-listing/hybrid-listing.module';
|
||||||
|
import { TextFilterModule } from '@common/modules/text-filter/text-filter.module';
|
||||||
|
import { UserSettingsModule } from '@common/modules/user-settings/user-settings.module';
|
||||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
import { UserCriteriaComponent } from './listing/criteria/user-criteria.component';
|
import { UserListingFiltersComponent } from './listing/filters/user-listing-filters.component';
|
||||||
import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component';
|
|
||||||
import { UserListingComponent } from './listing/user-listing.component';
|
import { UserListingComponent } from './listing/user-listing.component';
|
||||||
import { UserRoutingModule } from './user.routing';
|
import { UsersRoutingModule } from './user.routing';
|
||||||
|
import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
|
||||||
CommonUiModule,
|
|
||||||
CommonFormsModule,
|
|
||||||
FormattingModule,
|
|
||||||
UserRoutingModule
|
|
||||||
],
|
|
||||||
declarations: [
|
declarations: [
|
||||||
UserListingComponent,
|
UserListingComponent,
|
||||||
UserCriteriaComponent,
|
// UserEditorComponent,
|
||||||
UserRoleEditorComponent
|
UserRoleEditorComponent,
|
||||||
|
UserListingFiltersComponent
|
||||||
],
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
CommonUiModule,
|
||||||
|
CommonFormsModule,
|
||||||
|
CommonFormattingModule,
|
||||||
|
UsersRoutingModule,
|
||||||
|
HybridListingModule,
|
||||||
|
TextFilterModule,
|
||||||
|
UserSettingsModule
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
export class UsersModule { }
|
||||||
export class UserModule { }
|
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { UserListingComponent } from './listing/user-listing.component';
|
import { AuthGuard } from "@app/core/auth-guard.service";
|
||||||
import { AdminAuthGuard } from '@app/core/admin-auth-guard.service';
|
import { UserListingComponent } from "./listing/user-listing.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', component: UserListingComponent, canActivate: [AdminAuthGuard] },
|
{
|
||||||
// { path: ':id', component: UserProfileComponent }
|
path: '',
|
||||||
];
|
component: UserListingComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
|
},
|
||||||
|
{ path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) },
|
||||||
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class UserRoutingModule { }
|
export class UsersRoutingModule { }
|
||||||
|
|
|
@ -1787,20 +1787,25 @@
|
||||||
"CANCEL": "Cancel"
|
"CANCEL": "Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"USERS": {
|
"USER-LISTING": {
|
||||||
"LISTING": {
|
|
||||||
"TITLE": "Users",
|
"TITLE": "Users",
|
||||||
"EMAIL": "Email",
|
"FIELDS": {
|
||||||
"LAST-LOGGED-IN": "Last Logged In",
|
"CONTACT-INFO": "Email",
|
||||||
"LABEL": "Label",
|
|
||||||
"ROLES": "Roles",
|
"ROLES": "Roles",
|
||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
"PERMISSIONS": "Permissions",
|
"UPDATED-AT": "Updated",
|
||||||
"EXPORT": "Export users"
|
"CREATED-AT": "Created"
|
||||||
|
},
|
||||||
|
"FILTER": {
|
||||||
|
"TITLE": "Filters",
|
||||||
|
"IS-ACTIVE": "Is Active",
|
||||||
|
"CANCEL": "Cancel",
|
||||||
|
"APPLY-FILTERS": "Apply filters"
|
||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"EDIT": "Edit",
|
"EDIT": "Edit",
|
||||||
"SAVE": "Save"
|
"SAVE": "Save",
|
||||||
|
"EXPORT": "Export users"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
|
|
Loading…
Reference in New Issue