From 489129da5c1972062bfbb951357a9ba9813a1d8a Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 19 Nov 2025 12:31:33 +0100 Subject: [PATCH 01/25] Add option required majority to the poll forms --- .../app/domain/models/poll/poll-constants.ts | 6 ++++++ .../base-poll-form.component.html | 17 +++++++++++++++++ .../base-poll-form/base-poll-form.component.ts | 10 +++++++++- .../topic-poll-form.component.ts | 3 ++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/client/src/app/domain/models/poll/poll-constants.ts b/client/src/app/domain/models/poll/poll-constants.ts index b66939b7b9..d80b1970ab 100644 --- a/client/src/app/domain/models/poll/poll-constants.ts +++ b/client/src/app/domain/models/poll/poll-constants.ts @@ -204,6 +204,12 @@ export const VoteValuesVerbose = { A: `Abstain` }; +export const RequiredMajorityBaseVerbose = { + no_majority: _('No majority'), + two_third_majority: _('Two-thid majority'), + absolute_majority: _('Absolute majority') +}; + /** * Interface describes the possible data for the result-table. */ diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html index e2a0939c7b..9203347508 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html @@ -171,6 +171,23 @@

} +
+ @if (!hideSelects.requiredMajority) { + + {{ 'Required majority' | translate }} + + @for (option of requiredMajorityBases | keyvalue: keepEntryOrder; track option) { + + {{ option.value }} + + } + + + } +
+ +
+
@if (isEVotingSelected && isCreated && !hideSelects.backendDuration) { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts index e07858ce8a..20dee5ac66 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts @@ -18,7 +18,8 @@ import { PollPropertyVerbose, PollPropertyVerboseKey, PollType, - PollTypeVerbose + PollTypeVerbose, + RequiredMajorityBaseVerbose } from 'src/app/domain/models/poll'; import { isNumberRange } from 'src/app/infrastructure/utils/validators'; import { BaseComponent } from 'src/app/site/base/base.component'; @@ -41,6 +42,7 @@ export interface PollFormHideSelectsData { globalOptions?: boolean; hundredPercentBase?: boolean; backendDuration?: boolean; + requiredMajority?: boolean; } @Directive() @@ -68,6 +70,7 @@ export abstract class BasePollFormComponent extends BaseComponent implements OnI public percentBases: Record; public alternativePercentBases = PollPercentBaseVerbose; + public requiredMajorityBases = RequiredMajorityBaseVerbose; @Input() public set data(data: Partial) { @@ -188,6 +191,10 @@ export abstract class BasePollFormComponent extends BaseComponent implements OnI return this.contentForm.get(`onehundred_percent_base`); } + private get requiredMajority(): AbstractControl { + return this.contentForm.get(`required_majority`); + } + public abstract get hideSelects(): PollFormHideSelectsData; public get pollMethodChangedToListObservable(): Observable { @@ -526,6 +533,7 @@ export abstract class BasePollFormComponent extends BaseComponent implements OnI type: [``, Validators.required], pollmethod: [``, Validators.required], onehundred_percent_base: [``, Validators.required], + required_majority: [``, Validators.required], votes_amount: this.getVotesAmountControl(), entitled_group_ids: [], backend: [], diff --git a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-form/topic-poll-form.component.ts b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-form/topic-poll-form.component.ts index b37849b2a7..a24eedbe54 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-form/topic-poll-form.component.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/modules/topics/modules/topic-poll/components/topic-poll-form/topic-poll-form.component.ts @@ -19,7 +19,8 @@ export class TopicPollFormComponent extends BasePollFormComponent { pollMethod: true, globalOptions: true, hundredPercentBase: true, - backendDuration: true + backendDuration: true, + requiredMajority: true }; } } From 4d5aeb4df38bbe868f8f06ab901deab1d1dded10 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Fri, 28 Nov 2025 10:06:12 +0100 Subject: [PATCH 02/25] First work on logic for required majority --- client/src/app/domain/models/meetings/meeting.ts | 14 +++++++++++++- client/src/app/domain/models/poll/generic-poll.ts | 11 ++++++++++- .../src/app/domain/models/poll/poll-constants.ts | 8 +++++++- client/src/app/domain/models/poll/poll.ts | 5 ++++- .../poll-repository.service.ts | 9 ++++++++- .../base-poll-form/base-poll-form.component.html | 2 +- .../base-poll-form/base-poll-form.component.ts | 2 +- .../meetings/pages/polls/view-models/view-poll.ts | 5 +++++ meta | 2 +- 9 files changed, 50 insertions(+), 8 deletions(-) diff --git a/client/src/app/domain/models/meetings/meeting.ts b/client/src/app/domain/models/meetings/meeting.ts index 790697e5eb..01c0b56ff3 100644 --- a/client/src/app/domain/models/meetings/meeting.ts +++ b/client/src/app/domain/models/meetings/meeting.ts @@ -4,7 +4,13 @@ import { HasProperties } from '../../interfaces/has-properties'; import { AgendaItemCreation, AgendaItemType } from '../agenda/agenda-item'; import { BaseModel } from '../base/base-model'; import { ChangeRecoMode, LineNumberingMode } from '../motions/motions.constants'; -import { PollBackendDurationType, PollMethod, PollPercentBase, PollType } from '../poll/poll-constants'; +import { + PollBackendDurationType, + PollMethod, + PollPercentBase, + PollType, + RequiredMajorityBase +} from '../poll/poll-constants'; import { ApplauseType } from './applause'; import { BallotPaperSelection, @@ -148,6 +154,7 @@ export class Settings { public motion_poll_ballot_paper_number!: number; public motion_poll_default_type!: PollType; public motion_poll_default_onehundred_percent_base!: PollPercentBase; + public motion_poll_default_required_majority!: RequiredMajorityBase; public motion_poll_default_group_ids!: Id[]; // (group/used_as_motion_poll_default_id)[]; public motion_poll_default_backend!: PollBackendDurationType; public motion_poll_default_method!: PollMethod; @@ -185,6 +192,7 @@ export class Settings { public assignment_poll_default_type!: PollType; public assignment_poll_default_method!: PollMethod; public assignment_poll_default_onehundred_percent_base!: PollPercentBase; + public assignment_poll_default_required_majority!: RequiredMajorityBase; public assignment_poll_default_group_ids!: Id[]; // (group/used_as_assignment_poll_default_id)[]; public assignment_poll_default_backend!: PollBackendDurationType; @@ -406,11 +414,14 @@ export class Meeting extends BaseModel { `motions_export_preamble`, `motions_export_submitter_recommendation`, `motions_export_follow_recommendation`, + `motions_enable_restricted_editor_for_manager`, + `motions_enable_restricted_editor_for_non_manager`, `motion_poll_ballot_paper_selection`, `motion_poll_ballot_paper_number`, `motion_poll_default_type`, `motion_poll_default_method`, `motion_poll_default_onehundred_percent_base`, + `motion_poll_default_required_majority`, `motion_poll_default_group_ids`, `motion_poll_default_backend`, `motion_poll_projection_name_order_first`, @@ -445,6 +456,7 @@ export class Meeting extends BaseModel { `assignment_poll_default_type`, `assignment_poll_default_method`, `assignment_poll_default_onehundred_percent_base`, + `assignment_poll_default_required_majority`, `assignment_poll_default_group_ids`, `assignment_poll_default_backend`, `poll_ballot_paper_selection`, diff --git a/client/src/app/domain/models/poll/generic-poll.ts b/client/src/app/domain/models/poll/generic-poll.ts index 8675241a8c..3bf8602968 100644 --- a/client/src/app/domain/models/poll/generic-poll.ts +++ b/client/src/app/domain/models/poll/generic-poll.ts @@ -5,7 +5,15 @@ import { ViewMotion } from 'src/app/site/pages/meetings/pages/motions'; import { ViewPollCandidateList } from 'src/app/site/pages/meetings/pages/polls/view-models/view-poll-candidate-list'; import { ViewUser } from 'src/app/site/pages/meetings/view-models/view-user'; -import { EntitledUsersEntry, PollClassType, PollMethod, PollPercentBase, PollState, PollType } from './poll-constants'; +import { + EntitledUsersEntry, + PollClassType, + PollMethod, + PollPercentBase, + PollState, + PollType, + RequiredMajorityBase +} from './poll-constants'; export type PollContentObject = ViewAssignment | ViewMotion | ViewTopic; @@ -19,6 +27,7 @@ export interface PollData { pollmethod: PollMethod; state: PollState; onehundred_percent_base: PollPercentBase; + required_majority?: RequiredMajorityBase; votesvalid: number; votesinvalid: number; votescast: number; diff --git a/client/src/app/domain/models/poll/poll-constants.ts b/client/src/app/domain/models/poll/poll-constants.ts index d80b1970ab..9275eb5594 100644 --- a/client/src/app/domain/models/poll/poll-constants.ts +++ b/client/src/app/domain/models/poll/poll-constants.ts @@ -91,6 +91,12 @@ export enum PollPercentBase { Disabled = `disabled` } +export enum RequiredMajorityBase { + no_majority = `no_majority`, + two_third_majority = `two_third_majority`, + absolute_majority = `absolute_majority` +} + export interface EntitledUsersEntry { user_id: number; present: boolean; @@ -206,7 +212,7 @@ export const VoteValuesVerbose = { export const RequiredMajorityBaseVerbose = { no_majority: _('No majority'), - two_third_majority: _('Two-thid majority'), + two_third_majority: _('Two-third majority'), absolute_majority: _('Absolute majority') }; diff --git a/client/src/app/domain/models/poll/poll.ts b/client/src/app/domain/models/poll/poll.ts index 306bb4c39a..a48e840943 100644 --- a/client/src/app/domain/models/poll/poll.ts +++ b/client/src/app/domain/models/poll/poll.ts @@ -11,7 +11,8 @@ import { PollMethod, PollPercentBase, PollState, - PollType + PollType, + RequiredMajorityBase } from './poll-constants'; export class Poll extends BaseDecimalModel { @@ -29,6 +30,7 @@ export class Poll extends BaseDecimalModel { public live_votes: Record; public live_voting_enabled: number[]; public onehundred_percent_base!: PollPercentBase; + public required_majority!: RequiredMajorityBase; /** * TODO: @@ -166,6 +168,7 @@ export class Poll extends BaseDecimalModel { `global_no`, `global_abstain`, `onehundred_percent_base`, + `required_majority`, `votesvalid`, `votesinvalid`, `votescast`, 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 46e1dddc83..8a991d42a9 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 @@ -50,6 +50,7 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository {{ 'Required majority' | translate }} - @for (option of requiredMajorityBases | keyvalue: keepEntryOrder; track option) { + @for (option of requiredMajorityBaseVerbose | keyvalue: keepEntryOrder; track option.key) { {{ option.value }} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts index 20dee5ac66..fa4d739ee0 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts @@ -70,7 +70,7 @@ export abstract class BasePollFormComponent extends BaseComponent implements OnI public percentBases: Record; public alternativePercentBases = PollPercentBaseVerbose; - public requiredMajorityBases = RequiredMajorityBaseVerbose; + public requiredMajorityBaseVerbose = RequiredMajorityBaseVerbose; @Input() public set data(data: Partial) { diff --git a/client/src/app/site/pages/meetings/pages/polls/view-models/view-poll.ts b/client/src/app/site/pages/meetings/pages/polls/view-models/view-poll.ts index 674526e657..1678995c76 100644 --- a/client/src/app/site/pages/meetings/pages/polls/view-models/view-poll.ts +++ b/client/src/app/site/pages/meetings/pages/polls/view-models/view-poll.ts @@ -10,6 +10,7 @@ import { PollStateVerbose, PollType, PollTypeVerbose, + RequiredMajorityBaseVerbose, VOTE_MAJORITY } from 'src/app/domain/models/poll'; import { Poll } from 'src/app/domain/models/poll/poll'; @@ -72,6 +73,10 @@ export class ViewPoll return PollPercentBaseVerbose[this.onehundred_percent_base]; } + public get requiredMajorityBaseVerbose(): string { + return RequiredMajorityBaseVerbose[this.required_majority]; + } + public get pollClassTypeVerbose(): string { return this.pollClassType ? PollClassTypeVerbose[this.pollClassType] : ``; } diff --git a/meta b/meta index 1290188663..7586ade953 160000 --- a/meta +++ b/meta @@ -1 +1 @@ -Subproject commit 12901886637c61c4bb26d459498aabdc9bc78335 +Subproject commit 7586ade953b0f13600cd19dce59632b7c9601f89 From 84ef6fbdb10d13400f137ae34666dcb117af47cc Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Fri, 28 Nov 2025 10:32:58 +0100 Subject: [PATCH 03/25] Add required majority to poll-meta-info --- .../poll/base/base-poll-meta-information.component.ts | 6 +++++- .../assignment-poll-meta-info.component.html | 5 +++++ .../motion-poll-meta-information.component.html | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/base/base-poll-meta-information.component.ts b/client/src/app/site/pages/meetings/modules/poll/base/base-poll-meta-information.component.ts index 3de59e2616..af5bed3443 100644 --- a/client/src/app/site/pages/meetings/modules/poll/base/base-poll-meta-information.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/base/base-poll-meta-information.component.ts @@ -1,5 +1,5 @@ import { Directive, Input } from '@angular/core'; -import { PollBackendDurationChoices } from 'src/app/domain/models/poll/poll-constants'; +import { PollBackendDurationChoices, RequiredMajorityBaseVerbose } from 'src/app/domain/models/poll/poll-constants'; import { ViewPoll } from 'src/app/site/pages/meetings/pages/polls'; @Directive() @@ -8,4 +8,8 @@ export abstract class BasePollMetaInformationComponent { @Input() public poll!: ViewPoll; + + public get requiredMajorityVerbose(): string { + return RequiredMajorityBaseVerbose[this.poll.required_majority]; + } } diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html index 8902bdf7c9..abc8815e17 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html @@ -109,6 +109,11 @@ {{ '100% base' | translate }}: {{ poll.percentBaseVerbose | translate }}
} + + @if (poll.required_majority && poll.required_majority !== 'no_majority') { + {{ 'Required majority' | translate }}: {{ requiredMajorityVerbose | translate }} +
+ } @if (poll.backend) { diff --git a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-meta-information/motion-poll-meta-information.component.html b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-meta-information/motion-poll-meta-information.component.html index e3995c7f63..a2a9ffd1cd 100644 --- a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-meta-information/motion-poll-meta-information.component.html +++ b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-meta-information/motion-poll-meta-information.component.html @@ -9,6 +9,11 @@ } {{ '100% base' | translate }}: {{ poll.percentBaseVerbose | translate }} + + @if (poll.required_majority && poll.required_majority !== 'no_majority') { + {{ 'Required majority' | translate }}: {{ requiredMajorityVerbose | translate }} +
+ } @if (poll.backend) { From 12ccc4bdbb0f1aa46d9ba55b484b3282cdfa9860 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Tue, 2 Dec 2025 12:59:11 +0100 Subject: [PATCH 04/25] Add isRequiredMajority method to poll service --- .../poll/services/poll.service/poll.service.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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 a0e2c85a42..f24d1c151a 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 @@ -18,6 +18,7 @@ import { PollTypeVerbose, PollTypeVerboseKey, PollValues, + RequiredMajorityBase, VOTE_MAJORITY, VOTE_UNDOCUMENTED, VotingResult, @@ -279,6 +280,23 @@ export abstract class PollService { return ``; } + public isRequiredMajority(value: number, poll: PollData, row?: OptionData): boolean { + const totalByBase = this.getPercentBase(poll, row); + if (!totalByBase || totalByBase === 0) { + return false; + } + switch (poll.required_majority) { + case RequiredMajorityBase.no_majority: + return false; + case RequiredMajorityBase.two_third_majority: + return value > (totalByBase * 2) / 3; + case RequiredMajorityBase.absolute_majority: + return value > totalByBase / 2; + default: + return false; + } + } + /** * @deprecated Please rewrite this function * From 7b87db99a3da0abab425ba04af09a0aef613bb3f Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Tue, 2 Dec 2025 15:16:00 +0100 Subject: [PATCH 05/25] Add chart icon to the single option chart table --- .../single-option-chart-table.component.html | 9 +++++++++ .../single-option-chart-table.component.scss | 14 ++++++++++++++ .../single-option-chart-table.component.ts | 3 +++ 3 files changed, 26 insertions(+) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html index b019f10440..c0230115bd 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html @@ -143,6 +143,15 @@ @if (shouldShowChart) {
+ @if (shouldShowChartIcon === 'check') { +
+ check_circle +
+ } @else if (shouldShowChartIcon === 'cancel') { +
+ cancel +
+ }
}
diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss index fd816fba76..8b76344b78 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss @@ -168,4 +168,18 @@ justify-self: right; margin-right: 50px; } + + .majority-icon-check { + position: relative; + left: 90px; + top: -142px; + color: var(--theme-yes); + } + + .majority-icon-cancel { + position: relative; + left: 90px; + top: -142px; + color: var(--theme-no); + } } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts index 5a31facf53..dcb12ecb22 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts @@ -73,6 +73,9 @@ export class SingleOptionChartTableComponent { @Input() public shouldShowEntitledPresent = false; + @Input() + public shouldShowChartIcon: `check` | `cancel` | null = null; + @Input() public title = ``; From 9bf749a6d0af6f13f6f41bbc47fc0847f7e22682 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Tue, 2 Dec 2025 15:16:59 +0100 Subject: [PATCH 06/25] Use shouldShowChartIcon to show required majority in motion polls. --- .../poll/services/poll.service/poll.service.ts | 4 ++-- .../motion-poll-detail-content.component.html | 1 + .../motion-poll-detail-content.component.ts | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) 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 f24d1c151a..debab68989 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 @@ -289,9 +289,9 @@ export abstract class PollService { case RequiredMajorityBase.no_majority: return false; case RequiredMajorityBase.two_third_majority: - return value > (totalByBase * 2) / 3; + return value >= (totalByBase * 2) / 3; case RequiredMajorityBase.absolute_majority: - return value > totalByBase / 2; + return value >= totalByBase / 2; default: return false; } diff --git a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.html b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.html index 03e6438364..95441a1248 100644 --- a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.html +++ b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/components/motion-poll-detail-content/motion-poll-detail-content.component.html @@ -14,6 +14,7 @@ [inSlide]="inSlide" [poll]="poll" [pollService]="motionPollService" + [shouldShowChartIcon]="showRequiredMajority" [shouldShowEntitled]="isPercentBaseEntitled || shouldShowEntitled" [shouldShowEntitledPresent]="isPercentBaseEntitledPresent" [shouldShowHead]="true" 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 index 2f2747ea62..f349317687 100644 --- 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 @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { combineLatestWith, map } from 'rxjs'; import { Permission } from 'src/app/domain/definitions/permission'; -import { VOTE_UNDOCUMENTED } from 'src/app/domain/models/poll'; +import { RequiredMajorityBase, VOTE_UNDOCUMENTED } from 'src/app/domain/models/poll'; import { PollData } from 'src/app/domain/models/poll/generic-poll'; import { PollState, PollTableData } from 'src/app/domain/models/poll/poll-constants'; import { ChartData } from 'src/app/site/pages/meetings/modules/poll/components/chart/chart.component'; @@ -134,4 +134,17 @@ export class MotionPollDetailContentComponent extends BaseUiComponent implements .generateChartData(this.poll!) .filter(result => result.data[0] !== VOTE_UNDOCUMENTED); } + + public get showRequiredMajority(): `check` | `cancel` | null { + if ( + this.poll.required_majority === RequiredMajorityBase.absolute_majority || + this.poll.required_majority === RequiredMajorityBase.two_third_majority + ) { + if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { + return `check`; + } + return `cancel`; + } + return null; + } } From 7e433111de99d3a92b54be7de7f9179b7e262c81 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Tue, 2 Dec 2025 15:34:28 +0100 Subject: [PATCH 07/25] Update isRequiredMajority to use greater than limit --- .../modules/poll/services/poll.service/poll.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 debab68989..f24d1c151a 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 @@ -289,9 +289,9 @@ export abstract class PollService { case RequiredMajorityBase.no_majority: return false; case RequiredMajorityBase.two_third_majority: - return value >= (totalByBase * 2) / 3; + return value > (totalByBase * 2) / 3; case RequiredMajorityBase.absolute_majority: - return value >= totalByBase / 2; + return value > totalByBase / 2; default: return false; } From 11992cad782f8ee6e84e04a52315355877b42242 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 08:52:30 +0100 Subject: [PATCH 08/25] Changes to the chart icons --- .../single-option-chart-table.component.html | 4 ++-- .../single-option-chart-table.component.scss | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html index c0230115bd..9acf45202e 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html @@ -145,11 +145,11 @@ @if (shouldShowChartIcon === 'check') {
- check_circle + check
} @else if (shouldShowChartIcon === 'cancel') {
- cancel + close
}
diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss index 8b76344b78..aa3b745ac2 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss @@ -171,15 +171,29 @@ .majority-icon-check { position: relative; - left: 90px; - top: -142px; + left: 60px; + top: -170px; color: var(--theme-yes); + & mat-icon { + font-size: 80px; + width: 80px; + min-width: 80px; + height: 80px; + margin-top: 0 !important; + } } .majority-icon-cancel { position: relative; - left: 90px; - top: -142px; + left: 60px; + top: -170px; color: var(--theme-no); + & mat-icon { + font-size: 80px; + width: 80px; + min-width: 80px; + height: 80px; + margin-top: 0 !important; + } } } From 7611410b62be977126d232d32b489127da1e7ad4 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 10:40:27 +0100 Subject: [PATCH 09/25] Move chart icon into the chart component For use of position 'absolute'. --- .../components/chart/chart.component.html | 9 ++++++ .../components/chart/chart.component.scss | 18 ++++++++++++ .../poll/components/chart/chart.component.ts | 3 ++ .../single-option-chart-table.component.html | 11 +------- .../single-option-chart-table.component.scss | 28 ------------------- 5 files changed, 31 insertions(+), 38 deletions(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html index 775624767e..e9bba194b9 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html @@ -9,5 +9,14 @@ [options]="chartOptions" [type]="type" > + @if (chartIcon === 'check') { +
+ check +
+ } @else if (chartIcon === 'cancel') { +
+ close +
+ } } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.scss b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.scss index 6139970a50..084a1e76ac 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.scss +++ b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.scss @@ -4,3 +4,21 @@ height: 100%; margin: auto; } +.chart-icon { + position: absolute; + left: 60px; + top: 90px; + & mat-icon { + font-size: 80px; + width: 80px; + min-width: 80px; + height: 80px; + margin-top: 0 !important; + } + &.color-green { + color: var(--theme-yes); + } + &.color-red { + color: var(--theme-no); + } +} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts index baaf798006..a5a9fb1653 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts @@ -54,6 +54,9 @@ export class ChartComponent { this._circleColors = colors; } + @Input() + public chartIcon: null | `check` | `cancel` = null; + private _circleColors: { backgroundColor?: string[]; hoverBackgroundColor?: string[] }[]; public colors: { backgroundColor?: string[]; hoverBackgroundColor?: string[] }[]; diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html index 9acf45202e..a6f653abb2 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.html @@ -142,16 +142,7 @@ @if (shouldShowChart) {
- - @if (shouldShowChartIcon === 'check') { -
- check -
- } @else if (shouldShowChartIcon === 'cancel') { -
- close -
- } +
} diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss index aa3b745ac2..fd816fba76 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.scss @@ -168,32 +168,4 @@ justify-self: right; margin-right: 50px; } - - .majority-icon-check { - position: relative; - left: 60px; - top: -170px; - color: var(--theme-yes); - & mat-icon { - font-size: 80px; - width: 80px; - min-width: 80px; - height: 80px; - margin-top: 0 !important; - } - } - - .majority-icon-cancel { - position: relative; - left: 60px; - top: -170px; - color: var(--theme-no); - & mat-icon { - font-size: 80px; - width: 80px; - min-width: 80px; - height: 80px; - margin-top: 0 !important; - } - } } From 25722b6284c82f737f73e94b82ae4def46680f78 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 12:59:18 +0100 Subject: [PATCH 10/25] Add the two meeting settings --- .../meeting-settings-definitions.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/services/meeting-settings-definition.service/meeting-settings-definitions.ts b/client/src/app/site/pages/meetings/services/meeting-settings-definition.service/meeting-settings-definitions.ts index 25dda247b8..4a691a88a6 100644 --- a/client/src/app/site/pages/meetings/services/meeting-settings-definition.service/meeting-settings-definitions.ts +++ b/client/src/app/site/pages/meetings/services/meeting-settings-definition.service/meeting-settings-definitions.ts @@ -6,7 +6,8 @@ import { MotionWorkflow } from 'src/app/domain/models/motions/motion-workflow'; import { PollBackendDurationChoices, PollPercentBaseVerbose, - PollTypeVerbose + PollTypeVerbose, + RequiredMajorityBaseVerbose } from 'src/app/domain/models/poll/poll-constants'; import { ObjectReplaceKeysConfig } from 'src/app/infrastructure/utils'; import { createEmailValidator } from 'src/app/infrastructure/utils/validators/email'; @@ -829,6 +830,12 @@ export const meetingSettings: SettingsGroup[] = fillInSettingsDefaults([ type: `choice`, choices: PollPercentBaseVerbose }, + { + key: `motion_poll_default_required_majority`, + label: _(`Default required majority`), + type: `choice`, + choices: RequiredMajorityBaseVerbose + }, { key: `motion_poll_default_backend`, label: _(`Default voting duration`), @@ -934,6 +941,12 @@ export const meetingSettings: SettingsGroup[] = fillInSettingsDefaults([ type: `choice`, choices: PollPercentBaseVerbose }, + { + key: `assignment_poll_default_required_majority`, + label: _(`Default required majority`), + type: `choice`, + choices: RequiredMajorityBaseVerbose + }, { key: `assignment_poll_default_backend`, label: _(`Default voting duration`), From 6856de58740ce9caa94d6ae474c68dd12a50696f Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 13:12:45 +0100 Subject: [PATCH 11/25] Set default required majority values in the poll forms --- .../assignment-poll/services/assignment-poll.service.ts | 7 ++++++- .../services/motion-poll.service/motion-poll.service.ts | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 fb461357e9..f8f56b6f67 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 @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { _ } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core'; import { Assignment } from 'src/app/domain/models/assignments/assignment'; -import { PollPercentBaseVerboseKey, PollTypeVerboseKey } from 'src/app/domain/models/poll'; +import { PollPercentBaseVerboseKey, PollTypeVerboseKey, RequiredMajorityBase } from 'src/app/domain/models/poll'; import { OptionData, PollData } from 'src/app/domain/models/poll/generic-poll'; import { Poll } from 'src/app/domain/models/poll/poll'; import { @@ -36,6 +36,7 @@ export class AssignmentPollService extends PollService { public defaultPercentBase: PollPercentBase | undefined; public defaultPollType: PollType | undefined; public defaultGroupIds: number[] = []; + public defaultRequiredMajority: RequiredMajorityBase; public constructor( pollServiceMapper: PollServiceMapperService, @@ -63,12 +64,16 @@ export class AssignmentPollService extends PollService { this.meetingSettingsService .get(`assignment_poll_enable_max_votes_per_option`) .subscribe(enable_max_votes_per_option => (this.enableMaxVotesPerOption = enable_max_votes_per_option)); + this.meetingSettingsService + .get(`assignment_poll_default_required_majority`) + .subscribe(req_maj => (this.defaultRequiredMajority = req_maj)); } public getDefaultPollData(contentObject?: Assignment): Partial { const poll: Partial = { title: this.translate.instant(`Ballot`), onehundred_percent_base: this.defaultPercentBase, + required_majority: this.defaultRequiredMajority ?? RequiredMajorityBase.no_majority, entitled_group_ids: Object.values(this.defaultGroupIds ?? []), pollmethod: this.defaultPollMethod, type: this.isElectronicVotingEnabled ? this.defaultPollType : PollType.Analog diff --git a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/services/motion-poll.service/motion-poll.service.ts b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/services/motion-poll.service/motion-poll.service.ts index cad9479845..12edb886ac 100644 --- a/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/services/motion-poll.service/motion-poll.service.ts +++ b/client/src/app/site/pages/meetings/pages/motions/modules/motion-poll/services/motion-poll.service/motion-poll.service.ts @@ -13,6 +13,7 @@ import { PollPercentBase, PollTableData, PollType, + RequiredMajorityBase, VOTE_MAJORITY, VotingResult, YES_KEY @@ -38,6 +39,7 @@ export interface TableDataEntryCreationInput { export class MotionPollService extends PollService { public defaultPercentBase!: PollPercentBase; public defaultPollMethod: PollMethod | undefined; + public defaultRequiredMajority: RequiredMajorityBase; public defaultPollType!: PollType; public defaultGroupIds!: number[]; @@ -55,11 +57,15 @@ export class MotionPollService extends PollService { this.meetingSettingsService .get(`motion_poll_default_method`) .subscribe(method => (this.defaultPollMethod = method)); + this.meetingSettingsService + .get(`motion_poll_default_required_majority`) + .subscribe(req_maj => (this.defaultRequiredMajority = req_maj)); } public getDefaultPollData(contentObject?: Motion): Partial { const poll: Partial = { onehundred_percent_base: this.defaultPercentBase, + required_majority: this.defaultRequiredMajority ?? RequiredMajorityBase.no_majority, entitled_group_ids: Object.values(this.defaultGroupIds ?? []), type: this.isElectronicVotingEnabled ? this.defaultPollType : PollType.Analog, pollmethod: this.defaultPollMethod From 0365b5b495623dc338627ace362fc09db3576bed Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 13:30:53 +0100 Subject: [PATCH 12/25] Add display of required majority to single assignment poll --- .../assignment-poll-detail-content.component.html | 1 + .../assignment-poll-detail-content.component.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html index dfa936bcd1..9ce137a028 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html @@ -148,6 +148,7 @@

[inSlide]="inSlide" [poll]="poll" [pollService]="assignmentPollService" + [shouldShowChartIcon]="showRequiredMajority" [shouldShowEntitled]="isPercentBaseEntitled" [shouldShowHead]="false" [tableData]="reformedTableData" diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 1c9c070e72..ea1085d28a 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -7,6 +7,7 @@ import { PollPercentBase, PollState, PollTableData, + RequiredMajorityBase, VotingResult } from 'src/app/domain/models/poll/poll-constants'; import { ChartData } from 'src/app/site/pages/meetings/modules/poll/components/chart/chart.component'; @@ -202,6 +203,20 @@ export class AssignmentPollDetailContentComponent implements OnInit { this._chartData = this.pollService.generateChartData(this.poll).filter(option => option.data[0] > 0); } + public get showRequiredMajority(): `check` | `cancel` | null { + if ( + (this.poll.required_majority === RequiredMajorityBase.absolute_majority || + this.poll.required_majority === RequiredMajorityBase.two_third_majority) && + this._chartData[0].label === 'YES' + ) { + if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { + return `check`; + } + return `cancel`; + } + return null; + } + public getVoteClass(votingResult: VotingResult): string { const votingClass = votingResult.vote as string; if (this.isMethodN && votingClass === `no`) { From 6579b18d0ea5adfccff0697a401dd4db65f5eac7 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 3 Dec 2025 15:34:32 +0100 Subject: [PATCH 13/25] Add getRequiredMajorityBase and update isRequiredMajority --- .../services/poll.service/poll.service.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) 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 f24d1c151a..e1fedd5ce4 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 @@ -280,21 +280,27 @@ export abstract class PollService { return ``; } - public isRequiredMajority(value: number, poll: PollData, row?: OptionData): boolean { + public getRequiredMajorityBase(poll: PollData, row?: OptionData): number | null { const totalByBase = this.getPercentBase(poll, row); - if (!totalByBase || totalByBase === 0) { - return false; + if (!totalByBase) { + return null; } switch (poll.required_majority) { - case RequiredMajorityBase.no_majority: - return false; - case RequiredMajorityBase.two_third_majority: - return value > (totalByBase * 2) / 3; case RequiredMajorityBase.absolute_majority: - return value > totalByBase / 2; + return totalByBase / 2; + case RequiredMajorityBase.two_third_majority: + return (totalByBase * 2) / 3; default: - return false; + return null; + } + } + + public isRequiredMajority(value: number, poll: PollData, row?: OptionData): boolean { + const requiredMajorityBase = this.getRequiredMajorityBase(poll, row); + if (requiredMajorityBase === null) { + return false; } + return value > requiredMajorityBase; } /** From 4e1ab8feebc2059c22c04f5b45e15564084b51a8 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Thu, 4 Dec 2025 11:34:36 +0100 Subject: [PATCH 14/25] Add check icon and extra row to assignment result table --- ...ignment-poll-detail-content.component.html | 24 +++++++++++++++++++ ...ignment-poll-detail-content.component.scss | 5 ++++ ...ssignment-poll-detail-content.component.ts | 17 ++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html index 9ce137a028..24d370ba3f 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html @@ -7,6 +7,7 @@ + @if (showYHeader) { @if (!isMethodY) { @@ -63,6 +64,15 @@ } + + @if ( + row.class === 'user' && + !isMethodN && + showRequiredMajorityInTable(getVoteAmount(row.value[0], row), poll) + ) { + check + } + @for (vote of filterRelevantResults(row.value); track vote) {
@@ -92,6 +102,7 @@ {{ 'Entitled users' | translate }} +
@@ -108,6 +119,7 @@ {{ 'Entitled present users' | translate }} +
@@ -120,6 +132,18 @@ } + @if (getRequiredMajorityBase(poll)) { + + + {{ 'Required majority' | translate }} + + +
+ {{ getRequiredMajorityBase(poll) }} +
+ + + } } diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss index 107cd6f36b..d8cefc4f00 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss @@ -33,6 +33,11 @@ text-align: left; } + .check-icon { + width: 20px; + color: var(--theme-yes); + } + .user + .sums { td { padding-top: 0.5em; diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index ea1085d28a..09111951ba 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { auditTime, combineLatest, filter, iif, map, NEVER, startWith, switchMap } from 'rxjs'; import { Permission } from 'src/app/domain/definitions/permission'; -import { PollData } from 'src/app/domain/models/poll/generic-poll'; +import { OptionData, PollData } from 'src/app/domain/models/poll/generic-poll'; import { PollMethod, PollPercentBase, @@ -217,6 +217,21 @@ export class AssignmentPollDetailContentComponent implements OnInit { return null; } + public showRequiredMajorityInTable(votes: number, poll: PollData, row?: OptionData): boolean { + return this.pollService.isRequiredMajority(votes, poll, row); + } + + public getRequiredMajorityBase(poll: PollData, row?: OptionData): string { + const requiredMajorityBase = this.pollService.getRequiredMajorityBase(poll, row); + if (requiredMajorityBase) { + if (Number.isInteger(requiredMajorityBase)) { + return `${requiredMajorityBase + 1}`; + } + return `${Math.ceil(requiredMajorityBase)}`; + } + return ``; + } + public getVoteClass(votingResult: VotingResult): string { const votingClass = votingResult.vote as string; if (this.isMethodN && votingClass === `no`) { From b86b215ca2b0533e5449c6a30313c648aa09fe57 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Thu, 4 Dec 2025 11:43:46 +0100 Subject: [PATCH 15/25] Small change to the check icon position --- .../assignment-poll-detail-content.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss index d8cefc4f00..f25dfeb39d 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.scss @@ -36,6 +36,7 @@ .check-icon { width: 20px; color: var(--theme-yes); + padding-top: 0.5em; } .user + .sums { From 521b34349cccfbe0a2cb196a1e5e968a8cb5aece Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Fri, 5 Dec 2025 10:03:25 +0100 Subject: [PATCH 16/25] Update the way required majoity base is calculated in table --- .../modules/poll/services/poll.service/poll.service.ts | 7 ++++--- .../assignment-poll-detail-content.component.html | 2 +- .../assignment-poll-detail-content.component.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) 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 e1fedd5ce4..eddd708139 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 @@ -280,8 +280,9 @@ export abstract class PollService { return ``; } - public getRequiredMajorityBase(poll: PollData, row?: OptionData): number | null { - const totalByBase = this.getPercentBase(poll, row); + public getRequiredMajorityBase(poll: PollData, row?: OptionData | PollTableData): number | null { + const option: OptionData | undefined = isPollTableData(row) ? this.transformToOptionData(row) : row; + const totalByBase = this.getPercentBase(poll, option); if (!totalByBase) { return null; } @@ -295,7 +296,7 @@ export abstract class PollService { } } - public isRequiredMajority(value: number, poll: PollData, row?: OptionData): boolean { + public isRequiredMajority(value: number, poll: PollData, row?: OptionData | PollTableData): boolean { const requiredMajorityBase = this.getRequiredMajorityBase(poll, row); if (requiredMajorityBase === null) { return false; diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html index 24d370ba3f..e3d4789e46 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.html @@ -68,7 +68,7 @@ @if ( row.class === 'user' && !isMethodN && - showRequiredMajorityInTable(getVoteAmount(row.value[0], row), poll) + showRequiredMajorityInTable(getVoteAmount(row.value[0], row), poll, row) ) { check } diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 09111951ba..0c0229bc0c 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -217,11 +217,11 @@ export class AssignmentPollDetailContentComponent implements OnInit { return null; } - public showRequiredMajorityInTable(votes: number, poll: PollData, row?: OptionData): boolean { + public showRequiredMajorityInTable(votes: number, poll: PollData, row?: OptionData | PollTableData): boolean { return this.pollService.isRequiredMajority(votes, poll, row); } - public getRequiredMajorityBase(poll: PollData, row?: OptionData): string { + public getRequiredMajorityBase(poll: PollData, row?: OptionData | PollTableData): string { const requiredMajorityBase = this.pollService.getRequiredMajorityBase(poll, row); if (requiredMajorityBase) { if (Number.isInteger(requiredMajorityBase)) { From 9abfeb81da70a648b0bd166c488ed9aac6363e4d Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 15 Dec 2025 12:23:01 +0100 Subject: [PATCH 17/25] Code changes because of a CR --- .../poll/components/chart/chart.component.html | 14 +++++++------- .../poll/components/chart/chart.component.ts | 2 +- .../single-option-chart-table.component.ts | 2 +- .../assignment-poll-detail-content.component.ts | 14 +++++++------- .../motion-poll-detail-content.component.ts | 9 +++++---- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html index e9bba194b9..d22fb2939f 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.html @@ -9,13 +9,13 @@ [options]="chartOptions" [type]="type" > - @if (chartIcon === 'check') { -
- check -
- } @else if (chartIcon === 'cancel') { -
- close + @if (chartIcon) { +
+ {{ chartIcon }}
} } diff --git a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts index a5a9fb1653..c12ab39fcc 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/chart/chart.component.ts @@ -55,7 +55,7 @@ export class ChartComponent { } @Input() - public chartIcon: null | `check` | `cancel` = null; + public chartIcon: string | undefined; private _circleColors: { backgroundColor?: string[]; hoverBackgroundColor?: string[] }[]; diff --git a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts index dcb12ecb22..a2064b59bf 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/single-option-chart-table/single-option-chart-table.component.ts @@ -74,7 +74,7 @@ export class SingleOptionChartTableComponent { public shouldShowEntitledPresent = false; @Input() - public shouldShowChartIcon: `check` | `cancel` | null = null; + public shouldShowChartIcon: string | undefined; @Input() public title = ``; diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 0c0229bc0c..15865a3001 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -203,18 +203,18 @@ export class AssignmentPollDetailContentComponent implements OnInit { this._chartData = this.pollService.generateChartData(this.poll).filter(option => option.data[0] > 0); } - public get showRequiredMajority(): `check` | `cancel` | null { + public get showRequiredMajority(): string | undefined { if ( - (this.poll.required_majority === RequiredMajorityBase.absolute_majority || - this.poll.required_majority === RequiredMajorityBase.two_third_majority) && - this._chartData[0].label === 'YES' + [RequiredMajorityBase.absolute_majority, RequiredMajorityBase.two_third_majority].includes( + this.poll.required_majority + ) ) { if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { - return `check`; + return this._chartData[0].label === 'YES' ? `check` : `close`; } - return `cancel`; + return `close`; } - return null; + return undefined; } public showRequiredMajorityInTable(votes: number, poll: PollData, row?: OptionData | PollTableData): boolean { 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 index f349317687..fd275b0046 100644 --- 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 @@ -135,15 +135,16 @@ export class MotionPollDetailContentComponent extends BaseUiComponent implements .filter(result => result.data[0] !== VOTE_UNDOCUMENTED); } - public get showRequiredMajority(): `check` | `cancel` | null { + public get showRequiredMajority(): string | null { if ( - this.poll.required_majority === RequiredMajorityBase.absolute_majority || - this.poll.required_majority === RequiredMajorityBase.two_third_majority + [RequiredMajorityBase.absolute_majority, RequiredMajorityBase.two_third_majority].includes( + this.poll.required_majority + ) ) { if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { return `check`; } - return `cancel`; + return `close`; } return null; } From 7ca79cd680b11bf1487ee034736434ccfe8c418d Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 15 Dec 2025 12:49:50 +0100 Subject: [PATCH 18/25] Code cleanup: use ?-op more often, cause CR --- .../poll/services/poll.service/poll.service.ts | 5 +---- .../assignment-poll-detail-content.component.ts | 15 +++++++-------- .../motion-poll-detail-content.component.ts | 5 +---- 3 files changed, 9 insertions(+), 16 deletions(-) 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 eddd708139..71d16465d6 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 @@ -298,10 +298,7 @@ export abstract class PollService { public isRequiredMajority(value: number, poll: PollData, row?: OptionData | PollTableData): boolean { const requiredMajorityBase = this.getRequiredMajorityBase(poll, row); - if (requiredMajorityBase === null) { - return false; - } - return value > requiredMajorityBase; + return requiredMajorityBase !== null && value > requiredMajorityBase; } /** diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 15865a3001..1b085af41a 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -209,10 +209,10 @@ export class AssignmentPollDetailContentComponent implements OnInit { this.poll.required_majority ) ) { - if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { - return this._chartData[0].label === 'YES' ? `check` : `close`; - } - return `close`; + return this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll) && + this._chartData[0].label === `YES` + ? `check` + : `close`; } return undefined; } @@ -224,10 +224,9 @@ export class AssignmentPollDetailContentComponent implements OnInit { public getRequiredMajorityBase(poll: PollData, row?: OptionData | PollTableData): string { const requiredMajorityBase = this.pollService.getRequiredMajorityBase(poll, row); if (requiredMajorityBase) { - if (Number.isInteger(requiredMajorityBase)) { - return `${requiredMajorityBase + 1}`; - } - return `${Math.ceil(requiredMajorityBase)}`; + return Number.isInteger(requiredMajorityBase) + ? `${requiredMajorityBase + 1}` + : `${Math.ceil(requiredMajorityBase)}`; } return ``; } 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 index fd275b0046..cec0ab70f2 100644 --- 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 @@ -141,10 +141,7 @@ export class MotionPollDetailContentComponent extends BaseUiComponent implements this.poll.required_majority ) ) { - if (this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll)) { - return `check`; - } - return `close`; + return this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll) ? `check` : `close`; } return null; } From ed1ba2df0fe8aebb0c806ca069a17537e58ff0bd Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 5 Jan 2026 10:28:55 +0100 Subject: [PATCH 19/25] Change order of the req majority options --- client/src/app/domain/models/poll/poll-constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/app/domain/models/poll/poll-constants.ts b/client/src/app/domain/models/poll/poll-constants.ts index 9275eb5594..e7a4ee7260 100644 --- a/client/src/app/domain/models/poll/poll-constants.ts +++ b/client/src/app/domain/models/poll/poll-constants.ts @@ -212,8 +212,8 @@ export const VoteValuesVerbose = { export const RequiredMajorityBaseVerbose = { no_majority: _('No majority'), - two_third_majority: _('Two-third majority'), - absolute_majority: _('Absolute majority') + absolute_majority: _('Absolute majority'), + two_third_majority: _('Two-third majority') }; /** From 63a59e97a4a0a604af234a5258b830e768f1ad6e Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 5 Jan 2026 10:42:46 +0100 Subject: [PATCH 20/25] Change the isRequiredMajority check in two-third case --- .../modules/poll/services/poll.service/poll.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 71d16465d6..4f2c313e63 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 @@ -298,7 +298,12 @@ export abstract class PollService { public isRequiredMajority(value: number, poll: PollData, row?: OptionData | PollTableData): boolean { const requiredMajorityBase = this.getRequiredMajorityBase(poll, row); - return requiredMajorityBase !== null && value > requiredMajorityBase; + return ( + requiredMajorityBase !== null && + (poll.required_majority === RequiredMajorityBase.absolute_majority + ? value > requiredMajorityBase + : value >= requiredMajorityBase) + ); } /** From d6732295da0963b9ef14d2d369eecfb8a797a085 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 5 Jan 2026 10:53:05 +0100 Subject: [PATCH 21/25] Check for percent base disabled case --- .../assignment-poll-detail-content.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 1b085af41a..562f2d6d8b 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -207,7 +207,8 @@ export class AssignmentPollDetailContentComponent implements OnInit { if ( [RequiredMajorityBase.absolute_majority, RequiredMajorityBase.two_third_majority].includes( this.poll.required_majority - ) + ) && + this.poll.onehundred_percent_base !== PollPercentBase.Disabled ) { return this.pollService.isRequiredMajority(this._chartData[0].data[0], this.poll) && this._chartData[0].label === `YES` From a0ec7763ce301f754419ba81fde1a1a0fcc02120 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Mon, 5 Jan 2026 12:46:46 +0100 Subject: [PATCH 22/25] Add a mat-hint with description to the required_majority field --- .../base-poll-form.component.html | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html index eee2f4b230..d1d8474ed4 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html @@ -173,7 +173,7 @@

@if (!hideSelects.requiredMajority) { - + {{ 'Required majority' | translate }} @for (option of requiredMajorityBaseVerbose | keyvalue: keepEntryOrder; track option.key) { @@ -182,6 +182,21 @@

} + @if (contentForm.get('required_majority').value === 'absolute_majority') { + + } @else if (contentForm.get('required_majority').value === 'two_third_majority') { + + } }

From 858a56dbc0a66948e4a28a3bfe94d9948ee2e412 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Tue, 6 Jan 2026 12:40:12 +0100 Subject: [PATCH 23/25] Fix typos and small code restructure --- .../base-poll-form/base-poll-form.component.html | 8 ++++---- .../components/base-poll-form/base-poll-form.component.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html index d1d8474ed4..373a5a9dc8 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.html @@ -182,17 +182,17 @@

} - @if (contentForm.get('required_majority').value === 'absolute_majority') { + @if (requiredMajority === 'absolute_majority') { - } @else if (contentForm.get('required_majority').value === 'two_third_majority') { + } @else if (requiredMajority === 'two_third_majority') { diff --git a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts index fa4d739ee0..d982f0ae1c 100644 --- a/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts +++ b/client/src/app/site/pages/meetings/modules/poll/components/base-poll-form/base-poll-form.component.ts @@ -191,8 +191,8 @@ export abstract class BasePollFormComponent extends BaseComponent implements OnI return this.contentForm.get(`onehundred_percent_base`); } - private get requiredMajority(): AbstractControl { - return this.contentForm.get(`required_majority`); + public get requiredMajority(): string { + return this.contentForm.get(`required_majority`).value; } public abstract get hideSelects(): PollFormHideSelectsData; From 5a43996462663ffb5eae5908030598c6bd3e6f27 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Wed, 18 Feb 2026 13:55:44 +0100 Subject: [PATCH 24/25] Update displayed required majority --- .../assignment-poll-detail-content.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts index 562f2d6d8b..3b5814d40c 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-detail-content/assignment-poll-detail-content.component.ts @@ -225,8 +225,8 @@ export class AssignmentPollDetailContentComponent implements OnInit { public getRequiredMajorityBase(poll: PollData, row?: OptionData | PollTableData): string { const requiredMajorityBase = this.pollService.getRequiredMajorityBase(poll, row); if (requiredMajorityBase) { - return Number.isInteger(requiredMajorityBase) - ? `${requiredMajorityBase + 1}` + return poll.required_majority === `absolute_majority` && Number.isInteger(requiredMajorityBase) + ? `${Math.ceil(requiredMajorityBase + 1)}` : `${Math.ceil(requiredMajorityBase)}`; } return ``; From ecfac4bd375e5c10c97e2ed51dce1d8e5b5cbc56 Mon Sep 17 00:00:00 2001 From: Ludwig Reiter Date: Thu, 19 Feb 2026 09:00:10 +0100 Subject: [PATCH 25/25] Don't trigger update if required majority is changed --- .../assignment-poll-dialog.component.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.ts b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.ts index f05dc494fe..9b93cbfafb 100644 --- a/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.ts +++ b/client/src/app/site/pages/meetings/pages/assignments/modules/assignment-poll/components/assignment-poll-dialog/assignment-poll-dialog.component.ts @@ -58,7 +58,16 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent { public override onBeforeInit(): void { this.subscriptions.push( - this.pollForm!.contentForm.valueChanges.pipe(debounceTime(150), distinctUntilChanged()).subscribe(() => { + this.pollForm!.contentForm.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged( + (prev, current) => + prev.backend === current.backend && + prev.pollmethod === current.pollmethod && + prev.type === current.type && + prev.onehundred_percent_base === current.onehundred_percent_base + ) + ).subscribe(() => { this.triggerUpdate(); }) );