diff --git a/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.html b/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.html
index 2732bd3c29..46b5f737db 100644
--- a/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.html
+++ b/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.html
@@ -60,21 +60,21 @@
@if (user && isLoggedIn) {
- @if (isAllowedSelfSetPresent && hasActiveMeeting) {
-
- } @else if (hasActiveMeeting) {
- @if (isPresent) {
+ @if (hasActiveMeeting) {
+ @if (isAllowedSelfSetPresent) {
+
+ } @else if (isPresent) {
@@ -90,6 +90,13 @@
}
+
+ @if (isVoteDelegationEnabled && canEditOwnDelegation()) {
+
+ }
}
}
diff --git a/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.ts b/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.ts
index 026710e578..d1bc7384ff 100644
--- a/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.ts
+++ b/client/src/app/site/modules/global-headbar/components/account-button/account-button.component.ts
@@ -5,8 +5,12 @@ import { Router } from '@angular/router';
import { Id } from '@app/domain/definitions/key-types';
import { allAvailableTranslations, availableTranslations } from '@app/domain/definitions/languages';
import { getOmlVerboseName } from '@app/domain/definitions/organization-permission';
+import { Permission } from '@app/domain/definitions/permission';
import { largeDialogSettings } from '@app/infrastructure/utils/dialog-settings';
import { mediumDialogSettings } from '@app/infrastructure/utils/dialog-settings';
+import { ParticipantListInfoDialogService } from '@app/site/pages/meetings/pages/participants/pages/participant-list/modules/participant-list-info-dialog';
+import { ParticipantControllerService } from '@app/site/pages/meetings/pages/participants/services/common/participant-controller.service';
+import { ActiveMeetingService } from '@app/site/pages/meetings/services/active-meeting.service';
import { ActiveMeetingIdService } from '@app/site/pages/meetings/services/active-meeting-id.service';
import { MeetingSettingsService } from '@app/site/pages/meetings/services/meeting-settings.service';
import { ViewUser } from '@app/site/pages/meetings/view-models/view-user';
@@ -15,9 +19,10 @@ import { OperatorService } from '@app/site/services/operator.service';
import { ThemeService } from '@app/site/services/theme.service';
import { UserControllerService } from '@app/site/services/user-controller.service';
import { BaseUiComponent } from '@app/ui/base/base-ui-component';
+import { PromptService } from '@app/ui/modules/prompt-dialog/services/prompt.service';
import { ChessDialogComponent } from '@app/ui/modules/sidenav/modules/easter-egg/modules/chess-dialog/components/chess-dialog/chess-dialog.component';
import { ChessChallengeService } from '@app/ui/modules/sidenav/modules/easter-egg/modules/chess-dialog/services/chess-challenge.service';
-import { TranslateService } from '@ngx-translate/core';
+import { _, TranslateService } from '@ngx-translate/core';
import { Observable, Subscription } from 'rxjs';
import { AccountDialogMainComponent } from '../account-dialog-main/account-dialog-main.component';
@@ -71,6 +76,7 @@ export class AccountButtonComponent extends BaseUiComponent implements OnInit {
public username = ``;
public isLoggedIn = false;
+ private _voteDelegationEnabled = false;
public show1337 = -20;
@@ -78,6 +84,10 @@ export class AccountButtonComponent extends BaseUiComponent implements OnInit {
return this.activeMeetingIdService.meetingId;
}
+ protected get isVoteDelegationEnabled(): boolean {
+ return this._voteDelegationEnabled;
+ }
+
private _isAllowedSelfSetPresent = false;
private _languageTrigger: MatMenuTrigger | undefined = undefined;
private clickCounter = 0;
@@ -86,12 +96,17 @@ export class AccountButtonComponent extends BaseUiComponent implements OnInit {
private translate = inject(TranslateService);
private operator = inject(OperatorService);
private userRepo = inject(UserControllerService);
+ private participantRepo = inject(ParticipantControllerService);
private authService = inject(AuthService);
private theme = inject(ThemeService);
+ private activeMeeting = inject(ActiveMeetingService);
private meetingSettingsService = inject(MeetingSettingsService);
private activeMeetingIdService = inject(ActiveMeetingIdService);
private dialog = inject(MatDialog);
private router = inject(Router);
+ protected participantListDialog = inject(ParticipantListInfoDialogService);
+ private infoDialog = inject(ParticipantListInfoDialogService);
+ private prompt = inject(PromptService);
public constructor(chessChallengeService: ChessChallengeService) {
super();
@@ -104,6 +119,9 @@ export class AccountButtonComponent extends BaseUiComponent implements OnInit {
this.meetingSettingsService
.get(`users_allow_self_set_present`)
.subscribe(allowed => (this._isAllowedSelfSetPresent = allowed));
+ this.meetingSettingsService
+ .get(`users_enable_vote_delegations`)
+ .subscribe(enabled => (this._voteDelegationEnabled = enabled));
this.onOperatorUpdate(); // initially trigger the update manually to set initial values
}
@@ -226,4 +244,60 @@ export class AccountButtonComponent extends BaseUiComponent implements OnInit {
}
return this.user.short_name + ': ' + stringForUserPresent;
}
+
+ public canEditOwnDelegation(): boolean {
+ return (
+ this.operator.hasPerms(Permission.userCanEditOwnDelegation) &&
+ this.activeMeeting.meeting.user_ids.includes(this.operator.operatorId)
+ );
+ }
+
+ public async openEditInfo(user: ViewUser): Promise {
+ const dialogRef = await this.infoDialog.open({
+ id: user.id,
+ name: user.getName(),
+ number: user.number(),
+ group_ids: user.group_ids(),
+ structure_level_ids: user.structure_level_ids(),
+ vote_delegations_from_ids: user.vote_delegations_from_meeting_user_ids() || [],
+ vote_delegated_to_id: user.vote_delegated_to_meeting_user_id()
+ });
+ const selfGroupRemovalDialogTitle = _(`This action will remove you from one or more groups.`);
+ const selfGroupRemovalDialogContent = _(
+ `This may diminish your ability to do things in this meeting and you may not be able to revert it by yourself. Are you sure you want to do this?`
+ );
+
+ dialogRef.afterClosed().subscribe(async result => {
+ if (result) {
+ if (!result.group_ids?.length) {
+ result.group_ids = [this.activeMeeting.meeting!.default_group_id];
+ }
+ if (result.vote_delegated_to_id === 0) {
+ result.vote_delegated_to_id = null;
+ }
+ if (
+ !(
+ user.id === this.operator.operatorId &&
+ this.infoDialog.areGroupsDiminished(
+ this.operator.user.group_ids(),
+ result.group_ids,
+ this.activeMeeting.meeting
+ )
+ ) ||
+ (await this.prompt.open(selfGroupRemovalDialogTitle, selfGroupRemovalDialogContent))
+ ) {
+ if (
+ this.operator.hasPerms(Permission.userCanEditOwnDelegation) &&
+ !this.operator.hasPerms(Permission.userCanManage) &&
+ !this.operator.hasPerms(Permission.userCanUpdate) &&
+ user.id === this.operator.operatorId
+ ) {
+ this.participantRepo.updateSelfDelegation(result, user);
+ } else {
+ this.participantRepo.update(result, user).resolve();
+ }
+ }
+ }
+ });
+ }
}
diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-edit/participant-detail-edit.component.ts b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-edit/participant-detail-edit.component.ts
index 989762264e..a4c504d075 100644
--- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-edit/participant-detail-edit.component.ts
+++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-detail/components/participant-detail-edit/participant-detail-edit.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit, ViewChild } from '@angular/core';
import { Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Id } from '@app/domain/definitions/key-types';
@@ -28,10 +28,11 @@ import {
getParticipantMinimalSubscriptionConfig,
PARTICIPANT_DETAIL_SUBSCRIPTION
} from '../../../../participants.subscription';
-import { areGroupsDiminished } from '../../../participant-list/components/participant-list/participant-list.component';
+import { ParticipantListInfoDialogService } from '../../../participant-list/modules/participant-list-info-dialog/services';
import { ParticipantListSortService } from '../../../participant-list/services/participant-list-sort/participant-list-sort.service';
import { StructureLevelControllerService } from '../../../structure-levels/services/structure-level-controller.service';
import { ViewStructureLevel } from '../../../structure-levels/view-models/view-structure-level';
+
@Component({
selector: `os-participant-detail-edit`,
templateUrl: `./participant-detail-edit.component.html`,
@@ -41,6 +42,8 @@ import { ViewStructureLevel } from '../../../structure-levels/view-models/view-s
})
export class ParticipantDetailEditComponent extends BaseMeetingComponent implements OnInit {
@ViewChild(UserDetailViewComponent)
+ private infoDialog = inject(ParticipantListInfoDialogService);
+
private userDetailView;
public participantSubscriptionConfig = getParticipantMinimalSubscriptionConfig(this.activeMeetingId);
@@ -299,7 +302,7 @@ export class ParticipantDetailEditComponent extends BaseMeetingComponent impleme
public updateByValueChange(event: any): void {
this.personalInfoFormValue = event;
- if (this.userDetailView.personalInfoForm.get(`locked_out`).disabled !== this.lockoutCheckboxDisabled) {
+ if (this.userDetailView.personalInfoForm?.get(`locked_out`).disabled !== this.lockoutCheckboxDisabled) {
if (this.lockoutCheckboxDisabled) {
this.userDetailView.personalInfoForm.get(`locked_out`).disable();
} else {
@@ -334,12 +337,16 @@ export class ParticipantDetailEditComponent extends BaseMeetingComponent impleme
}
const title = _(`This action will remove you from one or more groups.`);
const content = _(
- `This may diminish your ability to do things in this meeting and you may not be able to revert it by youself. Are you sure you want to do this?`
+ `This may diminish your ability to do things in this meeting and you may not be able to revert it by yourself. Are you sure you want to do this?`
);
if (
!(
this.user.id === this.operator.operatorId &&
- areGroupsDiminished(this.operator.user.group_ids(), payload.group_ids, this.activeMeeting)
+ this.infoDialog.areGroupsDiminished(
+ this.operator.user.group_ids(),
+ payload.group_ids,
+ this.activeMeeting
+ )
) ||
(await this.promptService.open(title, content))
) {
diff --git a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.html b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.html
index 135d80a5f7..67372064d4 100644
--- a/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.html
+++ b/client/src/app/site/pages/meetings/pages/participants/pages/participant-list/components/participant-list/participant-list.component.html
@@ -369,14 +369,6 @@ {{ 'Participants' | translate }}
record_voice_over
{{ 'Contributions' | translate }}
-