Skip to content

Commit

Permalink
Created Data Sharing Terms component and integrated into create flow (#…
Browse files Browse the repository at this point in the history
…1968)

Co-authored-by: Gino Miceli <[email protected]>
  • Loading branch information
nwkotto and gino-m authored Aug 15, 2024
1 parent 39471eb commit c545e5d
Show file tree
Hide file tree
Showing 11 changed files with 401 additions and 7 deletions.
12 changes: 12 additions & 0 deletions web/src/app/pages/create-survey/create-survey.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@
(onValidationChange)="onValidationChange($event)"
></task-details>
</ground-step-card>
<ground-step-card
*ngSwitchCase="SetupPhase.DEFINE_DATA_SHARING_TERMS"
title="Data sharing consent"
description="Choose how data collectors agree to data sharing terms before collecting data"
>
<data-sharing-terms
#dataSharingTerms
[type]="survey.dataSharingTerms.type"
[customText]="survey.dataSharingTerms.customText ?? ''"
(onValidationChange)="onValidationChange($event)"
></data-sharing-terms>
</ground-step-card>
<survey-review
*ngSwitchCase="SetupPhase.REVIEW"
#surveyReview
Expand Down
4 changes: 2 additions & 2 deletions web/src/app/pages/create-survey/create-survey.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
.picture {
height: 377px;
width: 277px;
border-top-left-radius: 18px;
border-bottom-left-radius: 18px;
border-top-left-radius: 18px;
border-bottom-left-radius: 18px;
}

.create-survey-content {
Expand Down
39 changes: 36 additions & 3 deletions web/src/app/pages/create-survey/create-survey.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
CreateSurveyComponent,
SetupPhase,
} from 'app/pages/create-survey/create-survey.component';
import {DataSharingTermsComponent} from 'app/pages/create-survey/data-sharing-terms/data-sharing-terms.component';
import {JobDetailsComponent} from 'app/pages/create-survey/job-details/job-details.component';
import {SurveyDetailsComponent} from 'app/pages/create-survey/survey-details/survey-details.component';
import {JobService} from 'app/services/job/job.service';
Expand Down Expand Up @@ -92,7 +93,7 @@ describe('CreateSurveyComponent', () => {
job001: job,
}),
/* acl= */ Map(),
{type: DataSharingType.PRIVATE}
{type: DataSharingType.CUSTOM, customText: 'Good day, sir'}
);
const jobWithTask = new Job(
jobId,
Expand Down Expand Up @@ -141,12 +142,16 @@ describe('CreateSurveyComponent', () => {
'updateTitleAndDescription',
'createSurvey',
'getActiveSurvey',
'updateDataSharingTerms',
]);
surveyServiceSpy.createSurvey.and.returnValue(
new Promise(resolve => resolve(newSurveyId))
);
activeSurvey$ = new Subject<Survey>();
surveyServiceSpy.getActiveSurvey$.and.returnValue(activeSurvey$);
surveyServiceSpy.updateDataSharingTerms.and.returnValue(
new Promise(resolve => resolve(undefined))
);

jobServiceSpy = jasmine.createSpyObj<JobService>('JobService', [
'addOrUpdateJob',
Expand Down Expand Up @@ -178,6 +183,7 @@ describe('CreateSurveyComponent', () => {
CreateSurveyComponent,
SurveyDetailsComponent,
JobDetailsComponent,
DataSharingTermsComponent,
SurveyReviewComponent,
],
providers: [
Expand Down Expand Up @@ -437,6 +443,33 @@ describe('CreateSurveyComponent', () => {
});
});

describe('Data Sharing Terms', () => {
beforeEach(fakeAsync(() => {
surveyId$.next(surveyId);
activeSurvey$.next(surveyWithJob);
tick();
// Forcibly set phase to DEFINE_DATA_SHARING_TERMS
component.setupPhase = SetupPhase.DEFINE_DATA_SHARING_TERMS;
fixture.detectChanges();
}));

it('updates data sharing agreement after clicking continue', () => {
clickContinueButton(fixture);

expect(surveyServiceSpy.updateDataSharingTerms).toHaveBeenCalledOnceWith(
surveyId,
DataSharingType.CUSTOM,
'Good day, sir'
);
});

it('goes back to task definition component after back button is clicked', () => {
clickBackButton(fixture);

expect(component.setupPhase).toBe(SetupPhase.DEFINE_TASKS);
});
});

describe('Review', () => {
beforeEach(fakeAsync(() => {
surveyId$.next(surveyId);
Expand All @@ -447,10 +480,10 @@ describe('CreateSurveyComponent', () => {
fixture.detectChanges();
}));

it('goes back to task definition component after back button is clicked', () => {
it('goes back to data sharing component after back button is clicked', () => {
clickBackButton(fixture);

expect(component.setupPhase).toBe(SetupPhase.DEFINE_TASKS);
expect(component.setupPhase).toBe(SetupPhase.DEFINE_DATA_SHARING_TERMS);
});
});
});
Expand Down
29 changes: 28 additions & 1 deletion web/src/app/pages/create-survey/create-survey.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {filter, first, firstValueFrom} from 'rxjs';
import {Job} from 'app/models/job.model';
import {LocationOfInterest} from 'app/models/loi.model';
import {Survey} from 'app/models/survey.model';
import {DataSharingTermsComponent} from 'app/pages/create-survey/data-sharing-terms/data-sharing-terms.component';
import {JobDetailsComponent} from 'app/pages/create-survey/job-details/job-details.component';
import {SurveyDetailsComponent} from 'app/pages/create-survey/survey-details/survey-details.component';
import {TaskDetailsComponent} from 'app/pages/create-survey/task-details/task-details.component';
Expand Down Expand Up @@ -107,6 +108,9 @@ export class CreateSurveyComponent implements OnInit {
survey: Survey,
lois: Immutable.List<LocationOfInterest>
): SetupPhase {
if (this.hasTask(survey)) {
return SetupPhase.DEFINE_DATA_SHARING_TERMS;
}
if (!lois.isEmpty()) {
return SetupPhase.DEFINE_TASKS;
}
Expand Down Expand Up @@ -136,6 +140,7 @@ export class CreateSurveyComponent implements OnInit {
[SetupPhase.JOB_DETAILS, 'Add a job'],
[SetupPhase.DEFINE_LOIS, 'Data collection strategy'],
[SetupPhase.DEFINE_TASKS, 'Define data collection tasks'],
[SetupPhase.DEFINE_DATA_SHARING_TERMS, 'Define data sharing terms'],
[SetupPhase.REVIEW, 'Review and share survey'],
]);

Expand Down Expand Up @@ -179,9 +184,12 @@ export class CreateSurveyComponent implements OnInit {
this.canContinue = true;
this.setupPhase = SetupPhase.DEFINE_LOIS;
break;
case SetupPhase.REVIEW:
case SetupPhase.DEFINE_DATA_SHARING_TERMS:
this.setupPhase = SetupPhase.DEFINE_TASKS;
break;
case SetupPhase.REVIEW:
this.setupPhase = SetupPhase.DEFINE_DATA_SHARING_TERMS;
break;
default:
break;
}
Expand All @@ -207,6 +215,10 @@ export class CreateSurveyComponent implements OnInit {
break;
case SetupPhase.DEFINE_TASKS:
await this.saveTasks();
this.setupPhase = SetupPhase.DEFINE_DATA_SHARING_TERMS;
break;
case SetupPhase.DEFINE_DATA_SHARING_TERMS:
await this.saveDataSharingTerms();
this.setupPhase = SetupPhase.REVIEW;
break;
case SetupPhase.REVIEW:
Expand Down Expand Up @@ -273,17 +285,32 @@ export class CreateSurveyComponent implements OnInit {
);
}

private async saveDataSharingTerms() {
const type = this.dataSharingTerms?.formGroup.controls.type.value;
const customText =
this.dataSharingTerms?.formGroup.controls.customText.value ?? undefined;
await this.surveyService.updateDataSharingTerms(
this.survey!.id,
type,
customText
);
}

@ViewChild('surveyLoi')
surveyLoi?: SurveyLoiComponent;

@ViewChild('taskDetails')
taskDetails?: TaskDetailsComponent;

@ViewChild('dataSharingTerms')
dataSharingTerms?: DataSharingTermsComponent;
}

export enum SetupPhase {
SURVEY_DETAILS,
JOB_DETAILS,
DEFINE_LOIS,
DEFINE_TASKS,
DEFINE_DATA_SHARING_TERMS,
REVIEW,
}
2 changes: 2 additions & 0 deletions web/src/app/pages/create-survey/create-survey.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';

import {HeaderModule} from 'app/components/header/header.module';
import {CreateSurveyComponent} from 'app/pages/create-survey/create-survey.component';
import {DataSharingTermsModule} from 'app/pages/create-survey/data-sharing-terms/data-sharing-terms.module';
import {TaskDetailsModule} from 'app/pages/create-survey/task-details/task-details.module';

import {JobDetailsModule} from './job-details/job-details.module';
Expand All @@ -34,6 +35,7 @@ import {SurveyReviewModule} from './survey-review/survey-review.module';
@NgModule({
declarations: [CreateSurveyComponent],
imports: [
DataSharingTermsModule,
JobDetailsModule,
TaskDetailsModule,
SurveyDetailsModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!--
Copyright 2024 The Ground Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<form [formGroup]="formGroup">
<mat-radio-group class="data-sharing-terms"
[id]="typeControlKey"
[formControlName]="typeControlKey"
>
<mat-card *ngFor="let option of dataSharingTermsOptions"
class="data-sharing-terms-card"
>
<mat-card-content>
<mat-radio-button class="option-radio-button" [value]="option.value">
<span class="data-sharing-label">{{ option.label }}</span>
<p>{{ option.description }}</p>
</mat-radio-button>
<div *ngIf="shouldShowCustomizeAgreementSection(option.value)">
<h3 id="customize-agreement-header"
class="field-header">
Customize agreement
</h3>
<p class="field-description">
Create a custom agreement which will be shown to data collectors before they can
collect data. Data collectors must agree to the terms of this agreement.
</p>
<mat-form-field class="custom-terms">
<textarea matInput
formControlName="customText"
placeholder="Enter the terms of your custom agreement..."
aria-labelledby="customize-agreement-header">
</textarea>
<mat-error *ngIf="customTextControl.touched && customTextControl.invalid">
<ng-container *ngIf="customTextControl.getError('required')">
Required
</ng-container>
</mat-error>
</mat-form-field>
</div>
</mat-card-content>
</mat-card>
</mat-radio-group>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2024 The Ground Authors.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

.data-sharing-terms {
display: flex;
flex-direction: column;
gap: 12px;

.data-sharing-terms-card {
color: #202124;
font-size: 14px;
font-weight: 500;

.data-sharing-label {
font-size: 16px;
font-weight: 700;
}
}

.custom-terms {
display: block;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright 2024 The Ground Authors.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {CommonModule} from '@angular/common';
import {ComponentFixture, TestBed} from '@angular/core/testing';

import {DataSharingType} from 'app/models/survey.model';
import {DataSharingTermsComponent} from 'app/pages/create-survey/data-sharing-terms/data-sharing-terms.component';

describe('DataSharingTermsComponent', () => {
let component: DataSharingTermsComponent;
let fixture: ComponentFixture<DataSharingTermsComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [DataSharingTermsComponent],
imports: [CommonModule],
}).compileComponents();

fixture = TestBed.createComponent(DataSharingTermsComponent);
fixture.componentInstance.type = DataSharingType.PUBLIC;
fixture.componentInstance.customText = 'Hey there here is an agreement';
component = fixture.componentInstance;
fixture.detectChanges();
});

it('displays mat-cards for each of the three data sharing types', () => {
const matCards: HTMLElement[] =
fixture.debugElement.nativeElement.querySelectorAll(
'.data-sharing-terms-card'
);
const contentValues = Array.from(matCards).map((card: HTMLElement) => ({
label: card
.querySelector('.option-radio-button .data-sharing-label')!
.textContent!.trim(),
description: card
.querySelector('.option-radio-button p')!
.textContent!.trim(),
}));
expect(contentValues).toEqual([
{
label: 'Private',
description: 'Data will be shared with survey organizers only',
},
{
label: 'Public',
description:
'Survey organizers may share and use data publicly with no constraints',
},
{
label: 'Custom agreement',
description:
'Survey organizers create terms which must be accepted by data collectors before collecting data',
},
]);
});
});
Loading

0 comments on commit c545e5d

Please sign in to comment.