diff --git a/client/src/app/domain/models/meetings/meeting.ts b/client/src/app/domain/models/meetings/meeting.ts index f4e3e50f77a..d1f349f09d3 100644 --- a/client/src/app/domain/models/meetings/meeting.ts +++ b/client/src/app/domain/models/meetings/meeting.ts @@ -174,7 +174,9 @@ export class Settings { // 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_required_majority: + `no_majority` | `two_third_majority` | `absolute_majority` | `simple_majority`; + public poll_default_live_voting_enabled: boolean; public poll_enable_max_votes_per_option: boolean; public poll_default_allow_invalid: boolean; diff --git a/client/src/app/domain/models/poll/poll-config-rating-score.ts b/client/src/app/domain/models/poll/poll-config-rating-score.ts index 91769da7e16..076ff1f1cb5 100644 --- a/client/src/app/domain/models/poll/poll-config-rating-score.ts +++ b/client/src/app/domain/models/poll/poll-config-rating-score.ts @@ -1,6 +1,6 @@ import { HasMeetingId } from '../../interfaces/has-meeting-id'; -import { PollRequiredMajority } from '.'; import { BasePollConfigModel } from './base-poll-config'; +import { SelectionRequiredMajority } from './poll-config-selection'; import { BaseOnehundredPercentBase } from './poll-config-types'; export type RatingScoreOnehundredPercentBase = BaseOnehundredPercentBase | `yes_no`; @@ -14,7 +14,7 @@ export class PollConfigRatingScore extends BasePollConfigModel) { super(PollConfigRatingScore.COLLECTION, input); diff --git a/client/src/app/domain/models/poll/poll-config-selection.ts b/client/src/app/domain/models/poll/poll-config-selection.ts index eadc0f062c5..139f82e1e54 100644 --- a/client/src/app/domain/models/poll/poll-config-selection.ts +++ b/client/src/app/domain/models/poll/poll-config-selection.ts @@ -4,6 +4,7 @@ import { BasePollConfigModel } from './base-poll-config'; import { BaseOnehundredPercentBase } from './poll-config-types'; export type SelectionOnehundredPercentBase = BaseOnehundredPercentBase | `no_general`; +export type SelectionRequiredMajority = PollRequiredMajority | `simple_majority`; export class PollConfigSelection extends BasePollConfigModel { public static COLLECTION = `poll_config_selection`; @@ -14,7 +15,7 @@ export class PollConfigSelection extends BasePollConfigModel { public poll_id!: Id; public meeting_user_id: Id; + public present: boolean; public constructor(input?: Partial) { super(PollEntitledUser.COLLECTION, input); } - public static readonly REQUESTABLE_FIELDS: (keyof PollEntitledUser)[] = [`id`, `poll_id`, `meeting_user_id`]; + public static readonly REQUESTABLE_FIELDS: (keyof PollEntitledUser)[] = [ + `id`, + `poll_id`, + `meeting_user_id`, + `present` + ]; } diff --git a/client/src/app/domain/models/poll/poll.ts b/client/src/app/domain/models/poll/poll.ts index 18c48946faf..b0178be9ab3 100644 --- a/client/src/app/domain/models/poll/poll.ts +++ b/client/src/app/domain/models/poll/poll.ts @@ -19,6 +19,7 @@ export class Poll extends BaseModel { public result!: string; public anonymized!: boolean; public published!: boolean; + public allow_empty!: boolean; public allow_invalid!: boolean; public allow_vote_split!: boolean; public option_ids!: Id[]; @@ -42,6 +43,7 @@ export class Poll extends BaseModel { `result`, `published`, `anonymized`, + `allow_empty`, `allow_invalid`, `allow_vote_split`, `live_voting_enabled`, diff --git a/client/src/app/domain/translations/poll.ts b/client/src/app/domain/translations/poll.ts index e16ab7e429a..982a3978665 100644 --- a/client/src/app/domain/translations/poll.ts +++ b/client/src/app/domain/translations/poll.ts @@ -25,5 +25,6 @@ export default { '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`) + 'poll_required_majority.absolute_majority': _(`Absolute`), + 'poll_required_majority.simple_majority': _(`Simple`) }; diff --git a/client/src/app/gateways/repositories/polls/poll-repository.service/poll-repository.service.ts b/client/src/app/gateways/repositories/polls/poll-repository.service/poll-repository.service.ts index aff7fa7a8ff..dac3225ab37 100644 --- a/client/src/app/gateways/repositories/polls/poll-repository.service/poll-repository.service.ts +++ b/client/src/app/gateways/repositories/polls/poll-repository.service/poll-repository.service.ts @@ -47,36 +47,6 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository { - if (payload.visibility === PollVisibility.Manually) { - return this.updateAnalogPoll(poll, payload); - } - - return this.updateElectronicPoll(poll, payload); - } - - private async createAnalogPoll(payload: any): Promise { - delete payload.live_voting_enabled; - - return this.voteApi.create(payload); - } - - private async createElectronicPoll(payload: PollCreatePayload): Promise { - if (payload.visibility !== PollVisibility.Named && payload.visibility !== PollVisibility.Open) { - delete payload.live_voting_enabled; - } - - return this.voteApi.create(payload); - } - - private async updateAnalogPoll(poll: ViewPoll, payload: PollUpdatePayload): Promise { - if (poll.config.method === payload.method) { - delete payload[`method`]; - } - - return this.voteApi.update(poll.id, payload); - } - - private async updateElectronicPoll(poll: ViewPoll, payload: PollUpdatePayload): Promise { if (poll.state !== PollState.Created) { const methodConfig: { display_chart?: string; @@ -89,7 +59,8 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository { + delete payload.live_voting_enabled; + + return this.voteApi.create(payload); + } + + private async createElectronicPoll(payload: PollCreatePayload): Promise { + if (payload.visibility !== PollVisibility.Named && payload.visibility !== PollVisibility.Open) { + delete payload.live_voting_enabled; + } + + return this.voteApi.create(payload); + } + public async delete(poll: Identifiable): Promise { return this.voteApi.deletePoll(poll.id); } diff --git a/client/src/app/gateways/vote-api.service.ts b/client/src/app/gateways/vote-api.service.ts index 1b8e8a574e2..87e5421b8c6 100644 --- a/client/src/app/gateways/vote-api.service.ts +++ b/client/src/app/gateways/vote-api.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Service } from '@angular/core'; import { Id } from '../domain/definitions/key-types'; import { PollVisibility } from '../domain/models/poll/poll-constants'; @@ -20,13 +20,14 @@ export interface PollUpdatePayload { option_type?: 'text' | 'meeting_user'; result?: unknown; allow_vote_split: boolean; + allow_empty?: boolean; } -@Injectable({ providedIn: 'root' }) +@Service() export class VoteApiService { private readonly BASE = '/system/vote'; - public constructor(private http: HttpService) {} + private http = inject(HttpService); public create(payload: PollCreatePayload): Promise { return this.http.post(`${this.BASE}/poll`, payload); diff --git a/client/src/app/site/pages/meetings/modules/poll/base/base-poll-dialog.component.ts b/client/src/app/site/pages/meetings/modules/poll/base/base-poll-dialog.component.ts index 33e9f15774c..b7cddf35eb8 100644 --- a/client/src/app/site/pages/meetings/modules/poll/base/base-poll-dialog.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/base/base-poll-dialog.component.ts @@ -57,20 +57,46 @@ export abstract class BasePollDialogComponent extends BaseUiComponent { public dialogRef = inject(MatDialogRef); public pollData: ViewPoll = inject(MAT_DIALOG_DATA); + public currentPollData: ViewPoll = this.pollData; + public constructor() { super(); this.addKeyListener(); } + public toggleAnalogPollForm(): void { + const payload = this.getPayload(); + if (payload.result && !(payload.result instanceof String)) { + payload.result = JSON.stringify(payload.result); + } + this.currentPollData = Object.assign(this.currentPollData, payload); + this.analogPollFormOpen.set(!this.analogPollFormOpen()); + } + /** * Submits the values from dialog. */ public submitPoll(): void { + this.dialogRef.close(this.getPayload()); + } + + /** + * Returns the poll method related part of the payload + */ + public abstract methodPayload(): PollMethodPayload; + + /** + * Returns the poll options related part of the payload + */ + public abstract optionsPayload(): PollOptionsPayload; + + protected getPayload(): PollUpdatePayload { const formValues = this.pollForm().getValues(); const visibility: PollVisibility = formValues?.visibility; const payload: PollUpdatePayload = { title: formValues?.title, + allow_empty: formValues?.allow_empty, ...this.methodPayload(), ...this.optionsPayload(), visibility, @@ -87,19 +113,9 @@ export abstract class BasePollDialogComponent extends BaseUiComponent { } } - this.dialogRef.close(payload); + return payload; } - /** - * Returns the poll method related part of the payload - */ - public abstract methodPayload(): PollMethodPayload; - - /** - * Returns the poll options related part of the payload - */ - public abstract optionsPayload(): PollOptionsPayload; - private addKeyListener(): void { if (!this.dialogRef) { return; diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-config-form-base.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-config-form-base.component.ts index 7ddfff1b4c1..21bfbbb3853 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-config-form-base.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-config-form-base.component.ts @@ -29,6 +29,10 @@ export abstract class PollFormBaseComponent { protected abstract getPatchedFormData(data: Partial): Record; public abstract getSerialzedForm(): Record; + public getSerialzedAdditionalData(): Record { + return {}; + } + private onDataUpdated(): void { if (!this.data() || !this.form) { return; diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.html index 12c5e575b6c..6c898d956a8 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.html @@ -82,7 +82,7 @@

{{ option.title }}

Global abstain - +
} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.ts index e808af3112f..3c345a87315 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-edit-result/poll-edit-result.component.ts @@ -1,5 +1,5 @@ import { NgTemplateOutlet } from '@angular/common'; -import { ChangeDetectionStrategy, Component, computed, input, OnInit, signal } from '@angular/core'; +import { Component, computed, input, OnInit, signal } from '@angular/core'; import { applyEach, disabled, form, FormField, schema } from '@angular/forms/signals'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -39,7 +39,7 @@ interface OptionFormEntry { interface PollEditResultModel { options: OptionFormEntry[]; - abstain: number; + empty: number; nota: number; invalid: number; total_ballots: number; @@ -59,8 +59,7 @@ interface PollEditResultModel { FormField, TranslatePipe, NgTemplateOutlet - ], - changeDetection: ChangeDetectionStrategy.OnPush + ] }) export class PollEditResultComponent implements OnInit { public readonly pollData = input.required>(); @@ -72,7 +71,7 @@ export class PollEditResultComponent implements OnInit { private readonly model = signal({ options: [], - abstain: 0, + empty: 0, nota: 0, invalid: 0, total_ballots: 0 @@ -102,8 +101,7 @@ export class PollEditResultComponent implements OnInit { }); public showAbstain = computed(() => { - const cfg = this.pollConfigDataLax(); - return cfg.min_options_amount === 0 && (cfg.min_vote_sum === 0 || cfg.min_vote_sum === undefined); + return this.pollData().allow_empty; }); public showNota = computed(() => { @@ -154,7 +152,7 @@ export class PollEditResultComponent implements OnInit { this.model.set({ options: builtOptions, - abstain: this.showAbstain() ? (result[`abstain`] ?? null) : null, + empty: this.showAbstain() ? (result[`empty`] ?? null) : null, nota: result[`nota`] ?? null, invalid: result[`invalid`] ?? null, total_ballots: result[`total_ballots`] ?? null @@ -172,7 +170,7 @@ export class PollEditResultComponent implements OnInit { .total_ballots() .value.set( data.invalid + - data.abstain + + data.empty + data.nota + data.options.reduce((p, c) => p + c.value.yes + c.value.no + c.value.abstain, 0) ); @@ -181,7 +179,7 @@ export class PollEditResultComponent implements OnInit { .total_ballots() .value.set( data.invalid + - data.abstain + + data.empty + data.nota + data.options.reduce((p, c) => Math.max(c.value.yes + c.value.no + c.value.abstain, p), 0) ); @@ -223,7 +221,7 @@ export class PollEditResultComponent implements OnInit { : serializedOptions), ...(m.invalid !== null ? { invalid: m.invalid } : {}), ...(m.total_ballots !== null ? { total_ballots: m.total_ballots } : {}), - ...(this.showAbstain() ? { abstain: m.abstain } : {}), + ...(this.showAbstain() ? { empty: m.empty } : {}), ...(this.showNota() ? { nota: m.nota } : {}) }; } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-approval/poll-form-approval.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-approval/poll-form-approval.component.ts index 059fa645674..920ceae027c 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-approval/poll-form-approval.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-approval/poll-form-approval.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -26,8 +26,7 @@ export interface PollFormApproval { TranslateKeyPipe ], templateUrl: './poll-form-approval.component.html', - styleUrl: './poll-form-approval.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush + styleUrl: './poll-form-approval.component.scss' }) export class PollFormApprovalComponent extends PollFormBaseComponent { public hideMethod = input(false); @@ -38,7 +37,7 @@ export class PollFormApprovalComponent extends PollFormBaseComponent { [`valid`, _('All valid ballots')], [`cast`, _('All casted ballots')], [`entitled`, _('All entitled users')], - // [`entitled_present`, _('Present entitled users')], + [`entitled_present`, _('Present entitled users')], [`disabled`, _('Disabled (no percents)')] ]; diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-approval/poll-form-rating-approval.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-approval/poll-form-rating-approval.component.ts index 63d4fa010fa..e772c32e65d 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-approval/poll-form-rating-approval.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-approval/poll-form-rating-approval.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, effect, inject, input } from '@angular/core'; +import { Component, effect, inject, input } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AbstractControl, ReactiveFormsModule, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -34,8 +34,7 @@ export interface PollFormRatingApproval { TranslateKeyPipe ], templateUrl: './poll-form-rating-approval.component.html', - styleUrls: [`../poll-form/poll-form.component.scss`, `./poll-form-rating-approval.component.scss`], - changeDetection: ChangeDetectionStrategy.OnPush + styleUrls: [`../poll-form/poll-form.component.scss`, `./poll-form-rating-approval.component.scss`] }) export class PollFormRatingApprovalComponent extends PollFormBaseComponent { public validPercentBases: [RatingApprovalOnehundredPercentBase, string][] = [ @@ -44,7 +43,7 @@ export class PollFormRatingApprovalComponent extends PollFormBaseComponent { [`valid`, _('All valid ballots')], [`cast`, _('All casted ballots')], [`entitled`, _('All entitled users')], - // [`entitled_present`, _('Present entitled users')], + [`entitled_present`, _('Present entitled users')], [`disabled`, _('Disabled (no percents)')] ]; @@ -56,14 +55,17 @@ export class PollFormRatingApprovalComponent extends PollFormBaseComponent { public maxYesVotesEnabled = this.meetingSettingsService.signal(`poll_enable_max_yes_votes`); protected initForm(): void { - this.form = this.fb.group({ + const fields = { onehundred_percent_base: [`valid`], allow_abstain: [false], - max_yes_amount: [1, [Validators.required, Validators.min(1)]], max_options_amount: [1, [Validators.required, Validators.min(1)]], min_options_amount: [1, [Validators.required, Validators.min(0), this.minOptionsAmountValidator()]], required_majority: [`no_majority`] - }); + }; + if (inject(MeetingSettingsService).instant(`poll_enable_max_yes_votes`)) { + fields[`max_yes_amount`] = [1, [Validators.required, Validators.min(1)]]; + } + this.form = this.fb.group(fields); effect(this.onOptionAmountUpdate.bind(this)); diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-score/poll-form-rating-score.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-score/poll-form-rating-score.component.html index d0290936d03..03dcaf22d68 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-score/poll-form-rating-score.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-rating-score/poll-form-rating-score.component.html @@ -83,6 +83,9 @@ {{ 'absolute_majority' | translateKey: 'poll_required_majority' }} + + {{ 'simple_majority' | translateKey: 'poll_required_majority' }} + @if (form.get('required_majority').getRawValue() === 'absolute_majority') { (null); public getSerialzedForm(): Record { + const data = { + ...this.form.value + }; + delete data[`allow_empty`]; + + return data; + } + + public override getSerialzedAdditionalData(): Record { return { - ...this.form.value, - min_options_amount: this.form.value[`allow_general_abstain`] ? 0 : this.form.value[`min_options_amount`], - min_vote_sum: this.form.value[`allow_general_abstain`] ? 0 : this.form.value[`min_vote_sum`] + allow_empty: this.form.value[`allow_empty`] }; } protected initForm(): void { this.form = this.fb.group({ onehundred_percent_base: [`valid`], - allow_general_abstain: [false], + allow_empty: [false], max_votes_per_option: [null], max_options_amount: [1, [Validators.required, Validators.min(1)]], min_options_amount: [ @@ -97,16 +103,13 @@ export class PollFormRatingScoreComponent extends PollFormBaseComponent { `min_options_amount`, `max_vote_sum`, `min_vote_sum`, - `required_majority` + `required_majority`, + `allow_empty` ]) { if (data && data[field] !== undefined) patch[field] = data[field]; else if (data && data.config[field] !== undefined) patch[field] = data.config[field]; } - if (patch[`min_options_amount`] !== undefined && patch[`min_vote_sum`] !== undefined) { - patch[`allow_general_abstain`] = +patch[`min_options_amount`] === 0 && +patch[`min_vote_sum`] === 0; - } - return patch; } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-selection/poll-form-selection.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-selection/poll-form-selection.component.html index 288af8899a9..7d5ac22951a 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form-selection/poll-form-selection.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form-selection/poll-form-selection.component.html @@ -14,22 +14,18 @@
@if (!pollStarted()) {
- @if (!form.get('allow_general_abstain').value) { - - {{ 'Min amount of votes' | translate }} - - @if (form.get('min_options_amount').hasError('min')) { - {{ 'Amount needs to be at least 1' | translate }} - } - @if (form.get('min_options_amount').hasError('minGreaterThanMax')) { - - {{ 'Amount must not exceed amount of max amount of votes' | translate }} - - } - - } @else { -
- } + + {{ 'Min amount of votes' | translate }} + + @if (form.get('min_options_amount').hasError('min')) { + {{ 'Amount needs to be at least 1' | translate }} + } + @if (form.get('min_options_amount').hasError('minGreaterThanMax')) { + + {{ 'Amount must not exceed amount of max amount of votes' | translate }} + + } + {{ 'Max amount of votes' | translate }} @@ -44,7 +40,7 @@
- + {{ 'General abstain' | translate }} @@ -66,7 +62,7 @@ @if ( option[0] !== 'no_general' || form.get('allow_nota').getRawValue() || - form.get('allow_general_abstain').getRawValue() || + form.get('allow_empty').getRawValue() || form.get('min_options_amount').getRawValue() === 0 ) { @@ -89,6 +85,9 @@ {{ 'absolute_majority' | translateKey: 'poll_required_majority' }} + + {{ 'simple_majority' | translateKey: 'poll_required_majority' }} + @if (form.get('required_majority').getRawValue() === 'absolute_majority') { (null); public getSerialzedForm(): Record { + const data = { + ...this.form.value + }; + delete data[`allow_empty`]; + + return data; + } + + public override getSerialzedAdditionalData(): Record { return { - ...this.form.value, - min_options_amount: this.form.value[`allow_general_abstain`] ? 0 : this.form.value[`min_options_amount`] + allow_empty: this.form.value[`allow_empty`] }; } @@ -61,7 +68,7 @@ export class PollFormSelectionComponent extends PollFormBaseComponent { onehundred_percent_base: [`valid`], strike_out: [false], allow_nota: [false], - allow_general_abstain: [false], + allow_empty: [false], max_options_amount: [1, [Validators.required, Validators.min(1)]], min_options_amount: [1, [Validators.required, Validators.min(1), this.minOptionsAmountValidator()]], display_chart: [`table`], @@ -86,7 +93,8 @@ export class PollFormSelectionComponent extends PollFormBaseComponent { `max_options_amount`, `min_options_amount`, `display_chart`, - `required_majority` + `required_majority`, + `allow_empty` ]) { if (data && data[field] !== undefined) patch[field] = data[field]; else if (data && data.config[field] !== undefined) patch[field] = data.config[field]; @@ -96,10 +104,6 @@ export class PollFormSelectionComponent extends PollFormBaseComponent { patch[`onehundred_percent_base`] = `valid`; } - if (patch[`min_options_amount`] !== undefined) { - patch[`allow_general_abstain`] = +patch[`min_options_amount`] === 0; - } - return patch; } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.html index a992efec189..96fe01ae2c2 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.html @@ -54,64 +54,64 @@

}

- } - - @if (!customConfigForm()) { -
-
- - {{ 'Election method' | translate }} - - @if (optionAmount() <= 1 && form.options().value().length <= 1) { - {{ 'Yes/No' | translate }} - - {{ 'Yes/No/Abstain' | translate }} - - } - @if (optionAmount() > 1 || form.options().value().length > 1) { - @if (optionType() === 'text') { - - {{ 'Yes per option' | translate }} + @if (!customConfigForm()) { +
+
+ + {{ 'Election method' | translate }} + + @if (optionAmount() <= 1 && form.options().value().length <= 1) { + {{ 'Yes/No' | translate }} + + {{ 'Yes/No/Abstain' | translate }} - @if (allowCumulative()) { - - {{ 'Amount per option' | translate }} + } + + @if (optionAmount() > 1 || form.options().value().length > 1) { + @if (optionType() === 'text') { + + {{ 'Yes per option' | translate }} - } - } @else if (optionType() === 'meeting_user') { - - {{ 'Yes per candidate' | translate }} - - - {{ 'No per candidate' | translate }} - - - {{ 'Yes/No per candidate' | translate }} - - - {{ 'Yes/No/Abstain per candidate' | translate }} - - @if (allowCumulative()) { - - {{ 'Amount per candidate' | translate }} + @if (allowCumulative()) { + + {{ 'Amount per option' | translate }} + + } + } @else if (optionType() === 'meeting_user') { + + {{ 'Yes per candidate' | translate }} - } + + {{ 'No per candidate' | translate }} + + + {{ 'Yes/No per candidate' | translate }} + + + {{ 'Yes/No/Abstain per candidate' | translate }} + + @if (allowCumulative()) { + + {{ 'Amount per candidate' | translate }} + + } - - {{ 'Yes/No per list' | translate }} - - - {{ 'Yes/No/Abstain per list' | translate }} - + + {{ 'Yes/No per list' | translate }} + + + {{ 'Yes/No/Abstain per list' | translate }} + + } } - } - - {{ 'This field is required.' | translate }} - -
+ + {{ 'This field is required.' | translate }} + +
+
-
+ } }
diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.ts index 7f69b2744bb..d72287e07b9 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-form/poll-form.component.ts @@ -177,7 +177,12 @@ export class PollFormComponent extends BaseComponent { } public getValues(): Partial<{ [place in keyof ViewPoll]: any }> { - return { ...this.data, ...this.serializeForm() }; + let additional = {}; + if (this.methodForm()) { + additional = this.methodForm().getSerialzedAdditionalData(); + } + + return { ...this.data, ...this.serializeForm(), ...additional }; } public openVotingWarning(event: MouseEvent): void { @@ -207,12 +212,13 @@ export class PollFormComponent extends BaseComponent { private updateData(): void { const data = this.data(); if (data && this.form) { - if (data.entitled_group_ids !== undefined) + if (data.entitled_group_ids !== undefined && !this.form['entitled_group_ids']().dirty()) this.form['entitled_group_ids']().value.set(data.entitled_group_ids); - if (data.live_voting_enabled !== undefined) + if (data.live_voting_enabled !== undefined && !this.form['live_voting_enabled']().dirty()) this.form['live_voting_enabled']().value.set(!!data.live_voting_enabled); - if (data.title !== undefined) this.form['title']().value.set(data.title); - if (data.visibility !== undefined) this.form['visibility']().value.set(data.visibility); + if (data.title !== undefined && !this.form['title']().dirty()) this.form['title']().value.set(data.title); + if (data.visibility !== undefined && !this.form['visibility']().dirty()) + this.form['visibility']().value.set(data.visibility); if (data.options !== undefined && !data.options.some(option => option.content_object_id)) this.form['options']().value.set(data.options.map(option => option.text)); if (data.config?.method) { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.html index 8da3755793d..3bc16aae68b 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.html @@ -65,102 +65,102 @@
} - @if (config().strike_out !== undefined || config().allow_abstain !== undefined) { + @if (config.value().strike_out !== undefined || config.value().allow_abstain !== undefined) {
{{ 'Voting method' | translate }}: - @if (config().strike_out === true) { + @if (config.value().strike_out === true) { @if (isPersonPoll()) { {{ 'No per candidate' | translate }} } @else { {{ 'No per option' | translate }} } - } @else if (config().strike_out === false) { + } @else if (config.value().strike_out === false) { @if (isPersonPoll()) { {{ 'Yes per candidate' | translate }} } @else { {{ 'Yes per option' | translate }} } - } @else if (config().allow_abstain === true) { + } @else if (config.value().allow_abstain === true) { {{ 'Yes/No/Abstain' | translate }} - } @else if (config().allow_abstain === false) { + } @else if (config.value().allow_abstain === false) { {{ 'Yes/No' | translate }} }
} - @if (config().COLLECTION === `poll_config_selection`) { - @if (config().min_options_amount > 1 || config().max_options_amount > 1) { - @if (config().min_options_amount === config().max_options_amount) { + @if (config.value().COLLECTION === `poll_config_selection`) { + @if (config.value().min_options_amount > 1 || config.value().max_options_amount > 1) { + @if (config.value().min_options_amount === config.value().max_options_amount) {
{{ 'Votes on different options' | translate }}: - {{ config().min_options_amount }} + {{ config.value().min_options_amount }}
} @else { - @if (config().min_options_amount > 1) { + @if (config.value().min_options_amount > 1) {
{{ 'Minimum amount of options selected' | translate }}: - {{ config().min_options_amount }} + {{ config.value().min_options_amount }}
} - @if (config().max_options_amount > 1) { + @if (config.value().max_options_amount > 1) {
{{ 'Maximum amount options selected' | translate }}: - {{ config().max_options_amount }} + {{ config.value().max_options_amount }}
} } } } @else { - @if (config().min_vote_sum > 1 || config().max_vote_sum > 1) { - @if (config().min_vote_sum === config().max_vote_sum) { + @if (config.value().min_vote_sum > 1 || config.value().max_vote_sum > 1) { + @if (config.value().min_vote_sum === config.value().max_vote_sum) {
{{ 'Votes' | translate }}: - {{ config().min_vote_sum }} + {{ config.value().min_vote_sum }}
} @else { - @if (config().min_vote_sum > 1) { + @if (config.value().min_vote_sum > 1) {
{{ 'Minimum amount of votes' | translate }}: - {{ config().min_vote_sum }} + {{ config.value().min_vote_sum }}
} - @if (config().max_vote_sum > 1) { + @if (config.value().max_vote_sum > 1) {
{{ 'Maximum amount of votes' | translate }}: - {{ config().max_vote_sum }} + {{ config.value().max_vote_sum }}
} } } - @if (config().max_yes_amount >= 1 && config().max_yes_amount < config().max_vote_sum) { + @if (config.value().max_yes_amount >= 1 && config.value().max_yes_amount < config.value().max_vote_sum) {
{{ 'Maximum amount of yes votes' | translate }}: - {{ config().max_yes_amount }} + {{ config.value().max_yes_amount }}
} } - @if (config().max_votes_per_option > 1) { + @if (config.value().max_votes_per_option > 1) {
{{ 'Maximum amount of votes per option' | translate }}: - {{ config().max_votes_per_option }} + {{ config.value().max_votes_per_option }}
} - @if (config().onehundred_percent_base !== undefined) { + @if (config.value().onehundred_percent_base !== undefined) {
{{ '100% base' | translate }}: - {{ config().onehundred_percent_base | translateKey: 'poll_percent_base' }} + {{ config.value().onehundred_percent_base | translateKey: 'poll_percent_base' }}
} - @if (config().required_majority !== undefined && config().required_majority !== 'no_majority') { + @if (config.value().required_majority !== undefined && config.value().required_majority !== 'no_majority') {
{{ 'Required majority' | translate }}: - {{ config().required_majority | translateKey: 'poll_required_majority' }} + {{ config.value().required_majority | translateKey: 'poll_required_majority' }}
} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.ts index c5619b94154..92f60de2b07 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-meta/poll-meta.component.ts @@ -1,5 +1,6 @@ import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; +import { rxResource } from '@angular/core/rxjs-interop'; import { PollConfigApproval } from '@app/domain/models/poll/poll-config-approval'; import { ViewPoll } from '@app/site/pages/meetings/pages/polls/view-models'; import { ViewMeetingUser } from '@app/site/pages/meetings/view-models/view-meeting-user'; @@ -25,36 +26,36 @@ import { ViewPollOption } from '../../../../pages/polls/view-models/poll-option' export class PollMetaComponent { public poll = input.required(); - public config = computed< - Partial< - ViewPollConfigApproval & - ViewPollConfigRatingApproval & - ViewPollConfigRatingScore & - ViewPollConfigSelection & - ViewPollConfigStvScottish - > - >(() => { - return this.poll().config || {}; + public config = rxResource< + ViewPollConfigApproval & + ViewPollConfigRatingApproval & + ViewPollConfigRatingScore & + ViewPollConfigSelection & + ViewPollConfigStvScottish, + ViewPoll + >({ + params: () => this.poll(), + stream: ({ params }) => params.config$ }); public hasGlobalOptionEnabled = computed(() => { - return this.config().allow_nota || this.config().min_options_amount === 0; + return this.config.value().allow_nota || this.poll().allow_empty; }); public generalApprovalAllowed = computed(() => { - return this.config().allow_nota && this.config().strike_out; + return this.config.value().allow_nota && this.config.value().strike_out; }); public generalRejectionAllowed = computed(() => { - return this.config().allow_nota && !this.config().strike_out; + return this.config.value().allow_nota && !this.config.value().strike_out; }); public generalAbstainAllowed = computed(() => { - return this.config().min_options_amount === 0; + return this.poll().allow_empty; }); public isListPoll = computed(() => { - return this.config().collection === PollConfigApproval.COLLECTION && !!this.poll().options?.length; + return this.config.value().collection === PollConfigApproval.COLLECTION && !!this.poll().options?.length; }); public isPersonPoll = computed(() => { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-approval/poll-result-approval.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-approval/poll-result-approval.component.ts index aceb8952130..36eaa43dd98 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-approval/poll-result-approval.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-approval/poll-result-approval.component.ts @@ -94,7 +94,7 @@ export class PollResultApprovalComponent extends PollResultBaseComponent(() => { - if (!this.config().onehundredPercentBaseNum) { + if ( + !this.config().onehundredPercentBaseNum || + this.onehundredPercentBase() === `yes_no` || + this.onehundredPercentBase() === `disabled` + ) { return null; } @@ -194,8 +198,11 @@ export class PollResultApprovalComponent extends PollResultBaseComponent(() => { - // TODO: Implement if available - return null; + if (this.config().onehundred_percent_base !== `entitled_present`) { + return null; + } + + return this.poll().entitled_users?.filter(u => u.present).length; }); public majorityReached = computed(() => { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-base.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-base.component.ts index 1ad1b9161fe..d085f280fb5 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-base.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-base.component.ts @@ -9,6 +9,7 @@ export const PERCENT_DECIMAL_PLACES = 3; @Directive() export abstract class PollResultBaseComponent { public poll = input.required(); + public config = input.required(); public results = computed(() => { if (!this.poll().result) { @@ -18,10 +19,6 @@ export abstract class PollResultBaseComponent { return this.poll()?.config?.parsedResult() || {}; }); - public config = computed(() => { - return this.poll().config; - }); - /** * Contains the poll options sorted by weight */ diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-approval/poll-result-rating-approval.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-approval/poll-result-rating-approval.component.ts index aadddd7cfad..a586e52caec 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-approval/poll-result-rating-approval.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-approval/poll-result-rating-approval.component.ts @@ -138,8 +138,11 @@ export class PollResultRatingApprovalComponent extends PollResultBaseComponent< }); public presentEntitledUsers = computed(() => { - // TODO: Implement if available - return null; + if (this.config().onehundred_percent_base !== `entitled_present`) { + return null; + } + + return this.poll().entitled_users?.filter(u => u.present).length; }); public globalRequiredMajority = computed(() => { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.html index 4f81f6a1a49..80b39bd7286 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.html @@ -1 +1 @@ - + diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.ts index 2c824705b14..f44d627d13a 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-rating-score/poll-result-rating-score.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed } from '@angular/core'; +import { ViewPollConfigSelection } from '@app/site/pages/meetings/pages/polls'; import { ViewPollConfigRatingScore } from '../../../../pages/polls/view-models/poll-config-rating-score'; import { RatingScorePollResult } from '../../../../pages/polls/view-models/poll-result-rating-score'; @@ -15,4 +16,8 @@ import { PollResultSelectionComponent } from '../poll-result-selection/poll-resu export class PollResultRatingScoreComponent extends PollResultBaseComponent< ViewPollConfigRatingScore, RatingScorePollResult -> {} +> { + public configSelection = computed(() => { + return this.config() as unknown as ViewPollConfigSelection; + }); +} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-selection/poll-result-selection.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-selection/poll-result-selection.component.ts index 2b6edb4a27b..aea06253f26 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result-selection/poll-result-selection.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result-selection/poll-result-selection.component.ts @@ -189,8 +189,11 @@ export class PollResultSelectionComponent extends PollResultBaseComponent< }); public presentEntitledUsers = computed(() => { - // TODO: Implement if available - return null; + if (this.config().onehundred_percent_base !== `entitled_present`) { + return null; + } + + return this.poll().entitled_users?.filter(u => u.present).length; }); public requiredMajority = computed(() => { @@ -202,17 +205,24 @@ export class PollResultSelectionComponent extends PollResultBaseComponent< return Math.ceil(this.config().onehundredPercentBaseNum / 2 + 1); } else if (this.config().required_majority === PollRequiredMajority.TwoThirdMajority) { return Math.ceil((this.config().onehundredPercentBaseNum * 2) / 3); + } else if (this.config().required_majority === PollRequiredMajority.SimpleMajority) { + const result = this.config().parsedResult(); + return Math.max( + ...Object.keys({ ...result, 0: 1 }) + .filter(k => !isNaN(+k) && !isNaN(+result[k])) + .map(k => +result[k]) + ); } return null; }); public generalAbstain = computed(() => { - if (this.config().min_options_amount !== 0) { + if (!this.poll().allow_empty) { return null; } - return +this.config().parsedResult()?.abstain || 0; + return +this.config().parsedResult()?.empty || 0; }); public generalAbstainPercent = computed(() => { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.html index d334e2b7437..ed9d0d60dcb 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.html @@ -1,14 +1,14 @@ @switch (configType()) { @case ('approval') { - + } @case ('rating_approval') { - + } @case ('rating_score') { - + } @case ('selection') { - + } } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.ts index ab886423fef..7ab6e3ee3cb 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-result/poll-result.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; +import { rxResource } from '@angular/core/rxjs-interop'; import { ViewPoll } from '@app/site/pages/meetings/pages/polls/view-models'; import { PollResultApprovalComponent } from '../poll-result-approval/poll-result-approval.component'; @@ -21,6 +22,11 @@ import { PollResultSelectionComponent } from '../poll-result-selection/poll-resu export class PollResultComponent { public poll = input.required(); + public config = rxResource({ + params: () => this.poll(), + stream: ({ params }) => params.config$ + }); + public configType = computed(() => { return this.poll().config?.method ?? `none`; }); diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-approval/poll-vote-rating-approval.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-approval/poll-vote-rating-approval.component.html index c967886b5ff..47aa23c2246 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-approval/poll-vote-rating-approval.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-approval/poll-vote-rating-approval.component.html @@ -1,6 +1,6 @@
- +
@if (poll().option_ids.length > 1) {
@@ -15,10 +15,7 @@ } @else {
} - -
-
{{ 'Available votes' | translate }}: {{ availableVotes() }}/{{ config()?.max_vote_sum }} @@ -29,7 +26,7 @@ {{ 'Votes' | translate }}
} - @if (config()?.max_yes_amount < config()?.max_vote_sum) { + @if (config()?.max_yes_amount > 0 && config()?.max_yes_amount < config()?.max_vote_sum) {
{{ 'Available yes votes' | translate }}: {{ config()?.max_yes_amount - yesVotesUsed() }}/{{ config()?.max_yes_amount }} @@ -98,7 +95,7 @@ }
-@if (config()?.min_vote_sum === 0) { +@if (poll().allow_empty) {

{{ 'OR' | translate }}
@@ -106,7 +103,7 @@
- @if (config().min_vote_sum === 0) { + @if (poll().allow_empty) {
{{ 'General abstain' | translate }} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-score/poll-vote-rating-score.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-score/poll-vote-rating-score.component.html index 51a89a85f94..e36814ce440 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-score/poll-vote-rating-score.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-rating-score/poll-vote-rating-score.component.html @@ -92,7 +92,7 @@ }
-@if (config()?.min_vote_sum === 0 && config()?.min_options_amount === 0) { +@if (poll().allow_empty) {

{{ 'OR' | translate }}
diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.html b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.html index 9d5369cee23..71de2f8d3bc 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.html @@ -83,7 +83,7 @@ }
-@if (config()?.min_vote_sum === 0 || config()?.allow_nota) { +@if (config()?.allow_nota || poll().allow_empty) {

{{ 'OR' | translate }}
@@ -110,7 +110,7 @@
} - @if (config().min_vote_sum === 0) { + @if (poll().allow_empty) {
{{ 'General abstain' | translate }} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.ts index c33d581f4e3..05cf12e4568 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/poll-vote-selection/poll-vote-selection.component.ts @@ -100,7 +100,7 @@ export class PollVoteSelectionComponent extends PollVoteBaseComponent { protected getFilterDefinitions(): OsFilter[] { return [ + { + property: `isPresent`, + label: _(`Is present`), + options: [ + { label: _(`Is present`), condition: true }, + { label: _(`Is not present`), condition: [false, null] } + ] + }, { property: `hasVoted`, label: _(`Voted`), diff --git a/client/src/app/site/pages/meetings/modules/poll/services/poll-pdf.service.ts b/client/src/app/site/pages/meetings/modules/poll/services/poll-pdf.service.ts index 364b5eb554a..34ab4cb2094 100644 --- a/client/src/app/site/pages/meetings/modules/poll/services/poll-pdf.service.ts +++ b/client/src/app/site/pages/meetings/modules/poll/services/poll-pdf.service.ts @@ -273,10 +273,10 @@ export class PollPdfService { isSummary: true }); } - if (config.min_options_amount === 0) { + if (config.poll.allow_empty) { rows.push({ label: this.translate.instant(`General abstain`), - value: this.formatAmountAndPercent(+selectionResult.abstain || 0, config.onehundredPercentBaseNum), + value: this.formatAmountAndPercent(+selectionResult.empty || 0, config.onehundredPercentBaseNum), isSummary: true }); } diff --git a/client/src/app/site/pages/meetings/modules/poll/services/poll.service/poll.service.ts b/client/src/app/site/pages/meetings/modules/poll/services/poll.service/poll.service.ts index b4e539dda68..5cdd7c95961 100644 --- a/client/src/app/site/pages/meetings/modules/poll/services/poll.service/poll.service.ts +++ b/client/src/app/site/pages/meetings/modules/poll/services/poll.service/poll.service.ts @@ -3,7 +3,6 @@ import { OptionData } from '@app/domain/models/poll/generic-poll'; import { BaseOnehundredPercentBase } from '@app/domain/models/poll/poll-config-types'; import { ABSTAIN_KEY, - CalculablePollKey, isPollTableData, NO_KEY, PollTableData, @@ -13,7 +12,6 @@ import { VotingResult, YES_KEY } from '@app/domain/models/poll/poll-constants'; -import { ChartData, ChartDate } from '@app/site/pages/meetings/modules/poll/components/chart/chart.component'; import { ViewPoll } from '@app/site/pages/meetings/pages/polls/view-models'; import { OrganizationSettingsService } from '@app/site/pages/organization/services/organization-settings.service'; import { ThemeService } from '@app/site/services/theme.service'; @@ -22,7 +20,6 @@ import { TranslateService } from '@ngx-translate/core'; import { ActiveMeetingService } from '../../../../services/active-meeting.service'; import { MeetingSettingsService } from '../../../../services/meeting-settings.service'; -const PollChartBarThickness = 20; const PERCENT_DECIMAL_PLACES = 3; @Service() @@ -248,64 +245,6 @@ export abstract class PollService { return base === `valid` || base === `cast` || base === `entitled` || base === `entitled_present`; } - public getSumTableKeys(_poll: ViewPoll): VotingResult[] { - return []; - /* - return [ - { - vote: `votesvalid`, - hide: poll.votesvalid === VOTE_UNDOCUMENTED, - showPercent: this.showPercentOfValidOrCast(poll) - }, - { - vote: `votesinvalid`, - hide: poll.votesinvalid === VOTE_UNDOCUMENTED || poll.type !== PollType.Analog, - showPercent: poll.config?.onehundred_percent_base === PollPercentBase.Cast - }, - { - vote: `votescast`, - hide: poll.votescast === VOTE_UNDOCUMENTED || poll.type !== PollType.Analog, - showPercent: poll.config?.onehundred_percent_base === PollPercentBase.Cast - } - ]; - */ - } - - public generateChartData(poll: ViewPoll): ChartData { - let fields = this.getPollDataFields(poll); - if (poll.config?.onehundred_percent_base === `yes_no` || !poll.config?.allow_abstain) { - fields = fields.filter(key => key === YES_KEY || key === NO_KEY); - } - - const data: ChartData = fields - .map( - key => - ({ - data: this.getResultFromPoll(poll, key), // 0: option, 1: global_option - label: key.toUpperCase(), - backgroundColor: this.themeService.getPollColor(key), - hoverBackgroundColor: this.themeService.getPollColor(key), - barThickness: PollChartBarThickness, - maxBarThickness: PollChartBarThickness - }) as ChartDate - ) - .filter(chartDate => !!chartDate.data[0] || !!chartDate.data[1]); - - return data; - } - - /** - * Extracts yes-no-abstain such as valid, invalids and totals from Poll and PollData-Objects - */ - protected getResultFromPoll(_poll: ViewPoll, _key: CalculablePollKey): (number | undefined)[] { - // return (poll ? [...poll.options, poll.global_option] : []).map(option => (option ? option[key] : undefined)); - return []; - } - - protected getPollDataFields(_poll: ViewPoll): CalculablePollKey[] { - throw new Error(`Method not implemented`); - } - public isVoteDocumented(vote: number): boolean { return vote !== null && vote !== undefined && vote !== VOTE_UNDOCUMENTED; } diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.html b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.html index 7a35b2e5f9d..9baf638a939 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.html +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.html @@ -4,7 +4,7 @@ [data]="pollData" [isEVotingEnabled]="isEVotingEnabled" [optionAmount]="options().length" - [optionEdit]="selectedTab() === 0" + [optionEdit]="true" >
@@ -14,7 +14,7 @@ [configType]="pollForm().selectedMethod()" [options]="analogPollOptions()" [pollConfigData]="pollForm().methodConfig()" - [pollData]="pollData" + [pollData]="currentPollData" />
} @@ -36,11 +36,11 @@
@if (isAnalogPoll()) { @if (analogPollFormOpen()) { - } @else { - } diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.ts index 5b0a1bb06ef..2bc52a40a2e 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-dialog/topic-poll-dialog.component.ts @@ -1,9 +1,8 @@ -import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; import { MatTabsModule } from '@angular/material/tabs'; import { djb2hash } from '@app/infrastructure/utils'; -import { collectionFromFqid } from '@app/infrastructure/utils/transform-functions'; import { BasePollDialogComponent, PollMethodPayload, @@ -14,8 +13,6 @@ import { PollFormComponent } from '@app/site/pages/meetings/modules/poll/compone import { PollService } from '@app/site/pages/meetings/modules/poll/services/poll.service'; import { TranslatePipe } from '@ngx-translate/core'; -const TAB_METHOD_MAP = [`selection`, `approval`]; - @Component({ selector: `os-topic-poll-dialog`, templateUrl: `./topic-poll-dialog.component.html`, @@ -37,25 +34,12 @@ export class TopicPollDialogComponent extends BasePollDialogComponent { return this.pollService.isElectronicVotingEnabled; } - public selectedTab = signal(0); - public options = computed(() => { return this.pollForm().form.options().value(); }); private pollService = inject(PollService); - public constructor() { - super(); - - if (this.pollData?.config_id) { - const collection = collectionFromFqid(this.pollData?.config_id); - this.selectedTab.set(TAB_METHOD_MAP.indexOf(collection.replace(`poll_config_`, ``))); - } else if (this.pollData?.config?.method) { - this.selectedTab.set(TAB_METHOD_MAP.indexOf(this.pollData.config.method)); - } - } - public override methodPayload(): PollMethodPayload { return { method: this.pollForm().selectedMethod(), diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/services/topic-poll.service.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/services/topic-poll.service.ts index 69eae86eda5..e61161f0fa4 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/services/topic-poll.service.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/services/topic-poll.service.ts @@ -37,7 +37,7 @@ export class TopicPollService extends PollService { public getDefaultPollData(contentObject?: Topic): Partial { const poll: Partial = { - title: this.translate.instant(`Poll`), + title: this.translate.instant(`Vote`), entitled_group_ids: Object.values(this.defaultGroupIds() ?? []), visibility: this.isElectronicVotingEnabled ? this.defaultPollType() : PollVisibility.Manually, live_voting_enabled: this.defaultEnableLiveVote(), diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.html b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.html index 313b073aee3..d8f11655a89 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.html +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.html @@ -14,7 +14,7 @@ [configType]="pollForm().selectedMethod()" [options]="analogPollOptions()" [pollConfigData]="pollForm().methodConfig()" - [pollData]="pollData" + [pollData]="currentPollData" />
} @@ -36,11 +36,11 @@
@if (isAnalogPoll()) { @if (analogPollFormOpen()) { - } @else { - } diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/services/assignment-poll.service.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/services/assignment-poll.service.ts index 5148daf72d2..0e9aedbb0f4 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/services/assignment-poll.service.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/services/assignment-poll.service.ts @@ -48,7 +48,7 @@ export class AssignmentPollService extends PollService { public getDefaultPollData(contentObject?: Assignment): Partial { const poll: Partial = { - title: this.translate.instant(`Poll`), + title: this.translate.instant(`Ballot`), entitled_group_ids: Object.values(this.defaultGroupIds() ?? []), visibility: this.isElectronicVotingEnabled ? this.defaultPollType() : PollVisibility.Manually, live_voting_enabled: this.defaultEnableLiveVote(), diff --git a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.ts deleted file mode 100644 index d5cbce0ad3b..00000000000 --- a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { Permission } from '@app/domain/definitions/permission'; -import { VOTE_UNDOCUMENTED } from '@app/domain/models/poll'; -import { PollData } from '@app/domain/models/poll/generic-poll'; -import { PollState, PollTableData } from '@app/domain/models/poll/poll-constants'; -import { ChartData } from '@app/site/pages/meetings/modules/poll/components/chart/chart.component'; -import { PollService } from '@app/site/pages/meetings/modules/poll/services/poll.service'; -import { OperatorService } from '@app/site/services/operator.service'; -import { ThemeService } from '@app/site/services/theme.service'; -import { BaseUiComponent } from '@app/ui/base/base-ui-component'; -import { combineLatestWith, map } from 'rxjs'; - -import { MotionPollService } from '../../services/motion-poll.service/motion-poll.service'; - -const CHART_DATA_SUBSCRIPTION_NAME = `chart_data_subscription`; - -@Component({ - selector: `os-motion-poll-detail-content`, - templateUrl: `./motion-poll-detail-content.component.html`, - styleUrls: [`./motion-poll-detail-content.component.scss`], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false -}) -export class MotionPollDetailContentComponent extends BaseUiComponent implements OnDestroy, OnInit { - private _poll!: PollData | null; - - public get chartData(): ChartData { - return this._chartData!; - } - - public get tableData(): PollTableData[] { - return this._tableData; - } - - @Input() - public set poll(pollData: PollData | null) { - this._poll = pollData; - if (this._poll) { - this.setupTableData(); - this.cd.markForCheck(); - } - } - - public get poll(): PollData | null { - return this._poll; - } - - @Input() - public title = ``; - - @Input() - public shouldShowEntitled = false; - - @Input() - public iconSize: `medium` | `large` | `gigantic` = `large`; - - @Input() - public inSlide = false; - - private get state(): PollState | null { - return this.poll?.state || null; - } - - public get hasResults(): boolean { - return this.isFinished || this.isPublished; - } - - public get isStarted(): boolean { - return this.state === PollState.Started; - } - - public get isFinished(): boolean { - return this.state === PollState.Finished; - } - - public get isPublished(): boolean { - return this.state === PollState.Published; - } - - public get canSeeResults(): boolean { - return this.operator.hasPerms(Permission.motionCanManagePolls) || this.isPublished; - } - - public get shouldShowChart(): boolean { - return this.pollService.shouldShowChart(this.poll); - } - - public get motionPollService(): PollService { - return this.pollService; - } - - public isPercentBaseEntitled = false; - public isPercentBaseEntitledPresent = false; - - private _tableData: PollTableData[] = []; - private _chartData: ChartData | null = null; - - public constructor( - private pollService: MotionPollService, - private operator: OperatorService, - private cd: ChangeDetectorRef, - private themeService: ThemeService - ) { - super(); - } - - public ngOnInit(): void { - this.subscriptions.push( - this._poll.options$.subscribe(() => { - this.setupTableData(); - }) - ); - } - - private setupTableData(): void { - this.updateSubscription( - CHART_DATA_SUBSCRIPTION_NAME, - this.pollService - .generateTableDataAsObservable(this.poll!) - .pipe( - combineLatestWith(this.themeService.currentGeneralColorsSubject), - map(([tableData, _]) => tableData) - ) - .subscribe(tableData => { - this._tableData = tableData.filter(result => result.value[0].amount !== VOTE_UNDOCUMENTED); - this.setChartData(); - this.cd.markForCheck(); - }) - ); - } - - private setChartData(): void { - this._chartData = this.pollService - .generateChartData(this.poll!) - .filter(result => result.data[0] !== VOTE_UNDOCUMENTED); - } -} diff --git a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-dialog/motion-poll-dialog.component.html b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-dialog/motion-poll-dialog.component.html index 9ef0e48e7d4..20ec623407a 100644 --- a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-dialog/motion-poll-dialog.component.html +++ b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-dialog/motion-poll-dialog.component.html @@ -11,7 +11,7 @@ configType="approval" [options]="analogPollOptions()" [pollConfigData]="approvalFormValue" - [pollData]="pollData" + [pollData]="currentPollData" />
} @@ -32,11 +32,11 @@
@if (isAnalogPoll()) { @if (analogPollFormOpen()) { - } @else { - } diff --git a/client/src/app/site/pages/meetings/pages/polls/components/poll-entitled-users/poll-entitled-user.component.ts b/client/src/app/site/pages/meetings/pages/polls/components/poll-entitled-users/poll-entitled-user.component.ts index 598c00ad5aa..f9dd6d5d4df 100644 --- a/client/src/app/site/pages/meetings/pages/polls/components/poll-entitled-users/poll-entitled-user.component.ts +++ b/client/src/app/site/pages/meetings/pages/polls/components/poll-entitled-users/poll-entitled-user.component.ts @@ -10,6 +10,8 @@ import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Id } from '@app/domain/definitions/key-types'; import { Identifiable } from '@app/domain/interfaces'; +import { PollState } from '@app/domain/models/poll'; +import { MeetingUserRepositoryService } from '@app/gateways/repositories/meeting_user'; import { BaseComponent } from '@app/site/base/base.component'; import { EntitledUsersListFilterService } from '@app/site/pages/meetings/modules/poll/services/entitled-user-filter.service'; import { ViewPoll } from '@app/site/pages/meetings/pages/polls/view-models'; @@ -18,9 +20,10 @@ import { HeadBarModule } from '@app/ui/modules/head-bar'; import { ListModule } from '@app/ui/modules/list'; import { PipesModule } from '@app/ui/pipes'; import { TranslatePipe } from '@ngx-translate/core'; -import { combineLatest, map, Observable, startWith, switchMap, tap } from 'rxjs'; +import { combineLatest, map, Observable, of, startWith, switchMap, tap } from 'rxjs'; import { ViewMeetingUser } from '../../../../view-models/view-meeting-user'; +import { ViewPollEntitledUser } from '../../view-models/poll-entitled-user'; export interface EntitledUserData extends Identifiable { meetingUser: ViewMeetingUser; @@ -58,19 +61,53 @@ export class PollEntitledUserComponent extends BaseComponent { public filterProps = [`meetingUser.user.getFullName`]; public entitledUsers$: Observable = toObservable(this.poll).pipe( - switchMap(poll => combineLatest([poll.entitled_users$, poll.ballot_users$.pipe(startWith([]))])), - map(([users, voted]) => { + switchMap(poll => + poll.state === PollState.Finished + ? combineLatest([ + of(poll), + poll.entitled_users$, + poll.ballot_users$.pipe(startWith([])), + of([] as ViewMeetingUser[]) + ]) + : combineLatest([ + of(poll), + of([] as ViewPollEntitledUser[]), + poll.ballot_users$.pipe(startWith([])), + this.userRepo.getViewModelListObservable().pipe( + map(users => { + return users.filter(mu => { + const countable = mu.user.isVoteCountable; + const inVoteGroup = poll.entitled_group_ids.intersect(mu.group_ids).length; + + return countable && inVoteGroup; + }); + }) + ) + ]) + ), + map(([poll, entitledUsersAtStop, voted, entitledUsers]) => { const votedSet = new Set(); for (const user of voted) { votedSet.add(user.represented_meeting_user_id); } - return users.map( + if (poll.state === PollState.Finished) { + return entitledUsersAtStop.map( + eUser => + ({ + meetingUser: eUser.meeting_user, + isPresent: eUser.present, + hasVoted: votedSet.has(eUser.meeting_user_id) + }) as EntitledUserData + ); + } + + return entitledUsers.map( eUser => ({ - meetingUser: eUser.meeting_user, - isPresent: false, // TODO: Implement or remove - hasVoted: votedSet.has(eUser.meeting_user_id) + meetingUser: eUser, + isPresent: eUser.user.is_present, + hasVoted: votedSet.has(eUser.id) }) as EntitledUserData ); }), @@ -80,4 +117,5 @@ export class PollEntitledUserComponent extends BaseComponent { public totalCount = signal(0); public filter = inject(EntitledUsersListFilterService); + public userRepo = inject(MeetingUserRepositoryService); } diff --git a/client/src/app/site/pages/meetings/pages/polls/polls.subscription.ts b/client/src/app/site/pages/meetings/pages/polls/polls.subscription.ts index b8650b0dc54..b6b5c9cb002 100644 --- a/client/src/app/site/pages/meetings/pages/polls/polls.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/polls/polls.subscription.ts @@ -31,6 +31,10 @@ export const pollModelRequest: BaseSimplifiedModelRequest = { idField: `ballot_user_ids`, fieldset: [`acting_meeting_user_id`, `represented_meeting_user_id`] }, + { + idField: `entitled_user_ids`, + fieldset: [`present`] + }, { idField: `option_ids`, fieldset: FULL_FIELDSET, diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-approval.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-approval.ts index eb06e2029cf..116d9d9c4d0 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-approval.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-approval.ts @@ -21,7 +21,11 @@ export class ViewPollConfigApproval extends BasePollConfigViewModel u.present).length ?? null; } return null; @@ -40,8 +44,8 @@ export class ViewPollConfigApproval extends BasePollConfigViewModel u.present).length ?? null; } return null; @@ -64,7 +64,7 @@ export class ViewPollConfigRatingApproval extends BasePollConfigViewModel< case 'entitled': return this.poll.entitled_user_ids?.length; case 'entitled_present': - return null; + return this.poll?.entitled_users.filter(u => u.present).length ?? null; } return null; diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-rating-score.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-rating-score.ts index 2d6c2578024..8e2a9479843 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-rating-score.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-rating-score.ts @@ -28,9 +28,9 @@ export class ViewPollConfigRatingScore extends BasePollConfigViewModel u.present).length ?? null; } return null; diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-selection.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-selection.ts index 1b35b48c091..ec0d92fb534 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-selection.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-config-selection.ts @@ -28,9 +28,9 @@ export class ViewPollConfigSelection extends BasePollConfigViewModel u.present).length ?? null; } return null; diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-rating-approval.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-rating-approval.ts index 4c7bb2fb542..1b367009a56 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-rating-approval.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-rating-approval.ts @@ -8,5 +8,5 @@ export class RatingApprovalPollResult extends BasePollResult { [key: number]: string; - public abstain?: string; + public empty?: string; } diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-selection.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-selection.ts index ee4b4716d5e..8f75b7a9aa0 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-selection.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/poll-result-selection.ts @@ -6,7 +6,7 @@ import { ViewPollConfigSelection } from './poll-config-selection'; export class SelectionPollResult extends BasePollResult { [key: number]: string; public nota?: string; - public abstain?: string; + public empty?: string; public constructor( public override config: ViewPollConfigSelection, @@ -15,7 +15,7 @@ export class SelectionPollResult extends BasePollResult { const isElectronicVotingEnabled = orgaSettings.instant(`enable_electronic_voting`); if (!isElectronicVotingEnabled && typeof value !== `string`) { @@ -1002,7 +1002,7 @@ export const meetingSettings: SettingsGroup[] = fillInSettingsDefaults([ label: _(`Default voting type`), type: `choice`, choiceI18nPrefix: 'poll_visibility', - choices: ['manually', 'open', 'secret', 'named'], + choices: ['manually', 'named', 'open', 'secret'], restrictionFn: (orgaSettings, value: any): any => { const isElectronicVotingEnabled = orgaSettings.instant(`enable_electronic_voting`); if (!isElectronicVotingEnabled && typeof value !== `string`) { @@ -1065,7 +1065,7 @@ export const meetingSettings: SettingsGroup[] = fillInSettingsDefaults([ label: _(`Default voting type`), type: `choice`, choiceI18nPrefix: 'poll_visibility', - choices: ['manually', 'open', 'secret', 'named'], + choices: ['manually', 'named', 'open', 'secret'], restrictionFn: (orgaSettings, value: any): any => { const isElectronicVotingEnabled = orgaSettings.instant(`enable_electronic_voting`); if (!isElectronicVotingEnabled && typeof value !== `string`) { diff --git a/meta b/meta index 0d6c93a564c..a11c29cebe1 160000 --- a/meta +++ b/meta @@ -1 +1 @@ -Subproject commit 0d6c93a564c38e4ce584a3d888c38cfc0e6d148f +Subproject commit a11c29cebe12c4fcce9ccb78825e7e5b6d1aa743