diff --git a/.jhipster/Company.json b/.jhipster/Company.json index 36548315c..f340b22c8 100644 --- a/.jhipster/Company.json +++ b/.jhipster/Company.json @@ -42,6 +42,11 @@ { "fieldName": "legalContact", "fieldType": "String" + }, + { + "fieldName": "additionalInfo", + "fieldType": "byte[]", + "fieldTypeBlobContent": "text" } ], "relationships": [ diff --git a/jhipster-jdl.jdl b/jhipster-jdl.jdl index d33ddd22d..21a9f7961 100644 --- a/jhipster-jdl.jdl +++ b/jhipster-jdl.jdl @@ -40,6 +40,7 @@ entity Company { licenseStatus LicenseStatus required, businessContact String, legalContact String, + additionalInfo TextBlob } entity CompanyDomain { diff --git a/screenshot-test/__baseline_snapshots__/Company Details Page-snap.png b/screenshot-test/__baseline_snapshots__/Company Details Page-snap.png index 97dbe72f6..20a9c499b 100644 Binary files a/screenshot-test/__baseline_snapshots__/Company Details Page-snap.png and b/screenshot-test/__baseline_snapshots__/Company Details Page-snap.png differ diff --git a/screenshot-test/data/api-companies.json b/screenshot-test/data/api-companies.json index 9cda89be9..4dfbcebd6 100644 --- a/screenshot-test/data/api-companies.json +++ b/screenshot-test/data/api-companies.json @@ -10,7 +10,18 @@ "businessContact": "Representative", "legalContact": "override orchid reboot", "companyDomains": ["oncokb.org"], - "numberOfUsers": 0 + "numberOfUsers": 0, + "additionalInfo": { + "license": { + "termination": { + "date": "2025-09-21", + "notes": "notes", + "notificationDays": 3433 + }, + "autoRenewal": false, + "activation": "2024-09-28" + } + } }, { "id": 2, @@ -23,6 +34,17 @@ "businessContact": "Steel Configuration Designer", "legalContact": "system engine Australian Dollar", "companyDomains": ["mskcc.org"], - "numberOfUsers": 3 + "numberOfUsers": 3, + "additionalInfo": { + "license": { + "termination": { + "date": "2025-09-21", + "notes": "notes", + "notificationDays": 3433 + }, + "autoRenewal": false, + "activation": "2024-09-28" + } + } } ] diff --git a/screenshot-test/data/api-company-details.json b/screenshot-test/data/api-company-details.json index 3bd203521..a3b0be27a 100644 --- a/screenshot-test/data/api-company-details.json +++ b/screenshot-test/data/api-company-details.json @@ -9,5 +9,16 @@ "businessContact": "Representative", "legalContact": "override orchid reboot", "companyDomains": ["oncokb.org"], - "numberOfUsers": 0 + "numberOfUsers": 0, + "additionalInfo": { + "license": { + "termination": { + "date": "2024-09-21", + "notes": "notes", + "notificationDays": 3433 + }, + "autoRenewal": false, + "activation": "2024-09-28" + } + } } diff --git a/src/main/java/org/mskcc/cbio/oncokb/config/application/ApplicationProperties.java b/src/main/java/org/mskcc/cbio/oncokb/config/application/ApplicationProperties.java index e5bf920d5..cc3f917ce 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/config/application/ApplicationProperties.java +++ b/src/main/java/org/mskcc/cbio/oncokb/config/application/ApplicationProperties.java @@ -197,5 +197,4 @@ public RecaptchaProperties getRecaptcha() { public void setRecaptcha(RecaptchaProperties recaptcha) { this.recaptcha = recaptcha; } - } diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/Company.java b/src/main/java/org/mskcc/cbio/oncokb/domain/Company.java index 8d460937e..7942082c3 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/Company.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/Company.java @@ -66,6 +66,10 @@ public class Company implements Serializable { @Column(name = "legal_contact", length = 255) private String legalContact; + @Lob + @Column(name = "additional_info") + private String additionalInfo; + @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name = "company_company_domain", joinColumns = @JoinColumn(name = "company_id", referencedColumnName = "id"), @@ -185,6 +189,19 @@ public void setLegalContact(String legalContact) { this.legalContact = legalContact; } + public String getAdditionalInfo() { + return additionalInfo; + } + + public Company additionalInfo(String additionalInfo) { + this.additionalInfo = additionalInfo; + return this; + } + + public void setAdditionalInfo(String additionalInfo) { + this.additionalInfo = additionalInfo; + } + public Set getCompanyDomains() { return companyDomains; } @@ -240,6 +257,7 @@ public String toString() { ", licenseStatus='" + getLicenseStatus() + "'" + ", businessContact='" + getBusinessContact() + "'" + ", legalContact='" + getLegalContact() + "'" + + ", additionalInfo='" + getAdditionalInfo() + "'" + "}"; } } diff --git a/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/MailType.java b/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/MailType.java index bcd619376..f0815f48d 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/MailType.java +++ b/src/main/java/org/mskcc/cbio/oncokb/domain/enumeration/MailType.java @@ -119,6 +119,9 @@ public enum MailType { , LIST_OF_UNAPPROVED_USERS(new MailTypeBuilder() .templateName("listOfUnapprovedUsers") .description("List of unapproved users")) + , TERMINATION_NOTIFICATION_EMAIL(new MailTypeBuilder() + .templateName("terminationNotificationEmail") + .description("Notifies admins about companies with licenses about to expire")) , TEST(new MailTypeBuilder() .templateName("testEmail") .description("Test")) diff --git a/src/main/java/org/mskcc/cbio/oncokb/repository/CompanyRepository.java b/src/main/java/org/mskcc/cbio/oncokb/repository/CompanyRepository.java index 379cb7d22..1635321a9 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/repository/CompanyRepository.java +++ b/src/main/java/org/mskcc/cbio/oncokb/repository/CompanyRepository.java @@ -24,4 +24,7 @@ public interface CompanyRepository extends JpaRepository { @Cacheable(cacheResolver = "companyCacheResolver") Optional findOneByNameIgnoreCase(String name); + + @Query("select c from Company c where c.id in ?1") + List findCompaniesByIds(List ids); } diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/CompanyService.java b/src/main/java/org/mskcc/cbio/oncokb/service/CompanyService.java index 7859eda92..244de7930 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/service/CompanyService.java +++ b/src/main/java/org/mskcc/cbio/oncokb/service/CompanyService.java @@ -1,5 +1,6 @@ package org.mskcc.cbio.oncokb.service; +import org.mskcc.cbio.oncokb.domain.Company; import org.mskcc.cbio.oncokb.domain.enumeration.LicenseStatus; import org.mskcc.cbio.oncokb.service.dto.CompanyDTO; import org.mskcc.cbio.oncokb.web.rest.vm.CompanyVM; @@ -58,4 +59,7 @@ public interface CompanyService { * @param id the id of the entity. */ void delete(Long id); + + + List findCompaniesByIds(List ids); } diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/MailService.java b/src/main/java/org/mskcc/cbio/oncokb/service/MailService.java index 6b516f85d..e42eaec96 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/service/MailService.java +++ b/src/main/java/org/mskcc/cbio/oncokb/service/MailService.java @@ -10,6 +10,7 @@ import org.mskcc.cbio.oncokb.domain.Company; import org.mskcc.cbio.oncokb.domain.UserMessagePair; import org.mskcc.cbio.oncokb.domain.enumeration.MailType; +import org.mskcc.cbio.oncokb.service.dto.TerminationEmailDTO; import org.mskcc.cbio.oncokb.service.dto.UserDTO; import org.mskcc.cbio.oncokb.service.dto.UserMailsDTO; import org.mskcc.cbio.oncokb.web.rest.vm.ExposedToken; @@ -85,6 +86,20 @@ private UnknownMailTypeException() { } } + public void sendEmail(TerminationEmailDTO dto) throws MessagingException { + log.debug("Send email to company ID '{}' with subject '{}' and content={}", + dto.getCompanyId(), dto.getSubject(), dto.getContent()); + + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + MimeMessageHelper message = new MimeMessageHelper(mimeMessage, false, StandardCharsets.UTF_8.name()); + message.setFrom(dto.getFrom()); + message.setBcc(dto.getBcc().split(";")); + message.setCc(dto.getCc().split(";")); + message.setSubject(dto.getSubject()); + message.setText(dto.getContent(), false); + javaMailSender.send(mimeMessage); + } + @Async public void sendEmail(String to, String from, String cc, String subject, String content, List attachmentFilesNames, boolean isMultipart, boolean isHtml) throws MessagingException { log.debug("Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", @@ -236,6 +251,21 @@ public void sendEmailFromTemplate(UserDTO user, MailType mailType, String subjec } } + public void sendInternalEmailFromTemplate(MailType mailType, String subject, String to, Context additionalContext) { + Context context = new Context(Locale.ENGLISH); + + if (additionalContext != null) + additionalContext.getVariableNames().forEach(name -> context.setVariable(name, additionalContext.getVariable(name))); + + String from = jHipsterProperties.getMail().getFrom(); + String content = templateEngine.process("mail/" + mailType.getTemplateName(), context); + try { + sendEmail(to, from, null, subject, content, null, false, true); + } catch (MailException | MessagingException e) { + log.warn("Internal email could not be sent to '{}'", to, e); + } + } + @Async public void sendActivationEmail(UserDTO user) { log.debug("Sending activation email to '{}'", user.getEmail()); diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/dto/CompanyDTO.java b/src/main/java/org/mskcc/cbio/oncokb/service/dto/CompanyDTO.java index b5f5ebea8..314e05d7d 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/service/dto/CompanyDTO.java +++ b/src/main/java/org/mskcc/cbio/oncokb/service/dto/CompanyDTO.java @@ -7,6 +7,7 @@ import javax.persistence.Lob; import org.mskcc.cbio.oncokb.domain.enumeration.CompanyType; import org.mskcc.cbio.oncokb.domain.enumeration.LicenseType; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO; import org.mskcc.cbio.oncokb.domain.enumeration.LicenseModel; import org.mskcc.cbio.oncokb.domain.enumeration.LicenseStatus; @@ -14,7 +15,7 @@ * A DTO for the {@link org.mskcc.cbio.oncokb.domain.Company} entity. */ public class CompanyDTO implements Serializable { - + private Long id; @NotNull @@ -42,8 +43,11 @@ public class CompanyDTO implements Serializable { @NotEmpty private Set companyDomains = new HashSet<>(); + @Lob + private CompanyAdditionalInfoDTO additionalInfo; + private Integer numberOfUsers; - + public Long getId() { return id; } @@ -124,6 +128,14 @@ public void setCompanyDomains(Set companyDomains) { this.companyDomains = companyDomains; } + public CompanyAdditionalInfoDTO getAdditionalInfo() { + return additionalInfo; + } + + public void setAdditionalInfo(CompanyAdditionalInfoDTO additionalInfo) { + this.additionalInfo = additionalInfo; + } + public Integer getNumberOfUsers() { return this.numberOfUsers; } @@ -162,6 +174,7 @@ public String toString() { ", licenseStatus='" + getLicenseStatus() + "'" + ", businessContact='" + getBusinessContact() + "'" + ", legalContact='" + getLegalContact() + "'" + + ", additionalInfo='" + getAdditionalInfo() + "'" + ", companyDomains='" + getCompanyDomains() + "'" + ", numberOfUsers='" + getNumberOfUsers() + "'" + "}"; diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/dto/TerminationEmailDTO.java b/src/main/java/org/mskcc/cbio/oncokb/service/dto/TerminationEmailDTO.java new file mode 100644 index 000000000..1b31863c1 --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/service/dto/TerminationEmailDTO.java @@ -0,0 +1,61 @@ +package org.mskcc.cbio.oncokb.service.dto; + +import java.io.Serializable; + +public class TerminationEmailDTO implements Serializable { + private String subject; + private String from; + private String bcc; + private String cc; + private String content; + private Long companyId; + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getBcc() { + return bcc; + } + + public void setBcc(String bcc) { + this.bcc = bcc; + } + + public String getCc() { + return cc; + } + + public void setCc(String cc) { + this.cc = cc; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Long getCompanyId() { + return companyId; + } + + public void setCompanyId(Long companyId) { + this.companyId = companyId; + } +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyAdditionalInfoDTO.java b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyAdditionalInfoDTO.java new file mode 100644 index 000000000..314b7f905 --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyAdditionalInfoDTO.java @@ -0,0 +1,15 @@ +package org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo; + +import java.io.Serializable; + +public class CompanyAdditionalInfoDTO implements Serializable { + private CompanyLicense license; + + public CompanyLicense getLicense() { + return license; + } + + public void setLicense(CompanyLicense license) { + this.license = license; + } +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyLicense.java b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyLicense.java new file mode 100644 index 000000000..be55769bb --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyLicense.java @@ -0,0 +1,34 @@ +package org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo; + +import java.io.Serializable; +import java.time.Instant; + +public class CompanyLicense implements Serializable { + private Instant activation; + private boolean autoRenewal; + private CompanyTermination termination; + + public Instant getActivation() { + return activation; + } + + public void setActivation(Instant activation) { + this.activation = activation; + } + + public boolean isAutoRenewal() { + return autoRenewal; + } + + public void setAutoRenewal(boolean autoRenewal) { + this.autoRenewal = autoRenewal; + } + + public CompanyTermination getTermination() { + return termination; + } + + public void setTermination(CompanyTermination termination) { + this.termination = termination; + } +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyTermination.java b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyTermination.java new file mode 100644 index 000000000..7d8c0b11a --- /dev/null +++ b/src/main/java/org/mskcc/cbio/oncokb/service/dto/companyadditionalinfo/CompanyTermination.java @@ -0,0 +1,43 @@ +package org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo; + +import java.io.Serializable; +import java.time.Instant; + +public class CompanyTermination implements Serializable { + private Integer notificationDays; + private Instant date; + private String notes; + private Boolean hasBeenNotified; + + public Integer getNotificationDays() { + return notificationDays; + } + + public void setNotificationDays(Integer notificationDays) { + this.notificationDays = notificationDays; + } + + public Instant getDate() { + return date; + } + + public void setDate(Instant date) { + this.date = date; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String note) { + this.notes = note; + } + + public Boolean getHasBeenNotified() { + return hasBeenNotified; + } + + public void setHasBeenNotified(Boolean hasBeenNotified) { + this.hasBeenNotified = hasBeenNotified; + } +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/impl/CompanyServiceImpl.java b/src/main/java/org/mskcc/cbio/oncokb/service/impl/CompanyServiceImpl.java index 95b6efb73..59e7234e1 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/service/impl/CompanyServiceImpl.java +++ b/src/main/java/org/mskcc/cbio/oncokb/service/impl/CompanyServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -214,4 +215,14 @@ private void clearCompanyCaches(Company company) { Objects.requireNonNull(cacheManager.getCache(this.cacheNameResolver.getCacheName(COMPANIES_BY_NAME_CACHE))).evict(company.getName()); } + @Override + @Transactional(readOnly = true) + public List findCompaniesByIds(List ids) { + log.debug("Request to get Company : {}", ids); + return companyRepository.findCompaniesByIds(ids) + .stream() + .map(companyMapper::toDto) + .collect(Collectors.toCollection(ArrayList::new)); + } + } diff --git a/src/main/java/org/mskcc/cbio/oncokb/service/mapper/CompanyMapper.java b/src/main/java/org/mskcc/cbio/oncokb/service/mapper/CompanyMapper.java index aa05c3b4f..f25e50260 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/service/mapper/CompanyMapper.java +++ b/src/main/java/org/mskcc/cbio/oncokb/service/mapper/CompanyMapper.java @@ -12,8 +12,11 @@ import org.mskcc.cbio.oncokb.domain.*; import org.mskcc.cbio.oncokb.repository.CompanyDomainRepository; import org.mskcc.cbio.oncokb.service.dto.CompanyDTO; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO; import org.springframework.beans.factory.annotation.Autowired; +import com.google.gson.Gson; + /** * Mapper for the entity {@link Company} and its DTO {@link CompanyDTO}. */ @@ -47,6 +50,14 @@ protected Set fromCompanyDomainString(Set companyDomainSt .collect(Collectors.toCollection(HashSet::new)); } + protected String fromAdditionalInfo(CompanyAdditionalInfoDTO additionalInfo) { + return new Gson().toJson(additionalInfo); + } + + protected CompanyAdditionalInfoDTO toAdditionalInfo(String additionalInfo) { + return new Gson().fromJson(additionalInfo, CompanyAdditionalInfoDTO.class); + } + // Ensure that both sides of the relationship is updated. Company will have a list of CompanyDomains // and CompanyDomain will contain a list of companies. @AfterMapping @@ -66,5 +77,4 @@ protected Company fromId(Long id) { company.setId(id); return company; } - -} \ No newline at end of file +} diff --git a/src/main/java/org/mskcc/cbio/oncokb/web/rest/CompanyResource.java b/src/main/java/org/mskcc/cbio/oncokb/web/rest/CompanyResource.java index e44051e99..de08d0ab2 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/web/rest/CompanyResource.java +++ b/src/main/java/org/mskcc/cbio/oncokb/web/rest/CompanyResource.java @@ -16,6 +16,9 @@ import org.mskcc.cbio.oncokb.web.rest.vm.VerifyCompanyNameVM; import org.mskcc.cbio.oncokb.service.dto.CompanyDTO; import org.mskcc.cbio.oncokb.service.dto.UserDTO; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyLicense; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyTermination; import io.github.jhipster.web.util.ResponseUtil; import org.slf4j.Logger; @@ -28,6 +31,9 @@ import javax.validation.Valid; import java.net.URI; import java.net.URISyntaxException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -195,4 +201,32 @@ public ResponseEntity deleteCompany(@PathVariable Long id) { companyService.delete(id); return ResponseEntity.ok().build(); } + + @GetMapping("/companies/licenses-about-to-expire-pending-notification") + public List getLicensesAboutToExpirePendingNotification() { + log.debug("Request to get Company license is about to expire"); + Instant now = Instant.now(); + ArrayList companies = new ArrayList<>(); + for (CompanyDTO company : companyService.findAll()) { + Optional optionalTermination = Optional.ofNullable(company.getAdditionalInfo()) + .map(CompanyAdditionalInfoDTO::getLicense) + .map(CompanyLicense::getTermination); + if (optionalTermination.isPresent()) { + CompanyTermination termination = optionalTermination.get(); + Boolean hasBeenNotified = termination.getHasBeenNotified(); + if (hasBeenNotified == null) { + hasBeenNotified = false; + } + Integer notificationDays = termination.getNotificationDays(); + Instant terminationDate = termination.getDate(); + if (!hasBeenNotified && notificationDays != null && terminationDate != null) { + Instant start = terminationDate.minus(notificationDays, ChronoUnit.DAYS); + if (now.isAfter(start) && now.isBefore(terminationDate)) { + companies.add(company.getId()); + } + } + } + } + return companies; + } } diff --git a/src/main/java/org/mskcc/cbio/oncokb/web/rest/MailsController.java b/src/main/java/org/mskcc/cbio/oncokb/web/rest/MailsController.java index 0fbcd7943..a3ad23644 100644 --- a/src/main/java/org/mskcc/cbio/oncokb/web/rest/MailsController.java +++ b/src/main/java/org/mskcc/cbio/oncokb/web/rest/MailsController.java @@ -1,28 +1,52 @@ package org.mskcc.cbio.oncokb.web.rest; +import java.net.URISyntaxException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.mail.MessagingException; +import javax.validation.Valid; + import org.apache.commons.lang3.StringUtils; +import org.mskcc.cbio.oncokb.config.application.ApplicationProperties; +import org.mskcc.cbio.oncokb.config.application.EmailAddresses; import org.mskcc.cbio.oncokb.domain.MailTypeInfo; import org.mskcc.cbio.oncokb.domain.User; -import org.mskcc.cbio.oncokb.domain.enumeration.LicenseType; import org.mskcc.cbio.oncokb.domain.enumeration.MailType; -import org.mskcc.cbio.oncokb.security.AuthoritiesConstants; +import org.mskcc.cbio.oncokb.service.CompanyService; import org.mskcc.cbio.oncokb.service.MailService; import org.mskcc.cbio.oncokb.service.UserService; +import org.mskcc.cbio.oncokb.service.dto.CompanyDTO; +import org.mskcc.cbio.oncokb.service.dto.TerminationEmailDTO; +import org.mskcc.cbio.oncokb.service.dto.UserDTO; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyLicense; +import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyTermination; import org.mskcc.cbio.oncokb.service.mapper.UserMapper; import org.mskcc.cbio.oncokb.web.rest.errors.BadRequestAlertException; +import org.mskcc.cbio.oncokb.web.rest.vm.CompanyVM; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.mail.MessagingException; -import javax.validation.Valid; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.mskcc.cbio.oncokb.config.Constants; +import org.thymeleaf.context.Context; /** * REST controller for managing mails. @@ -39,10 +63,16 @@ public class MailsController { private final UserMapper userMapper; - public MailsController(MailService mailService, UserService userService, UserMapper userMapper) { + private final CompanyService companyService; + + private final ApplicationProperties applicationProperties; + + public MailsController(MailService mailService, UserService userService, UserMapper userMapper, CompanyService companyService, ApplicationProperties applicationProperties) { this.mailService = mailService; this.userService = userService; this.userMapper = userMapper; + this.companyService = companyService; + this.applicationProperties = applicationProperties; } /** @@ -79,14 +109,14 @@ public ResponseEntity sendFeedbackMails( @Valid @RequestParam String from, @RequestParam String userName ) throws MessagingException { - String emailContenet = description; + String emailContent = description; if (StringUtils.isNotEmpty(userName)) { - emailContenet += "\n\n" + userName; + emailContent += "\n\n" + userName; } if (StringUtils.isNotEmpty(from)) { - emailContenet += " (" + from + ")"; + emailContent += " (" + from + ")"; } - this.mailService.sendFeedback(from, subject, emailContenet); + this.mailService.sendFeedback(from, subject, emailContent); return new ResponseEntity<>(HttpStatus.OK); } @@ -117,4 +147,96 @@ public List getMailsTypes() { return mailTypeInfos; } + @GetMapping("mails/termination-warning/{companyId}") + public TerminationEmailDTO getTerminationWarningEmail(@PathVariable Long companyId) { + CompanyDTO company = this.companyService.findOne(companyId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + List users = this.userService.getCompanyUsers(companyId); + + String formattedDate; + + try { + Instant date = company.getAdditionalInfo().getLicense().getTermination().getDate(); + ZonedDateTime zoned = date.atZone(ZoneId.of(Constants.NY_ZONE_ID)); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy"); + formattedDate = formatter.format(zoned); + } catch (NullPointerException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Please save the termination date first."); + } + + EmailAddresses addresses = this.applicationProperties.getEmailAddresses(); + + String from = addresses.getLicenseAddress(); + String cc = addresses.getLicenseAddress(); + String bcc = users.stream().map(UserDTO::getEmail).collect(Collectors.joining(";")); + String subject = "Urgent: OncoKB API License Expiration on " + formattedDate + " - Action Required"; + String content = "Dear " + company.getName() + ",\n\n" + + "We are writing to inform you that your API license for OncoKB is set to expire on " + formattedDate + ".\n\n" + + "If you would like to continue using OncoKB, please let us know, and we will work with your company’s representative to facilitate the license renewal.\n\n" + + "Regards,\n" + + "The OncoKB Team"; + + TerminationEmailDTO dto = new TerminationEmailDTO(); + dto.setSubject(subject); + dto.setFrom(from); + dto.setCc(cc); + dto.setBcc(bcc); + dto.setCompanyId(companyId); + dto.setContent(content); + return dto; + } + + @PostMapping("mails/company-termination-warning") + public void sendTerminationWarningEmail(@Valid @RequestBody TerminationEmailDTO terminationEmailDTO) throws MessagingException { + this.mailService.sendEmail(terminationEmailDTO); + } + + @PostMapping("mails/company-termination-notification") + public void sendTerminationNotificationEmail(@Valid @RequestBody List companyIds) throws MessagingException { + List companiesToNotify = companyService.findCompaniesByIds(companyIds); + EmailAddresses addresses = this.applicationProperties.getEmailAddresses(); + Context context = new Context(); + final String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString(); + context.setVariable("baseUrl", baseUrl); + context.setVariable("companies", companiesToNotify); + String to = addresses.getLicenseAddress(); + mailService.sendInternalEmailFromTemplate(MailType.TERMINATION_NOTIFICATION_EMAIL, "Licenses are about to expire.", to, context); + + for (CompanyDTO company : companiesToNotify) { + if (company.getAdditionalInfo() == null) { + company.setAdditionalInfo(new CompanyAdditionalInfoDTO()); + } + + CompanyAdditionalInfoDTO additionalInfoDTO = company.getAdditionalInfo(); + + if (additionalInfoDTO.getLicense() == null) { + additionalInfoDTO.setLicense(new CompanyLicense()); + } + + CompanyLicense license = additionalInfoDTO.getLicense(); + + if (license.getTermination() == null) { + license.setTermination(new CompanyTermination()); + } + + CompanyTermination termination = license.getTermination(); + + termination.setHasBeenNotified(true); + + CompanyVM companyVM = new CompanyVM(); + companyVM.setId(company.getId()); + companyVM.setName(company.getName()); + companyVM.setDescription(company.getDescription()); + companyVM.setCompanyType(company.getCompanyType()); + companyVM.setLicenseType(company.getLicenseType()); + companyVM.setLicenseModel(company.getLicenseModel()); + companyVM.setLicenseStatus(company.getLicenseStatus()); + companyVM.setBusinessContact(company.getBusinessContact()); + companyVM.setLegalContact(company.getLegalContact()); + companyVM.setCompanyDomains(company.getCompanyDomains()); + companyVM.setAdditionalInfo(company.getAdditionalInfo()); + + companyService.updateCompany(companyVM); + } + + } } diff --git a/src/main/resources/config/liquibase/changelog/20240829165533_added_additionalInfo_to_Company.xml b/src/main/resources/config/liquibase/changelog/20240829165533_added_additionalInfo_to_Company.xml new file mode 100644 index 000000000..cfbca009c --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20240829165533_added_additionalInfo_to_Company.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index b90a7fbb7..da38ad349 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -27,6 +27,7 @@ + diff --git a/src/main/resources/templates/mail/terminationNotificationEmail.html b/src/main/resources/templates/mail/terminationNotificationEmail.html new file mode 100644 index 000000000..6d1920429 --- /dev/null +++ b/src/main/resources/templates/mail/terminationNotificationEmail.html @@ -0,0 +1,26 @@ + + + + Termination Notification Email + + + +

The following companies licenses are about to expire.

+ + + + + + + + + + + + +
NameLink to Company Page
+ + Company Page +
+ + diff --git a/src/main/webapp/app/components/newAccountForm/NewAccountForm.tsx b/src/main/webapp/app/components/newAccountForm/NewAccountForm.tsx index a91bd727e..37de55b96 100644 --- a/src/main/webapp/app/components/newAccountForm/NewAccountForm.tsx +++ b/src/main/webapp/app/components/newAccountForm/NewAccountForm.tsx @@ -34,7 +34,7 @@ import { getSectionClassName, } from 'app/pages/account/AccountUtils'; import { If, Then } from 'react-if'; -import { FormSelectWithLabelField } from 'app/shared/select/FormSelectWithLabelField'; +import FormSelectWithLabelField from 'app/shared/select/FormSelectWithLabelField'; import client from 'app/shared/api/clientInstance'; import { notifyError } from 'app/shared/utils/NotificationUtils'; import { diff --git a/src/main/webapp/app/components/newCompanyForm/NewCompanyForm.tsx b/src/main/webapp/app/components/newCompanyForm/NewCompanyForm.tsx index 4115bcb0d..17d799d45 100644 --- a/src/main/webapp/app/components/newCompanyForm/NewCompanyForm.tsx +++ b/src/main/webapp/app/components/newCompanyForm/NewCompanyForm.tsx @@ -2,11 +2,14 @@ import React from 'react'; import { observer } from 'mobx-react'; import { Alert, Button, Col, Row } from 'react-bootstrap'; import { observable, action, computed } from 'mobx'; -import { CompanyVM } from 'app/shared/api/generated/API'; +import { + CompanyVM, + CompanyAdditionalInfoDTO, +} from 'app/shared/api/generated/API'; import { FormListField } from 'app/shared/list/FormListField'; import { getSectionClassName } from 'app/pages/account/AccountUtils'; import { FormTextAreaField } from '../../shared/textarea/FormTextAreaField'; -import { FormSelectWithLabelField } from '../../shared/select/FormSelectWithLabelField'; +import FormSelectWithLabelField from '../../shared/select/FormSelectWithLabelField'; import { AvField, AvForm } from 'availity-reactstrap-validation'; import { LONG_TEXT_VAL, @@ -31,8 +34,10 @@ import { AdditionalInfoSelect } from 'app/shared/dropdown/AdditionalInfoSelect'; import { debouncedCompanyNameValidator, fieldRequiredValidation, - textValidation, } from 'app/shared/utils/FormValidationUtils'; +import CompanyAdditionalInfo, { + createDefaultAdditionalInfo, +} from 'app/pages/companyPage/CompanyAdditionalInfo'; type INewCompanyFormProps = { onValidSubmit: (newCompany: Partial) => void; @@ -70,6 +75,8 @@ export const COMPANY_FORM_OPTIONS = { export class NewCompanyForm extends React.Component { @observable companyDescription = ''; @observable companyDomains: string[] = []; + @observable + companyAdditionalInfo: CompanyAdditionalInfoDTO = createDefaultAdditionalInfo() as CompanyAdditionalInfoDTO; @observable selectedCompanyType: CompanyType = CompanyType.PARENT; @observable selectedLicenseModel: LicenseModel = LicenseModel.FULL; @observable selectedLicenseType: LicenseType = LicenseType.COMMERCIAL; @@ -118,6 +125,10 @@ export class NewCompanyForm extends React.Component { licenseType: this.selectedLicenseType, name: values.companyName, companyDomains: this.companyDomains, + additionalInfo: + this.selectedLicenseStatus !== 'TRIAL' + ? this.companyAdditionalInfo + : ((null as unknown) as CompanyAdditionalInfoDTO), }; this.props.onValidSubmit(newCompany); } @@ -247,6 +258,22 @@ export class NewCompanyForm extends React.Component { /> + {this.selectedLicenseStatus !== 'TRIAL' && ( + + +
Additional Info
+ + + { + this.companyAdditionalInfo = x as CompanyAdditionalInfoDTO; + }} + /> + +
+ )}
Review Information
diff --git a/src/main/webapp/app/pages/companyPage/CompanyAdditionalInfo.tsx b/src/main/webapp/app/pages/companyPage/CompanyAdditionalInfo.tsx new file mode 100644 index 000000000..f0201b915 --- /dev/null +++ b/src/main/webapp/app/pages/companyPage/CompanyAdditionalInfo.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import FormSelectWithLabelField from 'app/shared/select/FormSelectWithLabelField'; +import FormInputField from 'app/shared/input/FormInputField'; +import { CompanyAdditionalInfoDTO } from 'app/shared/api/generated/API'; +import { FormTextAreaField } from 'app/shared/textarea/FormTextAreaField'; +import { notifyError } from 'app/shared/utils/NotificationUtils'; +import { AvField } from 'availity-reactstrap-validation'; + +type DeepPartial = { + [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; +}; + +type PartialCompanyAdditionalInfoDTO = DeepPartial; + +export function createDefaultAdditionalInfo(): PartialCompanyAdditionalInfoDTO { + return { + license: { + autoRenewal: true, + activation: new Date().toISOString(), + }, + }; +} + +type ICompanyAdditionalInfo = { + setAdditionalInfo: ( + additionalInfo: PartialCompanyAdditionalInfoDTO | null | undefined + ) => void; + additionalInfo: PartialCompanyAdditionalInfoDTO | null | undefined; + mode: 'create' | 'update'; +}; + +export default function CompanyAdditionalInfo({ + additionalInfo, + setAdditionalInfo, + mode, +}: ICompanyAdditionalInfo) { + const boldLabel = mode === 'update'; + + return ( + <> + { + setAdditionalInfo({ + license: { + ...additionalInfo?.license, + autoRenewal: !!event.target.value, + activation: event.target.value + ? new Date(event.target.value).toISOString() + : undefined, + }, + }); + }} + /> + { + setAdditionalInfo({ + license: { + ...additionalInfo?.license, + autoRenewal: autoRenewalOption.value ?? false, + }, + }); + }} + /> + {mode === 'update' && ( + <> + {additionalInfo?.license?.activation && ( + <> + { + const terminationDate = event.target.value + ? new Date(event.target.value) + : undefined; + setAdditionalInfo({ + license: { + ...additionalInfo?.license, + termination: { + ...additionalInfo?.license?.termination, + hasBeenNotified: false, + date: terminationDate?.toISOString(), + notificationDays: + additionalInfo?.license?.termination + ?.notificationDays !== undefined + ? additionalInfo?.license?.termination + ?.notificationDays + : 90, + }, + }, + }); + }} + /> + {additionalInfo?.license?.termination?.date && ( + <> + { + const value = event?.target?.value; + setAdditionalInfo({ + license: { + ...additionalInfo?.license, + termination: { + ...additionalInfo?.license?.termination, + hasBeenNotified: false, + notificationDays: value + ? +value + : ((undefined as unknown) as number), + }, + }, + }); + }} + /> + + { + setAdditionalInfo({ + license: { + ...additionalInfo?.license, + termination: { + ...additionalInfo?.license?.termination, + notes: e.target.value, + }, + }, + }); + }} + /> + + )} + + )} + + )} + + ); +} diff --git a/src/main/webapp/app/pages/companyPage/CompanyPage.tsx b/src/main/webapp/app/pages/companyPage/CompanyPage.tsx index 85af5e3f3..72e93313e 100644 --- a/src/main/webapp/app/pages/companyPage/CompanyPage.tsx +++ b/src/main/webapp/app/pages/companyPage/CompanyPage.tsx @@ -19,6 +19,9 @@ import { Token, UserDTO, UserOverviewUsage, + CompanyAdditionalInfoDTO, + TerminationEmailDTO, + CompanyTermination, UserStats, } from 'app/shared/api/generated/API'; import client from 'app/shared/api/clientInstance'; @@ -26,10 +29,14 @@ import { action, computed, observable } from 'mobx'; import { Else, If, Then } from 'react-if'; import LoadingIndicator from 'app/components/loadingIndicator/LoadingIndicator'; import { RouteComponentProps } from 'react-router'; -import { notifyError, notifySuccess } from 'app/shared/utils/NotificationUtils'; +import { + notifyError, + notifySuccess, + notifyInfo, +} from 'app/shared/utils/NotificationUtils'; import { PromiseStatus } from 'app/shared/utils/PromiseUtils'; import { FormTextAreaField } from 'app/shared/textarea/FormTextAreaField'; -import { FormSelectWithLabelField } from 'app/shared/select/FormSelectWithLabelField'; +import FormSelectWithLabelField from 'app/shared/select/FormSelectWithLabelField'; import { COMPANY_FORM_OPTIONS } from 'app/components/newCompanyForm/NewCompanyForm'; import { FormListField } from 'app/shared/list/FormListField'; import { UserTable } from 'app/shared/table/UserTable'; @@ -69,6 +76,10 @@ import { DownloadButton } from 'app/components/downloadButton/DownloadButton'; import { RouterStore } from 'mobx-react-router'; import { TEXT_VAL } from 'app/shared/utils/FormValidationUtils'; import { Helmet } from 'react-helmet-async'; +import CompanyAdditionalInfo, { + createDefaultAdditionalInfo, +} from './CompanyAdditionalInfo'; +import FormInputField from 'app/shared/input/FormInputField'; import { ToggleValue } from '../usageAnalysisPage/usage-analysis-utils'; interface MatchParams { @@ -108,8 +119,18 @@ enum SimpleConfirmModalType { NA, DELETE_COMPANY, UPDATE_COMPANY, + SEND_TERMINATION_NOTICE, } +const defaultPayload: TerminationEmailDTO = { + subject: '', + from: '', + cc: '', + bcc: '', + content: '', + companyId: 0, +}; + @inject('routing') @observer export default class CompanyPage extends React.Component { @@ -133,6 +154,9 @@ export default class CompanyPage extends React.Component { @observable simpleConfirmModalType: SimpleConfirmModalType = SimpleConfirmModalType.NA; + sendTerminationNoticePayload: TerminationEmailDTO = defaultPayload; + @observable editAllModeEnabled = false; + @observable resourcesTypeToggleValue: ToggleValue = ToggleValue.PUBLIC_RESOURCES; @@ -205,6 +229,18 @@ export default class CompanyPage extends React.Component { const newCompanyUserEmails = this.selectedUsersOptions.map( selection => selection.value ); + if (this.selectedLicenseStatus === 'TRIAL') { + this.company.additionalInfo = (null as unknown) as CompanyAdditionalInfoDTO; + } else if (this.company.additionalInfo?.license) { + const license = this.company.additionalInfo?.license; + if (!license.activation) { + license.autoRenewal = false; + license.termination = (undefined as unknown) as CompanyTermination; + } else if (license.termination && !license.termination.date) { + license.termination.notes = (undefined as unknown) as string; + license.termination.notificationDays = (undefined as unknown) as number; + } + } const updatedCompany: CompanyVM = { ...this.company, licenseStatus: this.selectedLicenseStatus, @@ -249,6 +285,17 @@ export default class CompanyPage extends React.Component { ); } + @autobind + onConfirmSendTerminationNoticeButton() { + this.showModal = false; + client + .sendTerminationWarningEmailUsingPOST({ + terminationEmailDto: this.sendTerminationNoticePayload, + }) + .then(() => notifySuccess('Email sent successfully!')) + .catch(notifyError); + } + @autobind onConfirmSimpleConfirmModal() { switch (this.simpleConfirmModalType) { @@ -258,6 +305,9 @@ export default class CompanyPage extends React.Component { case SimpleConfirmModalType.DELETE_COMPANY: this.onConfirmDeleteAccountButton(); break; + case SimpleConfirmModalType.SEND_TERMINATION_NOTICE: + this.onConfirmSendTerminationNoticeButton(); + break; case SimpleConfirmModalType.NA: default: break; @@ -379,6 +429,11 @@ export default class CompanyPage extends React.Component { this.simpleConfirmModalType === SimpleConfirmModalType.DELETE_COMPANY ) { return 'Confirm Deleting Company'; + } else if ( + this.simpleConfirmModalType === + SimpleConfirmModalType.SEND_TERMINATION_NOTICE + ) { + return 'Send Termination Notice'; } } @@ -403,6 +458,75 @@ export default class CompanyPage extends React.Component { ); + } else if ( + this.simpleConfirmModalType === + SimpleConfirmModalType.SEND_TERMINATION_NOTICE + ) { + const isReadonly = !this.editAllModeEnabled; + return ( + +
+ +
+ + (this.sendTerminationNoticePayload.from = e.target.value) + } + /> + + (this.sendTerminationNoticePayload.subject = e.target.value) + } + /> + + (this.sendTerminationNoticePayload.cc = e.target.value) + } + /> + + (this.sendTerminationNoticePayload.bcc = e.target.value) + } + /> + + (this.sendTerminationNoticePayload.content = e.target.value) + } + /> +
+ ); } else { return undefined; } @@ -514,26 +638,61 @@ export default class CompanyPage extends React.Component {
Quick Tools
-
- - Create Company Users - - {this.companyHasTrialUsers && ( - - } +
+
+ - - Extend Trial Access - - - )} + Create Company Users + + {this.companyHasTrialUsers && ( + + } + > + + Extend Trial Access + + + )} +
+ +
@@ -674,13 +833,34 @@ export default class CompanyPage extends React.Component { LICENSE_STATUS_TITLES[this.selectedLicenseStatus], }} options={this.licenseStatusOptions} - onSelection={(selectedOption: any) => - (this.selectedLicenseStatus = selectedOption.value) - } + onSelection={(selectedOption: any) => { + const newValue: LicenseStatus = + selectedOption.value; + if ( + newValue !== 'TRIAL' && + !this.company.additionalInfo + ) { + this.company.additionalInfo = createDefaultAdditionalInfo() as CompanyAdditionalInfoDTO; + } + this.selectedLicenseStatus = selectedOption.value; + }} boldLabel /> + {this.selectedLicenseStatus !== 'TRIAL' && ( + + + { + this.company.additionalInfo = x as CompanyAdditionalInfoDTO; + }} + /> + + + )}
diff --git a/src/main/webapp/app/shared/api/generated/API-docs.json b/src/main/webapp/app/shared/api/generated/API-docs.json index 6b0fd7238..467c36dd1 100644 --- a/src/main/webapp/app/shared/api/generated/API-docs.json +++ b/src/main/webapp/app/shared/api/generated/API-docs.json @@ -881,6 +881,40 @@ "deprecated": false } }, + "/api/companies/licenses-about-to-expire-pending-notification": { + "get": { + "tags": [ + "company-resource" + ], + "summary": "getLicensesAboutToExpirePendingNotification", + "operationId": "getLicensesAboutToExpirePendingNotificationUsingGET", + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + }, + "deprecated": false + } + }, "/api/companies/lookup": { "get": { "tags": [ @@ -1560,6 +1594,98 @@ "deprecated": false } }, + "/api/mails/company-termination-notification": { + "post": { + "tags": [ + "mails-controller" + ], + "summary": "sendTerminationNotificationEmail", + "operationId": "sendTerminationNotificationEmailUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "companyIds", + "description": "companyIds", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + }, + "deprecated": false + } + }, + "/api/mails/company-termination-warning": { + "post": { + "tags": [ + "mails-controller" + ], + "summary": "sendTerminationWarningEmail", + "operationId": "sendTerminationWarningEmailUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "terminationEmailDTO", + "description": "terminationEmailDTO", + "required": true, + "schema": { + "$ref": "#/definitions/TerminationEmailDTO" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + }, + "deprecated": false + } + }, "/api/mails/feedback": { "post": { "tags": [ @@ -1656,6 +1782,46 @@ "deprecated": false } }, + "/api/mails/termination-warning/{companyId}": { + "get": { + "tags": [ + "mails-controller" + ], + "summary": "getTerminationWarningEmail", + "operationId": "getTerminationWarningEmailUsingGET", + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "companyId", + "in": "path", + "description": "companyId", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/TerminationEmailDTO" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + }, + "deprecated": false + } + }, "/api/mails/types": { "get": { "tags": [ @@ -1758,6 +1924,7 @@ "TOKEN_HAS_BEEN_EXPOSED_USER", "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED", "LIST_OF_UNAPPROVED_USERS", + "TERMINATION_NOTIFICATION_EMAIL", "TEST" ] }, @@ -2984,6 +3151,15 @@ }, "title": "ApiAccessRequest" }, + "CompanyAdditionalInfoDTO": { + "type": "object", + "properties": { + "license": { + "$ref": "#/definitions/CompanyLicense" + } + }, + "title": "CompanyAdditionalInfoDTO" + }, "CompanyDTO": { "type": "object", "required": [ @@ -2994,6 +3170,9 @@ "name" ], "properties": { + "additionalInfo": { + "$ref": "#/definitions/CompanyAdditionalInfoDTO" + }, "businessContact": { "type": "string" }, @@ -3073,6 +3252,42 @@ }, "title": "CompanyDomainDTO" }, + "CompanyLicense": { + "type": "object", + "properties": { + "activation": { + "type": "string", + "format": "date-time" + }, + "autoRenewal": { + "type": "boolean" + }, + "termination": { + "$ref": "#/definitions/CompanyTermination" + } + }, + "title": "CompanyLicense" + }, + "CompanyTermination": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "hasBeenNotified": { + "type": "boolean" + }, + "notes": { + "type": "string" + }, + "notificationDays": { + "type": "integer", + "format": "int32" + } + }, + "title": "CompanyTermination" + }, "CompanyVM": { "type": "object", "required": [ @@ -3083,6 +3298,9 @@ "name" ], "properties": { + "additionalInfo": { + "$ref": "#/definitions/CompanyAdditionalInfoDTO" + }, "businessContact": { "type": "string" }, @@ -3263,6 +3481,7 @@ "TOKEN_HAS_BEEN_EXPOSED_USER", "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED", "LIST_OF_UNAPPROVED_USERS", + "TERMINATION_NOTIFICATION_EMAIL", "TEST" ] } @@ -3414,6 +3633,31 @@ }, "title": "PasswordChangeDTO" }, + "TerminationEmailDTO": { + "type": "object", + "properties": { + "bcc": { + "type": "string" + }, + "cc": { + "type": "string" + }, + "companyId": { + "type": "integer", + "format": "int64" + }, + "content": { + "type": "string" + }, + "from": { + "type": "string" + }, + "subject": { + "type": "string" + } + }, + "title": "TerminationEmailDTO" + }, "Token": { "type": "object", "required": [ @@ -3772,6 +4016,7 @@ "TOKEN_HAS_BEEN_EXPOSED_USER", "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED", "LIST_OF_UNAPPROVED_USERS", + "TERMINATION_NOTIFICATION_EMAIL", "TEST" ] }, diff --git a/src/main/webapp/app/shared/api/generated/API.ts b/src/main/webapp/app/shared/api/generated/API.ts index efce42392..053824e58 100644 --- a/src/main/webapp/app/shared/api/generated/API.ts +++ b/src/main/webapp/app/shared/api/generated/API.ts @@ -24,9 +24,15 @@ export type ApiAccessRequest = { 'requested': boolean +}; +export type CompanyAdditionalInfoDTO = { + 'license': CompanyLicense + }; export type CompanyDTO = { - 'businessContact': string + 'additionalInfo': CompanyAdditionalInfoDTO + + 'businessContact': string 'companyDomains': Array < string > @@ -54,9 +60,29 @@ export type CompanyDomainDTO = { 'name': string +}; +export type CompanyLicense = { + 'activation': string + + 'autoRenewal': boolean + + 'termination': CompanyTermination + +}; +export type CompanyTermination = { + 'date': string + + 'hasBeenNotified': boolean + + 'notes': string + + 'notificationDays': number + }; export type CompanyVM = { - 'businessContact': string + 'additionalInfo': CompanyAdditionalInfoDTO + + 'businessContact': string 'companyDomains': Array < string > @@ -118,7 +144,7 @@ export type LoginVM = { export type MailTypeInfo = { 'description': string - 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TEST" + 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TERMINATION_NOTIFICATION_EMAIL" | "TEST" }; export type ManagedUserVM = { @@ -185,6 +211,20 @@ export type PasswordChangeDTO = { 'newPassword': string +}; +export type TerminationEmailDTO = { + 'bcc': string + + 'cc': string + + 'companyId': number + + 'content': string + + 'from': string + + 'subject': string + }; export type Token = { 'creation': string @@ -325,7 +365,7 @@ export type UserDetailsDTO = { export type UserMailsDTO = { 'id': number - 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TEST" + 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TERMINATION_NOTIFICATION_EMAIL" | "TEST" 'sentBy': string @@ -1910,6 +1950,68 @@ export default class API { return response.body; }); }; + getLicensesAboutToExpirePendingNotificationUsingGETURL(parameters: { + $queryParameters ? : any + }): string { + let queryParameters: any = {}; + let path = '/api/companies/licenses-about-to-expire-pending-notification'; + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + let keys = Object.keys(queryParameters); + return this.domain + path + (keys.length > 0 ? '?' + (keys.map(key => key + '=' + encodeURIComponent(queryParameters[key])).join('&')) : ''); + }; + + /** + * getLicensesAboutToExpirePendingNotification + * @method + * @name API#getLicensesAboutToExpirePendingNotificationUsingGET + */ + getLicensesAboutToExpirePendingNotificationUsingGETWithHttpInfo(parameters: { + $queryParameters ? : any, + $domain ? : string + }): Promise < request.Response > { + const domain = parameters.$domain ? parameters.$domain : this.domain; + const errorHandlers = this.errorHandlers; + const request = this.request; + let path = '/api/companies/licenses-about-to-expire-pending-notification'; + let body: any; + let queryParameters: any = {}; + let headers: any = {}; + let form: any = {}; + return new Promise(function(resolve, reject) { + headers['Accept'] = '*/*'; + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + + request('GET', domain + path, body, headers, queryParameters, form, reject, resolve, errorHandlers); + + }); + }; + + /** + * getLicensesAboutToExpirePendingNotification + * @method + * @name API#getLicensesAboutToExpirePendingNotificationUsingGET + */ + getLicensesAboutToExpirePendingNotificationUsingGET(parameters: { + $queryParameters ? : any, + $domain ? : string + }): Promise < Array < number > + > { + return this.getLicensesAboutToExpirePendingNotificationUsingGETWithHttpInfo(parameters).then(function(response: request.Response) { + return response.body; + }); + }; getCompanyByNameUsingGETURL(parameters: { 'name': string, $queryParameters ? : any @@ -3231,6 +3333,158 @@ export default class API { return response.body; }); }; + sendTerminationNotificationEmailUsingPOSTURL(parameters: { + 'companyIds': Array < number > , + $queryParameters ? : any + }): string { + let queryParameters: any = {}; + let path = '/api/mails/company-termination-notification'; + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + let keys = Object.keys(queryParameters); + return this.domain + path + (keys.length > 0 ? '?' + (keys.map(key => key + '=' + encodeURIComponent(queryParameters[key])).join('&')) : ''); + }; + + /** + * sendTerminationNotificationEmail + * @method + * @name API#sendTerminationNotificationEmailUsingPOST + * @param {} companyIds - companyIds + */ + sendTerminationNotificationEmailUsingPOSTWithHttpInfo(parameters: { + 'companyIds': Array < number > , + $queryParameters ? : any, + $domain ? : string + }): Promise < request.Response > { + const domain = parameters.$domain ? parameters.$domain : this.domain; + const errorHandlers = this.errorHandlers; + const request = this.request; + let path = '/api/mails/company-termination-notification'; + let body: any; + let queryParameters: any = {}; + let headers: any = {}; + let form: any = {}; + return new Promise(function(resolve, reject) { + headers['Accept'] = '*/*'; + headers['Content-Type'] = 'application/json'; + + if (parameters['companyIds'] !== undefined) { + body = parameters['companyIds']; + } + + if (parameters['companyIds'] === undefined) { + reject(new Error('Missing required parameter: companyIds')); + return; + } + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + + request('POST', domain + path, body, headers, queryParameters, form, reject, resolve, errorHandlers); + + }); + }; + + /** + * sendTerminationNotificationEmail + * @method + * @name API#sendTerminationNotificationEmailUsingPOST + * @param {} companyIds - companyIds + */ + sendTerminationNotificationEmailUsingPOST(parameters: { + 'companyIds': Array < number > , + $queryParameters ? : any, + $domain ? : string + }): Promise < any > { + return this.sendTerminationNotificationEmailUsingPOSTWithHttpInfo(parameters).then(function(response: request.Response) { + return response.body; + }); + }; + sendTerminationWarningEmailUsingPOSTURL(parameters: { + 'terminationEmailDto': TerminationEmailDTO, + $queryParameters ? : any + }): string { + let queryParameters: any = {}; + let path = '/api/mails/company-termination-warning'; + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + let keys = Object.keys(queryParameters); + return this.domain + path + (keys.length > 0 ? '?' + (keys.map(key => key + '=' + encodeURIComponent(queryParameters[key])).join('&')) : ''); + }; + + /** + * sendTerminationWarningEmail + * @method + * @name API#sendTerminationWarningEmailUsingPOST + * @param {} terminationEmailDto - terminationEmailDTO + */ + sendTerminationWarningEmailUsingPOSTWithHttpInfo(parameters: { + 'terminationEmailDto': TerminationEmailDTO, + $queryParameters ? : any, + $domain ? : string + }): Promise < request.Response > { + const domain = parameters.$domain ? parameters.$domain : this.domain; + const errorHandlers = this.errorHandlers; + const request = this.request; + let path = '/api/mails/company-termination-warning'; + let body: any; + let queryParameters: any = {}; + let headers: any = {}; + let form: any = {}; + return new Promise(function(resolve, reject) { + headers['Accept'] = '*/*'; + headers['Content-Type'] = 'application/json'; + + if (parameters['terminationEmailDto'] !== undefined) { + body = parameters['terminationEmailDto']; + } + + if (parameters['terminationEmailDto'] === undefined) { + reject(new Error('Missing required parameter: terminationEmailDto')); + return; + } + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + + request('POST', domain + path, body, headers, queryParameters, form, reject, resolve, errorHandlers); + + }); + }; + + /** + * sendTerminationWarningEmail + * @method + * @name API#sendTerminationWarningEmailUsingPOST + * @param {} terminationEmailDto - terminationEmailDTO + */ + sendTerminationWarningEmailUsingPOST(parameters: { + 'terminationEmailDto': TerminationEmailDTO, + $queryParameters ? : any, + $domain ? : string + }): Promise < any > { + return this.sendTerminationWarningEmailUsingPOSTWithHttpInfo(parameters).then(function(response: request.Response) { + return response.body; + }); + }; sendFeedbackMailsUsingPOSTURL(parameters: { 'description': string, 'from': string, @@ -3426,6 +3680,81 @@ export default class API { return response.body; }); }; + getTerminationWarningEmailUsingGETURL(parameters: { + 'companyId': number, + $queryParameters ? : any + }): string { + let queryParameters: any = {}; + let path = '/api/mails/termination-warning/{companyId}'; + + path = path.replace('{companyId}', parameters['companyId'] + ''); + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + let keys = Object.keys(queryParameters); + return this.domain + path + (keys.length > 0 ? '?' + (keys.map(key => key + '=' + encodeURIComponent(queryParameters[key])).join('&')) : ''); + }; + + /** + * getTerminationWarningEmail + * @method + * @name API#getTerminationWarningEmailUsingGET + * @param {integer} companyId - companyId + */ + getTerminationWarningEmailUsingGETWithHttpInfo(parameters: { + 'companyId': number, + $queryParameters ? : any, + $domain ? : string + }): Promise < request.Response > { + const domain = parameters.$domain ? parameters.$domain : this.domain; + const errorHandlers = this.errorHandlers; + const request = this.request; + let path = '/api/mails/termination-warning/{companyId}'; + let body: any; + let queryParameters: any = {}; + let headers: any = {}; + let form: any = {}; + return new Promise(function(resolve, reject) { + headers['Accept'] = '*/*'; + + path = path.replace('{companyId}', parameters['companyId'] + ''); + + if (parameters['companyId'] === undefined) { + reject(new Error('Missing required parameter: companyId')); + return; + } + + if (parameters.$queryParameters) { + Object.keys(parameters.$queryParameters).forEach(function(parameterName) { + var parameter = parameters.$queryParameters[parameterName]; + queryParameters[parameterName] = parameter; + }); + } + + request('GET', domain + path, body, headers, queryParameters, form, reject, resolve, errorHandlers); + + }); + }; + + /** + * getTerminationWarningEmail + * @method + * @name API#getTerminationWarningEmailUsingGET + * @param {integer} companyId - companyId + */ + getTerminationWarningEmailUsingGET(parameters: { + 'companyId': number, + $queryParameters ? : any, + $domain ? : string + }): Promise < TerminationEmailDTO > { + return this.getTerminationWarningEmailUsingGETWithHttpInfo(parameters).then(function(response: request.Response) { + return response.body; + }); + }; getMailsTypesUsingGETURL(parameters: { $queryParameters ? : any }): string { @@ -3492,7 +3821,7 @@ export default class API { 'by': string, 'cc' ? : string, 'from': string, - 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TEST", + 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TERMINATION_NOTIFICATION_EMAIL" | "TEST", 'to': string, $queryParameters ? : any }): string { @@ -3542,7 +3871,7 @@ export default class API { 'by': string, 'cc' ? : string, 'from': string, - 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TEST", + 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TERMINATION_NOTIFICATION_EMAIL" | "TEST", 'to': string, $queryParameters ? : any, $domain ? : string @@ -3625,7 +3954,7 @@ export default class API { 'by': string, 'cc' ? : string, 'from': string, - 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TEST", + 'mailType': "ACTIVATION" | "CREATION" | "APPROVAL" | "API_ACCESS_APPROVAL" | "APPROVAL_ALIGN_LICENSE_WITH_COMPANY" | "REJECTION" | "REJECTION_US_SANCTION" | "REJECT_ALUMNI_ADDRESS" | "PASSWORD_RESET" | "LICENSE_REVIEW_COMMERCIAL" | "LICENSE_REVIEW_RESEARCH_COMMERCIAL" | "LICENSE_REVIEW_HOSPITAL" | "CLARIFY_ACADEMIC_FOR_PROFIT" | "CLARIFY_ACADEMIC_NON_INSTITUTE_EMAIL" | "CLARIFY_USE_CASE" | "CLARIFY_DUPLICATE_USER" | "CLARIFY_REGISTRATION_INFO" | "LICENSE_OPTIONS" | "VERIFY_EMAIL_BEFORE_ACCOUNT_EXPIRES" | "ACTIVATE_FREE_TRIAL" | "TRIAL_ACCOUNT_IS_ABOUT_TO_EXPIRE" | "TRIAL_ACCOUNT_IS_ACTIVATED" | "DATA_USAGE_EXCEEDS_THRESHOLD" | "TOKEN_HAS_BEEN_EXPOSED" | "TOKEN_HAS_BEEN_EXPOSED_USER" | "SEARCHING_RESPONSE_STRUCTURE_HAS_CHANGED" | "LIST_OF_UNAPPROVED_USERS" | "TERMINATION_NOTIFICATION_EMAIL" | "TEST", 'to': string, $queryParameters ? : any, $domain ? : string diff --git a/src/main/webapp/app/shared/input/FormInputField.tsx b/src/main/webapp/app/shared/input/FormInputField.tsx new file mode 100644 index 000000000..54582783a --- /dev/null +++ b/src/main/webapp/app/shared/input/FormInputField.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import InfoIcon from '../icons/InfoIcon'; +import { AvField } from 'availity-reactstrap-validation'; + +// references +// https://github.com/Availity/availity-reactstrap-validation/blob/master/src/AvField.js +// https://github.com/Availity/availity-reactstrap-validation/tree/master/docs/lib/examples + +export type FormInputFieldProps = { + id: string; + onChange?: (event: React.ChangeEvent) => void; + label: string; + style?: React.CSSProperties; + disabled?: boolean; + value?: string | string[] | number; + boldLabel?: boolean; + infoIconOverlay?: Parameters[0]['overlay']; + type: string; + validate?: Record; +}; + +export default function FormInputField({ + id, + onChange, + label, + boldLabel, + style, + disabled, + value, + type, + infoIconOverlay, + validate, +}: FormInputFieldProps) { + return ( + + {label} + {infoIconOverlay && ( + + )} + + } + labelClass={boldLabel ? 'form-label font-weight-bold' : ''} + name={id} + onChange={onChange} + style={style} + className="form-control" + disabled={disabled ?? false} + value={value} + type={type} + validate={validate} + /> + ); +} diff --git a/src/main/webapp/app/shared/select/FormSelectWithLabelField.tsx b/src/main/webapp/app/shared/select/FormSelectWithLabelField.tsx index f74b72bbd..70dcd06ee 100644 --- a/src/main/webapp/app/shared/select/FormSelectWithLabelField.tsx +++ b/src/main/webapp/app/shared/select/FormSelectWithLabelField.tsx @@ -1,15 +1,15 @@ import React from 'react'; import Select from 'react-select'; -export type IFormSelectWithLabelProps = { - onSelection: (selectedOption: any) => void; +export type IFormSelectWithLabelProps = { + onSelection: (selectedOption: { label: Label; value: Value }) => void; labelText: string; name: string; - defaultValue?: { value: any; label: any }; - options: { value: any; label: any }[]; + defaultValue?: { label: Label; value: Value }; + options: { label: Label; value: Value }[]; boldLabel?: boolean; isClearable?: boolean; - value?: { value: any; label: any } | null; + value?: { label: Label; value: Value } | null; }; export type Option = { @@ -17,7 +17,9 @@ export type Option = { value: string; }; -export const FormSelectWithLabelField: React.FunctionComponent = props => { +export default function FormSelectWithLabelField( + props: IFormSelectWithLabelProps +) { return (
@@ -33,4 +35,4 @@ export const FormSelectWithLabelField: React.FunctionComponent
); -}; +} diff --git a/src/main/webapp/app/shared/textarea/FormTextAreaField.tsx b/src/main/webapp/app/shared/textarea/FormTextAreaField.tsx index 3b70e7dcc..341f1afd3 100644 --- a/src/main/webapp/app/shared/textarea/FormTextAreaField.tsx +++ b/src/main/webapp/app/shared/textarea/FormTextAreaField.tsx @@ -1,26 +1,35 @@ import React from 'react'; export type IFormTextAreaProps = { - onTextAreaChange?: (event: any) => void; + id?: string; + onTextAreaChange?: (event: React.ChangeEvent) => void; label: string; style?: React.CSSProperties; disabled?: boolean; value?: string; boldLabel?: boolean; + rows?: number; + readOnly?: boolean; }; export const FormTextAreaField: React.FunctionComponent = props => { return (
-