Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
82ac8b1
WIP: Migrate poll form to signal form
bastianjoel Jul 23, 2026
1abed83
Update poll form structure
bastianjoel Jul 24, 2026
c14dafe
Fix secret voting info button
bastianjoel Jul 24, 2026
1a5d7f2
Pass method preselection to poll forms
bastianjoel Jul 24, 2026
0657ae5
Fix config form validity check
bastianjoel Jul 24, 2026
72e84de
Cleanup assignment poll dialog
bastianjoel Jul 24, 2026
ffe2a6c
Fix motion poll form validation
bastianjoel Jul 27, 2026
2846d4a
Disable test getters test in meeting settings definition
bastianjoel Jul 27, 2026
7f2e86f
Update total ballots calculate button icon
bastianjoel Jul 27, 2026
c72d899
Add margin left to live voting
bastianjoel Jul 27, 2026
a189d0f
Fix poll edit
bastianjoel Jul 27, 2026
79eb0d4
Improve method preselection
bastianjoel Jul 27, 2026
9b25d1f
Display form error messages
bastianjoel Jul 27, 2026
91d0bd3
Fix strike out results
bastianjoel Jul 28, 2026
50e15a6
Update election methods for topic polls
bastianjoel Jul 30, 2026
d8b96da
Show general option 100% base on abstain active
bastianjoel Jul 30, 2026
e339130
Update meta
bastianjoel Jul 31, 2026
678eed9
Add new fields to meeting settings
bastianjoel Jul 31, 2026
b09ecc6
Add signal api to MeetingSettingsService
bastianjoel Jul 31, 2026
d775b87
Hide max amount of yes votes if disabled
bastianjoel Jul 31, 2026
da35870
Hide anonymize for secret votes
bastianjoel Jul 31, 2026
c119470
Anonymize button
bastianjoel Jul 31, 2026
9762af4
Custom poll stop dialog
bastianjoel Jul 31, 2026
a8d06ea
Fix list poll assignment export
bastianjoel Jul 31, 2026
43bf27d
Cleanup
bastianjoel Jul 31, 2026
bb8c99e
Migrate poll wrapper components to onpush
bastianjoel Jul 31, 2026
163be65
Fix result sometimes not correctly displayed
bastianjoel Jul 31, 2026
2be8661
Fix light mode
bastianjoel Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions client/src/app/domain/models/meetings/meeting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,14 @@ export class Settings {
public topic_poll_config_id: Id;

// General poll settings
public poll_enable_max_yes_votes: boolean;
public poll_default_required_majority: `no_majority` | `two_third_majority` | `absolute_majority`;
public poll_default_live_voting_enabled: boolean;
public poll_enable_max_votes_per_option: boolean;
public poll_default_allow_invalid: boolean;
public poll_default_allow_vote_split: boolean;
public poll_projection_name_order_first: `first_name` | `last_name`;
public poll_projection_max_columns: boolean;

// SSO
public external_id!: string;
Expand Down Expand Up @@ -412,11 +415,14 @@ export class Meeting extends BaseModel<Meeting> {
`motion_poll_config_id`,
`topic_poll_default_method`,
`topic_poll_config_id`,
`poll_enable_max_yes_votes`,
`poll_enable_max_votes_per_option`,
`poll_default_required_majority`,
`poll_default_live_voting_enabled`,
`poll_default_allow_invalid`,
`poll_default_allow_vote_split`,
`poll_projection_name_order_first`,
`poll_projection_max_columns`,
`projector_ids`,
`all_projection_ids`,
`projector_message_ids`,
Expand Down
5 changes: 4 additions & 1 deletion client/src/app/domain/translations/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@ export default {
'poll_percent_base.cast': _(`All casted ballots`),
'poll_percent_base.entitled': _(`All entitled users`),
'poll_percent_base.entitled_present': _(`Present entitled users`),
'poll_percent_base.disabled': _(`Disabled (no percents)`)
'poll_percent_base.disabled': _(`Disabled (no percents)`),
'poll_required_majority.no_majority': _(`No majority`),
'poll_required_majority.two_third_majority': _(`Two thirds`),
'poll_required_majority.absolute_majority': _(`Absolute`)
};
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository<ViewPoll
});
}

public async finalizePoll(poll: Identifiable, actions: ('publish' | 'anonymize')[]): Promise<void> {
return this.voteApi.finalize(poll.id, {
anonymize: actions.indexOf('anonymize') !== -1,
publish: actions.indexOf('publish') !== -1
});
}

public async updateOptionForPoll(poll: Poll, update: any): Promise<void> {
if (poll.visibility !== PollVisibility.Manually) {
throw new Error(`Cannot update an option for an electronic poll!`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Directive, inject, signal, viewChild } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { computed, Directive, inject, signal, viewChild } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Fqid, Id } from '@app/domain/definitions/key-types';
Expand All @@ -8,7 +7,6 @@ import { PollVisibility } from '@app/domain/models/poll';
import { PollUpdatePayload } from '@app/gateways/vote-api.service';
import { ViewPoll } from '@app/site/pages/meetings/pages/polls';
import { BaseUiComponent } from '@app/ui/base/base-ui-component';
import { map } from 'rxjs';

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

public get formsValid(): boolean {
public formsValid = computed<boolean>(() => {
if (!this.pollForm) {
return false;
}

return this.pollForm().pollForm.valid;
}
return this.pollForm().isValid();
});

public analogPollFormOpen = signal(false);
public isAnalogPoll = rxResource({
params: () => this.pollForm(),
defaultValue: false,
stream({ params }) {
return params.pollForm.get(`visibility`).valueChanges.pipe(map(v => v === PollVisibility.Manually));
}
public isAnalogPoll = computed(() => {
return this.pollForm().form.visibility().value() === PollVisibility.Manually;
});

protected formBuilder = inject(UntypedFormBuilder);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, computed, effect, inject, input } from '@angular/core';
import { Component, computed, effect, inject, input, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { PollState } from '@app/domain/models/poll';

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

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

public formValid = signal<boolean>(false);

public pollStarted = computed<boolean>(() => {
return this.data().state && this.data().state !== PollState.Created;
});

protected fb = inject(UntypedFormBuilder);
protected readonly fb = inject(UntypedFormBuilder);

public constructor() {
this.initForm();

effect(this.onDataUpdated.bind(this));
this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => this.formValid.set(this.form.valid));
}

protected abstract initForm(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ <h3>{{ option.title }}</h3>
[attr.aria-label]="'Update total ballots' | translate"
(click)="updateTotalBallots()"
>
<mat-icon>calculate</mat-icon>
<mat-icon>functions</mat-icon>
</button>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<form [formGroup]="form">
@if (!pollStarted()) {
@if (!pollStarted() && !hideMethod()) {
<div class="margin-bottom-15 info-grid">
<mat-form-field>
<mat-label>{{ 'Election method' | translate }}</mat-label>
Expand All @@ -8,19 +8,23 @@
<mat-option [value]="true">{{ 'Yes/No/Abstain' | translate }}</mat-option>
</mat-select>
</mat-form-field>
<div></div>
</div>
}

<mat-form-field>
<mat-label>{{ '100% base' | translate }}</mat-label>
<mat-select formControlName="onehundred_percent_base" panelClass="percent-base-panel" required>
@for (option of validPercentBases; track option[0]) {
@if (option[0] !== 'yes_no_abstain' || form.get('allow_abstain').getRawValue()) {
<mat-option [value]="option[0]">
{{ option[1] | translate }}
</mat-option>
<div class="info-grid">
<mat-form-field>
<mat-label>{{ '100% base' | translate }}</mat-label>
<mat-select formControlName="onehundred_percent_base" panelClass="percent-base-panel" required>
@for (option of validPercentBases; track option[0]) {
@if (option[0] !== 'yes_no_abstain' || form.get('allow_abstain').getRawValue()) {
<mat-option [value]="option[0]">
{{ option[1] | translate }}
</mat-option>
}
}
}
</mat-select>
</mat-form-field>
</mat-select>
</mat-form-field>
<div></div>
</div>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.info-grid {
display: grid;
column-gap: 1em;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));

> mat-checkbox {
padding-top: 10px;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
Expand All @@ -9,6 +9,11 @@ import { _, TranslatePipe } from '@ngx-translate/core';
import { ViewPoll } from '../../../../pages/polls';
import { PollFormBaseComponent } from '../poll-config-form-base.component';

export interface PollFormApproval {
allow_abstain: boolean;
onehundred_percent_base: ApprovalOnehundredPercentBase;
}

@Component({
selector: 'os-poll-form-approval',
imports: [ReactiveFormsModule, MatFormFieldModule, MatSelectModule, MatCheckboxModule, TranslatePipe],
Expand All @@ -17,6 +22,8 @@ import { PollFormBaseComponent } from '../poll-config-form-base.component';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PollFormApprovalComponent extends PollFormBaseComponent {
public hideMethod = input<boolean>(false);

public validPercentBases: [ApprovalOnehundredPercentBase, string][] = [
[`yes_no`, _('Yes/No')],
[`yes_no_abstain`, _('Yes/No/Abstain')],
Expand Down
Loading
Loading