diff --git a/ui/src/app/home/consortium/add-consortium-member.component.html b/ui/src/app/home/consortium/add-consortium-member.component.html
new file mode 100644
index 000000000..b614b60f8
--- /dev/null
+++ b/ui/src/app/home/consortium/add-consortium-member.component.html
@@ -0,0 +1,507 @@
+
+
+
+
Add new consortium member
+
New organization
+
+
+
\ No newline at end of file
diff --git a/ui/src/app/home/consortium/add-consortium-member.component.scss b/ui/src/app/home/consortium/add-consortium-member.component.scss
new file mode 100644
index 000000000..768ceb164
--- /dev/null
+++ b/ui/src/app/home/consortium/add-consortium-member.component.scss
@@ -0,0 +1,63 @@
+@use '../../../content/scss/bootstrap-variables' as global;
+
+input {
+ font-size: 14px;
+ letter-spacing: 0.25px;
+}
+
+input,
+select,
+option {
+ height: 40px;
+}
+
+.ng-invalid:not(form) {
+ border: 1px solid #d32f2f;
+ border-radius: 2px;
+}
+
+label {
+ margin-bottom: 0.25rem;
+}
+
+.error-message {
+ img {
+ padding: 0 16px 0 8px !important;
+ margin-top: -1.75rem;
+ }
+ div {
+ font-size: 0.875rem;
+ }
+ border: 2px solid #b71c1c;
+ border-radius: 4px;
+}
+
+.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 {
+ height: 1.125rem;
+ width: 1.125rem;
+ accent-color: global.$info;
+}
+
+.date-dropdown {
+ width: 7.5rem;
+}
+
+.postcode-input-field {
+ max-width: 180px;
+}
+
+.org-name-input-field {
+ max-width: 400px;
+}
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
new file mode 100644
index 000000000..a4f4bdfa0
--- /dev/null
+++ b/ui/src/app/home/consortium/add-consortium-member.component.spec.ts
@@ -0,0 +1,94 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+
+import { AddConsortiumMemberComponent } from './add-consortium-member.component'
+import { MemberService } from 'src/app/member/service/member.service'
+import { AccountService } from 'src/app/account'
+import { AlertService } from 'src/app/shared/service/alert.service'
+import { ActivatedRoute, Router } from '@angular/router'
+import { HttpClientTestingModule } from '@angular/common/http/testing'
+import { RouterTestingModule } from '@angular/router/testing'
+import { of } from 'rxjs'
+import { AlertType } from 'src/app/app.constants'
+
+describe('AddConsortiumMemberComponent', () => {
+ let component: AddConsortiumMemberComponent
+ let fixture: ComponentFixture
+
+ let memberServiceSpy: jasmine.SpyObj
+ let accountServiceSpy: jasmine.SpyObj
+ let alertServiceSpy: jasmine.SpyObj
+ let activatedRoute: jasmine.SpyObj
+ let router: jasmine.SpyObj
+
+ beforeEach(() => {
+ memberServiceSpy = jasmine.createSpyObj('MemberService', ['addConsortiumMember'])
+ accountServiceSpy = jasmine.createSpyObj('AccountService', ['getAccountData'])
+ alertServiceSpy = jasmine.createSpyObj('AlertService', ['broadcast'])
+
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule, RouterTestingModule],
+ declarations: [AddConsortiumMemberComponent],
+ providers: [
+ { provide: MemberService, useValue: memberServiceSpy },
+ { provide: AccountService, useValue: accountServiceSpy },
+ { provide: AlertService, useValue: alertServiceSpy },
+ ],
+ })
+
+ accountServiceSpy = TestBed.inject(AccountService) as jasmine.SpyObj
+ memberServiceSpy = TestBed.inject(MemberService) as jasmine.SpyObj
+ alertServiceSpy = TestBed.inject(AlertService) as jasmine.SpyObj
+ router = TestBed.inject(Router) as jasmine.SpyObj
+ activatedRoute = TestBed.inject(ActivatedRoute) as jasmine.SpyObj
+
+ fixture = TestBed.createComponent(AddConsortiumMemberComponent)
+ component = fixture.componentInstance
+
+ spyOn(router, 'navigate').and.returnValue(Promise.resolve(true))
+
+ accountServiceSpy.getAccountData.and.returnValue(
+ of({
+ activated: true,
+ authorities: ['test', 'test'],
+ email: 'email@email.com',
+ firstName: 'name',
+ langKey: 'en',
+ lastName: 'surname',
+ imageUrl: 'url',
+ salesforceId: 'sfid',
+ loggedAs: false,
+ loginAs: 'sfid',
+ mainContact: false,
+ mfaEnabled: true,
+ })
+ )
+
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('should call memberService.updateContact when saving', () => {
+ memberServiceSpy.addConsortiumMember.and.returnValue(of(true))
+
+ // set form to valid
+ for (const control in component.editForm.controls) {
+ component.editForm.controls[control].clearAsyncValidators()
+ component.editForm.controls[control].clearValidators()
+ component.editForm.controls[control].updateValueAndValidity({ onlySelf: true })
+ }
+ component.editForm.updateValueAndValidity()
+
+ component.save()
+
+ expect(memberServiceSpy.addConsortiumMember).toHaveBeenCalled()
+ })
+
+ it('alert service and router should be called on save success', () => {
+ component.onSaveSuccess()
+ expect(alertServiceSpy.broadcast).toHaveBeenCalledWith(AlertType.CONSORTIUM_MEMBER_ADDED)
+ expect(router.navigate).toHaveBeenCalled()
+ })
+})
diff --git a/ui/src/app/home/consortium/add-consortium-member.component.ts b/ui/src/app/home/consortium/add-consortium-member.component.ts
new file mode 100644
index 000000000..d64f19731
--- /dev/null
+++ b/ui/src/app/home/consortium/add-consortium-member.component.ts
@@ -0,0 +1,156 @@
+import { Component, OnInit } from '@angular/core'
+import { FormGroup, FormBuilder, Validators } from '@angular/forms'
+import { Router, ActivatedRoute } from '@angular/router'
+import { combineLatest, take } from 'rxjs'
+import { AccountService } from '../../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 { MemberService } from '../../member/service/member.service'
+
+@Component({
+ selector: 'app-add-consortium-member',
+ templateUrl: './add-consortium-member.component.html',
+ styleUrls: ['./add-consortium-member.component.scss'],
+})
+export class AddConsortiumMemberComponent implements OnInit {
+ countries: ISFCountry[] | undefined
+ states: ISFState[] | undefined
+ memberData: ISFMemberData | undefined | null
+ isSaving = false
+ invalidForm = false
+ routeData: any
+ currentMonth: number | undefined
+ currentYear: number | undefined
+ monthList: [number, string][] | undefined
+ yearList: number[] | undefined
+ editForm: FormGroup = this.fb.group({
+ orgName: [null, [Validators.required, Validators.maxLength(41)]],
+ emailDomain: [null, [Validators.maxLength(255)]],
+ street: [null, [Validators.maxLength(255)]],
+ city: [null, [Validators.maxLength(40)]],
+ state: [null, [Validators.maxLength(80)]],
+ country: [null, [Validators.required]],
+ 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)]],
+ contactJobTitle: [null, [Validators.maxLength(128)]],
+ contactEmail: [null, [Validators.required, Validators.pattern(EMAIL_REGEXP), Validators.maxLength(80)]],
+ })
+
+ rolesData = [
+ { id: 1, selected: false, name: 'Main relationship contact' },
+ { id: 2, selected: false, name: 'Voting contact' },
+ { id: 3, selected: false, name: 'Technical contact' },
+ { id: 4, selected: false, name: 'Invoice contact' },
+ { id: 5, selected: false, name: 'Comms contact' },
+ { id: 6, selected: false, name: 'Product contact' },
+ ]
+
+ constructor(
+ private memberService: MemberService,
+ private fb: FormBuilder,
+ private alertService: AlertService,
+ private router: Router,
+ private dateUtilService: DateUtilService,
+ private accountService: AccountService,
+ protected activatedRoute: ActivatedRoute
+ ) {}
+
+ ngOnInit() {
+ this.currentMonth = this.dateUtilService.getCurrentMonthNumber()
+ this.currentYear = this.dateUtilService.getCurrentYear()
+ this.monthList = this.dateUtilService.getMonthsList()
+ this.yearList = this.dateUtilService.getFutureYearsIncludingCurrent(1)
+
+ this.accountService.getAccountData().subscribe((account) => {
+ if (account) {
+ combineLatest([this.memberService.getMemberData(account.salesforceId), this.memberService.getCountries()])
+ .pipe(take(1))
+ .subscribe(([data, countries]) => {
+ this.memberData = data
+ this.countries = countries
+ })
+ }
+ })
+
+ this.editForm.valueChanges.subscribe(() => {
+ if (this.editForm!.status === 'VALID') {
+ this.invalidForm = false
+ }
+ })
+ }
+
+ 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,
+ }
+ return consortiumMember
+ }
+
+ onCountryChange(countryName: string) {
+ this.states = this.countries!.find((country) => country.name === countryName)?.states
+ }
+
+ save() {
+ if (this.editForm!.status === 'INVALID') {
+ Object.keys(this.editForm!.controls).forEach((key) => {
+ this.editForm!.get(key)?.markAsDirty()
+ })
+ this.editForm!.markAllAsTouched()
+ this.invalidForm = true
+ } else {
+ this.invalidForm = false
+ this.isSaving = true
+ const newConsortiumMember = this.createNewConsortiumMemberFromForm()
+
+ this.memberService.addConsortiumMember(newConsortiumMember).subscribe(
+ (res) => {
+ if (res) {
+ this.onSaveSuccess()
+ } else {
+ console.error(res)
+ this.onSaveError()
+ }
+ },
+ (err) => {
+ console.error(err)
+ this.onSaveError()
+ }
+ )
+ }
+ }
+
+ onSaveSuccess() {
+ this.isSaving = false
+ this.alertService.broadcast(AlertType.CONSORTIUM_MEMBER_ADDED)
+ this.router.navigate([''])
+ }
+
+ onSaveError() {
+ this.isSaving = false
+ }
+}
diff --git a/ui/src/app/home/contact/contact-update.component.html b/ui/src/app/home/contact/contact-update.component.html
new file mode 100644
index 000000000..05935d26e
--- /dev/null
+++ b/ui/src/app/home/contact/contact-update.component.html
@@ -0,0 +1,163 @@
+
+
+
+
Edit contact
+
{{ contact.name }}
+
Add new contact
+
New contact
+
+
diff --git a/ui/src/app/home/contact/contact-update.component.scss b/ui/src/app/home/contact/contact-update.component.scss
new file mode 100644
index 000000000..1995b8700
--- /dev/null
+++ b/ui/src/app/home/contact/contact-update.component.scss
@@ -0,0 +1,50 @@
+@use '../../../content/scss/bootstrap-variables' as global;
+
+.input-prompt {
+ color: global.$black-transparent;
+}
+
+input {
+ font-size: 14px;
+ letter-spacing: 0.25px;
+}
+
+.ng-invalid:not(form) {
+ border: 1px solid #d32f2f;
+ border-radius: 2px;
+}
+
+.error-message {
+ img {
+ padding: 0 16px 0 8px !important;
+ margin-top: -1.75rem;
+ }
+ div {
+ font-size: 0.875rem;
+ }
+ border: 2px solid #b71c1c;
+ border-radius: 4px;
+}
+
+.warning-message {
+ img {
+ padding: 0 16px 0 8px !important;
+ margin-top: -3rem;
+ }
+ div {
+ font-size: 0.875rem;
+ }
+ border: 2px solid #ff9c00;
+ border-radius: 4px;
+}
+
+.role-checkbox {
+ height: 1.125rem;
+ width: 1.125rem;
+ margin-right: 0.5rem;
+ vertical-align: middle;
+}
+
+input {
+ height: 40px;
+}
diff --git a/ui/src/app/home/contact/contact-update.component.spec.ts b/ui/src/app/home/contact/contact-update.component.spec.ts
new file mode 100644
index 000000000..e51a6f73d
--- /dev/null
+++ b/ui/src/app/home/contact/contact-update.component.spec.ts
@@ -0,0 +1,168 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+
+import { ContactUpdateComponent } from './contact-update.component'
+import { AppModule } from '../../app.module'
+import { MemberService } from 'src/app/member/service/member.service'
+import { AccountService } from 'src/app/account'
+import { HttpClientTestingModule } from '@angular/common/http/testing'
+import { ActivatedRoute, Router } from '@angular/router'
+import { RouterTestingModule } from '@angular/router/testing'
+import { of } from 'rxjs'
+import { SFMemberData } from 'src/app/member/model/salesforce-member-data.model'
+import { AlertService } from 'src/app/shared/service/alert.service'
+import { AlertType } from 'src/app/app.constants'
+
+describe('ContactUpdateComponent', () => {
+ let component: ContactUpdateComponent
+ let fixture: ComponentFixture
+ let memberServiceSpy: jasmine.SpyObj
+ let accountServiceSpy: jasmine.SpyObj
+ let alertServiceSpy: jasmine.SpyObj
+ let activatedRoute: jasmine.SpyObj
+ let router: jasmine.SpyObj
+
+ beforeEach(() => {
+ memberServiceSpy = jasmine.createSpyObj('MemberService', ['find', 'getMemberData', 'updateContact'])
+ accountServiceSpy = jasmine.createSpyObj('AccountService', ['getAccountData'])
+ alertServiceSpy = jasmine.createSpyObj('AlertService', ['broadcast'])
+
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule, RouterTestingModule],
+ declarations: [ContactUpdateComponent],
+ providers: [
+ { provide: MemberService, useValue: memberServiceSpy },
+ { provide: AccountService, useValue: accountServiceSpy },
+ { provide: AlertService, useValue: alertServiceSpy },
+ ],
+ })
+ accountServiceSpy = TestBed.inject(AccountService) as jasmine.SpyObj
+ memberServiceSpy = TestBed.inject(MemberService) as jasmine.SpyObj
+ alertServiceSpy = TestBed.inject(AlertService) as jasmine.SpyObj
+ router = TestBed.inject(Router) as jasmine.SpyObj
+ activatedRoute = TestBed.inject(ActivatedRoute) as jasmine.SpyObj
+
+ fixture = TestBed.createComponent(ContactUpdateComponent)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+
+ spyOn(router, 'navigate').and.returnValue(Promise.resolve(true))
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('should call getAccountData and getMemberData to get contact data on init', () => {
+ accountServiceSpy.getAccountData.and.returnValue(
+ of({
+ activated: true,
+ authorities: ['test', 'test'],
+ email: 'email@email.com',
+ firstName: 'name',
+ langKey: 'en',
+ lastName: 'surname',
+ imageUrl: 'url',
+ salesforceId: 'sfid',
+ loggedAs: false,
+ loginAs: 'sfid',
+ mainContact: false,
+ mfaEnabled: true,
+ })
+ )
+
+ memberServiceSpy.getMemberData.and.returnValue(
+ of({
+ id: 'some-id',
+ contacts: [
+ {
+ memberId: 'some-id',
+ votingContant: false,
+ memberOrgRole: ['role'],
+ name: 'contact 1',
+ contactEmail: 'contact1@orcid.org',
+ title: 'title',
+ phone: '0123456789',
+ },
+ ],
+ })
+ )
+
+ component.contactId = 'contact1@orcid.org'
+
+ component.ngOnInit()
+
+ expect(accountServiceSpy.getAccountData).toHaveBeenCalled()
+ expect(memberServiceSpy.getMemberData).toHaveBeenCalled()
+ expect(component.contact).toBeTruthy()
+ })
+
+ it('should call memberService.updateContact when saving', () => {
+ component.memberData = { name: 'member' }
+ component.contact = {
+ memberId: 'some-id',
+ votingContant: false,
+ memberOrgRole: ['role'],
+ name: 'contact 1',
+ contactEmail: 'contact1@orcid.org',
+ title: 'title',
+ phone: '0123456789',
+ }
+
+ memberServiceSpy.updateContact.and.returnValue(of(true))
+
+ // set form to valid
+ for (const control in component.editForm.controls) {
+ component.editForm.controls[control].clearAsyncValidators()
+ component.editForm.controls[control].clearValidators()
+ component.editForm.controls[control].updateValueAndValidity({ onlySelf: true })
+ }
+ component.editForm.updateValueAndValidity()
+
+ component.contactId = 'contact1@orcid.org'
+
+ component.save()
+
+ expect(memberServiceSpy.updateContact).toHaveBeenCalled()
+ })
+
+ it('should not call memberService.updateContact when saving if form is invalid', () => {
+ // form is invalid as we haven't set its values
+ component.save()
+ expect(memberServiceSpy.updateContact).toHaveBeenCalledTimes(0)
+ })
+
+ it('should call memberService.updateContact when deleting', () => {
+ component.memberData = { name: 'member' }
+ component.contact = {
+ memberId: 'some-id',
+ votingContant: false,
+ memberOrgRole: ['role'],
+ name: 'contact 1',
+ contactEmail: 'contact1@orcid.org',
+ title: 'title',
+ phone: '0123456789',
+ }
+
+ memberServiceSpy.updateContact.and.returnValue(of(true))
+
+ // set form to valid
+ for (const control in component.editForm.controls) {
+ component.editForm.controls[control].clearAsyncValidators()
+ component.editForm.controls[control].clearValidators()
+ component.editForm.controls[control].updateValueAndValidity({ onlySelf: true })
+ }
+ component.editForm.updateValueAndValidity()
+
+ component.contactId = 'contact1@orcid.org'
+
+ component.delete()
+
+ expect(memberServiceSpy.updateContact).toHaveBeenCalled()
+ })
+
+ it('alert service and router should be called on save success', () => {
+ component.onSaveSuccess()
+ expect(alertServiceSpy.broadcast).toHaveBeenCalledWith(AlertType.CONTACT_UPDATED)
+ expect(router.navigate).toHaveBeenCalled()
+ })
+})
diff --git a/ui/src/app/member/contact-update.component.ts b/ui/src/app/home/contact/contact-update.component.ts
similarity index 85%
rename from ui/src/app/member/contact-update.component.ts
rename to ui/src/app/home/contact/contact-update.component.ts
index 3942c4d17..d1409913d 100644
--- a/ui/src/app/member/contact-update.component.ts
+++ b/ui/src/app/home/contact/contact-update.component.ts
@@ -1,13 +1,13 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
-import { IUser } from '../user/model/user.model'
-import { ISFMemberData } from './model/salesforce-member-data.model'
+import { IUser } from '../../user/model/user.model'
+import { ISFMemberData } from '../../member/model/salesforce-member-data.model'
import {
ISFMemberContact,
ISFMemberContactUpdate,
SFMemberContact,
SFMemberContactRole,
SFMemberContactUpdate,
-} from './model/salesforce-member-contact.model'
+} from '../../member/model/salesforce-member-contact.model'
import {
AbstractControl,
FormArray,
@@ -17,13 +17,13 @@ import {
ValidatorFn,
Validators,
} from '@angular/forms'
-import { MemberService } from './service/member.service'
-import { AccountService } from '../account/service/account.service'
-import { AlertService } from '../shared/service/alert.service'
+import { MemberService } from '../../member/service/member.service'
+import { AccountService } from '../../account/service/account.service'
+import { AlertService } from '../../shared/service/alert.service'
import { ActivatedRoute, Router } from '@angular/router'
-import { AlertType, EMAIL_REGEXP } from '../app.constants'
+import { AlertType, EMAIL_REGEXP } from '../../app.constants'
import { EMPTY, Subject, combineLatest, switchMap, takeUntil } from 'rxjs'
-import { IAccount } from '../account/model/account.model'
+import { IAccount } from '../../account/model/account.model'
@Component({
selector: 'app-contact-update',
@@ -37,7 +37,6 @@ export class ContactUpdateComponent implements OnInit, OnDestroy {
isSaving = false
invalidForm = false
routeData: any
- editForm: FormGroup | undefined
contactId: string | undefined
managedMember: string | undefined
destroy$ = new Subject()
@@ -51,6 +50,28 @@ export class ContactUpdateComponent implements OnInit, OnDestroy {
new SFMemberContactRole(6, false, 'Product contact'),
]
+ validateContactRoles: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ const rolesArray = control as FormArray
+ const selectedRoles = rolesArray.controls.filter((control) => control.value.selected)
+ if (selectedRoles.length < 1) {
+ return { oneRoleSelected: true }
+ }
+ return null
+ }
+
+ editForm: FormGroup = this.fb.group({
+ name: [null, [Validators.required, Validators.maxLength(80)]],
+ phone: [null, [Validators.maxLength(40)]],
+ email: [null, [Validators.required, Validators.pattern(EMAIL_REGEXP), Validators.maxLength(80)]],
+ title: [null, [Validators.maxLength(128)]],
+ roles: this.fb.array(
+ this.rolesData.map((val: SFMemberContactRole) =>
+ this.fb.group({ id: val.id, selected: val.selected, name: val.name })
+ ),
+ [this.validateContactRoles]
+ ),
+ })
+
constructor(
private memberService: MemberService,
private accountService: AccountService,
@@ -70,18 +91,7 @@ export class ContactUpdateComponent implements OnInit, OnDestroy {
this.memberService.setManagedMember(params['id'])
}
})
- this.editForm = this.fb.group({
- name: [null, [Validators.required, Validators.maxLength(80)]],
- phone: [null, [Validators.maxLength(40)]],
- email: [null, [Validators.required, Validators.pattern(EMAIL_REGEXP), Validators.maxLength(80)]],
- title: [null, [Validators.maxLength(128)]],
- roles: this.fb.array(
- this.rolesData.map((val: SFMemberContactRole) =>
- this.fb.group({ id: val.id, selected: val.selected, name: val.name })
- ),
- [this.validateContactRoles]
- ),
- })
+
combineLatest([this.activatedRoute.params, this.accountService.getAccountData()])
.pipe(
switchMap(([params, account]) => {
@@ -122,15 +132,6 @@ export class ContactUpdateComponent implements OnInit, OnDestroy {
})
}
- validateContactRoles: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
- const rolesArray = control as FormArray
- const selectedRoles = rolesArray.controls.filter((control) => control.value.selected)
- if (selectedRoles.length < 1) {
- return { oneRoleSelected: true }
- }
- return null
- }
-
updateForm(contact: ISFMemberContact) {
this.editForm!.patchValue({
name: contact.name,
@@ -173,6 +174,8 @@ export class ContactUpdateComponent implements OnInit, OnDestroy {
})
this.editForm!.markAllAsTouched()
} else {
+ console.log('form valid')
+
this.invalidForm = false
this.isSaving = true
const contact = this.createContactFromForm()
diff --git a/ui/src/app/home/home.route.ts b/ui/src/app/home/home.route.ts
index 69278315b..7812febc3 100644
--- a/ui/src/app/home/home.route.ts
+++ b/ui/src/app/home/home.route.ts
@@ -2,10 +2,12 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, Route
import { HomeComponent } from '../home/home.component'
import { AuthGuard } from '../account/auth.guard'
import { MemberInfoComponent } from './member-info/member-info.component'
-import { Injectable, inject } from '@angular/core'
+import { inject } from '@angular/core'
import { MemberService } from '../member/service/member.service'
import { Observable, map } from 'rxjs'
import { MemberInfoEditComponent } from './member-info/member-info-edit.component'
+import { AddConsortiumMemberComponent } from './consortium/add-consortium-member.component'
+import { ContactUpdateComponent } from './contact/contact-update.component'
export const ManageMemberGuard = (route: ActivatedRouteSnapshot): Observable | boolean => {
const router = inject(Router)
@@ -82,6 +84,51 @@ export const routes: Routes = [
},
canActivate: [AuthGuard],
},
+ {
+ path: 'contact/new',
+ component: ContactUpdateComponent,
+ data: {
+ authorities: ['ROLE_USER'],
+ pageTitle: 'home.title.string',
+ },
+ canActivate: [AuthGuard, ManageMemberGuard],
+ },
+ {
+ path: 'manage/:id/contact/new',
+ component: ContactUpdateComponent,
+ data: {
+ authorities: ['ROLE_USER'],
+ pageTitle: 'home.title.string',
+ },
+ canActivate: [AuthGuard],
+ },
+ {
+ path: 'contact/:contactId/edit',
+ component: ContactUpdateComponent,
+ data: {
+ authorities: ['ROLE_USER'],
+ pageTitle: 'home.title.string',
+ },
+ canActivate: [AuthGuard, ManageMemberGuard],
+ },
+ {
+ path: 'manage/:id/contact/:contactId/edit',
+ component: ContactUpdateComponent,
+ data: {
+ authorities: ['ROLE_USER'],
+ pageTitle: 'home.title.string',
+ },
+ canActivate: [AuthGuard],
+ },
+ {
+ path: 'consortium-member/new',
+ component: AddConsortiumMemberComponent,
+ data: {
+ authorities: ['ROLE_USER', 'ROLE_CONSORTIUM_LEAD'],
+ pageTitle: 'home.title.string',
+ },
+ canActivate: [AuthGuard],
+ },
],
},
]
diff --git a/ui/src/app/member/contact-update.component.html b/ui/src/app/member/contact-update.component.html
deleted file mode 100644
index 26e257c06..000000000
--- a/ui/src/app/member/contact-update.component.html
+++ /dev/null
@@ -1 +0,0 @@
-contact-update works!
diff --git a/ui/src/app/member/contact-update.component.scss b/ui/src/app/member/contact-update.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ui/src/app/member/contact-update.component.spec.ts b/ui/src/app/member/contact-update.component.spec.ts
deleted file mode 100644
index 66fe8b231..000000000
--- a/ui/src/app/member/contact-update.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing'
-
-import { ContactUpdateComponent } from './contact-update.component'
-import { AppModule } from '../app.module'
-
-describe('ContactUpdateComponent', () => {
- let component: ContactUpdateComponent
- let fixture: ComponentFixture
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [AppModule],
- declarations: [ContactUpdateComponent],
- })
- fixture = TestBed.createComponent(ContactUpdateComponent)
- component = fixture.componentInstance
- fixture.detectChanges()
- })
-
- it('should create', () => {
- expect(component).toBeTruthy()
- })
-})
diff --git a/ui/src/app/member/member.module.ts b/ui/src/app/member/member.module.ts
index 5708326a6..62c037829 100644
--- a/ui/src/app/member/member.module.ts
+++ b/ui/src/app/member/member.module.ts
@@ -8,8 +8,9 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MembersComponent } from './members.component'
import { MemberUpdateComponent } from './member-update.component'
import { MemberDetailComponent } from './member-detail.component'
-import { MemberImportDialogComponent } from './member-import-dialog.component';
-import { ContactUpdateComponent } from './contact-update.component'
+import { MemberImportDialogComponent } from './member-import-dialog.component'
+import { ContactUpdateComponent } from '../home/contact/contact-update.component'
+import { AddConsortiumMemberComponent } from '../home/consortium/add-consortium-member.component'
@NgModule({
imports: [
@@ -20,6 +21,13 @@ import { ContactUpdateComponent } from './contact-update.component'
FormsModule,
ReactiveFormsModule,
],
- declarations: [MembersComponent, MemberUpdateComponent, MemberDetailComponent, MemberImportDialogComponent, ContactUpdateComponent],
+ declarations: [
+ MembersComponent,
+ MemberUpdateComponent,
+ MemberDetailComponent,
+ MemberImportDialogComponent,
+ ContactUpdateComponent,
+ AddConsortiumMemberComponent,
+ ],
})
export class MemberModule {}
diff --git a/ui/src/app/member/service/member.service.ts b/ui/src/app/member/service/member.service.ts
index 4ed41eec6..2a348b346 100644
--- a/ui/src/app/member/service/member.service.ts
+++ b/ui/src/app/member/service/member.service.ts
@@ -35,6 +35,7 @@ import {
} from '../model/salesforce-member-contact.model'
import { ISFRawMemberOrgIds, SFMemberOrgIds } from '../model/salesforce-member-org-id.model'
import { ISFMemberUpdate } from '../model/salesforce-member-update.model'
+import { ISFNewConsortiumMember } from '../model/salesforce-new-consortium-member.model'
@Injectable({ providedIn: 'root' })
export class MemberService {
@@ -155,6 +156,20 @@ export class MemberService {
this.memberData.next(memberData)
}
+ addConsortiumMember(consortiumMember: ISFNewConsortiumMember): Observable {
+ return this.http
+ .post(`${this.resourceUrl}/members/add-consortium-member`, consortiumMember, {
+ observe: 'response',
+ })
+ .pipe(
+ map((res: HttpResponse) => res.status === 200),
+ catchError((err) => {
+ console.log('error adding consortium member', err)
+ return of(false)
+ })
+ )
+ }
+
private fetchMemberData(salesforceId: string) {
this.fetchingMemberDataState = true
this.fetchSFMemberData(salesforceId)