Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9bc4dd9
Hide method field on edit if poll started
bastianjoel Sep 22, 2026
f29233e
Fix poll result if on no votes
bastianjoel Sep 22, 2026
0587fba
Update meta
bastianjoel Sep 23, 2026
0d28768
Display entitled users for non finished polls
bastianjoel Sep 23, 2026
84cb075
Rename poll titles
bastianjoel Sep 23, 2026
9adf713
Fix poll meta not updating
bastianjoel Sep 23, 2026
9aa4eb7
Fix poll form field sideeffects
bastianjoel Sep 23, 2026
cf3e8e3
majority/undocumented update on language switch
bastianjoel Sep 23, 2026
a08114c
Fix analog poll update
bastianjoel Sep 23, 2026
a69ac8e
Fix analog poll result not updated
bastianjoel Sep 24, 2026
6ad87a2
Allow empty support for selection/score polls
bastianjoel Sep 28, 2026
5400d43
Entitled users present 100% base
bastianjoel Sep 29, 2026
65bb2d0
Fix edit rating approval with max yes votes disabled
bastianjoel Sep 29, 2026
fec8fba
Fix max yes votes displayed when disabled
bastianjoel Sep 29, 2026
67e2697
Cleanup
bastianjoel Sep 29, 2026
8997b75
Update yes_no 100% base
bastianjoel Sep 29, 2026
01ff2bd
Implement simple majority
bastianjoel Sep 29, 2026
b972ca4
Update meeting settings
bastianjoel Oct 5, 2026
a2687fd
Fix autopilot titles for topics sometimes not displayed
bastianjoel Oct 16, 2026
3098a1b
Fix incomplete model updates
bastianjoel Oct 16, 2026
d498a83
Fix no general 100% base
bastianjoel Oct 16, 2026
1b9e834
Prevent edit of published poll result
bastianjoel Oct 16, 2026
c79556d
Improve poll method selection on optionEdit
bastianjoel Oct 16, 2026
27d926d
Check config resource availability in poll meta
bastianjoel Oct 16, 2026
e70e815
Fix console error in poll-single-votes
bastianjoel Oct 16, 2026
179a8be
Hide option edit after poll is started
bastianjoel Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,8 @@ export const meetingSettingsDefaults: Record<string, any> = {
motions_enable_origin_motion_display: false,
motions_origin_motion_toggle_default: false,
assignment_poll_add_candidates_to_list_of_speakers: false,
poll_enable_max_yes_votes: false,
poll_enable_max_votes_per_option: false,
poll_default_live_voting_enabled: false,
poll_default_allow_invalid: false,
poll_default_allow_vote_split: false,
poll_default_required_majority: 'no_majority',
poll_projection_name_order_first: 'last_name',
poll_projection_max_columns: 6,
topic_poll_default_method: 'selection',
assignment_poll_default_method: 'selection',
users_allow_self_set_present: true,
users_enable_presence_view: false,
users_enable_vote_weight: false,
Expand Down
37 changes: 0 additions & 37 deletions client/src/app/domain/models/meetings/meeting-poll-default.ts

This file was deleted.

43 changes: 43 additions & 0 deletions client/src/app/domain/models/meetings/meeting-poll-setting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { HasMeetingId } from '../../interfaces/has-meeting-id';
import { BaseModel } from '../base/base-model';
import { BaseOnehundredPercentBase } from '../poll/poll-config-types';
import { PollVisibility } from '../poll/poll-constants';

export class MeetingPollSetting extends BaseModel<MeetingPollSetting> {
public static COLLECTION = `meeting_poll_setting`;

public allow_live_voting: boolean;
public sort_result_by_votes: boolean;

public method: string; // TODO: Enum
public required_majority: string; // TODO: Enum
public visibility: PollVisibility;
public onehundred_percent_base: BaseOnehundredPercentBase;
public group_ids: number[];
public enable_max_yes_votes: boolean;
public enable_cumulative_voting: boolean;
public enable_max_options_limit: boolean;
public enable_live_voting: boolean;

public constructor(input?: Partial<MeetingPollSetting>) {
super(MeetingPollSetting.COLLECTION, input);
}

public static readonly REQUESTABLE_FIELDS: (keyof MeetingPollSetting)[] = [
`id`,
`sort_result_by_votes`,
`allow_live_voting`,
`enable_max_yes_votes`,
`enable_cumulative_voting`,
`enable_max_options_limit`,
`method`,
`visibility`,
`enable_live_voting`,
`required_majority`,
`onehundred_percent_base`,
`group_ids`,
`meeting_id`
];
}

export interface MeetingPollSetting extends HasMeetingId {}
16 changes: 0 additions & 16 deletions client/src/app/domain/models/meetings/meeting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,12 @@ export class Settings {
public assignments_export_preamble!: string;

public assignment_poll_add_candidates_to_list_of_speakers!: boolean;
public assignment_poll_default_method: string;
public assignment_poll_config_id: Id;

// topic poll
public topic_poll_default_method: string;
public topic_poll_config_id: Id;

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

Expand Down Expand Up @@ -412,17 +404,9 @@ export class Meeting extends BaseModel<Meeting> {
`assignments_export_title`,
`assignments_export_preamble`,
`assignment_poll_add_candidates_to_list_of_speakers`,
`assignment_poll_default_method`,
`assignment_poll_config_id`,
`motion_poll_config_id`,
`topic_poll_default_method`,
`topic_poll_config_id`,
`poll_enable_max_yes_votes`,
`poll_enable_max_votes_per_option`,
`poll_default_required_majority`,
`poll_default_live_voting_enabled`,
`poll_default_allow_invalid`,
`poll_default_allow_vote_split`,
`poll_projection_name_order_first`,
`poll_projection_max_columns`,
`projector_ids`,
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/domain/models/poll/poll-config-rating-score.ts
Original file line number Diff line number Diff line change
@@ -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`;
Expand All @@ -14,7 +14,7 @@ export class PollConfigRatingScore extends BasePollConfigModel<PollConfigRatingS
public max_vote_sum!: number;
public min_vote_sum!: number;
public onehundred_percent_base!: RatingScoreOnehundredPercentBase;
public required_majority!: PollRequiredMajority;
public required_majority!: SelectionRequiredMajority;

public constructor(input?: Partial<PollConfigRatingScore>) {
super(PollConfigRatingScore.COLLECTION, input);
Expand Down
3 changes: 2 additions & 1 deletion client/src/app/domain/models/poll/poll-config-selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PollConfigSelection> {
public static COLLECTION = `poll_config_selection`;
Expand All @@ -14,7 +15,7 @@ export class PollConfigSelection extends BasePollConfigModel<PollConfigSelection
public allow_nota!: boolean;
public display_chart!: string;
public onehundred_percent_base!: SelectionOnehundredPercentBase;
public required_majority!: PollRequiredMajority;
public required_majority!: SelectionRequiredMajority;

public get max_vote_sum(): number {
return this.max_options_amount;
Expand Down
3 changes: 2 additions & 1 deletion client/src/app/domain/models/poll/poll-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export enum PollVisibility {
export enum PollRequiredMajority {
NoMajority = `no_majority`,
TwoThirdMajority = `two_third_majority`,
AbsoluteMajority = `absolute_majority`
AbsoluteMajority = `absolute_majority`,
SimpleMajority = `simple_majority`
}

/**
Expand Down
8 changes: 7 additions & 1 deletion client/src/app/domain/models/poll/poll-entitled-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ export class PollEntitledUser extends BaseModel<PollEntitledUser> {

public poll_id!: Id;
public meeting_user_id: Id;
public present: boolean;

public constructor(input?: Partial<PollEntitledUser>) {
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`
];
}
2 changes: 2 additions & 0 deletions client/src/app/domain/models/poll/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class Poll extends BaseModel<Poll> {
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[];
Expand All @@ -42,6 +43,7 @@ export class Poll extends BaseModel<Poll> {
`result`,
`published`,
`anonymized`,
`allow_empty`,
`allow_invalid`,
`allow_vote_split`,
`live_voting_enabled`,
Expand Down
5 changes: 3 additions & 2 deletions client/src/app/domain/translations/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default {
'poll_percent_base.entitled': _(`All entitled users`),
'poll_percent_base.entitled_present': _(`Present entitled users`),
'poll_percent_base.disabled': _(`Disabled (no percents)`),
'poll_required_majority.no_majority': _(`No majority`),
'poll_required_majority.no_majority': _(`Deactivate`),
'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`)
};
17 changes: 12 additions & 5 deletions client/src/app/gateways/repositories/base-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,18 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
// All data is piped through an throttleTime of 20ms. This is to prevent massive
// updates, if e.g. an autoupdate with a lot motions come in. The result is just one
// update of the new list instead of many unnecessary updates.
this.unsafeViewModelListSubject.pipe(throttleTime(20)).subscribe(models => {
if (models) {
this.updateViewModelListSubject(models);
}
});
this.unsafeViewModelListSubject
.pipe(
throttleTime(20, undefined, {
leading: true,
trailing: true
})
)
.subscribe(models => {
if (models) {
this.updateViewModelListSubject(models);
}
});

this.languageCollator = getIntlCollatorForLang(this.translate.getCurrentLang() ?? `en`);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { MeetingPollSetting } from '@app/domain/models/meetings/meeting-poll-setting';
import { ViewMeetingPollSetting } from '@app/site/pages/meetings/view-models/view-meeting-poll-setting';

import { BaseMeetingRelatedRepository } from '../base-meeting-related-repository';

@Injectable({
providedIn: `root`
})
export class MeetingPollSettingRepositoryService extends BaseMeetingRelatedRepository<
ViewMeetingPollSetting,
MeetingPollSetting
> {
public baseModelCtor = MeetingPollSetting;

public getTitle = (_viewMeetingPollDefault: ViewMeetingPollSetting): string => `Meeting poll setting`;

public getVerboseName = (): string => this.translate.instant(`Meeting poll setting`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,36 +47,6 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository<ViewPoll
}

public async update(poll: ViewPoll, payload: PollUpdatePayload): Promise<void> {
if (payload.visibility === PollVisibility.Manually) {
return this.updateAnalogPoll(poll, payload);
}

return this.updateElectronicPoll(poll, payload);
}

private async createAnalogPoll(payload: any): Promise<Identifiable> {
delete payload.live_voting_enabled;

return this.voteApi.create(payload);
}

private async createElectronicPoll(payload: PollCreatePayload): Promise<Identifiable> {
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<void> {
if (poll.config.method === payload.method) {
delete payload[`method`];
}

return this.voteApi.update(poll.id, payload);
}

private async updateElectronicPoll(poll: ViewPoll, payload: PollUpdatePayload): Promise<void> {
if (poll.state !== PollState.Created) {
const methodConfig: {
display_chart?: string;
Expand All @@ -89,7 +59,8 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository<ViewPoll
display_chart: methodConfig.display_chart,
onehundred_percent_base: methodConfig.onehundred_percent_base,
required_majority: methodConfig.required_majority
}
},
result: poll.visibility === PollVisibility.Manually && payload.result ? payload.result : undefined
});
} else if (poll.config.method === payload.method) {
delete payload[`method`];
Expand All @@ -98,6 +69,20 @@ export class PollRepositoryService extends BaseMeetingRelatedRepository<ViewPoll
return this.voteApi.update(poll.id, payload);
}

private async createAnalogPoll(payload: any): Promise<Identifiable> {
delete payload.live_voting_enabled;

return this.voteApi.create(payload);
}

private async createElectronicPoll(payload: PollCreatePayload): Promise<Identifiable> {
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<void> {
return this.voteApi.deletePoll(poll.id);
}
Expand Down
7 changes: 4 additions & 3 deletions client/src/app/gateways/vote-api.service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<any> {
return this.http.post(`${this.BASE}/poll`, payload);
Expand Down
Loading
Loading