Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Commit

Permalink
Changes apply to backlog (#57)
Browse files Browse the repository at this point in the history
* Fixes deletion on invalid settings

* Changes behaviour on team change for grid

* Moves apply to backlog option

* Adds markdown entry
  • Loading branch information
oxxr authored May 8, 2019
1 parent 62f65f9 commit b99dcad
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 249 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ For more detailed release notes please refer to our [release blog posts](https:/
- Support for fixed voting period: start and end date for a voting can be configured
- Summary of finished voting on admin page with possibility to copy to HTML
- Icon in vertical navigation
- Moved apply to backlog option to report page for terminated votings
- Bugfixes
- Solves problem that terms of use can not be confirmed
- "Assigned to" is displayed correctly
Expand Down
12 changes: 12 additions & 0 deletions Source/Extension.Voting/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@
background-color: rgb(204, 41, 61);
}

.testcase-color {
background-color: rgb(0, 75, 80);
}

.testsuite-color {
background-color: rgb(0, 75, 80);
}

.testplan-color {
background-color: rgb(0, 75, 80);
}

.work-item-color {
width: 6px;
height: 100%;
Expand Down
4 changes: 2 additions & 2 deletions Source/Extension.Voting/src/adminPage/adminPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ <h1 class="votingheadline">Voting settings</h1>

<div class="row">
<div class="form-group col-md-3" data-toggle="tooltip"
title="Select a voting type. A backlog-based voting allows to vote on items of a specified backlog level. The order of the backlog items from the voting can then be applied to the backlog. A query-based voting allows to vote on work items specified by a shared (flat) query.">
title="Select a voting type. A backlog-based voting allows to vote on items of a specified backlog level. The order of the backlog items from the voting can then be applied to the backlog.">
<label>Type
<span class="required" v-if="actualVoting.type === ''">*</span></label>
<select class="form-control" name="type" v-model="actualVoting.type"
Expand Down Expand Up @@ -97,7 +97,7 @@ <h1 class="votingheadline">Voting settings</h1>
</div>

<div class="form-group col-md-9" v-show="isQueryBased" data-toggle="tooltip"
title="Select the query to vote on.">
title="Select the query to vote on. A query-based voting allows to vote on work items specified by a shared (flat) query.">
<label>Vote on Query
<span class="required" v-if="actualVoting.query == null">*</span></label>
<div class="form-control">
Expand Down
119 changes: 69 additions & 50 deletions Source/Extension.Voting/src/adminPage/adminPageController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ export class AdminPageController extends Vue {
buttons: {
Confirm: async () => {
dialog.close();
await this.adminPageService.removeVotesByTeamAsync();
await this.saveSettingsAsync(true, false);
if (this.isVotingValid()) {
await this.adminPageService.removeVotesByTeamAsync();
await this.saveSettingsAsync(true, false);
}
},
Cancel: () => {
dialog.close();
Expand Down Expand Up @@ -378,54 +380,7 @@ export class AdminPageController extends Vue {
voting.end = this.getEndDate().valueOf();
}

voting.title = escapeText(voting.title);
if ((voting.title == null || voting.title === "") && isEnabled) {
bsNotify("danger", "Please provide a title for the voting.");
return;
}

if (voting.useStartTime) {
if (!voting.start) {
bsNotify(
"danger",
"Invalid time period. Please make sure that start date and time is filled!"
);
return;
}
}

if (voting.useEndTime) {
if (!voting.end) {
bsNotify(
"danger",
"Invalid time period. Please make sure that end date and time is filled!"
);
return;
}
}

if (voting.useEndTime && voting.end < Date.now()) {
bsNotify(
"danger",
"Invalid time period. Please make sure that End is in the future!"
);
return;
}
if (voting.useEndTime && voting.useStartTime) {
if (voting.start >= voting.end) {
bsNotify(
"danger",
"Invalid time period. Please make sure that End is later than Start!"
);
return;
}
}

if (voting.voteLimit >= voting.numberOfVotes) {
bsNotify(
"danger",
"Invalid votes per work item. Please make sure that votes per work item do not exceed the votes per user!"
);
if (!this.isVotingValid()) {
return;
}

Expand Down Expand Up @@ -710,4 +665,68 @@ export class AdminPageController extends Vue {
this.initAsync();
}

private isVotingValid(): boolean {
const voting = this.actualVoting;
if (voting.useStartTime) {
voting.start = this.getStartDate().valueOf();
}

if (voting.useEndTime) {
voting.end = this.getEndDate().valueOf();
}


voting.title = escapeText(voting.title);
if ((voting.title == null || voting.title === "")) {
bsNotify("danger", "Please provide a title for the voting.");
return false;
}

if (voting.useStartTime) {
if (!voting.start) {
bsNotify(
"danger",
"Invalid time period. Please make sure that start date and time is filled!"
);
return false;
}
}

if (voting.useEndTime) {
if (!voting.end) {
bsNotify(
"danger",
"Invalid time period. Please make sure that end date and time is filled!"
);
return false;
}
}

if (voting.useEndTime && voting.end < Date.now()) {
bsNotify(
"danger",
"Invalid time period. Please make sure that End is in the future!"
);
return false;
}
if (voting.useEndTime && voting.useStartTime) {
if (voting.start >= voting.end) {
bsNotify(
"danger",
"Invalid time period. Please make sure that End is later than Start!"
);
return false;
}
}

if (voting.voteLimit > voting.numberOfVotes) {
bsNotify(
"danger",
"Invalid votes per work item. Please make sure that votes per work item do not exceed the votes per user!"
);
return false;
}
return true;
}

}
2 changes: 2 additions & 0 deletions Source/Extension.Voting/src/entities/report.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { User } from "./user";
import { ReportItem } from "./reportItem";
import { Voting } from "./voting";

export class Report {
name: string;
Expand All @@ -8,4 +9,5 @@ export class Report {
description: string;
workItems: ReportItem[];
users: User[];
voting: Voting;
}
2 changes: 1 addition & 1 deletion Source/Extension.Voting/src/entities/voting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class Voting {
public voteLimit: number = 1;
public group = "Team";
public team: string;
public type: string = VotingTypes.LEVEL;
public type: string;
public level: string;
public item: string;
public query: string = "";
Expand Down
100 changes: 73 additions & 27 deletions Source/Extension.Voting/src/reportPage/reportPageController.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import Vue from "vue";
import Component from "vue-class-component";
import * as controls from "VSS/Controls";
import * as menus from "VSS/Controls/Menus";
import * as grids from "VSS/Controls/Grids";
import * as workItemTrackingService from "TFS/WorkItemTracking/Services";
import { MenuBar, IMenuItemSpec } from "VSS/Controls/Menus";
import { MenuBar } from "VSS/Controls/Menus";
import { WaitControl } from "VSS/Controls/StatusIndicator";
import { LogExtension } from "../shared/logExtension";
import { ReportPageService } from "./reportPageService";
import { Report } from "../entities/report";
import { bsNotify } from "../shared/common";
import { CopyToClipboardService } from "../services/copyToClipboardService";
import { ReportDisplayService } from "./reportDisplayService";
import { VotingTypes } from "../entities/votingTypes";

@Component
export class ReportPageController extends Vue {

private reportPageService: ReportPageService;
private copyToClipboardService: CopyToClipboardService;
private grid: grids.Grid;
private menuBar: menus.MenuBar;
private actualVotingHasVotes: boolean = false;

public reportDisplayService: ReportDisplayService;
public report: Report = null;
Expand All @@ -37,13 +41,14 @@ export class ReportPageController extends Vue {
this.$el.classList.remove("hide");

this.reportDisplayService.subscribeToShowReport(
() => {
this.refreshAsync();
async () => {
await this.refreshAsync();
}
);
}

public async refreshAsync(): Promise<void> {
this.actualVotingHasVotes = await this.reportPageService.votingHasVotes();
this.waitControl.startWait();
try {
await this.loadReportAsync();
Expand All @@ -54,6 +59,8 @@ export class ReportPageController extends Vue {
this.grid.setDataSource(dataSource);
} finally {
this.waitControl.endWait();
await this.createMenuBar();

}
}

Expand Down Expand Up @@ -89,15 +96,25 @@ export class ReportPageController extends Vue {
return this.waitControl;
}

protected async createMenuBar() {
let menuItems = [] as IMenuItemSpec[];
menuItems.push(...[
{
private getMenuItems(): IContributedMenuItem[] {
const items = [
<any>{
id: "createNewVoting",
text: "Create new voting",
icon: "icon icon-add",
disabled: false
}, {
},
{
separator: true,
},
{
id: "applyToBacklog",
title:
"Apply to backlog (this applies the order of the backlog items from the voting to your backlog)",
icon: "icon icon-tfs-query-edit",
disabled: !this.isApplyable()
},
{

separator: true
},
Expand All @@ -106,26 +123,22 @@ export class ReportPageController extends Vue {
title: "Copy to clipboard",
icon: "bowtie-icon bowtie-clone",
disabled: false
}
]);
menuItems.push(...[
]);
controls.create(MenuBar, $(`#${ this.report_menu_container }`), {
showIcon: true,
items: menuItems,
executeAction: (args) => {
var command = args.get_commandName();
switch (command) {
case "copy":
this.copyToClipboard();
break;
case "createNewVoting":
this.createNewVoting();
break;
}
}
}];

});
return items;
}

protected async createMenuBar() {
if (this.menuBar == null) {
this.menuBar = controls.create(MenuBar, $(`#${ this.report_menu_container }`), {
showIcon: true,
executeAction: (args) => {
var command = args.get_commandName();
this.executeMenuAction(command);
}
});
}
this.menuBar.updateItems(this.getMenuItems());
}

protected createReportTable() {
Expand Down Expand Up @@ -186,6 +199,10 @@ export class ReportPageController extends Vue {
{
index: "totalVotes",
order: "desc"
},
{
index: "order",
order: "asc"
}
],
autoSort: true
Expand Down Expand Up @@ -221,8 +238,23 @@ export class ReportPageController extends Vue {
}
}

private executeMenuAction(command: string) {
switch (command) {
case "copy":
this.copyToClipboard();
break;
case "applyToBacklog":
this.applyToBacklogAsync();
break;
case "createNewVoting":
this.createNewVoting();
break;
}
}

private async initializeAsync(): Promise<void> {
this.waitControl.startWait();
this.actualVotingHasVotes = await this.reportPageService.votingHasVotes();
await this.loadReportAsync();
try {
this.createMenuBar();
Expand All @@ -231,4 +263,18 @@ export class ReportPageController extends Vue {
this.waitControl.endWait();
}
}

private isApplyable() {
return this.report.voting.type === VotingTypes.LEVEL && this.report.voting.isVotingTerminated && this.actualVotingHasVotes;
}

private async applyToBacklogAsync() {
this.waitControl.startWait();

try {
await this.reportPageService.applyToBacklogAsync(this.report);
} finally {
this.waitControl.endWait();
}
}
}
Loading

0 comments on commit b99dcad

Please sign in to comment.