Merge pull request #6070 from tsiegleauq/stop-voting-publish-prompt
stop-voting shows prompt
This commit is contained in:
commit
c60553e376
@ -48,7 +48,7 @@
|
||||
<button
|
||||
mat-stroked-button
|
||||
[ngClass]="pollStateActions[poll.state].css"
|
||||
(click)="changeState(poll.nextState)"
|
||||
(click)="nextPollState()"
|
||||
[disabled]="stateChangePending"
|
||||
>
|
||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||
|
@ -8,6 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { VotingService } from 'app/core/ui-services/voting.service';
|
||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||
@ -82,6 +83,7 @@ export class AssignmentPollComponent
|
||||
translate: TranslateService,
|
||||
dialog: MatDialog,
|
||||
promptService: PromptService,
|
||||
choiceService: ChoiceService,
|
||||
repo: AssignmentPollRepositoryService,
|
||||
pollDialog: AssignmentPollDialogService,
|
||||
private pollService: AssignmentPollService,
|
||||
@ -90,7 +92,7 @@ export class AssignmentPollComponent
|
||||
private operator: OperatorService,
|
||||
private votingService: VotingService
|
||||
) {
|
||||
super(titleService, matSnackBar, translate, dialog, promptService, repo, pollDialog);
|
||||
super(titleService, matSnackBar, translate, dialog, promptService, choiceService, repo, pollDialog);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
|
@ -50,7 +50,7 @@
|
||||
<button
|
||||
mat-stroked-button
|
||||
[ngClass]="pollStateActions[poll.state].css"
|
||||
(click)="changeState(poll.nextState)"
|
||||
(click)="nextPollState()"
|
||||
[disabled]="stateChangePending"
|
||||
>
|
||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||
|
@ -7,6 +7,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { OperatorService, Permission } from 'app/core/core-services/operator.service';
|
||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
@ -68,6 +69,7 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
||||
titleService: Title,
|
||||
matSnackBar: MatSnackBar,
|
||||
promptService: PromptService,
|
||||
choiceService: ChoiceService,
|
||||
pollDialog: MotionPollDialogService,
|
||||
protected dialog: MatDialog,
|
||||
protected pollRepo: MotionPollRepositoryService,
|
||||
@ -76,7 +78,7 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
||||
private pdfService: MotionPollPdfService,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
|
||||
super(titleService, matSnackBar, translate, dialog, promptService, choiceService, pollRepo, pollDialog);
|
||||
}
|
||||
|
||||
public openVotingWarning(): void {
|
||||
|
@ -6,13 +6,14 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
||||
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ChartData } from 'app/shared/components/charts/charts.component';
|
||||
import { PollState, PollType } from 'app/shared/models/poll/base-poll';
|
||||
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
|
||||
import { PollService } from '../services/poll.service';
|
||||
import { ViewBasePoll } from '../models/view-base-poll';
|
||||
import { PollStateChangeActionVerbose, ViewBasePoll } from '../models/view-base-poll';
|
||||
|
||||
export abstract class BasePollComponent<
|
||||
V extends ViewBasePoll,
|
||||
@ -49,38 +50,46 @@ export abstract class BasePollComponent<
|
||||
protected translate: TranslateService,
|
||||
protected dialog: MatDialog,
|
||||
protected promptService: PromptService,
|
||||
protected choiceService: ChoiceService,
|
||||
protected repo: BasePollRepositoryService,
|
||||
protected pollDialog: BasePollDialogService<V, S>
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
}
|
||||
|
||||
public async changeState(key: PollState): Promise<void> {
|
||||
if (key === PollState.Created) {
|
||||
const title = this.translate.instant('Are you sure you want to reset this vote?');
|
||||
const content = this.translate.instant('All votes will be lost.');
|
||||
if (await this.promptService.open(title, content)) {
|
||||
this.stateChangePending = true;
|
||||
this.repo
|
||||
.resetPoll(this._poll)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.stateChangePending = false;
|
||||
});
|
||||
public async nextPollState(): Promise<void> {
|
||||
const currentState: PollState = this._poll.state;
|
||||
if (currentState === PollState.Created || currentState === PollState.Finished) {
|
||||
await this.changeState(this._poll.nextState);
|
||||
} else if (currentState === PollState.Started) {
|
||||
const title = this.translate.instant('Are you sure you want to stop this voting?');
|
||||
const actions = [this.translate.instant('Stop'), this.translate.instant('Stop & publish')];
|
||||
const choice = await this.choiceService.open(title, null, false, actions);
|
||||
|
||||
if (choice?.action === 'Stop') {
|
||||
await this.changeState(PollState.Finished);
|
||||
} else if (choice?.action === 'Stop & publish') {
|
||||
await this.changeState(PollState.Published);
|
||||
}
|
||||
} else {
|
||||
this.stateChangePending = true;
|
||||
this.repo
|
||||
.changePollState(this._poll)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.stateChangePending = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resetState(): void {
|
||||
this.changeState(PollState.Created);
|
||||
private async changeState(targetState: PollState): Promise<void> {
|
||||
this.stateChangePending = true;
|
||||
this.repo
|
||||
.changePollState(this._poll, targetState)
|
||||
.catch(this.raiseError)
|
||||
.finally(() => {
|
||||
this.stateChangePending = false;
|
||||
});
|
||||
}
|
||||
|
||||
public async resetState(): Promise<void> {
|
||||
const title = this.translate.instant('Are you sure you want to reset this vote?');
|
||||
const content = this.translate.instant('All votes will be lost.');
|
||||
if (await this.promptService.open(title, content)) {
|
||||
this.changeState(PollState.Created);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,17 +56,17 @@ export abstract class BasePollRepositoryService<
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public changePollState(poll: BasePoll): Promise<void> {
|
||||
public changePollState(poll: BasePoll, targetState: PollState): Promise<void> {
|
||||
const path = this.restPath(poll);
|
||||
switch (poll.state) {
|
||||
switch (targetState) {
|
||||
case PollState.Created:
|
||||
return this.http.post(`${path}/start/`);
|
||||
return this.http.post(`${path}/reset/`);
|
||||
case PollState.Started:
|
||||
return this.http.post(`${path}/stop/`);
|
||||
return this.http.post(`${path}/start/`);
|
||||
case PollState.Finished:
|
||||
return this.http.post(`${path}/publish/`);
|
||||
return this.http.post(`${path}/stop/`);
|
||||
case PollState.Published:
|
||||
return this.resetPoll(poll);
|
||||
return this.http.post(`${path}/publish/`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,10 +74,6 @@ export abstract class BasePollRepositoryService<
|
||||
return `/rest/${poll.collectionString}/${poll.id}`;
|
||||
}
|
||||
|
||||
public resetPoll(poll: BasePoll): Promise<void> {
|
||||
return this.http.post(`${this.restPath(poll)}/reset/`);
|
||||
}
|
||||
|
||||
public pseudoanonymize(poll: BasePoll): Promise<void> {
|
||||
return this.http.post(`${this.restPath(poll)}/pseudoanonymize/`);
|
||||
}
|
||||
|
@ -164,9 +164,13 @@ class BasePollViewSet(ModelViewSet):
|
||||
@transaction.atomic
|
||||
def publish(self, request, pk):
|
||||
poll = self.get_locked_object()
|
||||
if poll.state != BasePoll.STATE_FINISHED:
|
||||
if poll.state not in (BasePoll.STATE_STARTED, BasePoll.STATE_FINISHED):
|
||||
raise ValidationError({"detail": "Wrong poll state"})
|
||||
|
||||
# stop poll if needed
|
||||
if poll.state == BasePoll.STATE_STARTED:
|
||||
poll.stop()
|
||||
|
||||
poll.state = BasePoll.STATE_PUBLISHED
|
||||
poll.save()
|
||||
inform_changed_data(
|
||||
|
@ -1284,6 +1284,29 @@ class PublishMotionPoll(TestCase):
|
||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(MotionPoll.objects.get().state, MotionPoll.STATE_CREATED)
|
||||
|
||||
def test_publish_from_started(self):
|
||||
self.poll.state = MotionPoll.STATE_STARTED
|
||||
self.poll.save()
|
||||
response = self.client.post(reverse("motionpoll-publish", args=[self.poll.pk]))
|
||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||
self.assertEqual(MotionPoll.objects.get().state, MotionPoll.STATE_PUBLISHED)
|
||||
|
||||
def test_publish_from_started_with_entitled_users(self):
|
||||
self.poll.state = MotionPoll.STATE_STARTED
|
||||
self.poll.save()
|
||||
admin = get_user_model().objects.get(username="admin")
|
||||
admin.is_present = True
|
||||
admin.save()
|
||||
self.poll.groups.add(GROUP_ADMIN_PK)
|
||||
response = self.client.post(reverse("motionpoll-publish", args=[self.poll.pk]))
|
||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||
poll = MotionPoll.objects.get()
|
||||
self.assertEqual(poll.state, MotionPoll.STATE_PUBLISHED)
|
||||
self.assertEqual(
|
||||
poll.entitled_users_at_stop,
|
||||
[{"user_id": admin.id, "voted": False, "vote_delegated_to_id": None}],
|
||||
)
|
||||
|
||||
|
||||
class PseudoanonymizeMotionPoll(TestCase):
|
||||
def setUp(self):
|
||||
|
Loading…
Reference in New Issue
Block a user