Skip to content

Commit a2910af

Browse files
authored
Update poll form (#6439)
* Migrate poll form to signal form * Use select box for poll method changes * Disable test getters test in meeting settings definition * Update total ballots calculate button icon * Add margin to live voting * Display form error messages * Fix strike out results * Update election methods for topic polls * Show general option 100% base on abstain active * Add new fields to meeting settings * Add signal api to MeetingSettingsService * Hide max amount of yes votes if disabled * Hide anonymize for secret votes * Anonymize button * Implement custom poll stop dialog * Fix list poll assignment export * Migrate poll wrapper components to onpush * Fix result sometimes not correctly displayed * Fix light mode
1 parent 2e11f3d commit a2910af

62 files changed

Lines changed: 821 additions & 727 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/src/app/domain/models/meetings/meeting.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,14 @@ export class Settings {
172172
public topic_poll_config_id: Id;
173173

174174
// General poll settings
175+
public poll_enable_max_yes_votes: boolean;
176+
public poll_default_required_majority: `no_majority` | `two_third_majority` | `absolute_majority`;
175177
public poll_default_live_voting_enabled: boolean;
176178
public poll_enable_max_votes_per_option: boolean;
177179
public poll_default_allow_invalid: boolean;
178180
public poll_default_allow_vote_split: boolean;
179181
public poll_projection_name_order_first: `first_name` | `last_name`;
182+
public poll_projection_max_columns: boolean;
180183

181184
// SSO
182185
public external_id!: string;
@@ -412,11 +415,14 @@ export class Meeting extends BaseModel<Meeting> {
412415
`motion_poll_config_id`,
413416
`topic_poll_default_method`,
414417
`topic_poll_config_id`,
418+
`poll_enable_max_yes_votes`,
415419
`poll_enable_max_votes_per_option`,
420+
`poll_default_required_majority`,
416421
`poll_default_live_voting_enabled`,
417422
`poll_default_allow_invalid`,
418423
`poll_default_allow_vote_split`,
419424
`poll_projection_name_order_first`,
425+
`poll_projection_max_columns`,
420426
`projector_ids`,
421427
`all_projection_ids`,
422428
`projector_message_ids`,

client/src/app/domain/translations/poll.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,8 @@ export default {
2222
'poll_percent_base.cast': _(`All casted ballots`),
2323
'poll_percent_base.entitled': _(`All entitled users`),
2424
'poll_percent_base.entitled_present': _(`Present entitled users`),
25-
'poll_percent_base.disabled': _(`Disabled (no percents)`)
25+
'poll_percent_base.disabled': _(`Disabled (no percents)`),
26+
'poll_required_majority.no_majority': _(`No majority`),
27+
'poll_required_majority.two_third_majority': _(`Two thirds`),
28+
'poll_required_majority.absolute_majority': _(`Absolute`)
2629
};

client/src/app/gateways/repositories/polls/poll-repository.service/poll-repository.service.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository<ViewPoll
136136
});
137137
}
138138

139+
public async finalizePoll(poll: Identifiable, actions: ('publish' | 'anonymize')[]): Promise<void> {
140+
return this.voteApi.finalize(poll.id, {
141+
anonymize: actions.indexOf('anonymize') !== -1,
142+
publish: actions.indexOf('publish') !== -1
143+
});
144+
}
145+
139146
public async updateOptionForPoll(poll: Poll, update: any): Promise<void> {
140147
if (poll.visibility !== PollVisibility.Manually) {
141148
throw new Error(`Cannot update an option for an electronic poll!`);

client/src/app/site/pages/meetings/modules/poll/base/base-poll-dialog.component.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { Directive, inject, signal, viewChild } from '@angular/core';
2-
import { rxResource } from '@angular/core/rxjs-interop';
1+
import { computed, Directive, inject, signal, viewChild } from '@angular/core';
32
import { UntypedFormBuilder } from '@angular/forms';
43
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
54
import { Fqid, Id } from '@app/domain/definitions/key-types';
@@ -8,7 +7,6 @@ import { PollVisibility } from '@app/domain/models/poll';
87
import { PollUpdatePayload } from '@app/gateways/vote-api.service';
98
import { ViewPoll } from '@app/site/pages/meetings/pages/polls';
109
import { BaseUiComponent } from '@app/ui/base/base-ui-component';
11-
import { map } from 'rxjs';
1210

1311
import { PollEditResultComponent } from '../components/poll-edit-result/poll-edit-result.component';
1412
import { PollFormComponent } from '../components/poll-form/poll-form.component';
@@ -42,21 +40,17 @@ export abstract class BasePollDialogComponent extends BaseUiComponent {
4240
protected pollForm = viewChild.required(PollFormComponent);
4341
protected pollResultForm = viewChild(PollEditResultComponent);
4442

45-
public get formsValid(): boolean {
43+
public formsValid = computed<boolean>(() => {
4644
if (!this.pollForm) {
4745
return false;
4846
}
4947

50-
return this.pollForm().pollForm.valid;
51-
}
48+
return this.pollForm().isValid();
49+
});
5250

5351
public analogPollFormOpen = signal(false);
54-
public isAnalogPoll = rxResource({
55-
params: () => this.pollForm(),
56-
defaultValue: false,
57-
stream({ params }) {
58-
return params.pollForm.get(`visibility`).valueChanges.pipe(map(v => v === PollVisibility.Manually));
59-
}
52+
public isAnalogPoll = computed(() => {
53+
return this.pollForm().form.visibility().value() === PollVisibility.Manually;
6054
});
6155

6256
protected formBuilder = inject(UntypedFormBuilder);

client/src/app/site/pages/meetings/modules/poll/base/base-poll.component.ts

Lines changed: 0 additions & 138 deletions
This file was deleted.

client/src/app/site/pages/meetings/modules/poll/components/poll-config-form-base.component.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Component, computed, effect, inject, input } from '@angular/core';
1+
import { Component, computed, effect, inject, input, signal } from '@angular/core';
2+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
23
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
34
import { PollState } from '@app/domain/models/poll';
45

@@ -10,16 +11,19 @@ export abstract class PollFormBaseComponent {
1011

1112
public data = input.required<Partial<ViewPoll>>();
1213

14+
public formValid = signal<boolean>(false);
15+
1316
public pollStarted = computed<boolean>(() => {
1417
return this.data().state && this.data().state !== PollState.Created;
1518
});
1619

17-
protected fb = inject(UntypedFormBuilder);
20+
protected readonly fb = inject(UntypedFormBuilder);
1821

1922
public constructor() {
2023
this.initForm();
2124

2225
effect(this.onDataUpdated.bind(this));
26+
this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => this.formValid.set(this.form.valid));
2327
}
2428

2529
protected abstract initForm(): void;

client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ <h3>{{ option.title }}</h3>
113113
[attr.aria-label]="'Update total ballots' | translate"
114114
(click)="updateTotalBallots()"
115115
>
116-
<mat-icon>calculate</mat-icon>
116+
<mat-icon>functions</mat-icon>
117117
</button>
118118
}
119119
</div>
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<form [formGroup]="form">
2-
@if (!pollStarted()) {
2+
@if (!pollStarted() && !hideMethod()) {
33
<div class="margin-bottom-15 info-grid">
44
<mat-form-field>
55
<mat-label>{{ 'Election method' | translate }}</mat-label>
@@ -8,19 +8,23 @@
88
<mat-option [value]="true">{{ 'Yes/No/Abstain' | translate }}</mat-option>
99
</mat-select>
1010
</mat-form-field>
11+
<div></div>
1112
</div>
1213
}
1314

14-
<mat-form-field>
15-
<mat-label>{{ '100% base' | translate }}</mat-label>
16-
<mat-select formControlName="onehundred_percent_base" panelClass="percent-base-panel" required>
17-
@for (option of validPercentBases; track option[0]) {
18-
@if (option[0] !== 'yes_no_abstain' || form.get('allow_abstain').getRawValue()) {
19-
<mat-option [value]="option[0]">
20-
{{ option[1] | translate }}
21-
</mat-option>
15+
<div class="info-grid">
16+
<mat-form-field>
17+
<mat-label>{{ '100% base' | translate }}</mat-label>
18+
<mat-select formControlName="onehundred_percent_base" panelClass="percent-base-panel" required>
19+
@for (option of validPercentBases; track option[0]) {
20+
@if (option[0] !== 'yes_no_abstain' || form.get('allow_abstain').getRawValue()) {
21+
<mat-option [value]="option[0]">
22+
{{ option[1] | translate }}
23+
</mat-option>
24+
}
2225
}
23-
}
24-
</mat-select>
25-
</mat-form-field>
26+
</mat-select>
27+
</mat-form-field>
28+
<div></div>
29+
</div>
2630
</form>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.info-grid {
2+
display: grid;
3+
column-gap: 1em;
4+
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
5+
6+
> mat-checkbox {
7+
padding-top: 10px;
8+
}
9+
}

client/src/app/site/pages/meetings/modules/poll/components/poll-form-approval/poll-form-approval.component.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeDetectionStrategy, Component } from '@angular/core';
1+
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
22
import { ReactiveFormsModule } from '@angular/forms';
33
import { MatCheckboxModule } from '@angular/material/checkbox';
44
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -9,6 +9,11 @@ import { _, TranslatePipe } from '@ngx-translate/core';
99
import { ViewPoll } from '../../../../pages/polls';
1010
import { PollFormBaseComponent } from '../poll-config-form-base.component';
1111

12+
export interface PollFormApproval {
13+
allow_abstain: boolean;
14+
onehundred_percent_base: ApprovalOnehundredPercentBase;
15+
}
16+
1217
@Component({
1318
selector: 'os-poll-form-approval',
1419
imports: [ReactiveFormsModule, MatFormFieldModule, MatSelectModule, MatCheckboxModule, TranslatePipe],
@@ -17,6 +22,8 @@ import { PollFormBaseComponent } from '../poll-config-form-base.component';
1722
changeDetection: ChangeDetectionStrategy.OnPush
1823
})
1924
export class PollFormApprovalComponent extends PollFormBaseComponent {
25+
public hideMethod = input<boolean>(false);
26+
2027
public validPercentBases: [ApprovalOnehundredPercentBase, string][] = [
2128
[`yes_no`, _('Yes/No')],
2229
[`yes_no_abstain`, _('Yes/No/Abstain')],

0 commit comments

Comments
 (0)