diff --git a/member-service/src/main/java/org/orcid/memberportal/service/member/services/MailService.java b/member-service/src/main/java/org/orcid/memberportal/service/member/services/MailService.java index 840101600..3899aa8b7 100644 --- a/member-service/src/main/java/org/orcid/memberportal/service/member/services/MailService.java +++ b/member-service/src/main/java/org/orcid/memberportal/service/member/services/MailService.java @@ -127,6 +127,8 @@ public void sendAddConsortiumMemberEmail(AddConsortiumMember addConsortiumMember context.setVariable("contactFamilyName", addConsortiumMember.getContactFamilyName()); context.setVariable("contactJobTitle", addConsortiumMember.getContactJobTitle()); context.setVariable("contactEmail", addConsortiumMember.getContactEmail()); + context.setVariable("organizationTier", addConsortiumMember.getOrganizationTier()); + context.setVariable("integrationPlans", addConsortiumMember.getIntegrationPlans()); String content = templateEngine.process("mail/addConsortiumMember", context); try { diff --git a/member-service/src/main/java/org/orcid/memberportal/service/member/web/rest/vm/AddConsortiumMember.java b/member-service/src/main/java/org/orcid/memberportal/service/member/web/rest/vm/AddConsortiumMember.java index 354355f59..36ea2d19b 100644 --- a/member-service/src/main/java/org/orcid/memberportal/service/member/web/rest/vm/AddConsortiumMember.java +++ b/member-service/src/main/java/org/orcid/memberportal/service/member/web/rest/vm/AddConsortiumMember.java @@ -38,6 +38,10 @@ public class AddConsortiumMember { private String contactJobTitle; + private String organizationTier; + + private String integrationPlans; + public String getRequestedByName() { return requestedByName; } @@ -173,4 +177,20 @@ public String getContactJobTitle() { public void setContactJobTitle(String contactJobTitle) { this.contactJobTitle = contactJobTitle; } + + public String getOrganizationTier() { + return organizationTier; + } + + public void setOrganizationTier(String organizationTier) { + this.organizationTier = organizationTier; + } + + public String getIntegrationPlans() { + return integrationPlans; + } + + public void setIntegrationPlans(String integrationPlans) { + this.integrationPlans = integrationPlans; + } } diff --git a/member-service/src/main/resources/templates/mail/addConsortiumMember.html b/member-service/src/main/resources/templates/mail/addConsortiumMember.html index 56925fe89..197454105 100644 --- a/member-service/src/main/resources/templates/mail/addConsortiumMember.html +++ b/member-service/src/main/resources/templates/mail/addConsortiumMember.html @@ -56,6 +56,16 @@ Yes

+

+ Organization tier
+ Yes +

+ +

+ Integration plans
+ Yes +

+


Main contact diff --git a/ui/src/app/home/consortium/add-consortium-member.component.html b/ui/src/app/home/consortium/add-consortium-member.component.html index e264bca8f..346ddf910 100644 --- a/ui/src/app/home/consortium/add-consortium-member.component.html +++ b/ui/src/app/home/consortium/add-consortium-member.component.html @@ -7,7 +7,7 @@

New organization

-
+
Warning sign
Your changes cannot be saved
@@ -28,7 +28,7 @@

Name & email domain

'text-danger': editForm.get('orgName')?.invalid && editForm.get('orgName')?.touched && editForm.get('orgName')?.dirty }" - >Organization name (Required)Organization name (Required) Billing address for="country" class="wide-text font-size-12 font-weight-bold" [ngClass]="{ 'text-danger': editForm.get('country')?.invalid && editForm.get('country')?.dirty }" - >Country (Required)Country -
- - Please select a country - -
-
- Warning sign +
+ Warning sign
-
Please note
It can take up to 24 hours before a new organization is available in the Member Portal. Please contact your ORCID engagement lead for more information. diff --git a/ui/src/app/home/consortium/add-consortium-member.component.scss b/ui/src/app/home/consortium/add-consortium-member.component.scss index 768ceb164..3f14e053a 100644 --- a/ui/src/app/home/consortium/add-consortium-member.component.scss +++ b/ui/src/app/home/consortium/add-consortium-member.component.scss @@ -20,28 +20,26 @@ label { margin-bottom: 0.25rem; } -.error-message { +.required { + color: #d32f2f; +} + +.message-box { img { - padding: 0 16px 0 8px !important; - margin-top: -1.75rem; + padding: 0 16px 0 8px; } div { font-size: 0.875rem; } - border: 2px solid #b71c1c; border-radius: 4px; } +.error-message { + border: 2px solid #b71c1c; +} + .warning-message { - img { - padding: 0 16px 0 8px !important; - margin-top: -3rem; - } - div { - font-size: 0.875rem; - } border: 2px solid #ff9c00; - border-radius: 4px; } .radio { diff --git a/ui/src/app/home/consortium/add-consortium-member.component.spec.ts b/ui/src/app/home/consortium/add-consortium-member.component.spec.ts index 4ea15b4fe..0173ae11b 100644 --- a/ui/src/app/home/consortium/add-consortium-member.component.spec.ts +++ b/ui/src/app/home/consortium/add-consortium-member.component.spec.ts @@ -71,6 +71,28 @@ describe('AddConsortiumMemberComponent', () => { expect(component).toBeTruthy() }) + it('should be a valid form when all required fields are filled', () => { + component.editForm.controls['orgName'].setValue('Orcid') + component.editForm.controls['startMonth'].setValue('01') + component.editForm.controls['startYear'].setValue('2025') + component.editForm.controls['trademarkLicense'].setValue('Yes') + component.editForm.controls['organizationTier'].setValue('Small') + fixture.detectChanges() + + expect(component.editForm.valid).toBeTrue() + }) + + it('should be a invalid form when a required field is missing', () => { + component.editForm.controls['orgName'].setValue('Orcid') + component.editForm.controls['startMonth'].setValue('01') + component.editForm.controls['startYear'].setValue('2025') + component.editForm.controls['trademarkLicense'].setValue('') + component.editForm.controls['organizationTier'].setValue('Small') + fixture.detectChanges() + + expect(component.editForm.invalid).toBeTrue() + }) + it('should call memberService.addConsortiumMember when saving', () => { memberServiceSpy.addConsortiumMember.and.returnValue(of(true)) diff --git a/ui/src/app/home/consortium/add-consortium-member.component.ts b/ui/src/app/home/consortium/add-consortium-member.component.ts index 6205a20f1..5d865a57f 100644 --- a/ui/src/app/home/consortium/add-consortium-member.component.ts +++ b/ui/src/app/home/consortium/add-consortium-member.component.ts @@ -1,16 +1,16 @@ import { Component, OnInit } from '@angular/core' -import { FormGroup, FormBuilder, Validators } from '@angular/forms' -import { Router, ActivatedRoute } from '@angular/router' +import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms' +import { ActivatedRoute, Router } from '@angular/router' import { combineLatest, take } from 'rxjs' -import { AccountService } from '../../account' +import { AccountService } from 'src/app/account' import { AlertType, EMAIL_REGEXP } from '../../app.constants' -import { AlertService } from '../../shared/service/alert.service' -import { DateUtilService } from '../../shared/service/date-util.service' import { ISFCountry } from '../../member/model/salesforce-country.model' import { ISFState } from '../../member/model/salesforce-country.model copy' import { ISFMemberData } from '../../member/model/salesforce-member-data.model' -import { ISFNewConsortiumMember } from '../../member/model/salesforce-new-consortium-member.model' +import { ISFNewConsortiumMember, OrganizationTierOption, TrademarkLicenseOption } from '../../member/model/salesforce-new-consortium-member.model' import { MemberService } from '../../member/service/member.service' +import { AlertService } from '../../shared/service/alert.service' +import { DateUtilService } from '../../shared/service/date-util.service' @Component({ selector: 'app-add-consortium-member', @@ -34,15 +34,17 @@ export class AddConsortiumMemberComponent implements OnInit { street: [null, [Validators.maxLength(255)]], city: [null, [Validators.maxLength(40)]], state: [null, [Validators.maxLength(80)]], - country: [null, [Validators.required]], + country: [null], postcode: [null, [Validators.maxLength(20)]], trademarkLicense: [null, [Validators.required]], startMonth: [null, [Validators.required]], startYear: [null, [Validators.required]], - contactGivenName: [null, [Validators.required, Validators.maxLength(40)]], - contactFamilyName: [null, [Validators.required, Validators.maxLength(80)]], + contactGivenName: [null, [Validators.maxLength(40)]], + contactFamilyName: [null, [Validators.maxLength(80)]], contactJobTitle: [null, [Validators.maxLength(128)]], - contactEmail: [null, [Validators.required, Validators.pattern(EMAIL_REGEXP), Validators.maxLength(80)]], + contactEmail: [null, [Validators.pattern(EMAIL_REGEXP), Validators.maxLength(80)]], + organizationTier: [null, [Validators.required]], + integrationPlans: [null, [Validators.maxLength(1000)]], }) rolesData = [ @@ -56,6 +58,33 @@ export class AddConsortiumMemberComponent implements OnInit { { id: 8, selected: false, name: 'Other contact' }, ] + + trademarkLicenseOptions: TrademarkLicenseOption[] = [ + { + value: 'Yes', + description: `ORCID can use this organization's trademarked name and logos`, + }, + { + value: 'No', + description: `ORCID cannot use this organization's trademarked name and logos`, + }, + ]; + + organizationTiers: OrganizationTierOption[] = [ + { + value: 'Small', + description: `Legal entity's annual operating budget below 10 M USD`, + }, + { + value: 'Standard', + description: `Legal entity's annual operating budget between 10 M and 1 B USD`, + }, + { + value: 'Large', + description: `Legal entity's annual operating budget above 1 B USD`, + }, + ]; + constructor( private memberService: MemberService, private fb: FormBuilder, @@ -64,7 +93,7 @@ export class AddConsortiumMemberComponent implements OnInit { private dateUtilService: DateUtilService, private accountService: AccountService, protected activatedRoute: ActivatedRoute - ) {} + ) { } ngOnInit() { this.currentMonth = this.dateUtilService.getCurrentMonthNumber() @@ -90,27 +119,38 @@ export class AddConsortiumMemberComponent implements OnInit { }) } + getControl(name: string): AbstractControl { + return this.editForm.get(name)!; + } + + getFormValue(controlName: string): string { + return this.getControl(controlName)?.value; + } + + trackByValue(_idx: number, tier: OrganizationTierOption | TrademarkLicenseOption): string { + return tier.value; + } + createNewConsortiumMemberFromForm(): ISFNewConsortiumMember { - const consortiumMember: ISFNewConsortiumMember = { - orgName: this.editForm!.get('orgName')?.value, - trademarkLicense: this.editForm!.get('trademarkLicense')?.value, - startMonth: this.editForm!.get('startMonth')?.value, - startYear: this.editForm!.get('startYear')?.value, - emailDomain: this.editForm!.get('emailDomain')?.value, - street: this.editForm!.get('street')?.value, - city: this.editForm!.get('city')?.value, - state: - this.editForm!.get(['state'])?.value == '-- No state or province --' - ? null - : this.editForm!.get(['state'])?.value, - country: this.editForm!.get('country')?.value, - postcode: this.editForm!.get('postcode')?.value, - contactGivenName: this.editForm!.get('contactGivenName')?.value, - contactFamilyName: this.editForm!.get('contactFamilyName')?.value, - contactJobTitle: this.editForm!.get('contactJobTitle')?.value, - contactEmail: this.editForm!.get('contactEmail')?.value, + const stateValue = this.getFormValue('state'); + return { + orgName: this.getFormValue('orgName'), + trademarkLicense: this.getFormValue('trademarkLicense'), + startMonth: this.getFormValue('startMonth'), + startYear: this.getFormValue('startYear'), + emailDomain: this.getFormValue('emailDomain'), + street: this.getFormValue('street'), + city: this.getFormValue('city'), + state: stateValue === '-- No state or province --' ? undefined : stateValue, + country: this.getFormValue('country'), + postcode: this.getFormValue('postcode'), + contactGivenName: this.getFormValue('contactGivenName'), + contactFamilyName: this.getFormValue('contactFamilyName'), + contactJobTitle: this.getFormValue('contactJobTitle'), + contactEmail: this.getFormValue('contactEmail'), + organizationTier: this.getFormValue('organizationTier'), + integrationPlans: this.getFormValue('integrationPlans'), } - return consortiumMember } onCountryChange(countryName: string) { diff --git a/ui/src/app/member/model/salesforce-new-consortium-member.model.ts b/ui/src/app/member/model/salesforce-new-consortium-member.model.ts index 23e99620f..6adee98ed 100644 --- a/ui/src/app/member/model/salesforce-new-consortium-member.model.ts +++ b/ui/src/app/member/model/salesforce-new-consortium-member.model.ts @@ -14,6 +14,8 @@ export interface ISFNewConsortiumMember { contactJobTitle?: string contactEmail?: string contactPhone?: string + organizationTier?: string + integrationPlans?: string } export class SFNewConsortiumMember implements ISFNewConsortiumMember { @@ -35,3 +37,14 @@ export class SFNewConsortiumMember implements ISFNewConsortiumMember { public contactPhone?: string ) {} } + +export interface Option { + value: T; + description: string; +} + +export type TrademarkLicenseValue = 'Yes' | 'No'; +export type OrganizationTierValue = 'Small' | 'Standard' | 'Large'; + +export type TrademarkLicenseOption = Option; +export type OrganizationTierOption = Option;