Skip to content

Commit bc2596d

Browse files
authored
[Create/edit survey] Allow organizers to control whether data collectors can see each other's data (#2216)
1 parent 5905871 commit bc2596d

11 files changed

+188
-56
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!--
2+
Copyright 2025 The Ground Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
<div class="data-visibility-control-container">
18+
<mat-slide-toggle
19+
[checked]="selectedDataVisibility === SurveyDataVisibility.ALL_SURVEY_PARTICIPANTS"
20+
(change)="changeDataVisibility($event)"
21+
>
22+
</mat-slide-toggle>
23+
</div>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2025 The Ground Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the 'License');
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an 'AS IS' BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {Component} from '@angular/core';
18+
import {MatSlideToggleChange} from '@angular/material/slide-toggle';
19+
import {Subscription} from 'rxjs';
20+
21+
import {Survey, SurveyDataVisibility} from 'app/models/survey.model';
22+
import {AuthService} from 'app/services/auth/auth.service';
23+
import {DraftSurveyService} from 'app/services/draft-survey/draft-survey.service';
24+
25+
@Component({
26+
selector: 'ground-data-visibility-control',
27+
templateUrl: './data-visibility-control.component.html',
28+
})
29+
export class DataVisibilityControlComponent {
30+
private subscription = new Subscription();
31+
32+
selectedDataVisibility!: SurveyDataVisibility;
33+
34+
SurveyDataVisibility = SurveyDataVisibility;
35+
36+
constructor(
37+
readonly authService: AuthService,
38+
readonly draftSurveyService: DraftSurveyService
39+
) {
40+
this.subscription.add(
41+
this.draftSurveyService
42+
.getSurvey$()
43+
.subscribe(survey => this.onSurveyLoaded(survey))
44+
);
45+
}
46+
47+
private async onSurveyLoaded(survey: Survey): Promise<void> {
48+
this.selectedDataVisibility =
49+
survey.dataVisibility || SurveyDataVisibility.CONTRIBUTOR_AND_ORGANIZERS;
50+
}
51+
52+
changeDataVisibility(event: MatSlideToggleChange) {
53+
const dataVisibility = event.checked
54+
? SurveyDataVisibility.ALL_SURVEY_PARTICIPANTS
55+
: SurveyDataVisibility.CONTRIBUTOR_AND_ORGANIZERS;
56+
57+
this.selectedDataVisibility = dataVisibility;
58+
59+
this.draftSurveyService.updateDataVisibility(dataVisibility);
60+
}
61+
62+
ngOnDestroy() {
63+
this.subscription.unsubscribe();
64+
}
65+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright 2025 The Ground Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the 'License');
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an 'AS IS' BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {CommonModule} from '@angular/common';
18+
import {NgModule} from '@angular/core';
19+
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
20+
21+
import {DataVisibilityControlComponent} from './data-visibility-control.component';
22+
23+
@NgModule({
24+
declarations: [DataVisibilityControlComponent],
25+
imports: [CommonModule, MatSlideToggleModule],
26+
exports: [DataVisibilityControlComponent],
27+
})
28+
export class DataVisibilityControlModule {}

web/src/app/components/general-access-control/_general-access-control.component-theme.scss

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,33 @@
1818
@use '@angular/material' as mat;
1919

2020
@mixin color($theme) {
21-
.general-access-select {
22-
color: mat.get-theme-color($theme, neutral, 10);
23-
}
24-
25-
.general-access-description {
26-
color: mat.get-theme-color($theme, neutral-variant, 30);
27-
}
28-
29-
.general-access-icon {
30-
background-color: mat.get-theme-color($theme, neutral-variant, 90);
21+
.general-access-control-container {
22+
.general-access-select {
23+
color: mat.get-theme-color($theme, neutral, 10);
24+
}
25+
26+
.general-access-description {
27+
color: mat.get-theme-color($theme, neutral-variant, 30);
28+
}
29+
30+
.general-access-icon {
31+
background-color: mat.get-theme-color($theme, neutral-variant, 90);
32+
}
3133
}
3234
}
3335

3436

3537
@mixin typography($theme) {
36-
.general-access-select {
37-
font: mat.get-theme-typography($theme, body-medium, font);
38-
font-weight: 600;
39-
}
40-
41-
.general-access-description {
42-
font: mat.get-theme-typography($theme, body-small, font);
43-
font-weight: 500;
38+
.general-access-control-container {
39+
.general-access-select {
40+
font: mat.get-theme-typography($theme, body-medium, font);
41+
font-weight: 600;
42+
}
43+
44+
.general-access-description {
45+
font: mat.get-theme-typography($theme, body-small, font);
46+
font-weight: 500;
47+
}
4448
}
4549
}
4650

web/src/app/components/share-survey/_share-survey.component-theme.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
.mat-mdc-card {
2323
--mdc-elevated-card-container-elevation: 0;
2424
border: 1px solid mat.get-theme-color($theme, neutral, 90);
25+
26+
.mat-mdc-card-content {
27+
color: mat.get-theme-color($theme, neutral-variant, 30);
28+
}
2529
}
2630

2731
.mdc-button {

web/src/app/components/share-survey/share-survey.component.html

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,15 @@
1515
-->
1616

1717
<div class="share-survey-container">
18-
<mat-card class="card-with-button">
19-
<div class="relative-container">
20-
<div class="positioned-button">
21-
<button class="share-survey-button" mat-flat-button (click)="openShareDialog()">
22-
<mat-icon class="material-symbols-outlined">add</mat-icon>
23-
24-
<span>Add participants</span>
25-
</button>
26-
</div>
27-
</div>
28-
18+
<mat-card>
2919
<mat-card-header>
3020
<mat-card-title>People with access</mat-card-title>
21+
22+
<button class="share-survey-button" mat-flat-button (click)="openShareDialog()">
23+
<mat-icon class="material-symbols-outlined">add</mat-icon>
24+
25+
<span>Add participants</span>
26+
</button>
3127
</mat-card-header>
3228

3329
<mat-card-content>
@@ -44,4 +40,16 @@
4440
<ground-general-access-control></ground-general-access-control>
4541
</mat-card-content>
4642
</mat-card>
43+
44+
<mat-card>
45+
<ground-data-visibility-control class="floating-button"></ground-data-visibility-control>
46+
47+
<mat-card-header>
48+
<mat-card-title>Data visibility</mat-card-title>
49+
</mat-card-header>
50+
51+
<mat-card-content>
52+
<div>Allow data collectors to see each others' data</div>
53+
</mat-card-content>
54+
</mat-card>
4755
</div>

web/src/app/components/share-survey/share-survey.component.scss

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,16 @@
2020
gap: 16px;
2121

2222
.mat-mdc-card {
23-
&.card-with-button {
24-
.relative-container {
25-
position: relative;
26-
}
27-
28-
.positioned-button {
29-
position: absolute;
30-
right: 24px;
31-
top: 24px;
32-
}
23+
.floating-button {
24+
position: absolute;
25+
top: 34px;
26+
right: 24px;
3327
}
3428

3529
.mat-mdc-card-header {
30+
display: flex;
31+
align-items: center;
32+
justify-content: space-between;
3633
padding: 24px 24px 0;
3734
}
3835

web/src/app/components/share-survey/share-survey.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ import {ShareDialogModule} from 'app/components/share-dialog/share-dialog.module
2727
import {ShareListModule} from 'app/components/share-list/share-list.module';
2828

2929
import {ShareSurveyComponent} from './share-survey.component';
30+
import {DataVisibilityControlModule} from '../data-visibility-control/data-visibility-control.module';
3031
import {GeneralAccessControlModule} from '../general-access-control/general-access-control.module';
3132

3233
@NgModule({
3334
declarations: [ShareSurveyComponent],
3435
imports: [
3536
CommonModule,
37+
DataVisibilityControlModule,
3638
FormsModule,
3739
ReactiveFormsModule,
3840
GeneralAccessControlModule,

web/src/app/pages/edit-survey/edit-details/edit-details.component.html

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,11 @@
4848
</mat-card-content>
4949
</mat-card>
5050

51-
<mat-card class="card-with-button">
52-
<div class="relative-container">
53-
<div class="positioned-button">
54-
<button class="delete-survey-button" mat-stroked-button (click)="openDeleteSurveyDialog()">
55-
<span>Delete survey</span>
56-
</button>
57-
</div>
51+
<mat-card>
52+
<div class="floating-button">
53+
<button class="delete-survey-button" mat-stroked-button (click)="openDeleteSurveyDialog()">
54+
<span>Delete survey</span>
55+
</button>
5856
</div>
5957

6058
<mat-card-header>

web/src/app/pages/edit-survey/edit-details/edit-details.component.scss

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,10 @@
2020
gap: 16px;
2121

2222
.mat-mdc-card {
23-
&.card-with-button {
24-
.relative-container {
25-
position: relative;
26-
}
27-
28-
.positioned-button {
29-
position: absolute;
30-
right: 24px;
31-
top: 24px;
32-
}
23+
.floating-button {
24+
position: absolute;
25+
top: 34px;
26+
right: 24px;
3327
}
3428

3529
.mat-mdc-card-header {

web/src/app/services/draft-survey/draft-survey.service.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {Role} from 'app/models/role.model';
2323
import {
2424
DataSharingType,
2525
Survey,
26+
SurveyDataVisibility,
2627
SurveyGeneralAccess,
2728
SurveyState,
2829
} from 'app/models/survey.model';
@@ -140,6 +141,14 @@ export class DraftSurveyService {
140141
this.dirty = true;
141142
}
142143

144+
updateDataVisibility(dataVisibility: SurveyDataVisibility): void {
145+
const currentSurvey = this.survey$.getValue();
146+
147+
this.survey$.next(currentSurvey.copyWith({dataVisibility}));
148+
149+
this.dirty = true;
150+
}
151+
143152
updateDataSharingTerms(type: DataSharingType, customText?: string): void {
144153
const currentSurvey = this.survey$.getValue();
145154

0 commit comments

Comments
 (0)