Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring
# Conflicts: # dmp-frontend/src/app/core/common/enum/app-role.ts # dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts # dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html # dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss # dmp-frontend/src/assets/i18n/en.json
This commit is contained in:
commit
48352f0461
|
@ -4,10 +4,7 @@ import eu.eudat.authorization.AuthorizationFlags;
|
||||||
import eu.eudat.authorization.Permission;
|
import eu.eudat.authorization.Permission;
|
||||||
import eu.eudat.commons.enums.*;
|
import eu.eudat.commons.enums.*;
|
||||||
import eu.eudat.commons.scope.user.UserScope;
|
import eu.eudat.commons.scope.user.UserScope;
|
||||||
import eu.eudat.data.DmpDescriptionTemplateEntity;
|
import eu.eudat.data.*;
|
||||||
import eu.eudat.data.DmpEntity;
|
|
||||||
import eu.eudat.data.DmpReferenceEntity;
|
|
||||||
import eu.eudat.data.DmpUserEntity;
|
|
||||||
import eu.eudat.model.Dmp;
|
import eu.eudat.model.Dmp;
|
||||||
import eu.eudat.model.PublicDmp;
|
import eu.eudat.model.PublicDmp;
|
||||||
import eu.eudat.query.utils.QueryUtilsService;
|
import eu.eudat.query.utils.QueryUtilsService;
|
||||||
|
@ -364,6 +361,11 @@ public class DmpQuery extends QueryBase<DmpEntity> {
|
||||||
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
|
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.entityDoiQuery != null) {
|
||||||
|
QueryContext<EntityDoiEntity, UUID> subQuery = this.applySubQuery(this.entityDoiQuery, queryContext, UUID.class, entityDoiEntityRoot -> entityDoiEntityRoot.get(EntityDoiEntity._entityId));
|
||||||
|
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
|
||||||
|
}
|
||||||
|
|
||||||
if (!predicates.isEmpty()) {
|
if (!predicates.isEmpty()) {
|
||||||
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
|
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
|
||||||
return queryContext.CriteriaBuilder.and(predicatesArray);
|
return queryContext.CriteriaBuilder.and(predicatesArray);
|
||||||
|
|
|
@ -5,13 +5,24 @@ import eu.eudat.authorization.Permission;
|
||||||
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
|
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
|
||||||
import eu.eudat.commonmodels.models.FileEnvelopeModel;
|
import eu.eudat.commonmodels.models.FileEnvelopeModel;
|
||||||
import eu.eudat.commonmodels.models.dmp.DmpModel;
|
import eu.eudat.commonmodels.models.dmp.DmpModel;
|
||||||
|
import eu.eudat.commons.JsonHandlingService;
|
||||||
|
import eu.eudat.commons.enums.ContactInfoType;
|
||||||
|
import eu.eudat.commons.enums.IsActive;
|
||||||
import eu.eudat.commons.enums.StorageType;
|
import eu.eudat.commons.enums.StorageType;
|
||||||
|
import eu.eudat.commons.notification.NotificationProperties;
|
||||||
import eu.eudat.commons.scope.user.UserScope;
|
import eu.eudat.commons.scope.user.UserScope;
|
||||||
|
import eu.eudat.commons.types.notification.*;
|
||||||
|
import eu.eudat.convention.ConventionService;
|
||||||
import eu.eudat.data.DmpEntity;
|
import eu.eudat.data.DmpEntity;
|
||||||
|
import eu.eudat.data.DmpUserEntity;
|
||||||
|
import eu.eudat.data.UserEntity;
|
||||||
import eu.eudat.depositinterface.repository.DepositClient;
|
import eu.eudat.depositinterface.repository.DepositClient;
|
||||||
import eu.eudat.depositinterface.repository.DepositConfiguration;
|
import eu.eudat.depositinterface.repository.DepositConfiguration;
|
||||||
|
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
|
||||||
|
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
|
||||||
import eu.eudat.model.EntityDoi;
|
import eu.eudat.model.EntityDoi;
|
||||||
import eu.eudat.model.StorageFile;
|
import eu.eudat.model.StorageFile;
|
||||||
|
import eu.eudat.model.UserContactInfo;
|
||||||
import eu.eudat.model.builder.commonmodels.DepositConfigurationBuilder;
|
import eu.eudat.model.builder.commonmodels.DepositConfigurationBuilder;
|
||||||
import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
|
import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
|
||||||
import eu.eudat.model.persist.StorageFilePersist;
|
import eu.eudat.model.persist.StorageFilePersist;
|
||||||
|
@ -19,6 +30,9 @@ import eu.eudat.model.persist.deposit.DepositAuthenticateRequest;
|
||||||
import eu.eudat.model.persist.deposit.DepositRequest;
|
import eu.eudat.model.persist.deposit.DepositRequest;
|
||||||
import eu.eudat.model.persist.EntityDoiPersist;
|
import eu.eudat.model.persist.EntityDoiPersist;
|
||||||
import eu.eudat.query.DmpQuery;
|
import eu.eudat.query.DmpQuery;
|
||||||
|
import eu.eudat.query.DmpUserQuery;
|
||||||
|
import eu.eudat.query.UserContactInfoQuery;
|
||||||
|
import eu.eudat.query.UserQuery;
|
||||||
import eu.eudat.service.entitydoi.EntityDoiService;
|
import eu.eudat.service.entitydoi.EntityDoiService;
|
||||||
import eu.eudat.service.storage.StorageFileProperties;
|
import eu.eudat.service.storage.StorageFileProperties;
|
||||||
import eu.eudat.service.storage.StorageFileService;
|
import eu.eudat.service.storage.StorageFileService;
|
||||||
|
@ -28,6 +42,7 @@ import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
|
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
|
||||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
|
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
|
||||||
import gr.cite.tools.data.builder.BuilderFactory;
|
import gr.cite.tools.data.builder.BuilderFactory;
|
||||||
|
import gr.cite.tools.data.query.Ordering;
|
||||||
import gr.cite.tools.data.query.QueryFactory;
|
import gr.cite.tools.data.query.QueryFactory;
|
||||||
import gr.cite.tools.exception.MyNotFoundException;
|
import gr.cite.tools.exception.MyNotFoundException;
|
||||||
import gr.cite.tools.fieldset.BaseFieldSet;
|
import gr.cite.tools.fieldset.BaseFieldSet;
|
||||||
|
@ -42,11 +57,13 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import javax.management.InvalidApplicationException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DepositServiceImpl implements DepositService {
|
public class DepositServiceImpl implements DepositService {
|
||||||
|
@ -67,6 +84,10 @@ public class DepositServiceImpl implements DepositService {
|
||||||
private final ValidatorFactory validatorFactory;
|
private final ValidatorFactory validatorFactory;
|
||||||
private final StorageFileProperties storageFileProperties;
|
private final StorageFileProperties storageFileProperties;
|
||||||
private final AuthorizationContentResolver authorizationContentResolver;
|
private final AuthorizationContentResolver authorizationContentResolver;
|
||||||
|
private final ConventionService conventionService;
|
||||||
|
private final JsonHandlingService jsonHandlingService;
|
||||||
|
private final NotificationProperties notificationProperties;
|
||||||
|
private final NotifyIntegrationEventHandler eventHandler;
|
||||||
@Autowired
|
@Autowired
|
||||||
public DepositServiceImpl(DepositProperties depositProperties,
|
public DepositServiceImpl(DepositProperties depositProperties,
|
||||||
TokenExchangeCacheService tokenExchangeCacheService,
|
TokenExchangeCacheService tokenExchangeCacheService,
|
||||||
|
@ -74,7 +95,7 @@ public class DepositServiceImpl implements DepositService {
|
||||||
EntityDoiService doiService,
|
EntityDoiService doiService,
|
||||||
QueryFactory queryFactory,
|
QueryFactory queryFactory,
|
||||||
MessageSource messageSource,
|
MessageSource messageSource,
|
||||||
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver) {
|
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver, ConventionService conventionService, JsonHandlingService jsonHandlingService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler) {
|
||||||
this.depositProperties = depositProperties;
|
this.depositProperties = depositProperties;
|
||||||
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
|
@ -89,7 +110,11 @@ public class DepositServiceImpl implements DepositService {
|
||||||
this.validatorFactory = validatorFactory;
|
this.validatorFactory = validatorFactory;
|
||||||
this.storageFileProperties = storageFileProperties;
|
this.storageFileProperties = storageFileProperties;
|
||||||
this.authorizationContentResolver = authorizationContentResolver;
|
this.authorizationContentResolver = authorizationContentResolver;
|
||||||
this.clients = new HashMap<>();
|
this.conventionService = conventionService;
|
||||||
|
this.jsonHandlingService = jsonHandlingService;
|
||||||
|
this.notificationProperties = notificationProperties;
|
||||||
|
this.eventHandler = eventHandler;
|
||||||
|
this.clients = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DepositClient getDepositClient(String repositoryId) {
|
private DepositClient getDepositClient(String repositoryId) {
|
||||||
|
@ -181,8 +206,47 @@ public class DepositServiceImpl implements DepositService {
|
||||||
doiPersist.setRepositoryId(dmpDepositModel.getRepositoryId());
|
doiPersist.setRepositoryId(dmpDepositModel.getRepositoryId());
|
||||||
doiPersist.setDoi(doi);
|
doiPersist.setDoi(doi);
|
||||||
doiPersist.setEntityId(dmpEntity.getId());
|
doiPersist.setEntityId(dmpEntity.getId());
|
||||||
|
this.sendNotification(dmpEntity);
|
||||||
return doiService.persist(doiPersist, dmpDepositModel.getProject());
|
return doiService.persist(doiPersist, dmpDepositModel.getProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendNotification(DmpEntity dmpEntity) throws InvalidApplicationException {
|
||||||
|
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).ids(dmpEntity.getId()).isActives(IsActive.Active).collect();
|
||||||
|
if (this.conventionService.isListNullOrEmpty(dmpUsers)){
|
||||||
|
throw new MyNotFoundException("Dmp does not have Users");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserEntity> users = this.queryFactory.query(UserQuery.class).ids(dmpUsers.stream().map(x -> x.getUserId()).collect(Collectors.toList())).isActive(IsActive.Active).collect();
|
||||||
|
|
||||||
|
for (UserEntity user: users) {
|
||||||
|
if (!user.getId().equals(this.userScope.getUserIdSafe()) && !this.conventionService.isListNullOrEmpty(dmpUsers.stream().filter(x -> x.getUserId().equals(user.getId())).collect(Collectors.toList()))){
|
||||||
|
this.createDmpDepositNotificationEvent(dmpEntity, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDmpDepositNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
|
||||||
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
|
event.setUserId(user.getId());
|
||||||
|
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||||
|
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||||
|
|
||||||
|
List<ContactPair> contactPairs = new ArrayList<>();
|
||||||
|
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||||
|
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||||
|
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||||
|
event.setNotificationType(notificationProperties.getDmpDepositType());
|
||||||
|
NotificationFieldData data = new NotificationFieldData();
|
||||||
|
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
||||||
|
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName()));
|
||||||
|
fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserId()).first().getName()));
|
||||||
|
fieldInfoList.add(new FieldInfo("{name}", DataType.String, dmp.getLabel()));
|
||||||
|
fieldInfoList.add(new FieldInfo("{id}", DataType.String, dmp.getId().toString()));
|
||||||
|
data.setFields(fieldInfoList);
|
||||||
|
event.setData(jsonHandlingService.toJsonSafe(data));
|
||||||
|
|
||||||
|
eventHandler.handle(event);
|
||||||
|
}
|
||||||
|
|
||||||
private String addFileToSharedStorage(eu.eudat.model.file.FileEnvelope file) throws IOException {
|
private String addFileToSharedStorage(eu.eudat.model.file.FileEnvelope file) throws IOException {
|
||||||
StorageFilePersist storageFilePersist = new StorageFilePersist();
|
StorageFilePersist storageFilePersist = new StorageFilePersist();
|
||||||
|
|
|
@ -302,8 +302,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
||||||
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
||||||
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
||||||
if (user != null){
|
if (user != null){
|
||||||
this.createDescriptionNotificationEvent(description, user, NotificationContactType.EMAIL);
|
this.createDescriptionNotificationEvent(description, user);
|
||||||
this.createDescriptionNotificationEvent(description, user, NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,9 +352,9 @@ public class DescriptionServiceImpl implements DescriptionService {
|
||||||
return cleanData;
|
return cleanData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDescriptionNotificationEvent(DescriptionEntity description, UserEntity user, NotificationContactType type) throws InvalidApplicationException {
|
private void createDescriptionNotificationEvent(DescriptionEntity description, UserEntity user) throws InvalidApplicationException {
|
||||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
event.setUserId(this.userScope.getUserId());
|
event.setUserId(user.getId());
|
||||||
|
|
||||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||||
|
@ -364,7 +363,6 @@ public class DescriptionServiceImpl implements DescriptionService {
|
||||||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||||
event.setContactTypeHint(type);
|
|
||||||
|
|
||||||
event = this.applyNotificationType(description.getStatus(), event);
|
event = this.applyNotificationType(description.getStatus(), event);
|
||||||
NotificationFieldData data = new NotificationFieldData();
|
NotificationFieldData data = new NotificationFieldData();
|
||||||
|
@ -421,6 +419,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
||||||
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
|
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
|
||||||
|
|
||||||
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
|
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
|
||||||
|
if (data.getStatus().equals(DescriptionStatus.Finalized)) this.sendNotification(data);
|
||||||
}
|
}
|
||||||
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
|
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,8 +245,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
data.setRole(user.getRole());
|
data.setRole(user.getRole());
|
||||||
this.entityManager.persist(data);
|
this.entityManager.persist(data);
|
||||||
if (!this.userScope.getUserId().equals(user.getUserId())) {
|
if (!this.userScope.getUserId().equals(user.getUserId())) {
|
||||||
this.sendDescriptionTemplateInvitationEvent(data, NotificationContactType.EMAIL);
|
this.sendDescriptionTemplateInvitationEvent(data);
|
||||||
this.sendDescriptionTemplateInvitationEvent(data, NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updatedCreatedIds.add(data.getUserId());
|
updatedCreatedIds.add(data.getUserId());
|
||||||
|
@ -256,13 +255,18 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class).delete(toDelete);
|
this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class).delete(toDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate, NotificationContactType type) throws InvalidApplicationException {
|
private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate) throws InvalidApplicationException {
|
||||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
event.setUserId(userScope.getUserIdSafe());
|
|
||||||
|
|
||||||
UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId());
|
UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId());
|
||||||
|
if (user == null){
|
||||||
|
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userDescriptionTemplate.getUserId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
}
|
||||||
DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).isActive(IsActive.Active).ids(userDescriptionTemplate.getDescriptionTemplateId()).first();
|
DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).isActive(IsActive.Active).ids(userDescriptionTemplate.getDescriptionTemplateId()).first();
|
||||||
|
if (descriptionTemplate == null){
|
||||||
|
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userDescriptionTemplate.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
}
|
||||||
|
event.setUserId(user.getId());
|
||||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||||
|
|
||||||
|
@ -270,7 +274,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||||
event.setContactTypeHint(type);
|
|
||||||
event.setNotificationType(notificationProperties.getDescriptionTemplateInvitationType());
|
event.setNotificationType(notificationProperties.getDescriptionTemplateInvitationType());
|
||||||
NotificationFieldData data = new NotificationFieldData();
|
NotificationFieldData data = new NotificationFieldData();
|
||||||
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
||||||
|
|
|
@ -258,16 +258,15 @@ public class DmpServiceImpl implements DmpService {
|
||||||
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
||||||
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
||||||
if (user != null){
|
if (user != null){
|
||||||
this.createDmpNotificationEvent(dmp, user, NotificationContactType.EMAIL);
|
this.createDmpNotificationEvent(dmp, user);
|
||||||
this.createDmpNotificationEvent(dmp, user, NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDmpNotificationEvent(DmpEntity dmp, UserEntity user, NotificationContactType type) throws InvalidApplicationException {
|
private void createDmpNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
|
||||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
event.setUserId(this.userScope.getUserId());
|
event.setUserId(user.getId());
|
||||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||||
|
|
||||||
|
@ -275,7 +274,6 @@ public class DmpServiceImpl implements DmpService {
|
||||||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||||
event.setContactTypeHint(type);
|
|
||||||
|
|
||||||
this.applyNotificationType(dmp.getStatus(), event);
|
this.applyNotificationType(dmp.getStatus(), event);
|
||||||
NotificationFieldData data = new NotificationFieldData();
|
NotificationFieldData data = new NotificationFieldData();
|
||||||
|
@ -1186,6 +1184,15 @@ public class DmpServiceImpl implements DmpService {
|
||||||
throw new InvalidApplicationException("Dmp does not exist!");
|
throw new InvalidApplicationException("Dmp does not exist!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class)
|
||||||
|
.dmpIds(dmp.getId())
|
||||||
|
.isActives(IsActive.Active)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if (this.conventionService.isListNullOrEmpty(existingUsers)){
|
||||||
|
throw new InvalidApplicationException("Dmp does not have users!");
|
||||||
|
}
|
||||||
|
|
||||||
List<DmpUserPersist> usersToAssign = new ArrayList<>();
|
List<DmpUserPersist> usersToAssign = new ArrayList<>();
|
||||||
for (DmpUserPersist user :users) {
|
for (DmpUserPersist user :users) {
|
||||||
UUID userId = null;
|
UUID userId = null;
|
||||||
|
@ -1200,7 +1207,7 @@ public class DmpServiceImpl implements DmpService {
|
||||||
if (userId != null){
|
if (userId != null){
|
||||||
user.setUser(userId);
|
user.setUser(userId);
|
||||||
usersToAssign.add(user);
|
usersToAssign.add(user);
|
||||||
if (this.userScope.getUserId() != userId){
|
if (this.userScope.getUserId() != userId && !existingUsers.stream().map(x -> x.getUserId()).collect(Collectors.toList()).contains(userId)){
|
||||||
this.sendDmpInvitationExistingUser(user.getUser(), dmp, user.getRole());
|
this.sendDmpInvitationExistingUser(user.getUser(), dmp, user.getRole());
|
||||||
}
|
}
|
||||||
}else if (user.getEmail() != null) {
|
}else if (user.getEmail() != null) {
|
||||||
|
@ -1215,20 +1222,18 @@ public class DmpServiceImpl implements DmpService {
|
||||||
private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException {
|
private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException {
|
||||||
UserEntity recipient = this.queryFactory.query(UserQuery.class).ids(userId).isActive(IsActive.Active).first();
|
UserEntity recipient = this.queryFactory.query(UserQuery.class).ids(userId).isActive(IsActive.Active).first();
|
||||||
String email = this.queryFactory.query(UserContactInfoQuery.class).userIds(recipient.getId()).first().getValue();
|
String email = this.queryFactory.query(UserContactInfoQuery.class).userIds(recipient.getId()).first().getValue();
|
||||||
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email, NotificationContactType.EMAIL);
|
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email);
|
||||||
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email, NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDmpInvitationExistingUserEvent(UserEntity recipient, DmpEntity dmp, DmpUserRole role, String email, NotificationContactType type) throws InvalidApplicationException {
|
private void createDmpInvitationExistingUserEvent(UserEntity recipient, DmpEntity dmp, DmpUserRole role, String email) throws InvalidApplicationException {
|
||||||
|
|
||||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
event.setUserId(this.userScope.getUserIdSafe());
|
event.setUserId(recipient.getId());
|
||||||
|
|
||||||
List<ContactPair> contactPairs = new ArrayList<>();
|
List<ContactPair> contactPairs = new ArrayList<>();
|
||||||
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
|
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
|
||||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||||
event.setContactTypeHint(type);
|
|
||||||
event.setNotificationType(notificationProperties.getDmpInvitationExistingUserType());
|
event.setNotificationType(notificationProperties.getDmpInvitationExistingUserType());
|
||||||
NotificationFieldData data = new NotificationFieldData();
|
NotificationFieldData data = new NotificationFieldData();
|
||||||
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
||||||
|
@ -1246,7 +1251,6 @@ public class DmpServiceImpl implements DmpService {
|
||||||
String token = this.createActionConfirmation(email, dmp, role);
|
String token = this.createActionConfirmation(email, dmp, role);
|
||||||
|
|
||||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||||
event.setUserId(this.userScope.getUserIdSafe());
|
|
||||||
|
|
||||||
List<ContactPair> contactPairs = new ArrayList<>();
|
List<ContactPair> contactPairs = new ArrayList<>();
|
||||||
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
|
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
|
||||||
|
|
|
@ -117,11 +117,17 @@ public class ExternalFetcherServiceImpl implements ExternalFetcherService {
|
||||||
Map<String, Object> rawData = new HashMap<>();
|
Map<String, Object> rawData = new HashMap<>();
|
||||||
for (Object object: item.getOptions()) {
|
for (Object object: item.getOptions()) {
|
||||||
StaticOptionEntity staticOption = (StaticOptionEntity) object;
|
StaticOptionEntity staticOption = (StaticOptionEntity) object;
|
||||||
if (!this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike()) && !externalReferenceCriteria.getLike().toUpperCase().contains(staticOption.getValue())) continue;
|
if (this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike())){
|
||||||
rawData.put(staticOption.getCode(), staticOption.getValue());
|
rawData.put(staticOption.getCode(), staticOption.getValue());
|
||||||
result.put(staticOption.getCode(), staticOption.getValue());
|
result.put(staticOption.getCode(), staticOption.getValue());
|
||||||
result.put(ReferenceEntity.KnownFields.SourceLabel, staticSource.getLabel());
|
result.put(ReferenceEntity.KnownFields.SourceLabel, staticSource.getLabel());
|
||||||
result.put(ReferenceEntity.KnownFields.Key, staticSource.getKey());
|
result.put(ReferenceEntity.KnownFields.Key, staticSource.getKey());
|
||||||
|
}else if (!this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike()) && externalReferenceCriteria.getLike().toUpperCase().contains(staticOption.getValue().toUpperCase())){
|
||||||
|
rawData.put(staticOption.getCode(), staticOption.getValue());
|
||||||
|
result.put(staticOption.getCode(), staticOption.getValue());
|
||||||
|
result.put(ReferenceEntity.KnownFields.SourceLabel, staticSource.getLabel());
|
||||||
|
result.put(ReferenceEntity.KnownFields.Key, staticSource.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!rawData.isEmpty()) externalDataResult.getRawData().add(rawData);
|
if (!rawData.isEmpty()) externalDataResult.getRawData().add(rawData);
|
||||||
|
|
|
@ -66,15 +66,15 @@ public class MetricsServiceImpl implements MetricsService {
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculatePublishedDmps(true), MetricLabels.PUBLISHED);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculatePublishedDmps(true), MetricLabels.PUBLISHED);
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDoiedDmps(true), MetricLabels.DOIED);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDoiedDmps(true), MetricLabels.DOIED);
|
||||||
|
|
||||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateDraftDmpsWithGrant(false), MetricLabels.DRAFT);
|
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateDraftDmpsWithGrant(false), MetricLabels.DRAFT);
|
||||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateFinalizedDmpsWithGrant(false), MetricLabels.FINALIZED);
|
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateFinalizedDmpsWithGrant(false), MetricLabels.FINALIZED);
|
||||||
this.setGaugeValue(gauges, MetricNames.DMP, calculatePublishedDmpsWithGrant(false), MetricLabels.PUBLISHED);
|
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculatePublishedDmpsWithGrant(false), MetricLabels.PUBLISHED);
|
||||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateDoiedDmpsWithGrant(false), MetricLabels.DOIED);
|
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateDoiedDmpsWithGrant(false), MetricLabels.DOIED);
|
||||||
|
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDraftDmpsWithGrant(true), MetricLabels.DRAFT);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateDraftDmpsWithGrant(true), MetricLabels.DRAFT);
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateFinalizedDmpsWithGrant(true), MetricLabels.FINALIZED);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateFinalizedDmpsWithGrant(true), MetricLabels.FINALIZED);
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculatePublishedDmpsWithGrant(true), MetricLabels.PUBLISHED);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculatePublishedDmpsWithGrant(true), MetricLabels.PUBLISHED);
|
||||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDoiedDmpsWithGrant(true), MetricLabels.DOIED);
|
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateDoiedDmpsWithGrant(true), MetricLabels.DOIED);
|
||||||
|
|
||||||
this.setGaugeValue(gauges, MetricNames.FUNDERS, calculateAllFunders(false), null);
|
this.setGaugeValue(gauges, MetricNames.FUNDERS, calculateAllFunders(false), null);
|
||||||
this.setGaugeValue(gauges, MetricNames.GRANTS, calculateAllGrants(false), null);
|
this.setGaugeValue(gauges, MetricNames.GRANTS, calculateAllGrants(false), null);
|
||||||
|
@ -181,6 +181,7 @@ public class MetricsServiceImpl implements MetricsService {
|
||||||
dmpQuery.after(_config.getNexusDate());
|
dmpQuery.after(_config.getNexusDate());
|
||||||
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).types(EntityType.DMP).isActive(IsActive.Active);
|
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).types(EntityType.DMP).isActive(IsActive.Active);
|
||||||
dmpQuery.entityDoiSubQuery(entityDoiQuery);
|
dmpQuery.entityDoiSubQuery(entityDoiQuery);
|
||||||
|
dmpQuery.setDistinct(true);
|
||||||
return dmpQuery.count();
|
return dmpQuery.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,7 +423,6 @@ public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
String token = this.createMergeAccountConfirmation(model.getEmail());
|
String token = this.createMergeAccountConfirmation(model.getEmail());
|
||||||
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.EMAIL);
|
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.EMAIL);
|
||||||
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMergeNotificationEvent(String token, UserEntity user, String email, NotificationContactType type) throws InvalidApplicationException {
|
private void createMergeNotificationEvent(String token, UserEntity user, String email, NotificationContactType type) throws InvalidApplicationException {
|
||||||
|
@ -453,7 +452,6 @@ public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
String token = this.createRemoveConfirmation(data.getId());
|
String token = this.createRemoveConfirmation(data.getId());
|
||||||
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.EMAIL);
|
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.EMAIL);
|
||||||
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.IN_APP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRemoveCredentialNotificationEvent(String token, UUID userId, NotificationContactType type) throws InvalidApplicationException {
|
private void createRemoveCredentialNotificationEvent(String token, UUID userId, NotificationContactType type) throws InvalidApplicationException {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
metrics:
|
metrics:
|
||||||
task:
|
task:
|
||||||
|
enable: true
|
||||||
|
intervalSeconds: 600
|
||||||
|
nexusDate: "2021-01-01T00:00:00.00Z"
|
||||||
|
usersLoginClient: ${IDP_APIKEY_CLIENT_ID_UUID:}
|
||||||
referenceTypes:
|
referenceTypes:
|
||||||
funderIds: ["538928bb-c7c6-452e-b66d-08e539f5f082"]
|
funderIds: ["538928bb-c7c6-452e-b66d-08e539f5f082"]
|
||||||
grantIds: ["5b9c284f-f041-4995-96cc-fad7ad13289c"]
|
grantIds: ["5b9c284f-f041-4995-96cc-fad7ad13289c"]
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
metrics:
|
metrics:
|
||||||
task:
|
task:
|
||||||
enable: true
|
enable: false
|
||||||
intervalSeconds: 600
|
intervalSeconds: null
|
||||||
nexusDate: "2021-01-01T00:00:00.00Z"
|
nexusDate: null
|
||||||
usersLoginClient: ${IDP_APIKEY_CLIENT_ID_UUID:}
|
usersLoginClient: null
|
||||||
referenceTypes:
|
referenceTypes:
|
||||||
funderIds: []
|
funderIds: []
|
||||||
grantIds: []
|
grantIds: []
|
||||||
|
|
|
@ -4,6 +4,5 @@ export enum AppRole {
|
||||||
TenantAdmin = "TenantAdmin",
|
TenantAdmin = "TenantAdmin",
|
||||||
TenantUser = "TenantUser",
|
TenantUser = "TenantUser",
|
||||||
TenantManager = "TenantManager",
|
TenantManager = "TenantManager",
|
||||||
TenantDescriptionTemplateEditor = "TenantDescriptionTemplateEditor",
|
TenantDescriptionTemplateEditor = "TenantDescriptionTemplateEditor"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ ngx-guided-tour {
|
||||||
&.tour-top-right {
|
&.tour-top-right {
|
||||||
.tour-arrow::before {
|
.tour-arrow::before {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
left: calc(100% - 5px);
|
left: calc(100% - 15px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,32 +7,47 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
<div>
|
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
<mat-label>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.STATUS' | translate}}
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.STATUS' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
||||||
<mat-option *ngFor="let descriptionTemplateStatus of descriptionTemplateStatusEnumValues" [value]="descriptionTemplateStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateStatus)}}</mat-option>
|
<mat-option *ngFor="let descriptionTemplateStatus of descriptionTemplateStatusEnumValues" [value]="descriptionTemplateStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateStatus)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,32 +7,47 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CLEAR-ALL-FILTERS' | translate}}
|
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.STATUS' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.STATUS' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
||||||
<mat-option *ngFor="let descriptionTemplateTypeStatus of descriptionTemplateTypeStatusEnumValues" [value]="descriptionTemplateTypeStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateTypeStatus)}}</mat-option>
|
<mat-option *ngFor="let descriptionTemplateTypeStatus of descriptionTemplateTypeStatusEnumValues" [value]="descriptionTemplateTypeStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateTypeStatus)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'DMP-BLUEPRINT-LISTING.FILTER.CANCEL' | translate}}
|
{{'DMP-BLUEPRINT-LISTING.FILTER.CANCEL' | translate}}
|
||||||
|
|
|
@ -7,39 +7,47 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'LOCK-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'LOCK-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="row mt-3">
|
||||||
<div>
|
<div class="col-12">
|
||||||
<mat-form-field class="col-12">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{'LOCK-LISTING.FILTER.USERS' | translate}}</mat-label>
|
<mat-label>{{'LOCK-LISTING.FILTER.USERS' | translate}}</mat-label>
|
||||||
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userService.multipleAutocompleteConfiguration">
|
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userService.multipleAutocompleteConfiguration">
|
||||||
</app-multiple-auto-complete>
|
</app-multiple-auto-complete>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'LOCK-LISTING.FILTER.TARGET-TYPE' | translate}}
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.targetTypes">
|
|
||||||
<mat-option *ngFor="let targetType of lockTargetTypeEnumValues" [value]="targetType">{{enumUtils.toLockTargetTypeString(targetType)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-label>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-between mt-4 gap-1-rem">
|
<div class="row mt-3">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'LOCK-LISTING.FILTER.TARGET-TYPE' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.targetTypes">
|
||||||
|
<mat-option *ngFor="let targetType of lockTargetTypeEnumValues" [value]="targetType">{{enumUtils.toLockTargetTypeString(targetType)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-between mt-4 mb-1 gap-1-rem">
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'LOCK-LISTING.FILTER.CANCEL' | translate}}
|
{{'LOCK-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'LOCK-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'LOCK-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,5 +17,3 @@
|
||||||
// height: 1.2em;
|
// height: 1.2em;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-4" *ngIf="isNew">
|
<div class="col-6" *ngIf="isNew">
|
||||||
<mat-form-field class="w-100">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
||||||
<input matInput type="text" name="code" [formControl]="formGroup.get('code')" required>
|
<input matInput type="text" name="code" [formControl]="formGroup.get('code')" required>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4" *ngIf="!isNew">
|
<div class="col-6" *ngIf="!isNew">
|
||||||
<mat-form-field class="w-100">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
||||||
<mat-select (selectionChange)="selectedCodeChanged($event.value)" name= "code" [formControl]="formGroup.get('code')">
|
<mat-select (selectionChange)="selectedCodeChanged($event.value)" name= "code" [formControl]="formGroup.get('code')">
|
||||||
|
|
|
@ -7,24 +7,36 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'LANGUAGE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'LANGUAGE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'LANGUAGE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'LANGUAGE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'LANGUAGE-LISTING.FILTER.CANCEL' | translate}}
|
{{'LANGUAGE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'LANGUAGE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'LANGUAGE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,4 +18,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
|
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined" class="mb-1">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title *ngIf="isNew">{{'PREFILLING-SOURCE-EDITOR.NEW' | translate}}</mat-card-title>
|
<mat-card-title *ngIf="isNew">{{'PREFILLING-SOURCE-EDITOR.NEW' | translate}}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined" class="mb-1">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIXED-VALUE-FIELDS' | translate}}</mat-card-title>
|
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIXED-VALUE-FIELDS' | translate}}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
</div>
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined" class="mb-1">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIELDS' | translate}}</mat-card-title>
|
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIELDS' | translate}}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined" class="mb-1">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
@ -138,7 +138,7 @@
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-card appearance="outlined" *ngIf="formGroup.get('definition').get('getEnabled').value == true">
|
<mat-card appearance="outlined" *ngIf="formGroup.get('definition').get('getEnabled').value == true" class="mb-1">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.GET-SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.GET-SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|
|
@ -7,24 +7,36 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'PREFILLING-SOURCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'PREFILLING-SOURCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'PREFILLING-SOURCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'PREFILLING-SOURCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'PREFILLING-SOURCE-LISTING.FILTER.CANCEL' | translate}}
|
{{'PREFILLING-SOURCE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'PREFILLING-SOURCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'PREFILLING-SOURCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="row reference-type-editor">
|
<div class="row reference-type-editor">
|
||||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||||
<div class="row justify-content-between align-items-center">
|
<div class="row justify-content-between align-items-center">
|
||||||
<div class="col">
|
<div class="col-md col-12">
|
||||||
<h3 *ngIf="isNew">{{'REFERENCE-TYPE-EDITOR.NEW' | translate}}</h3>
|
<h3 *ngIf="isNew">{{'REFERENCE-TYPE-EDITOR.NEW' | translate}}</h3>
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,24 +7,36 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'REFERENCE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'REFERENCE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'REFERENCE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'REFERENCE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'REFERENCE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
{{'REFERENCE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'REFERENCE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'REFERENCE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="row reference-editor">
|
<div class="row reference-editor">
|
||||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||||
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
||||||
<div class="col-auto">
|
<div class="col-md col-12">
|
||||||
<h3 *ngIf="isNew && !isClone">{{'REFERENCE-EDITOR.NEW' | translate}}</h3>
|
<h3 *ngIf="isNew && !isClone">{{'REFERENCE-EDITOR.NEW' | translate}}</h3>
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,39 +7,58 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'REFERENCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'REFERENCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'REFERENCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'REFERENCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}</mat-label>
|
||||||
<app-single-auto-complete [(ngModel)]="internalFilters.typeIds" placeholder="{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}" [configuration]="referenceTypeService.singleAutocompleteConfiguration">
|
<app-single-auto-complete [(ngModel)]="internalFilters.typeIds" placeholder="{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}" [configuration]="referenceTypeService.singleAutocompleteConfiguration">
|
||||||
</app-single-auto-complete>
|
</app-single-auto-complete>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'REFERENCE-LISTING.FILTER.SOURCE-TYPE' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'REFERENCE-LISTING.FILTER.SOURCE-TYPE' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.sourceTypes">
|
<mat-select multiple [(ngModel)]="internalFilters.sourceTypes">
|
||||||
<mat-option *ngFor="let referenceSourceType of referenceSourceTypeEnumValues" [value]="referenceSourceType">{{enumUtils.toReferenceSourceTypeString(referenceSourceType)}}</mat-option>
|
<mat-option *ngFor="let referenceSourceType of referenceSourceTypeEnumValues" [value]="referenceSourceType">{{enumUtils.toReferenceSourceTypeString(referenceSourceType)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'REFERENCE-LISTING.FILTER.CANCEL' | translate}}
|
{{'REFERENCE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'REFERENCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'REFERENCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,4 +18,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10 offset-md-1 tenant-editor">
|
<div class="col-md-10 offset-md-1 tenant-editor">
|
||||||
<div class="row align-items-center mb-4 mt-4" *ngIf="formGroup">
|
<div class="row align-items-center mb-4 mt-4" *ngIf="formGroup">
|
||||||
<div class="col-auto">
|
<div class="col-md col-12">
|
||||||
<h3 *ngIf="isNew && !isClone">{{'TENANT-EDITOR.NEW' | translate}}</h3>
|
<h3 *ngIf="isNew && !isClone">{{'TENANT-EDITOR.NEW' | translate}}</h3>
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,24 +7,36 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'TENANT-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'TENANT-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'TENANT-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'TENANT-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'TENANT-LISTING.FILTER.CANCEL' | translate}}
|
{{'TENANT-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'TENANT-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'TENANT-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,32 +7,47 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'USER-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'USER-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'USER-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'USER-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'USER-LISTING.FILTER.ROLES' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'USER-LISTING.FILTER.ROLES' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.userRoleSubQuery.roles">
|
<mat-select multiple [(ngModel)]="internalFilters.userRoleSubQuery.roles">
|
||||||
<mat-option *ngFor="let appRole of appRoleEnumValues" [value]="appRole">{{enumUtils.toAppRoleString(appRole)}}</mat-option>
|
<mat-option *ngFor="let appRole of appRoleEnumValues" [value]="appRole">{{enumUtils.toAppRoleString(appRole)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'USER-LISTING.FILTER.CANCEL' | translate}}
|
{{'USER-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'USER-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'USER-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,32 +1,39 @@
|
||||||
<form class="row user-role-editor" *ngIf="formGroup" [formGroup]="formGroup" (ngSubmit)="formSubmit()">
|
<form class="row user-role-editor" *ngIf="formGroup" [formGroup]="formGroup" (ngSubmit)="formSubmit()">
|
||||||
<div class="row align-items-center">
|
<div class="container-fluid">
|
||||||
<div *ngIf="!this.nowEditing"class="roles col">
|
<div class="row align-items-center">
|
||||||
<ng-container *ngFor="let role of this.formGroup.get('roles').value">
|
<div *ngIf="!this.nowEditing"class="roles col-8">
|
||||||
<div>
|
<div *ngFor="let role of this.formGroup.get('roles').value" class="row">
|
||||||
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'tenant-manager': role == appRole.TenantManager, 'admin': role == appRole.Admin, 'tenant-admin': role == appRole.TenantAdmin, 'tenant-user': role == appRole.TenantUser,'tenant-description-template-editor': role == appRole.TenantDescriptionTemplateEditor}">
|
<div class="col-auto p-0">
|
||||||
{{enumUtils.toAppRoleString(role)}}
|
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'tenant-manager': role == appRole.TenantManager, 'admin': role == appRole.Admin, 'tenant-description-template-editor': role == appRole.TenantDescriptionTemplateEditor, 'tenant-admin': role == appRole.TenantAdmin, 'tenant-user': role == appRole.TenantUser}">
|
||||||
</span>
|
{{enumUtils.toAppRoleString(role)}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
<div *ngIf="this.nowEditing" class="pl-0 col-8">
|
||||||
</div>
|
<mat-form-field class="w-100">
|
||||||
<div class="col-8">
|
<mat-select formControlName="roles" [panelWidth]="auto" multiple required>
|
||||||
<mat-form-field *ngIf="this.nowEditing" class="w-100">
|
<ng-container *ngFor="let role of appRoleEnumValues">
|
||||||
<mat-select formControlName="roles" multiple required>
|
<mat-option [value]="role">{{enumUtils.toAppRoleString(role)}}</mat-option>
|
||||||
<ng-container *ngFor="let role of appRoleEnumValues">
|
</ng-container>
|
||||||
<mat-option [value]="role">{{enumUtils.toAppRoleString(role)}}</mat-option>
|
</mat-select>
|
||||||
</ng-container>
|
<mat-error *ngIf="formGroup.get('roles').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-select>
|
</mat-form-field>
|
||||||
<mat-error *ngIf="formGroup.get('roles').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
</div>
|
||||||
</mat-form-field>
|
|
||||||
|
<div *ngIf="!this.nowEditing" class="col-2 p-0">
|
||||||
|
<button mat-icon-button color="primary" type="button" (click)="editItem()">
|
||||||
|
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.EDIT' | translate}}">edit</mat-icon>
|
||||||
|
<span class="row-action"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="this.nowEditing" class="col-2 p-0">
|
||||||
|
<button class="save-button" mat-icon-button color="primary" type="submit">
|
||||||
|
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.SAVE' | translate}}">save</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button *ngIf="!this.nowEditing" class="col" mat-icon-button color="primary" type="button" (click)="editItem()">
|
|
||||||
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.EDIT' | translate}}">edit</mat-icon>
|
|
||||||
<span class="row-action"></span>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="this.nowEditing" class="col-auto save-button" mat-icon-button color="primary" type="submit">
|
|
||||||
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.SAVE' | translate}}">save</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -85,6 +85,24 @@
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tenant-admin {
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: center;
|
||||||
|
// align-items: center;
|
||||||
|
min-width: 77px;
|
||||||
|
min-height: 28px;
|
||||||
|
color: #ffa631;
|
||||||
|
background: #ffe6c6 0% 0% no-repeat padding-box;
|
||||||
|
border-radius: 44px;
|
||||||
|
letter-spacing: 0.11px;
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 1;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.tenant-description-template-editor {
|
.tenant-description-template-editor {
|
||||||
// display: flex;
|
// display: flex;
|
||||||
// justify-content: center;
|
// justify-content: center;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<div class="main-content dashboard-main-container h-100" [class.non-auth-main-container]="!this.isAuthenticated()">
|
<div class="main-content dashboard-main-container h-100">
|
||||||
<div *ngIf="this.isAuthenticated()" class="container-fluid">
|
<div *ngIf="this.isAuthenticated()" class="container-fluid">
|
||||||
<div *ngIf="this.dashboardStatistics" class="main-content">
|
<div *ngIf="this.dashboardStatistics" class="main-content">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row flex-column-reverse flex-xl-row">
|
<div class="row flex-column-reverse flex-xl-row">
|
||||||
|
|
||||||
<div class="col-12 col-xl-10">
|
<div class="col-12 col-xl-10">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div *ngIf="newReleaseNotificationVisible" class="new-releases-card col-auto mt-0 mr-4">
|
<div *ngIf="newReleaseNotificationVisible" class="new-releases-card col-auto mt-0 mr-4">
|
||||||
|
@ -79,7 +78,6 @@
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Sidebar -->
|
<!-- Right Sidebar -->
|
||||||
<div *ngIf="!this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
<div *ngIf="!this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -108,7 +106,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
<div *ngIf="this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
@ -147,13 +144,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Home screen on log out -->
|
<!-- Home screen on log out -->
|
||||||
<div class="col p-0" *ngIf="!this.isAuthenticated()">
|
<div class="container-fluid" *ngIf="!this.isAuthenticated()">
|
||||||
<div class="col-auto">
|
<div class="main-content">
|
||||||
<div class="main-content">
|
<div class="container-fluid">
|
||||||
<div class="col">
|
<div class="row flex-column-reverse flex-xl-row">
|
||||||
<div class="row">
|
<div class="col-12 col-xl">
|
||||||
<div class="col d-flex flex-column">
|
<div class="row">
|
||||||
<div class="card non-auth-card" [style.display]="isIntroCardVisible ? 'block' : 'none'">
|
<div class="col-auto card" [style.display]="isIntroCardVisible ? 'block' : 'none'">
|
||||||
<a class="col-auto d-flex" (click)="dismissIntroCard()"><span class="ml-auto mt-3 material-icons clear-icon">clear</span></a>
|
<a class="col-auto d-flex" (click)="dismissIntroCard()"><span class="ml-auto mt-3 material-icons clear-icon">clear</span></a>
|
||||||
<div class="d-flex flex-column align-items-center non-auth-title-container">
|
<div class="d-flex flex-column align-items-center non-auth-title-container">
|
||||||
<h4 class="pt-4">{{'DASHBOARD.TITLE' | translate}}</h4>
|
<h4 class="pt-4">{{'DASHBOARD.TITLE' | translate}}</h4>
|
||||||
|
@ -163,49 +160,69 @@
|
||||||
<img class="col-auto ml-auto laptop-img" src="../../../assets/images/dashboard-popup.png">
|
<img class="col-auto ml-auto laptop-img" src="../../../assets/images/dashboard-popup.png">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div *ngIf="hasDmps()" class="col activity">
|
|
||||||
<div class="latest-activity-title">{{'DASHBOARD.LATEST-ACTIVITY' | translate}}</div>
|
|
||||||
<mat-tab-group mat-align-tabs="start" class="remove-border-bottom" ( [selectedIndex]="indexFromCurrentType" selectedTabChange)="currentType = $event.tab.ariaLabel">
|
|
||||||
<mat-tab aria-label="recent" label="{{'DASHBOARD.ALL' | translate}}">
|
|
||||||
<app-recent-edited-activity (totalCountRecentEdited)="onCountAllRecent($event)" [isActive]="currentType == 'recent'"></app-recent-edited-activity>
|
|
||||||
<div *ngIf="totalRecents === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
|
||||||
</mat-tab>
|
|
||||||
<mat-tab aria-label="dmps" label="{{'DASHBOARD.PUBLIC-DMPS' | translate}}">
|
|
||||||
<app-recent-edited-dmp-activity (totalCountDmps)="onCountDmps($event)" [isActive]="currentType == 'dmps'"></app-recent-edited-dmp-activity>
|
|
||||||
<div *ngIf="totalDmps === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
|
||||||
</mat-tab>
|
|
||||||
<mat-tab aria-label="datasets" label="{{'DASHBOARD.PUBLIC-DATASETS' | translate}}">
|
|
||||||
<app-recent-edited-description-activity (totalCountDatasets)="onCountDatasets($event)" [isActive]="currentType == 'datasets'"></app-recent-edited-description-activity>
|
|
||||||
<div *ngIf="totalDatasets === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
|
||||||
</mat-tab>
|
|
||||||
</mat-tab-group>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<!-- <div *ngIf="hasDmps()" class="col activity">
|
||||||
<div *ngIf="!hasDmps()" class="ml-auto pl-4 personal-usage-block">
|
<div class="latest-activity-title">{{'DASHBOARD.LATEST-ACTIVITY' | translate}}</div>
|
||||||
<div class="personal-usage">{{'DASHBOARD.PUBLIC-USAGE' | translate}}</div>
|
<mat-tab-group mat-align-tabs="start" class="remove-border-bottom" ( [selectedIndex]="indexFromCurrentType" selectedTabChange)="currentType = $event.tab.ariaLabel">
|
||||||
<div class="counter-zero">0</div>
|
<mat-tab aria-label="recent" label="{{'DASHBOARD.ALL' | translate}}">
|
||||||
<a>{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
<app-recent-edited-activity (totalCountRecentEdited)="onCountAllRecent($event)" [isActive]="currentType == 'recent'"></app-recent-edited-activity>
|
||||||
<div class="counter-zero">0</div>
|
<div *ngIf="totalRecents === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
||||||
<a>{{'DASHBOARD.PUBLIC-DATASETS' | translate}}</a>
|
</mat-tab>
|
||||||
<div class="counter-zero">0</div>
|
<mat-tab aria-label="dmps" label="{{'DASHBOARD.PUBLIC-DMPS' | translate}}">
|
||||||
|
<app-recent-edited-dmp-activity (totalCountDmps)="onCountDmps($event)" [isActive]="currentType == 'dmps'"></app-recent-edited-dmp-activity>
|
||||||
|
<div *ngIf="totalDmps === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab aria-label="datasets" label="{{'DASHBOARD.PUBLIC-DATASETS' | translate}}">
|
||||||
|
<app-recent-edited-description-activity (totalCountDatasets)="onCountDatasets($event)" [isActive]="currentType == 'datasets'"></app-recent-edited-description-activity>
|
||||||
|
<div *ngIf="totalDatasets === 0" class="empty-list">{{'DASHBOARD.EMPTY-LIST' | translate}}</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-xl-auto mb-4 stats">
|
||||||
|
<div *ngIf="!hasDmps()" class="row flex-xl-column">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="personal-usage"><span>{{'DASHBOARD.PUBLIC-USAGE' | translate}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="counter-zero"><span>0</span></div>
|
||||||
|
<a class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="counter-zero"><span>0</span></div>
|
||||||
|
<a class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="counter-zero"><span>0</span></div>
|
||||||
<a class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
<a class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
||||||
<div class="counter-zero">0</div>
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="counter-zero"><span>0</span></div>
|
||||||
<a class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
<a class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasDmps()" class="ml-auto stats">
|
</div>
|
||||||
<div class="personal-usage">{{'DASHBOARD.PUBLIC-USAGE' | translate}}</div>
|
|
||||||
<div [ngClass]="{'counter': dashboardStatistics?.dmpCount != 0, 'counter-zero': dashboardStatistics?.dmpCount == 0}">
|
<div *ngIf="hasDmps()" class="row flex-xl-column">
|
||||||
{{dashboardStatistics?.dmpCount}}</div>
|
<div class="col-12">
|
||||||
|
<div class="personal-usage" ><span>{{'DASHBOARD.PUBLIC-USAGE' | translate}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div [ngClass]="{'counter': dashboardStatistics?.dmpCount != 0, 'counter-zero': dashboardStatistics?.dmpCount == 0}" ><span>{{dashboardStatistics?.dmpCount}}</span></div>
|
||||||
<a [routerLink]="['/explore-plans']" class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
<a [routerLink]="['/explore-plans']" class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
||||||
<div [ngClass]="{'counter': dashboardStatistics?.descriptionCount != 0, 'counter-zero': dashboardStatistics?.descriptionCount == 0}">
|
</div>
|
||||||
{{dashboardStatistics?.descriptionCount}}</div>
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<div [ngClass]="{'counter': dashboardStatistics?.descriptionCount != 0, 'counter-zero': dashboardStatistics?.descriptionCount == 0}"><span>{{dashboardStatistics?.descriptionCount}}</span></div>
|
||||||
<a [routerLink]="['/explore-descriptions']" class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
|
<a [routerLink]="['/explore-descriptions']" class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
|
||||||
<div [ngClass]="{'counter': grantCount != 0, 'counter-zero': grantCount == 0}">
|
</div>
|
||||||
{{grantCount}}</div>
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<div [ngClass]="{'counter': grantCount != 0, 'counter-zero': grantCount == 0}" ><span>{{grantCount}}</span></div>
|
||||||
<a href="#" class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
<a href="#" class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
||||||
<div [ngClass]="{'counter': organizationCount != 0, 'counter-zero': organizationCount == 0}">
|
</div>
|
||||||
{{organizationCount}}</div>
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<div [ngClass]="{'counter': organizationCount != 0, 'counter-zero': organizationCount == 0}"><span>{{organizationCount}}</span></div>
|
||||||
<a href="#" class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
<a href="#" class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -374,14 +374,6 @@ input[type="text"] {
|
||||||
padding: 0rem 7em 0rem 3rem;
|
padding: 0rem 7em 0rem 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.non-auth-main-container {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.non-auth-card {
|
|
||||||
margin-left: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-info {
|
.app-info {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { DescriptionStatus } from '@app/core/common/enum/description-status';
|
||||||
|
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
|
||||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
||||||
import { Description } from '@app/core/model/description/description';
|
import { Description } from '@app/core/model/description/description';
|
||||||
import { DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
|
||||||
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
|
|
||||||
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
|
|
||||||
import { DescriptionService } from '@app/core/services/description/description.service';
|
import { DescriptionService } from '@app/core/services/description/description.service';
|
||||||
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
|
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component';
|
import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component';
|
||||||
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
|
|
||||||
import { DescriptionStatus } from '@app/core/common/enum/description-status';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-description-base-fields-editor-component',
|
selector: 'app-description-base-fields-editor-component',
|
||||||
|
@ -47,15 +44,17 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
|
||||||
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
|
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
|
||||||
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
|
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
|
||||||
|
|
||||||
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
|
if (this.description?.descriptionTemplate != null) {
|
||||||
if (isPreviousVersion === true) {
|
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
|
||||||
if (this.description.status === DescriptionStatus.Draft) {
|
if (isPreviousVersion === true) {
|
||||||
this.openDeprecatedDescriptionTemplateDialog();
|
if (this.description.status === DescriptionStatus.Draft) {
|
||||||
|
this.openDeprecatedDescriptionTemplateDialog();
|
||||||
|
} else {
|
||||||
|
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,19 +66,19 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
|
||||||
});
|
});
|
||||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
result => {
|
result => {
|
||||||
if(result) {
|
if (result) {
|
||||||
this.descriptionService.updateDescriptionTemplate({
|
this.descriptionService.updateDescriptionTemplate({
|
||||||
id: this.description.id,
|
id: this.description.id,
|
||||||
hash: this.description.hash
|
hash: this.description.hash
|
||||||
})
|
})
|
||||||
.subscribe(
|
.subscribe(
|
||||||
result => {
|
result => {
|
||||||
this.refresh.emit(result);
|
this.refresh.emit(result);
|
||||||
},
|
},
|
||||||
error => console.error(error));
|
error => console.error(error));
|
||||||
} else {
|
} else {
|
||||||
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,13 +111,13 @@
|
||||||
<span class="material-icons">chevron_left</span>
|
<span class="material-icons">chevron_left</span>
|
||||||
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div>
|
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="this.step < this.maxStep" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxStep, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
|
<div *ngIf="this.step < this.maxSteps" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxSteps, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
|
||||||
<div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div>
|
<div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div>
|
||||||
<span class="material-icons">chevron_right</span>
|
<span class="material-icons">chevron_right</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto pr-0" *ngIf="this.step !== 0">
|
<div class="col-auto pr-0" *ngIf="this.step !== 0">
|
||||||
<!-- <app-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup" [isDmpEditor]="true"></app-form-progress-indication> -->
|
<app-dmp-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup"></app-dmp-form-progress-indication>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
|
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
|
||||||
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
||||||
displayFn: (item: DmpBlueprint) => item.label,
|
displayFn: (item: DmpBlueprint) => item.label,
|
||||||
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version,
|
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' ' + item.version,
|
||||||
titleFn: (item: DmpBlueprint) => item.label,
|
titleFn: (item: DmpBlueprint) => item.label,
|
||||||
valueAssign: (item: DmpBlueprint) => item.id,
|
valueAssign: (item: DmpBlueprint) => item.id,
|
||||||
};
|
};
|
||||||
|
@ -174,14 +174,14 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
try {
|
try {
|
||||||
this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel();
|
this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel();
|
||||||
if (data) {
|
if (data) {
|
||||||
if(data.descriptions){
|
if (data.descriptions) {
|
||||||
if (data.status == DmpStatus.Finalized) {
|
if (data.status == DmpStatus.Finalized) {
|
||||||
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
|
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
|
||||||
} else {
|
} else {
|
||||||
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
|
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(data.dmpDescriptionTemplates){
|
if (data.dmpDescriptionTemplates) {
|
||||||
data.dmpDescriptionTemplates = data.dmpDescriptionTemplates.filter(x => x.isActive === IsActive.Active);
|
data.dmpDescriptionTemplates = data.dmpDescriptionTemplates.filter(x => x.isActive === IsActive.Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +304,11 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
// Steps
|
// Steps
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
get maxSteps(): number {
|
||||||
|
return this.item?.blueprint?.definition?.sections?.length ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
changeStep(index: number) {
|
changeStep(index: number) {
|
||||||
this.step = index;
|
this.step = index;
|
||||||
this.resetScroll();
|
this.resetScroll();
|
||||||
|
@ -389,7 +394,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
DmpEditorModel.reApplyPropertiesValidators(
|
DmpEditorModel.reApplyPropertiesValidators(
|
||||||
{
|
{
|
||||||
formGroup: this.formGroup,
|
formGroup: this.formGroup,
|
||||||
validationErrorModel: this.editorModel.validationErrorModel
|
validationErrorModel: this.editorModel.validationErrorModel,
|
||||||
|
blueprint: this.item.blueprint
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.formGroup.get('properties').get('contacts').markAsDirty();
|
this.formGroup.get('properties').get('contacts').markAsDirty();
|
||||||
|
@ -404,7 +410,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
DmpEditorModel.reApplyPropertiesValidators(
|
DmpEditorModel.reApplyPropertiesValidators(
|
||||||
{
|
{
|
||||||
formGroup: this.formGroup,
|
formGroup: this.formGroup,
|
||||||
validationErrorModel: this.editorModel.validationErrorModel
|
validationErrorModel: this.editorModel.validationErrorModel,
|
||||||
|
blueprint: this.item.blueprint
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.formGroup.get('properties').get('contacts').markAsDirty();
|
this.formGroup.get('properties').get('contacts').markAsDirty();
|
||||||
|
@ -455,32 +462,32 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
canAddDescription(section: DmpBlueprintDefinitionSection ): boolean{
|
canAddDescription(section: DmpBlueprintDefinitionSection): boolean {
|
||||||
if(section.hasTemplates){
|
if (section.hasTemplates) {
|
||||||
if (section.descriptionTemplates?.length > 0){
|
if (section.descriptionTemplates?.length > 0) {
|
||||||
const descriptions = this.descriptionsInSection(section.id)
|
const descriptions = this.descriptionsInSection(section.id)
|
||||||
|
|
||||||
if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length){
|
if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let multiplicityValidResults :boolean[] = [];
|
let multiplicityValidResults: boolean[] = [];
|
||||||
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
|
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
|
||||||
if (sectionDescriptionTemplate.maxMultiplicity != null){
|
if (sectionDescriptionTemplate.maxMultiplicity != null) {
|
||||||
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
|
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
|
||||||
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
|
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
|
||||||
else multiplicityValidResults.push(true);
|
else multiplicityValidResults.push(true);
|
||||||
}else{
|
} else {
|
||||||
multiplicityValidResults.push(true);
|
multiplicityValidResults.push(true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if(multiplicityValidResults.includes(true)) return true
|
if (multiplicityValidResults.includes(true)) return true
|
||||||
else return false;
|
else return false;
|
||||||
}else{
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,10 +511,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => {
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => {
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[];
|
let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[];
|
||||||
if (data){
|
if (data) {
|
||||||
data.push(groupId);
|
data.push(groupId);
|
||||||
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data);
|
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data);
|
||||||
} else{
|
} else {
|
||||||
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue([groupId]);
|
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue([groupId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { FormArray, FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
import { FormArray, FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
import { DmpAccessType } from "@app/core/common/enum/dmp-access-type";
|
import { DmpAccessType } from "@app/core/common/enum/dmp-access-type";
|
||||||
|
import { DmpBlueprintFieldCategory } from "@app/core/common/enum/dmp-blueprint-field-category";
|
||||||
import { DmpContactType } from "@app/core/common/enum/dmp-contact-type";
|
import { DmpContactType } from "@app/core/common/enum/dmp-contact-type";
|
||||||
import { DmpStatus } from "@app/core/common/enum/dmp-status";
|
import { DmpStatus } from "@app/core/common/enum/dmp-status";
|
||||||
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
|
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
|
||||||
import { DmpUserType } from "@app/core/common/enum/dmp-user-type";
|
import { DmpUserType } from "@app/core/common/enum/dmp-user-type";
|
||||||
import { IsActive } from "@app/core/common/enum/is-active.enum";
|
import { IsActive } from "@app/core/common/enum/is-active.enum";
|
||||||
import { DmpBlueprint } from "@app/core/model/dmp-blueprint/dmp-blueprint";
|
import { DmpBlueprint, FieldInSection } from "@app/core/model/dmp-blueprint/dmp-blueprint";
|
||||||
import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist, DmpUser, DmpUserPersist } from "@app/core/model/dmp/dmp";
|
import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist, DmpUser, DmpUserPersist } from "@app/core/model/dmp/dmp";
|
||||||
import { DmpReference } from "@app/core/model/dmp/dmp-reference";
|
import { DmpReference } from "@app/core/model/dmp/dmp-reference";
|
||||||
import { ReferencePersist } from "@app/core/model/reference/reference";
|
import { ReferencePersist } from "@app/core/model/reference/reference";
|
||||||
|
@ -103,8 +104,8 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
||||||
|
|
||||||
const descriptionTemplatesFormGroup = this.formBuilder.group({});
|
const descriptionTemplatesFormGroup = this.formBuilder.group({});
|
||||||
(this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map(
|
(this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map(
|
||||||
(item, index) => descriptionTemplatesFormGroup.addControl(item.toString(),
|
(item, index) => descriptionTemplatesFormGroup.addControl(item.toString(),
|
||||||
new FormControl(this.descriptionTemplates?.filter(x => x.sectionId === item)?.filter(x => x.descriptionTemplateGroupId).map(x => x.descriptionTemplateGroupId) || [], context.getValidation('descriptionTemplates').validators))
|
new FormControl(this.descriptionTemplates?.filter(x => x.sectionId === item)?.filter(x => x.descriptionTemplateGroupId).map(x => x.descriptionTemplateGroupId) || [], context.getValidation('descriptionTemplates').validators))
|
||||||
);
|
);
|
||||||
// // buildForm({
|
// // buildForm({
|
||||||
// // rootPath: `descriptionTemplates[${index}].`
|
// // rootPath: `descriptionTemplates[${index}].`
|
||||||
|
@ -120,13 +121,16 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
||||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||||
baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
|
baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
|
||||||
baseValidationArray.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
|
baseValidationArray.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
|
||||||
baseValidationArray.push({ key: 'properties', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'properties')] });
|
baseValidationArray.push({ key: 'properties', validators: [BackendErrorValidator(this.validationErrorModel, 'properties')] });
|
||||||
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
|
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
|
||||||
baseValidationArray.push({ key: 'language', validators: [BackendErrorValidator(this.validationErrorModel, 'language')] });
|
baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] });
|
||||||
baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
|
baseValidationArray.push({ key: 'blueprint', validators: [BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
|
||||||
baseValidationArray.push({ key: 'accessType', validators: [BackendErrorValidator(this.validationErrorModel, 'accessType')] });
|
baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] });
|
||||||
baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });
|
baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });
|
||||||
|
|
||||||
|
baseValidationArray.push({ key: 'dmpDescriptionValidator', validators: [] });
|
||||||
|
|
||||||
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] });
|
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] });
|
||||||
baseValidationArray.push({ key: 'hash', validators: [] });
|
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||||
|
|
||||||
|
@ -143,6 +147,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
||||||
static reApplyPropertiesValidators(params: {
|
static reApplyPropertiesValidators(params: {
|
||||||
formGroup: UntypedFormGroup,
|
formGroup: UntypedFormGroup,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
blueprint: DmpBlueprint
|
||||||
}): void {
|
}): void {
|
||||||
|
|
||||||
const { formGroup, validationErrorModel } = params;
|
const { formGroup, validationErrorModel } = params;
|
||||||
|
@ -150,20 +155,36 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
||||||
DmpPropertiesEditorModel.reapplyValidators({
|
DmpPropertiesEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
rootPath: `properties.`,
|
rootPath: `properties.`,
|
||||||
validationErrorModel: validationErrorModel
|
validationErrorModel: validationErrorModel,
|
||||||
|
blueprint: params.blueprint
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static reApplyDescriptionTemplateValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, validationErrorModel } = params;
|
||||||
|
|
||||||
const descriptionTemplates = formGroup?.get('descriptionTemplates') as UntypedFormGroup;
|
const descriptionTemplates = formGroup?.get('descriptionTemplates') as UntypedFormGroup;
|
||||||
const keys = Object.keys(descriptionTemplates.value as Object);
|
const keys = Object.keys(descriptionTemplates.value as Object);
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const control = descriptionTemplates?.get(key);
|
const control = descriptionTemplates?.get(key);
|
||||||
DmpBlueprintValueEditorModel.reapplyValidators({
|
DmpDescriptionTemplateEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
rootPath: `descriptionTemplates[${key}].`,
|
rootPath: `descriptionTemplates[${key}].`,
|
||||||
validationErrorModel: validationErrorModel
|
validationErrorModel: validationErrorModel
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static reApplyUsersValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, validationErrorModel } = params;
|
||||||
(formGroup.get('users') as FormArray).controls?.forEach(
|
(formGroup.get('users') as FormArray).controls?.forEach(
|
||||||
(control, index) => DmpUserEditorModel.reapplyValidators({
|
(control, index) => DmpUserEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
|
@ -186,14 +207,16 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
||||||
|
|
||||||
fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel {
|
fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel {
|
||||||
|
|
||||||
|
|
||||||
dmpBlueprint.definition.sections.forEach(section => {
|
dmpBlueprint.definition.sections.forEach(section => {
|
||||||
section.fields?.forEach(field => {
|
section.fields?.forEach(field => {
|
||||||
this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel(
|
if (field.category !== DmpBlueprintFieldCategory.System) {
|
||||||
{
|
this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel(
|
||||||
fieldId: field.id,
|
{
|
||||||
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue,
|
fieldId: field.id,
|
||||||
}, dmpReferences));
|
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue,
|
||||||
|
}, dmpReferences, field));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel(this.validationErrorModel).fromModel(x))); }
|
if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel(this.validationErrorModel).fromModel(x))); }
|
||||||
|
@ -228,8 +251,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
||||||
|
|
||||||
const dmpBlueprintValuesFormGroup = this.formBuilder.group({});
|
const dmpBlueprintValuesFormGroup = this.formBuilder.group({});
|
||||||
this.dmpBlueprintValues.forEach((value, key) => dmpBlueprintValuesFormGroup.addControl(key.toString(), value.buildForm({
|
this.dmpBlueprintValues.forEach((value, key) => dmpBlueprintValuesFormGroup.addControl(key.toString(), value.buildForm({
|
||||||
rootPath: `${rootPath}dmpBlueprintValues[${key}].`
|
rootPath: `${rootPath}dmpBlueprintValues[${key}].`
|
||||||
})), context.getValidation('dmpBlueprintValues')
|
})), context.getValidation('dmpBlueprintValues')
|
||||||
)
|
)
|
||||||
formGroup.addControl('dmpBlueprintValues', dmpBlueprintValuesFormGroup);
|
formGroup.addControl('dmpBlueprintValues', dmpBlueprintValuesFormGroup);
|
||||||
|
|
||||||
|
@ -254,7 +277,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
||||||
static reapplyValidators(params: {
|
static reapplyValidators(params: {
|
||||||
formGroup: UntypedFormGroup,
|
formGroup: UntypedFormGroup,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
rootPath: string
|
rootPath: string,
|
||||||
|
blueprint: DmpBlueprint
|
||||||
}): void {
|
}): void {
|
||||||
|
|
||||||
const { formGroup, rootPath, validationErrorModel } = params;
|
const { formGroup, rootPath, validationErrorModel } = params;
|
||||||
|
@ -266,9 +290,10 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
||||||
DmpBlueprintValueEditorModel.reapplyValidators({
|
DmpBlueprintValueEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
rootPath: `${rootPath}dmpBlueprintValues[${key}].`,
|
rootPath: `${rootPath}dmpBlueprintValues[${key}].`,
|
||||||
validationErrorModel: validationErrorModel
|
validationErrorModel: validationErrorModel,
|
||||||
|
isRequired: params.blueprint.definition.sections.flatMap(x => x.fields).find(x => x.id.toString() == key).required
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
(formGroup.get('contacts') as FormArray).controls?.forEach(
|
(formGroup.get('contacts') as FormArray).controls?.forEach(
|
||||||
(control, index) => DmpContactEditorModel.reapplyValidators({
|
(control, index) => DmpContactEditorModel.reapplyValidators({
|
||||||
|
@ -284,6 +309,8 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
||||||
fieldId: Guid;
|
fieldId: Guid;
|
||||||
fieldValue: string;
|
fieldValue: string;
|
||||||
references: DmpReferencePersist[] = [];
|
references: DmpReferencePersist[] = [];
|
||||||
|
isRequired: boolean = false;
|
||||||
|
category: DmpBlueprintFieldCategory;
|
||||||
|
|
||||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
@ -291,7 +318,7 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
||||||
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel {
|
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[], field: FieldInSection): DmpBlueprintValueEditorModel {
|
||||||
this.fieldId = item.fieldId;
|
this.fieldId = item.fieldId;
|
||||||
this.fieldValue = item.fieldValue;
|
this.fieldValue = item.fieldValue;
|
||||||
this.references = dmpReferences?.filter(x => x.data.blueprintFieldId == this.fieldId && x.isActive == IsActive.Active).map(x => {
|
this.references = dmpReferences?.filter(x => x.data.blueprintFieldId == this.fieldId && x.isActive == IsActive.Active).map(x => {
|
||||||
|
@ -311,6 +338,10 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.isRequired = field.required;
|
||||||
|
if (this.isRequired) console.log(field);
|
||||||
|
this.category = field.category;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,28 +354,39 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = DmpBlueprintValueEditorModel.createValidationContext({
|
context = DmpBlueprintValueEditorModel.createValidationContext({
|
||||||
validationErrorModel: this.validationErrorModel,
|
validationErrorModel: this.validationErrorModel,
|
||||||
rootPath
|
rootPath,
|
||||||
|
isRequired: this.isRequired
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.formBuilder.group({
|
const formGroup = this.formBuilder.group({
|
||||||
fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators],
|
fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators],
|
||||||
fieldValue: [{ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators],
|
|
||||||
references: [{ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators],
|
|
||||||
});
|
});
|
||||||
|
switch (this.category) {
|
||||||
|
case DmpBlueprintFieldCategory.ReferenceType:
|
||||||
|
formGroup.addControl('references', new FormControl({ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators));
|
||||||
|
break;
|
||||||
|
case DmpBlueprintFieldCategory.System:
|
||||||
|
case DmpBlueprintFieldCategory.Extra:
|
||||||
|
formGroup.addControl('fieldValue', new FormControl({ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createValidationContext(params: {
|
static createValidationContext(params: {
|
||||||
rootPath?: string,
|
rootPath?: string,
|
||||||
validationErrorModel: ValidationErrorModel
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
isRequired: boolean,
|
||||||
}): ValidationContext {
|
}): ValidationContext {
|
||||||
const { rootPath = '', validationErrorModel } = params;
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
const baseContext: ValidationContext = new ValidationContext();
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
baseValidationArray.push({ key: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] });
|
baseValidationArray.push({ key: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] });
|
||||||
baseValidationArray.push({ key: 'fieldValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
|
baseValidationArray.push({ key: 'fieldValue', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
|
||||||
baseValidationArray.push({ key: 'references', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
baseValidationArray.push({ key: 'references', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}references`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
||||||
|
|
||||||
baseContext.validation = baseValidationArray;
|
baseContext.validation = baseValidationArray;
|
||||||
return baseContext;
|
return baseContext;
|
||||||
|
@ -353,13 +395,15 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
||||||
static reapplyValidators(params: {
|
static reapplyValidators(params: {
|
||||||
formGroup: UntypedFormGroup,
|
formGroup: UntypedFormGroup,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
rootPath: string
|
rootPath: string,
|
||||||
|
isRequired: boolean
|
||||||
}): void {
|
}): void {
|
||||||
|
|
||||||
const { formGroup, rootPath, validationErrorModel } = params;
|
const { formGroup, rootPath, validationErrorModel } = params;
|
||||||
const context = DmpBlueprintValueEditorModel.createValidationContext({
|
const context = DmpBlueprintValueEditorModel.createValidationContext({
|
||||||
rootPath,
|
rootPath,
|
||||||
validationErrorModel
|
validationErrorModel,
|
||||||
|
isRequired: params.isRequired
|
||||||
});
|
});
|
||||||
|
|
||||||
['fieldId', 'fieldValue', 'references'].forEach(keyField => {
|
['fieldId', 'fieldValue', 'references'].forEach(keyField => {
|
||||||
|
@ -375,7 +419,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
email: string;
|
email: string;
|
||||||
contactType: DmpContactType= DmpContactType.Internal;
|
contactType: DmpContactType = DmpContactType.Internal;
|
||||||
|
|
||||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
@ -384,11 +428,11 @@ export class DmpContactEditorModel implements DmpContactPersist {
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
fromModel(item: DmpContact): DmpContactEditorModel {
|
fromModel(item: DmpContact): DmpContactEditorModel {
|
||||||
if(item?.user?.id) this.userId = item.user.id;
|
if (item?.user?.id) this.userId = item.user.id;
|
||||||
this.firstName = item.firstName;
|
this.firstName = item.firstName;
|
||||||
this.lastName = item.lastName;
|
this.lastName = item.lastName;
|
||||||
this.email = item.email;
|
this.email = item.email;
|
||||||
this.contactType = (item == null || this.userId != null) ? DmpContactType.Internal : DmpContactType.External;
|
this.contactType = (item == null || this.userId != null) ? DmpContactType.Internal : DmpContactType.External;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -457,7 +501,7 @@ export class DmpUserEditorModel implements DmpUserPersist {
|
||||||
user: Guid;
|
user: Guid;
|
||||||
role: DmpUserRole;
|
role: DmpUserRole;
|
||||||
email: string;
|
email: string;
|
||||||
userType: DmpUserType= DmpUserType.Internal;
|
userType: DmpUserType = DmpUserType.Internal;
|
||||||
sectionId: Guid;
|
sectionId: Guid;
|
||||||
|
|
||||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
@ -467,10 +511,10 @@ export class DmpUserEditorModel implements DmpUserPersist {
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
fromModel(item: DmpUser): DmpUserEditorModel {
|
fromModel(item: DmpUser): DmpUserEditorModel {
|
||||||
if(item?.user?.id) this.user = item.user.id;
|
if (item?.user?.id) this.user = item.user.id;
|
||||||
this.role = item.role;
|
this.role = item.role;
|
||||||
// TODO this.email = item.email;
|
// TODO this.email = item.email;
|
||||||
this.userType = (item == null || this.user != null) ? DmpUserType.Internal : DmpUserType.External;
|
this.userType = (item == null || this.user != null) ? DmpUserType.Internal : DmpUserType.External;
|
||||||
this.sectionId = item.sectionId;
|
this.sectionId = item.sectionId;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -623,7 +667,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = DmpDescriptionTemplateEditorModel.createValidationContext({
|
context = DmpDescriptionTemplateEditorModel.createValidationContext({
|
||||||
validationErrorModel: this.validationErrorModel,
|
validationErrorModel: this.validationErrorModel,
|
||||||
rootPath
|
rootPath: rootPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +679,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
|
||||||
|
|
||||||
static createValidationContext(params: {
|
static createValidationContext(params: {
|
||||||
rootPath?: string,
|
rootPath?: string,
|
||||||
validationErrorModel: ValidationErrorModel
|
validationErrorModel: ValidationErrorModel,
|
||||||
}): ValidationContext {
|
}): ValidationContext {
|
||||||
const { rootPath = '', validationErrorModel } = params;
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormattingModule } from '@app/core/formatting.module';
|
import { FormattingModule } from '@app/core/formatting.module';
|
||||||
|
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
|
||||||
import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module';
|
import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module';
|
||||||
|
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
|
||||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||||
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
|
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
|
||||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
||||||
import { DmpEditorComponent } from './dmp-editor.component';
|
import { DmpEditorComponent } from './dmp-editor.component';
|
||||||
import { DmpEditorRoutingModule } from './dmp-editor.routing';
|
import { DmpEditorRoutingModule } from './dmp-editor.routing';
|
||||||
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
|
import { DmpFormProgressIndicationModule } from './form-progress-indication/dmp-form-progress-indication.module';
|
||||||
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
||||||
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -22,7 +23,8 @@ import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
||||||
AutoCompleteModule,
|
AutoCompleteModule,
|
||||||
ReferenceFieldModule,
|
ReferenceFieldModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
DmpUserFieldModule
|
DmpUserFieldModule,
|
||||||
|
DmpFormProgressIndicationModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DmpEditorComponent,
|
DmpEditorComponent,
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="demo-progress-bar-container">
|
||||||
|
<div class="percentage d-flex justify-content-center">{{progressSoFar}} {{'GENERAL.PREPOSITIONS.OF' | translate}} {{total}}</div>
|
||||||
|
<mat-progress-bar class="form-progress-bar" [ngClass]="{'progress-bar': true}" mode="determinate" [value]="value"></mat-progress-bar>
|
||||||
|
<div class="percentage" [ngStyle]="{'padding-left': value ? value - 10 + '%' : 0 + '%' }">{{value}}%</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,16 @@
|
||||||
|
.percentage {
|
||||||
|
color: #212121;
|
||||||
|
opacity: 0.7;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
border-radius: 20px;
|
||||||
|
height: 11px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-progress-bar .mat-progress-bar-fill::after {
|
||||||
|
border-radius: 20px !important;
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||||
|
import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||||
|
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||||
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dmp-form-progress-indication',
|
||||||
|
templateUrl: './dmp-form-progress-indication.component.html',
|
||||||
|
styleUrls: ['./dmp-form-progress-indication.component.scss']
|
||||||
|
})
|
||||||
|
export class DmpFormProgressIndicationComponent extends BaseComponent implements OnInit, OnChanges {
|
||||||
|
@Input() formGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
@Input() public progressValueAccuracy = 2;
|
||||||
|
progressSoFar: number;
|
||||||
|
total: number;
|
||||||
|
percent: number;
|
||||||
|
|
||||||
|
constructor(private visibilityRulesService: VisibilityRulesService) { super(); }
|
||||||
|
|
||||||
|
public value = 0;
|
||||||
|
ngOnInit() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes.formGroup) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
setTimeout(() => { this.calculateValueForProgressbar(); });
|
||||||
|
this.formGroup
|
||||||
|
.valueChanges
|
||||||
|
.pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(control => {
|
||||||
|
setTimeout(() => { this.calculateValueForProgressbar(); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateValueForProgressbar() {
|
||||||
|
this.progressSoFar = this.countFormControlsValidForProgress(this.formGroup);
|
||||||
|
this.total = this.countFormControlsRequiredFieldsForTotal(this.formGroup);
|
||||||
|
this.percent = (this.progressSoFar / this.total) * 100;
|
||||||
|
this.value = Number.parseFloat(this.percent.toPrecision(this.progressValueAccuracy));
|
||||||
|
}
|
||||||
|
|
||||||
|
countFormControlsValidForProgress(formControl: AbstractControl): number {
|
||||||
|
let valueCurrent = 0;
|
||||||
|
if (formControl instanceof UntypedFormControl) {
|
||||||
|
if (this.controlRequired(formControl) && this.controlEnabled(formControl) && formControl.valid) {
|
||||||
|
valueCurrent++;
|
||||||
|
}
|
||||||
|
} else if (formControl instanceof UntypedFormGroup) {
|
||||||
|
Object.keys(formControl.controls).forEach(item => {
|
||||||
|
const control = formControl.get(item);
|
||||||
|
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(control);
|
||||||
|
});
|
||||||
|
} else if (formControl instanceof UntypedFormArray) {
|
||||||
|
formControl.controls.forEach(item => {
|
||||||
|
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return valueCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
countFormControlsRequiredFieldsForTotal(formControl: AbstractControl, checkVisibility = false): number {
|
||||||
|
let valueCurrent = 0;
|
||||||
|
if (formControl instanceof UntypedFormControl) {
|
||||||
|
if (this.controlRequired(formControl) && this.controlEnabled(formControl)) {
|
||||||
|
valueCurrent++;
|
||||||
|
}
|
||||||
|
} else if (formControl instanceof UntypedFormGroup) {
|
||||||
|
if (!checkVisibility || (!formControl.get('id')?.value || (this.visibilityRulesService.isVisibleMap[formControl.get('id').value] ?? true))) {
|
||||||
|
Object.keys(formControl.controls).forEach(item => {
|
||||||
|
const control = formControl.get(item);
|
||||||
|
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(control, checkVisibility);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (formControl instanceof UntypedFormArray) {
|
||||||
|
formControl.controls.forEach(item => {
|
||||||
|
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(item, checkVisibility);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return valueCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
controlRequired(formControl: AbstractControl) {
|
||||||
|
if (formControl.validator) {
|
||||||
|
const validator = formControl.validator({} as AbstractControl);
|
||||||
|
if (validator && validator.required) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else { return false }
|
||||||
|
}
|
||||||
|
|
||||||
|
controlEnabled(formControl: AbstractControl) {
|
||||||
|
if (formControl.enabled) {
|
||||||
|
return true;
|
||||||
|
} else { return false }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||||
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
import { DmpFormProgressIndicationComponent } from './dmp-form-progress-indication.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonUiModule,
|
||||||
|
CommonFormsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DmpFormProgressIndicationComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
DmpFormProgressIndicationComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class DmpFormProgressIndicationModule { }
|
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
|
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
|
||||||
<mat-form-field class="w-100">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}*</mat-label>
|
<mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}</mat-label>
|
||||||
<app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete>
|
<app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete>
|
||||||
<mat-error *ngIf="user.get('user').hasError('backendError')">{{user.get('user').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="user.get('user').hasError('backendError')">{{user.get('user').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
|
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
|
||||||
<mat-form-field class="w-100">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}*</mat-label>
|
<mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}</mat-label>
|
||||||
<input matInput type="text" name="email" [formControl]="user.get('email')">
|
<input matInput type="text" name="email" [formControl]="user.get('email')">
|
||||||
<mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
|
|
@ -57,10 +57,10 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
|
||||||
removeUser(userIndex: number): void {
|
removeUser(userIndex: number): void {
|
||||||
(this.form.get('users') as FormArray).removeAt(userIndex);
|
(this.form.get('users') as FormArray).removeAt(userIndex);
|
||||||
|
|
||||||
DmpEditorModel.reApplyPropertiesValidators(
|
DmpEditorModel.reApplyUsersValidators(
|
||||||
{
|
{
|
||||||
formGroup: this.form,
|
formGroup: this.form,
|
||||||
validationErrorModel: this.validationErrorModel
|
validationErrorModel: this.validationErrorModel,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.form.get('users').markAsDirty();
|
this.form.get('users').markAsDirty();
|
||||||
|
@ -78,7 +78,7 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
|
||||||
moveItemInArray(usersFormArray.controls, event.previousIndex, event.currentIndex);
|
moveItemInArray(usersFormArray.controls, event.previousIndex, event.currentIndex);
|
||||||
usersFormArray.updateValueAndValidity();
|
usersFormArray.updateValueAndValidity();
|
||||||
|
|
||||||
DmpEditorModel.reApplyPropertiesValidators(
|
DmpEditorModel.reApplyUsersValidators(
|
||||||
{
|
{
|
||||||
formGroup: this.form,
|
formGroup: this.form,
|
||||||
validationErrorModel: this.validationErrorModel
|
validationErrorModel: this.validationErrorModel
|
||||||
|
|
|
@ -131,7 +131,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
|
||||||
selector: '.dataset-tour',
|
selector: '.dataset-tour',
|
||||||
content: 'Step 2',
|
content: 'Step 2',
|
||||||
orientation: Orientation.Right,
|
orientation: Orientation.Right,
|
||||||
isStepUnique: false
|
isStepUnique: false,
|
||||||
|
useHighlightPadding: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -195,8 +195,8 @@
|
||||||
<p class="mb-0 mr-0 pl-2 frame-txt" [matMenuTriggerFor]="exportMenu">{{ 'DMP-OVERVIEW.ACTIONS.EXPORT' | translate }}</p>
|
<p class="mb-0 mr-0 pl-2 frame-txt" [matMenuTriggerFor]="exportMenu">{{ 'DMP-OVERVIEW.ACTIONS.EXPORT' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="canCreateNewVersion()" (click)="newVersionClicked()">
|
<ng-container *ngIf="canCreateNewVersion()">
|
||||||
<div class="col-12 d-flex align-items-center">
|
<div class="col-12 d-flex align-items-center" (click)="newVersionClicked()">
|
||||||
<button mat-mini-fab class="frame-btn">
|
<button mat-mini-fab class="frame-btn">
|
||||||
<mat-icon class="mat-mini-fab-icon">add_to_photos</mat-icon>
|
<mat-icon class="mat-mini-fab-icon">add_to_photos</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
.nav-row {
|
.nav-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -43,6 +44,19 @@
|
||||||
font-family: 'Roboto',sans-serif;
|
font-family: 'Roboto',sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-subrow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
color: #000000;
|
||||||
|
opacity: 1;
|
||||||
|
font-size: 0.93rem;
|
||||||
|
font-family: 'Roboto',sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-row:hover {
|
.nav-row:hover {
|
||||||
background-color: #ececec;
|
background-color: #ececec;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
|
@ -9,11 +9,13 @@
|
||||||
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>
|
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>
|
||||||
<span [ngClass]="{'pl-0': groupMenuRoute.path == '/plans'}">{{groupMenuRoute.title | translate}}</span>
|
<span [ngClass]="{'pl-0': groupMenuRoute.path == '/plans'}">{{groupMenuRoute.title | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="nav-link nav-row dataset-tour" *ngIf="groupMenuRoute.path === '/descriptions'" [routerLink]="[groupMenuRoute.path]">
|
<a class="nav-link nav-row" *ngIf="groupMenuRoute.path === '/descriptions'" [routerLink]="[groupMenuRoute.path]">
|
||||||
<span class="inner-line"></span>
|
<span class="mb-2 inner-line"></span>
|
||||||
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
<div class="pl-0 pt-1 pb-1 container-fluid nav-subrow dataset-tour">
|
||||||
<i class="material-symbols-outlined icon-mask">person</i>
|
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
||||||
<span class="pl-0">{{groupMenuRoute.title | translate}}</span>
|
<i class="material-symbols-outlined icon-mask">person</i>
|
||||||
|
<span class="pl-0">{{groupMenuRoute.title | translate}}</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="nav-link nav-row" *ngIf="groupMenuRoute.path === '/co-branding'" href="/splash/resources/co-branding.html">
|
<a class="nav-link nav-row" *ngIf="groupMenuRoute.path === '/co-branding'" href="/splash/resources/co-branding.html">
|
||||||
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { UntypedFormArray } from '@angular/forms';
|
import { UntypedFormArray } from '@angular/forms';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
||||||
import { NotificationFieldInfoEditorModel, NotificationFieldOptionsEditorModel } from '../notification-template-editor.model';
|
import { NotificationFieldInfoEditorModel, NotificationFieldOptionsEditorModel } from '../notification-template-editor.model';
|
||||||
|
@ -25,7 +25,7 @@ export class NotificationTemplateFieldOptionsComponent extends BaseComponent imp
|
||||||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public enumUtils: EnumUtils,
|
public enumUtils: NotificationServiceEnumUtils,
|
||||||
) { super(); }
|
) { super(); }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="row notification-template-editor">
|
<div class="row notification-template-editor">
|
||||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||||
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
||||||
<div class="col-auto">
|
<div class="col-md col-12">
|
||||||
<h3 *ngIf="isNew && !isClone">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.NEW' | translate}}</h3>
|
<h3 *ngIf="isNew && !isClone">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.NEW' | translate}}</h3>
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,48 +7,69 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.notificationTypes">
|
<mat-select multiple [(ngModel)]="internalFilters.notificationTypes">
|
||||||
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.KIND' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.KIND' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.kinds">
|
<mat-select multiple [(ngModel)]="internalFilters.kinds">
|
||||||
<mat-option *ngFor="let kind of notificationTemplateKindEnumValues" [value]="kind">{{enumUtils.toNotificationTemplateKindString(kind)}}</mat-option>
|
<mat-option *ngFor="let kind of notificationTemplateKindEnumValues" [value]="kind">{{enumUtils.toNotificationTemplateKindString(kind)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="row mt-3">
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CHANNEL' | translate}}
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CHANNEL' | translate}}</mat-label>
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.channels">
|
<mat-select multiple [(ngModel)]="internalFilters.channels">
|
||||||
<mat-option *ngFor="let channel of notificationTemplateChannelEnumValues" [value]="channel">{{enumUtils.toNotificationTemplateChannelString(channel)}}</mat-option>
|
<mat-option *ngFor="let channel of notificationTemplateChannelEnumValues" [value]="channel">{{enumUtils.toNotificationTemplateChannelString(channel)}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-label>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,4 +22,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,79 +7,101 @@
|
||||||
|
|
||||||
|
|
||||||
<mat-menu #filterMenu>
|
<mat-menu #filterMenu>
|
||||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||||
<div class="search-listing-filters-container">
|
<div class="row justify-content-between">
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="col-auto mt-2">
|
||||||
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TITLE' | translate}}</h4>
|
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
<button color="accent" mat-button (click)="clearFilters()">
|
<button color="accent" mat-button (click)="clearFilters()">
|
||||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
<div class="row mt-3">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.IS-ACTIVE' | translate}}
|
<div class="col-12">
|
||||||
</mat-slide-toggle>
|
<section class="w-100">
|
||||||
|
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||||
<div class="mt-4">
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||||
<div>
|
</mat-slide-toggle>
|
||||||
<mat-form-field class="col-12">
|
</section>
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.USERS' | translate}}</mat-label>
|
|
||||||
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userAutoCompleteConfiguration">
|
|
||||||
</app-multiple-auto-complete>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}</mat-label>
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.type">
|
|
||||||
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CONTACT-TYPE' | translate}}</mat-label>
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.contactType">
|
|
||||||
<mat-option *ngFor="let contactType of notificationContactTypeEnumValues" [value]="contactType">{{enumUtils.toNotificationContactTypeString(contactType)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFY-STATE' | translate}}</mat-label>
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.notifyState">
|
|
||||||
<mat-option *ngFor="let notifyState of notificationNotifyStateEnumValues" [value]="notifyState">{{enumUtils.toNotificationNotifyStateString(notifyState)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-STATE' | translate}}</mat-label>
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.trackingState">
|
|
||||||
<mat-option *ngFor="let trackingState of notificationTrackingStateEnumValues" [value]="trackingState">{{enumUtils.toNotificationTrackingStateString(trackingState)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-form-field class="col-12">
|
|
||||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-PROCESS' | translate}}</mat-label>
|
|
||||||
<mat-select multiple [(ngModel)]="internalFilters.trackingProcess">
|
|
||||||
<mat-option *ngFor="let trackingProcess of notificationTrackingProcessEnumValues" [value]="trackingProcess">{{enumUtils.toNotificationTrackingProcessString(trackingProcess)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-between mt-4 gap-1-rem">
|
<div class="row mt-3">
|
||||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.USERS' | translate}}</mat-label>
|
||||||
|
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userAutoCompleteConfiguration">
|
||||||
|
</app-multiple-auto-complete>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.type">
|
||||||
|
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CONTACT-TYPE' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.contactType">
|
||||||
|
<mat-option *ngFor="let contactType of notificationContactTypeEnumValues" [value]="contactType">{{enumUtils.toNotificationContactTypeString(contactType)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFY-STATE' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.notifyState">
|
||||||
|
<mat-option *ngFor="let notifyState of notificationNotifyStateEnumValues" [value]="notifyState">{{enumUtils.toNotificationNotifyStateString(notifyState)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-STATE' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.trackingState">
|
||||||
|
<mat-option *ngFor="let trackingState of notificationTrackingStateEnumValues" [value]="trackingState">{{enumUtils.toNotificationTrackingStateString(trackingState)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-PROCESS' | translate}}</mat-label>
|
||||||
|
<mat-select multiple [(ngModel)]="internalFilters.trackingProcess">
|
||||||
|
<mat-option *ngFor="let trackingProcess of notificationTrackingProcessEnumValues" [value]="trackingProcess">{{enumUtils.toNotificationTrackingProcessString(trackingProcess)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</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()">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// ::ng-deep.mat-mdc-menu-panel {
|
::ng-deep.mat-mdc-menu-panel {
|
||||||
// max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
// height: 100% !important;
|
height: 100% !important;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// :host::ng-deep.mat-mdc-menu-content:not(:empty) {
|
:host::ng-deep.mat-mdc-menu-content:not(:empty) {
|
||||||
// padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
.filter-button{
|
.filter-button{
|
||||||
|
@ -18,4 +18,8 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mdc-form-field {
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
||||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||||
import { NotificationFilter } from '@notification-service/core/query/notification.lookup';
|
import { NotificationFilter } from '@notification-service/core/query/notification.lookup';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service';
|
||||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
|
@ -37,7 +37,7 @@ export class NotificationListingFiltersComponent extends BaseComponent implement
|
||||||
|
|
||||||
protected appliedFilterCount: number = 0;
|
protected appliedFilterCount: number = 0;
|
||||||
constructor(
|
constructor(
|
||||||
public enumUtils: EnumUtils,
|
public enumUtils: NotificationServiceEnumUtils,
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
) { super(); }
|
) { super(); }
|
||||||
|
|
||||||
|
|
|
@ -99,9 +99,9 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
|
||||||
maxWidth: '300px',
|
maxWidth: '300px',
|
||||||
restoreFocus: false,
|
restoreFocus: false,
|
||||||
data: {
|
data: {
|
||||||
message: this.language.instant('COMMONS.CONFIRMATION-DIALOG.DELETE-ITEM'),
|
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
|
||||||
confirmButton: this.language.instant('COMMONS.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||||
cancelButton: this.language.instant('COMMONS.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||||
|
|
|
@ -2,28 +2,26 @@
|
||||||
<div class="notification-popup-wrapper">
|
<div class="notification-popup-wrapper">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-nav-list class="inapp-notification-listing-dialog">
|
<mat-nav-list class="inapp-notification-listing-dialog">
|
||||||
<a *ngFor="let inappNotification of inappNotifications; last as last;" mat-list-item (click)="goToNotification(inappNotification)">
|
<a *ngFor="let inappNotification of inappNotifications; last as last;" mat-list-item (click)="goToNotification(inappNotification)">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<mat-icon *ngIf="inappNotification.trackingState === notificationInAppTrackingEnum.Delivered" mat-list-icon class="mr-3">drafts</mat-icon>
|
<mat-icon *ngIf="inappNotification.trackingState === notificationInAppTrackingEnum.Delivered" mat-list-icon class="mr-3">drafts</mat-icon>
|
||||||
<mat-icon *ngIf="inappNotification.trackingState === notificationInAppTrackingEnum.Stored" mat-list-icon class="mr-3">mail</mat-icon>
|
<mat-icon *ngIf="inappNotification.trackingState === notificationInAppTrackingEnum.Stored" mat-list-icon class="mr-3">mail</mat-icon>
|
||||||
<span mat-line>{{ inappNotification.subject | sumarizeText:21 }}</span>
|
<span mat-line>{{ inappNotification.subject | sumarizeText:21 }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span mat-line class="secondary-text">{{ inappNotification.createdAt | date : 'short'}}</span>
|
||||||
|
<mat-divider inset *ngIf="!last"></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
<span mat-line class="secondary-text">{{ inappNotification.createdAt | date : 'short'}}</span>
|
<a *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)" (click)="goToNotifications()">
|
||||||
<mat-divider inset *ngIf="!last"></mat-divider>
|
<mat-list-item>{{'NAV-BAR.INAPP-NOTIFICATIONS' | translate}}</mat-list-item>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
<a *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)" (click)="readAllNotifications()">
|
||||||
<mat-list-item *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
|
<mat-list-item>{{'NAV-BAR.READ-ALL-INAPP-NOTIFICATIONS' | translate}}</mat-list-item>
|
||||||
<a (click)="goToNotifications()">{{'NAV-BAR.INAPP-NOTIFICATIONS'
|
</a>
|
||||||
| translate}}</a>
|
</mat-nav-list>
|
||||||
</mat-list-item>
|
|
||||||
<mat-list-item *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
|
|
||||||
<a (click)="readAllNotifications()">{{'NAV-BAR.READ-ALL-INAPP-NOTIFICATIONS'
|
|
||||||
| translate}}</a>
|
|
||||||
</mat-list-item>
|
|
||||||
</mat-nav-list>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -40,7 +40,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row justify-content-end align-items-center mt-4 gap-1-rem">
|
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||||
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col"></div>
|
<div class="col"></div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<button mat-raised-button color="primary" class="rounded-button" (click)="formSubmit()">
|
<button class="normal-btn-sm" (click)="formSubmit()">
|
||||||
{{'NOTIFICATION-SERVICE.USER-PROFILE.NOTIFIER-LIST-EDITOR.ACTIONS.SAVE' | translate}}
|
{{'NOTIFICATION-SERVICE.USER-PROFILE.NOTIFIER-LIST-EDITOR.ACTIONS.SAVE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gr.cite.notification.web.controllers;
|
||||||
|
|
||||||
import gr.cite.notification.audit.AuditableAction;
|
import gr.cite.notification.audit.AuditableAction;
|
||||||
import gr.cite.notification.authorization.AuthorizationFlags;
|
import gr.cite.notification.authorization.AuthorizationFlags;
|
||||||
|
import gr.cite.notification.common.enums.IsActive;
|
||||||
import gr.cite.notification.common.enums.TenantConfigurationType;
|
import gr.cite.notification.common.enums.TenantConfigurationType;
|
||||||
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
||||||
import gr.cite.notification.data.UserNotificationPreferenceEntity;
|
import gr.cite.notification.data.UserNotificationPreferenceEntity;
|
||||||
|
@ -18,6 +19,7 @@ import gr.cite.notification.web.model.QueryResult;
|
||||||
import gr.cite.tools.auditing.AuditService;
|
import gr.cite.tools.auditing.AuditService;
|
||||||
import gr.cite.tools.data.builder.BuilderFactory;
|
import gr.cite.tools.data.builder.BuilderFactory;
|
||||||
import gr.cite.tools.data.censor.CensorFactory;
|
import gr.cite.tools.data.censor.CensorFactory;
|
||||||
|
import gr.cite.tools.data.query.Ordering;
|
||||||
import gr.cite.tools.data.query.QueryFactory;
|
import gr.cite.tools.data.query.QueryFactory;
|
||||||
import gr.cite.tools.exception.MyApplicationException;
|
import gr.cite.tools.exception.MyApplicationException;
|
||||||
import gr.cite.tools.exception.MyForbiddenException;
|
import gr.cite.tools.exception.MyForbiddenException;
|
||||||
|
@ -80,15 +82,16 @@ public class UserNotificationPreferenceController {
|
||||||
|
|
||||||
@GetMapping("user/{userId}/current")
|
@GetMapping("user/{userId}/current")
|
||||||
@Transactional
|
@Transactional
|
||||||
public UserNotificationPreference current(@PathVariable UUID userId, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
public List<UserNotificationPreference> current(@PathVariable UUID userId, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + UserNotificationPreference.class.getSimpleName()).And("userId", userId).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("retrieving" + UserNotificationPreference.class.getSimpleName()).And("userId", userId).And("fields", fieldSet));
|
||||||
|
|
||||||
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(fieldSet, userId);
|
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(fieldSet, userId);
|
||||||
|
|
||||||
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(userId);
|
Ordering ordering = new Ordering();
|
||||||
UserNotificationPreference model = this.builderFactory.builder(UserNotificationPreferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
|
ordering.addAscending(UserNotificationPreference._ordinal);
|
||||||
if (model == null)
|
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(userId).isActives(IsActive.Active);
|
||||||
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
query.setOrder(ordering);
|
||||||
|
List<UserNotificationPreference> model = this.builderFactory.builder(UserNotificationPreferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.collectAs(fieldSet));
|
||||||
|
|
||||||
this.auditService.track(AuditableAction.User_Notification_Preference_Lookup, Map.ofEntries(
|
this.auditService.track(AuditableAction.User_Notification_Preference_Lookup, Map.ofEntries(
|
||||||
new AbstractMap.SimpleEntry<String, Object>("userId", userId),
|
new AbstractMap.SimpleEntry<String, Object>("userId", userId),
|
||||||
|
|
|
@ -3,7 +3,7 @@ notification:
|
||||||
fields:
|
fields:
|
||||||
- key: "{installation-url}"
|
- key: "{installation-url}"
|
||||||
type: "String"
|
type: "String"
|
||||||
value: "http://localhost:42000"
|
value: "http://localhost:4200"
|
||||||
resolver:
|
resolver:
|
||||||
global-policies:
|
global-policies:
|
||||||
- #dmpInvitationExternalUser
|
- #dmpInvitationExternalUser
|
||||||
|
@ -14,16 +14,16 @@ notification:
|
||||||
contacts: [ inapp, email ]
|
contacts: [ inapp, email ]
|
||||||
- #dpmModified
|
- #dpmModified
|
||||||
type: 4542262A-22F8-4BAA-9DB6-1C8E70AC1DBB
|
type: 4542262A-22F8-4BAA-9DB6-1C8E70AC1DBB
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #dmpFinalised
|
- #dmpFinalised
|
||||||
type: 90DB0B46-42DE-BD89-AEBF-6F27EFEB256E
|
type: 90DB0B46-42DE-BD89-AEBF-6F27EFEB256E
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #descriptionModified
|
- #descriptionModified
|
||||||
type: 4FDBFA80-7A71-4A69-B854-67CBB70648F1
|
type: 4FDBFA80-7A71-4A69-B854-67CBB70648F1
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #descriptionFinalised
|
- #descriptionFinalised
|
||||||
type: 33790bad-94d4-488a-8ee2-7f6295ca18ea
|
type: 33790bad-94d4-488a-8ee2-7f6295ca18ea
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #mergeAcountConfirmation
|
- #mergeAcountConfirmation
|
||||||
type: BFE68845-CB05-4C5A-A03D-29161A7C9660
|
type: BFE68845-CB05-4C5A-A03D-29161A7C9660
|
||||||
contacts: [ email ]
|
contacts: [ email ]
|
||||||
|
@ -32,10 +32,10 @@ notification:
|
||||||
contacts: [ email ]
|
contacts: [ email ]
|
||||||
- #dmpDeposit
|
- #dmpDeposit
|
||||||
type: 55736F7A-83AB-4190-AF43-9D031A6F9612
|
type: 55736F7A-83AB-4190-AF43-9D031A6F9612
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #descriptionTemplateInvitation
|
- #descriptionTemplateInvitation
|
||||||
type: 223BB607-EFA1-4CE7-99EC-4BEABFEF9A8B
|
type: 223BB607-EFA1-4CE7-99EC-4BEABFEF9A8B
|
||||||
contacts: [ email ]
|
contacts: [ inapp, email ]
|
||||||
- #contactSupportType
|
- #contactSupportType
|
||||||
type: 5B1D6C52-88F9-418B-9B8A-6F1F963D9EAD
|
type: 5B1D6C52-88F9-418B-9B8A-6F1F963D9EAD
|
||||||
contacts: [ email ]
|
contacts: [ email ]
|
||||||
|
|
|
@ -2,7 +2,7 @@ notification:
|
||||||
task:
|
task:
|
||||||
processor:
|
processor:
|
||||||
enable: true
|
enable: true
|
||||||
interval-seconds: 3
|
interval-seconds: 5
|
||||||
options:
|
options:
|
||||||
retry-threshold: 300
|
retry-threshold: 300
|
||||||
max-retry-delay-seconds: 10800
|
max-retry-delay-seconds: 10800
|
||||||
|
|
|
@ -27,7 +27,7 @@ queue:
|
||||||
exchange: null
|
exchange: null
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
enable: false
|
enable: false
|
||||||
interval-seconds: 30
|
interval-seconds: 5
|
||||||
options:
|
options:
|
||||||
retry-threashold: 100
|
retry-threashold: 100
|
||||||
retry-delay-step-seconds: 300
|
retry-delay-step-seconds: 300
|
||||||
|
@ -45,7 +45,7 @@ queue:
|
||||||
user-touched-topic: user.touch
|
user-touched-topic: user.touch
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
enable: false
|
enable: false
|
||||||
interval-seconds: 30
|
interval-seconds: 5
|
||||||
options:
|
options:
|
||||||
retry-threashold: 100
|
retry-threashold: 100
|
||||||
retry-delay-step-seconds: 300
|
retry-delay-step-seconds: 300
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
</head>
|
||||||
|
<body class="">
|
||||||
|
<p>Dear {recipient},</p>
|
||||||
|
<p>{reasonName} just finalised the Description {name}.</p>
|
||||||
|
<a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -3,302 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>{reasonName} just made changes to the Description {name}.</p>
|
||||||
<td> </td>
|
<a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a>
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just made changes to the Description {name}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,303 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>You have been invited to co-develop the Template {templateName}.</p>
|
||||||
<td> </td>
|
<p>Click the button to redirect to {templateName}.</p>
|
||||||
<td class="container">
|
<a href="{installation-url}/description-templates/{templateID}" target="_blank">{templateName}</a>
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>You have been invited to co-develop the Template {templateName}.</p>
|
|
||||||
<p>Click the button to redirect to {templateName}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/description-templates/{templateID}" target="_blank">{templateName}</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,304 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just finalised the Description {name}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -3,302 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>{reasonName} just publish the {name}.</p>
|
||||||
<td> </td>
|
<a href="{installation-url}/{path}/{id}" target="_blank">Click here to view it.</a>
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just publish the {name}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/{path}/{id}" target="_blank">Click here to view it.</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,302 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>{reasonName} just finalised the Dmp {name}.</p>
|
||||||
<td> </td>
|
<a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a>
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just finalised the Dmp {name}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -4,301 +4,11 @@
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>Simple Transactional Email</title>
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>{reasonName} just add you to collaborate to Data Management plan {dmpname} with role {dmprole}.</p>
|
||||||
<td> </td>
|
<p>Click the button to redirect to {dmpname}.</p>
|
||||||
<td class="container">
|
<a href="{installation-url}/plans/edit/{id}" target="_blank">Join</a>
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just add you to collaborate to Data Management plan {dmpname} with role {dmprole}.</p>
|
|
||||||
<p>Click the button to redirect to {dmpname}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/plans/edit/{id}" target="_blank">Join</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,302 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%; }
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%; }
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%; }
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px; }
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
Margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px; }
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%; }
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px; }
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
Margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%; }
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center; }
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 30px; }
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
Margin-bottom: 15px; }
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px; }
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline; }
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%; }
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto; }
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center; }
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize; }
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db; }
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff; }
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.first {
|
|
||||||
margin-top: 0; }
|
|
||||||
.align-center {
|
|
||||||
text-align: center; }
|
|
||||||
.align-right {
|
|
||||||
text-align: right; }
|
|
||||||
.align-left {
|
|
||||||
text-align: left; }
|
|
||||||
.clear {
|
|
||||||
clear: both; }
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0; }
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0; }
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0; }
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none; }
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
Margin: 20px 0; }
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important; }
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important; }
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important; }
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important; }
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important; }
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important; }
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important; }}
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%; }
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%; }
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important; }
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important; }
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important; } }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<p>Dear {recipient},</p>
|
||||||
<tr>
|
<p>{reasonName} just made changes to the Dmp {name}.</p>
|
||||||
<td> </td>
|
<a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a>
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
|
||||||
<table class="main">
|
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>Dear {recipient},</p>
|
|
||||||
<p>{reasonName} just made changes to the Dmp {name}.</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ import gr.cite.tools.data.query.FieldResolver;
|
||||||
import gr.cite.tools.data.query.QueryBase;
|
import gr.cite.tools.data.query.QueryBase;
|
||||||
import gr.cite.tools.data.query.QueryContext;
|
import gr.cite.tools.data.query.QueryContext;
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
@ -89,15 +90,20 @@ public class UserNotificationPreferenceQuery extends QueryBase<UserNotificationP
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isActives != null) {
|
if (this.isActives != null) {
|
||||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._isActive).in(isActives));
|
CriteriaBuilder.In<IsActive> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserNotificationPreferenceEntity._isActive));
|
||||||
}
|
for (IsActive item : this.isActives)
|
||||||
|
inClause.value(item);
|
||||||
|
predicates.add(inClause); }
|
||||||
|
|
||||||
if (this.type != null) {
|
if (this.type != null) {
|
||||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._type).in(this.type));
|
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._type).in(this.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.channel != null) {
|
if (this.channel != null) {
|
||||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._channel).in(this.channel));
|
CriteriaBuilder.In<NotificationContactType> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserNotificationPreferenceEntity._channel));
|
||||||
|
for (NotificationContactType item : this.channel)
|
||||||
|
inClause.value(item);
|
||||||
|
predicates.add(inClause);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!predicates.isEmpty()) {
|
if (!predicates.isEmpty()) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package gr.cite.notification.service.inappnotification;
|
package gr.cite.notification.service.inappnotification;
|
||||||
|
|
||||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||||
|
import gr.cite.notification.authorization.OwnedResource;
|
||||||
import gr.cite.notification.authorization.Permission;
|
import gr.cite.notification.authorization.Permission;
|
||||||
import gr.cite.notification.common.enums.NotificationInAppTracking;
|
import gr.cite.notification.common.enums.NotificationInAppTracking;
|
||||||
import gr.cite.notification.common.scope.user.UserScope;
|
import gr.cite.notification.common.scope.user.UserScope;
|
||||||
|
@ -92,7 +93,7 @@ public class InAppNotificationServiceImpl implements InAppNotificationService {
|
||||||
|
|
||||||
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
|
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
|
||||||
logger.debug("deleting in-app notification: {}", id);
|
logger.debug("deleting in-app notification: {}", id);
|
||||||
this.authService.authorizeForce(Permission.DeleteInAppNotification);
|
this.authService.authorizeAtLeastOneForce(this.userScope.getUserId() != null ? List.of(new OwnedResource(this.userScope.getUserId())) : null, Permission.DeleteInAppNotification);
|
||||||
this.deleterFactory.deleter(InAppNotificationDeleter.class).deleteAndSaveByIds(List.of(id));
|
this.deleterFactory.deleter(InAppNotificationDeleter.class).deleteAndSaveByIds(List.of(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class InAppMessageBuilder extends MessageBuilderBase implements MessageBu
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationProperties.Flow options = this.flowMap.get("in-app").getOrDefault(notification.getType(), null);
|
NotificationProperties.Flow options = this.flowMap.get("in-app").getOrDefault(notification.getType(), null);
|
||||||
NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email, messageInfo.getLanguage());
|
NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.InApp, messageInfo.getLanguage());
|
||||||
|
|
||||||
if (options == null && template == null) {
|
if (options == null && template == null) {
|
||||||
logger.error("Could not retrieve flow options for notification " + notification.getId() + " of type " + notification.getType());
|
logger.error("Could not retrieve flow options for notification " + notification.getId() + " of type " + notification.getType());
|
||||||
|
|
|
@ -165,7 +165,6 @@ public abstract class MessageBuilderBase {
|
||||||
return formatting;
|
return formatting;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Here check with a language accepted list and fallback to default
|
|
||||||
protected String lookupOrReadLocalizedFile(NotificationProperties.Template.TemplateCache templateCache, String path, String language) {
|
protected String lookupOrReadLocalizedFile(NotificationProperties.Template.TemplateCache templateCache, String path, String language) {
|
||||||
String filename = path.replace("{language}", language);
|
String filename = path.replace("{language}", language);
|
||||||
File file = null;
|
File file = null;
|
||||||
|
|
|
@ -10,13 +10,16 @@ import gr.cite.notification.convention.ConventionService;
|
||||||
import gr.cite.notification.data.TenantEntity;
|
import gr.cite.notification.data.TenantEntity;
|
||||||
import gr.cite.notification.data.TenantEntityManager;
|
import gr.cite.notification.data.TenantEntityManager;
|
||||||
import gr.cite.notification.data.NotificationEntity;
|
import gr.cite.notification.data.NotificationEntity;
|
||||||
|
import gr.cite.notification.data.UserNotificationPreferenceEntity;
|
||||||
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
|
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
|
||||||
import gr.cite.notification.model.SendNotificationResult;
|
import gr.cite.notification.model.SendNotificationResult;
|
||||||
|
import gr.cite.notification.model.UserNotificationPreference;
|
||||||
import gr.cite.notification.model.builder.NotificationBuilder;
|
import gr.cite.notification.model.builder.NotificationBuilder;
|
||||||
import gr.cite.notification.model.Notification;
|
import gr.cite.notification.model.Notification;
|
||||||
import gr.cite.notification.model.deleter.NotificationDeleter;
|
import gr.cite.notification.model.deleter.NotificationDeleter;
|
||||||
import gr.cite.notification.model.persist.NotificationPersist;
|
import gr.cite.notification.model.persist.NotificationPersist;
|
||||||
import gr.cite.notification.query.NotificationQuery;
|
import gr.cite.notification.query.NotificationQuery;
|
||||||
|
import gr.cite.notification.query.UserNotificationPreferenceQuery;
|
||||||
import gr.cite.notification.service.channelResolution.ChannelResolutionService;
|
import gr.cite.notification.service.channelResolution.ChannelResolutionService;
|
||||||
import gr.cite.notification.service.contact.extractor.ContactExtractorFactory;
|
import gr.cite.notification.service.contact.extractor.ContactExtractorFactory;
|
||||||
import gr.cite.notification.service.contact.model.Contact;
|
import gr.cite.notification.service.contact.model.Contact;
|
||||||
|
@ -25,6 +28,7 @@ import gr.cite.notification.service.message.model.Message;
|
||||||
import gr.cite.notification.service.notify.NotifierFactory;
|
import gr.cite.notification.service.notify.NotifierFactory;
|
||||||
import gr.cite.tools.data.builder.BuilderFactory;
|
import gr.cite.tools.data.builder.BuilderFactory;
|
||||||
import gr.cite.tools.data.deleter.DeleterFactory;
|
import gr.cite.tools.data.deleter.DeleterFactory;
|
||||||
|
import gr.cite.tools.data.query.Ordering;
|
||||||
import gr.cite.tools.data.query.QueryFactory;
|
import gr.cite.tools.data.query.QueryFactory;
|
||||||
import gr.cite.tools.exception.MyApplicationException;
|
import gr.cite.tools.exception.MyApplicationException;
|
||||||
import gr.cite.tools.exception.MyForbiddenException;
|
import gr.cite.tools.exception.MyForbiddenException;
|
||||||
|
@ -49,6 +53,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequestScope
|
@RequestScope
|
||||||
|
@ -141,7 +146,9 @@ public class NotificationServiceImpl implements NotificationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendNotificationResult doNotify(NotificationEntity notification) {
|
public SendNotificationResult doNotify(NotificationEntity notification) {
|
||||||
List<NotificationContactType> contactTypes = this.orderContactTypes(notification);
|
List<NotificationContactType> contactTypes = this.orderContactTypesFromPreferences(notification);
|
||||||
|
if (this.conventionService.isListNullOrEmpty(contactTypes)) contactTypes = this.orderContactTypes(notification);
|
||||||
|
|
||||||
for (NotificationContactType contactType: contactTypes) {
|
for (NotificationContactType contactType: contactTypes) {
|
||||||
SendNotificationResult result = this.sendNotification(notification, contactType);
|
SendNotificationResult result = this.sendNotification(notification, contactType);
|
||||||
if (result.getSuccess()) return result;
|
if (result.getSuccess()) return result;
|
||||||
|
@ -149,6 +156,17 @@ public class NotificationServiceImpl implements NotificationService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<NotificationContactType> orderContactTypesFromPreferences(NotificationEntity notification) {
|
||||||
|
Ordering ordering = new Ordering();
|
||||||
|
ordering.addAscending(UserNotificationPreference._ordinal);
|
||||||
|
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(notification.getUserId()).type(notification.getType()).isActives(IsActive.Active);
|
||||||
|
query.setOrder(ordering);
|
||||||
|
|
||||||
|
List<UserNotificationPreferenceEntity> preferences = query.collectAs(new BaseFieldSet().ensure(UserNotificationPreference._channel));
|
||||||
|
if (!this.conventionService.isListNullOrEmpty(preferences)) return preferences.stream().map(x -> x.getChannel()).collect(Collectors.toList());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private List<NotificationContactType> orderContactTypes(NotificationEntity notification) {
|
private List<NotificationContactType> orderContactTypes(NotificationEntity notification) {
|
||||||
List<NotificationContactType> contactTypes = this.channelResolutionService.resolve(notification.getType(), notification.getUserId());
|
List<NotificationContactType> contactTypes = this.channelResolutionService.resolve(notification.getType(), notification.getUserId());
|
||||||
if (notification.getContactTypeHint() == null) return contactTypes;
|
if (notification.getContactTypeHint() == null) return contactTypes;
|
||||||
|
|
|
@ -141,8 +141,8 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling
|
||||||
} else {
|
} else {
|
||||||
tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode());
|
tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode());
|
||||||
}
|
}
|
||||||
notification = entityManager.merge(notification);
|
// notification = entityManager.merge(notification);
|
||||||
entityManager.persist(notification);
|
entityManager.merge(notification);
|
||||||
entityManager.flush();
|
entityManager.flush();
|
||||||
} finally {
|
} finally {
|
||||||
tenantScope.removeTempTenant(entityManager);
|
tenantScope.removeTempTenant(entityManager);
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class InAppNotifier implements Notify{
|
||||||
inApp.setTenantId(tenantScope.getTenant());
|
inApp.setTenantId(tenantScope.getTenant());
|
||||||
|
|
||||||
entityManager.persist(inApp);
|
entityManager.persist(inApp);
|
||||||
|
entityManager.flush();
|
||||||
|
|
||||||
InAppTrackingData trackingData = new InAppTrackingData(inApp.getId());
|
InAppTrackingData trackingData = new InAppTrackingData(inApp.getId());
|
||||||
data = this.jsonHandlingService.toJsonSafe(trackingData);
|
data = this.jsonHandlingService.toJsonSafe(trackingData);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package gr.cite.notification.service.userNotificationPreference;
|
package gr.cite.notification.service.userNotificationPreference;
|
||||||
|
|
||||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||||
|
import gr.cite.notification.authorization.OwnedResource;
|
||||||
import gr.cite.notification.authorization.Permission;
|
import gr.cite.notification.authorization.Permission;
|
||||||
|
import gr.cite.notification.common.enums.IsActive;
|
||||||
import gr.cite.notification.common.enums.NotificationContactType;
|
import gr.cite.notification.common.enums.NotificationContactType;
|
||||||
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
||||||
import gr.cite.notification.config.notification.NotificationConfig;
|
import gr.cite.notification.config.notification.NotificationConfig;
|
||||||
|
@ -66,7 +68,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
||||||
public List<UserNotificationPreference> persist(UserNotificationPreferencePersist model, FieldSet fieldSet) {
|
public List<UserNotificationPreference> persist(UserNotificationPreferencePersist model, FieldSet fieldSet) {
|
||||||
logger.debug(new MapLogEntry("persisting").And("model", model).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("persisting").And("model", model).And("fields", fieldSet));
|
||||||
|
|
||||||
this.authService.authorizeForce(Permission.EditUserNotificationPreference);
|
this.authService.authorizeAtLeastOneForce(model.getUserId() != null ? List.of(new OwnedResource(model.getUserId())) : null, Permission.EditUserNotificationPreference);
|
||||||
|
|
||||||
Map<UUID, List<NotificationContactType>> currentNotificationListPolicies;
|
Map<UUID, List<NotificationContactType>> currentNotificationListPolicies;
|
||||||
NotifierListConfigurationDataContainer tenantNotifierListPolicies = this.tenantConfigurationService.collectTenantNotifierList();
|
NotifierListConfigurationDataContainer tenantNotifierListPolicies = this.tenantConfigurationService.collectTenantNotifierList();
|
||||||
|
@ -169,6 +171,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
||||||
preferences = this.queryFactory
|
preferences = this.queryFactory
|
||||||
.query(UserNotificationPreferenceQuery.class)
|
.query(UserNotificationPreferenceQuery.class)
|
||||||
.type(type)
|
.type(type)
|
||||||
|
.isActives(IsActive.Active)
|
||||||
.userId(userId).collect();
|
.userId(userId).collect();
|
||||||
int ordinal = 0;
|
int ordinal = 0;
|
||||||
|
|
||||||
|
@ -176,6 +179,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
||||||
for (NotificationContactType contactType : contactTypes) {
|
for (NotificationContactType contactType : contactTypes) {
|
||||||
UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> x.getChannel() == contactType).findFirst().orElse(null);
|
UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> x.getChannel() == contactType).findFirst().orElse(null);
|
||||||
|
|
||||||
|
boolean isUpdate = preference != null;
|
||||||
if (preference != null) {
|
if (preference != null) {
|
||||||
preference.setOrdinal(ordinal);
|
preference.setOrdinal(ordinal);
|
||||||
|
|
||||||
|
@ -186,9 +190,12 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
||||||
preference.setOrdinal(ordinal);
|
preference.setOrdinal(ordinal);
|
||||||
preference.setChannel(contactType);
|
preference.setChannel(contactType);
|
||||||
preference.setCreatedAt(Instant.now());
|
preference.setCreatedAt(Instant.now());
|
||||||
|
preference.setIsActive(IsActive.Active);
|
||||||
}
|
}
|
||||||
this.entityManager.merge(preference);
|
preference.setUpdatedAt(Instant.now());
|
||||||
this.entityManager.persist(preference);
|
|
||||||
|
if(isUpdate) this.entityManager.merge(preference);
|
||||||
|
else this.entityManager.persist(preference);
|
||||||
updatedPreferences.add(preference);
|
updatedPreferences.add(preference);
|
||||||
ordinal++;
|
ordinal++;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue