Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion src/IssueService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ export class IssueService {
const isSev = priorityText.toLowerCase().indexOf("sev") > -1;
const summary = issue.fields.summary;
const issueType = issue.fields.issuetype.name;
const labels = issue.fields.labels;
const team = issue.fields.customfield_10001?.name ?? "";

const ticket = new JiraTicket(ticketNumber, codeBase, priorityText, isPbi, isSev, issueType, summary)
const ticket = new JiraTicket(ticketNumber, codeBase, priorityText, isPbi, isSev, issueType, summary, labels, team);
issueList.push(ticket);
});

Expand Down
6 changes: 5 additions & 1 deletion src/JiraTicket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ export class JiraTicket {
Priority: string;
Summary: string;
IssueType: string;
Labels: string[];
Team: string;

constructor(number: string, codeBase: string, priorityText: string, isPbi: boolean, isSev: boolean, issueType: string, summary?: string) {
constructor(number: string, codeBase: string, priorityText: string, isPbi: boolean, isSev: boolean, issueType: string, summary?: string, labels?: string[], team?: string) {
this.Number = number;
this.CodeBase = codeBase;
this.Priority = priorityText;
this.IsPbi = isPbi;
this.IsSev = isSev;
this.IssueType = issueType;
this.Summary = summary ?? "";
this.Labels = labels ?? [];
this.Team = team ?? "";
}
}

16 changes: 16 additions & 0 deletions src/UserSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@ export class UserSettings {
BoardNumber: string = "";
SlackGroup: string = "";
FirstPiStartDate: string = "";
SlackGroups: { [key: string]: string };

constructor() {
this.SlackGroups = {
"DSCO": "@dsco-integrators",
"LEA": "@lea-integration",
"OMNI": "@omni-integration",
"PIT": "@pit-integration",
"PLG": "@plg-integrators",
"PPE": "@ppe-integration",
"SPEC": "@spec-integration",
"SPR": "@spr-integrators",
"TAG": "@tag-integration",
"TNT": "@tnt-integration",
};
}
}

Expand All @@ -28,6 +41,9 @@ export const fetchSettings = () => {
const date = new Date(year, month - 1, day);
userSettings.FirstPiStartDate = date.toISOString().split("T")[0];
}
if(settings.SlackGroups && Object.keys(settings.SlackGroups).length > 0) {
userSettings.SlackGroups = settings.SlackGroups;
}
}
return userSettings;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const isRegression = (issue: JiraTicket) => {
return issue.IssueType === "Regression";
}

export function hasValue(value: string) {
return value?.length > 0;
export function hasValue(value: string | undefined | null): boolean {
return value != null && value.length > 0;
}

export const getIssueUrl = (issueNumber: string) => {
Expand Down
15 changes: 11 additions & 4 deletions src/components/Actionbuttons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import type { Version } from "@/Version";
import { deleteItem } from "@/CosmosDb";
import { sendMessage } from "@/UserMessageService";
import { getIssueUrl, isRegression } from "@/Utils";
import { getIssueUrl, hasValue, isRegression } from "@/Utils";
import type { UserSettings } from "@/UserSettings";

const props = defineProps<{
Expand Down Expand Up @@ -48,12 +48,19 @@ const removeVersion = async(versionNumber: string, codeBase: string) =>{
const copyVersionForSlack = function(versionNumber: string, codeBase: string) {
const v = props.versions.find( v => v.Number === versionNumber && v.CodeBase == codeBase);
if (v) {
let output = `${props.settings?.SlackGroup}\r\n`;
let slackGroups = [props.settings?.SlackGroup];

let output = `\r\n`;
output += `${v.FullVersion}\r\n`;
v.Issues.forEach( (issue) => {
output += `[${issue.Number}](${getIssueUrl(issue.Number)}) - ${issue.Summary} ${issue.IsSev ? ` [${issue.Priority}]` : ""}${isRegression(issue) ? ` [regression]` : ""} \r\n`;
const team = props.settings.SlackGroups[issue.Team];
if(hasValue(team) && !slackGroups.includes(team!)) {
slackGroups.push(team!);
}
output += `[${issue.Number}](${getIssueUrl(issue.Number)}) ${hasValue(issue.Team) ? `[${issue.Team}]` : ""} - ${issue.Summary} ${issue.IsSev ? ` [${issue.Priority}]` : ""}${isRegression(issue) ? ` [regression]` : ""} \r\n`;
});
output += `\r\n`;

output = slackGroups.join(" ") + output;

// Copy the text inside the text field
navigator.clipboard.writeText(output);
Expand Down
24 changes: 18 additions & 6 deletions src/components/DraggableIssueList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,29 @@
item-key="Number">
<template #item="{element}">
<div class="border m-1 p-1 rounded">
<div class="bg-dark">
{{ element.Number }}
<div>
<strong>{{ element.Number }}</strong>
<a class="ms-2" :href="getIssueUrl(element.Number)" target="_blank" :title="`Jira Issue ${element.Number}`"><i class="fas"></i></a>
<a class="ms-2" :href="getIssueBranchUrl(element.Number)" target="_blank" :title="`${element.Number} Branch`"><i class="fas"></i></a>
<i v-if="element.IsPbi" title="pbi" class="badge bg-secondary small ms-1">pbi</i>
<i v-if="element.IsSev" :title="element.Priority" class="badge bg-secondary small ms-1">sev</i>
<i v-if="isRegression(element)" title="Regression sub-task" class="badge bg-warning small ms-1">regression</i>
<i v-if="element.IsSev" :title="element.Priority" class="badge bg-danger small ms-1">sev</i>
<i v-if="isRegression(element)" title="Regression sub-task" class="badge bg-warning text-dark small ms-1">regression</i>
</div>
<span v-if="hasValue(element.Team)" class="badge bg-info text-dark">{{ element.Team }}</span>
<small>{{ element.Summary }}</small>
<div>
<template v-for="label in element.Labels" :key="label" >
<span class="badge rounded-pill bg-secondary">{{ label }}</span>
</template>
</div>
<small>{{element.Summary}}</small>
</div>
</template>
</draggable>
</template>
<script setup lang="ts">
import draggable from 'vuedraggable';
import type { JiraTicket } from '@/JiraTicket';
import { isRegression } from '@/Utils';
import { isRegression, hasValue } from '@/Utils';
import { getIssueUrl } from '@/Utils';

const props = defineProps<{
Expand All @@ -36,3 +42,9 @@ const props = defineProps<{
const getIssueBranchUrl = (issueNumber: string) => `https://dev.azure.com/dealeron/${props.team}/_git/${props.repoName}?version=GB${issueNumber}`;

</script>

<style scoped>
.badge {
margin: 0 5px 0 0;
}
</style>
67 changes: 67 additions & 0 deletions src/components/NewSlackTeam.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<div class="modal" :class="{ 'fade show': showNewTeam }" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content bg-dark border border-success border-3 rounded-3">
<div class="modal-header">
<h5 class="modal-title">Add New Integration Slack Team</h5>
<button type="button" class="close" aria-label="Close" @click="handleCancelNewTeam">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="team">Team</label>
<input type="text" class="form-control" id="team" aria-describedby="teamlHelp" placeholder="DO"
v-model="teamName">
<small id="teamlHelp" class="form-text text-muted">DealerOn Engineering Team</small>
</div>
<div class="form-group">
<label for="slackGroup">Salck Integration Group</label>
<input type="text" class="form-control" id="slackGroup" aria-describedby="slackGroupHelp"
placeholder="@do-integration" v-model="slackGroup">
<small id="slackGroupHelp" class="form-text text-muted">Slack Integration group for this
Team</small>
</div>
</div>
<div class="d-grid gap-2 col-6 mx-auto">
<button type="button" class="btn btn-primary" @click="handleSaveNewTeam">Save changes</button>
<button type="button" class="btn btn-danger" @click="handleCancelNewTeam">Cancel</button>
</div>
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
const teamName = ref("");
const slackGroup = ref("");

const emits = defineEmits(["cancelNewTeam", "saveNewTeam"]);

const props = defineProps({
showNewTeam: Boolean
});

const handleSaveNewTeam = () => {
emits("saveNewTeam", { TeamName: teamName.value, SlackGroup: slackGroup.value });
clearValues();
};
const handleCancelNewTeam = () => {
emits("cancelNewTeam");
clearValues();
};

const clearValues = () => {
teamName.value = "";
slackGroup.value = "";
};

</script>

<style scoped>
.btn-danger {
margin-bottom: 1em;

}
</style>
41 changes: 33 additions & 8 deletions src/components/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,47 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div v-if="userSettings !== undefined" class="modal-body">
<div class="form-group">
<label for="email">E-mail</label>
<input v-if="userSettings !== undefined" type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter e-mail" v-model="userSettings.Email">
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter e-mail" v-model="userSettings.Email">
<small id="emailHelp" class="form-text text-muted">DealerOn e-mail address</small>
</div>
<div class="form-group">
<label for="apiKey">Jira Api Key</label>
<input v-if="userSettings !== undefined" type="text" class="form-control" id="apiKey" aria-describedby="apiKeyHelp" placeholder="Enter Jira api key" v-model="userSettings.ApiKey">
<input type="text" class="form-control" id="apiKey" aria-describedby="apiKeyHelp" placeholder="Enter Jira api key" v-model="userSettings.ApiKey">
<small id="apiKeyHelp" class="form-text text-muted">Jira Api Key <a href="https://id.atlassian.com/manage-profile/security/api-tokens" target="_blank"><i class="fas"></i></a></small>
</div>
<div class="form-group">
<label for="team">Team</label>
<input v-if="userSettings !== undefined" v-uppercase type="text" class="form-control" id="team" aria-describedby="teamHelp" placeholder="Enter name" v-model="userSettings.TeamName">
<input v-uppercase type="text" class="form-control" id="team" aria-describedby="teamHelp" placeholder="Enter name" v-model="userSettings.TeamName">
<small id="teamHelp" class="form-text text-muted">Three character team name: C2C, B2C, TNT, etc</small>
</div>
<div class="form-group">
<label for="boardNumber">Jira Board #</label>
<input v-if="userSettings !== undefined" type="text" class="form-control" id="boardNumber" aria-describedby="boardNumberHelp" placeholder="Enter board number" v-model="userSettings.BoardNumber">
<input type="text" class="form-control" id="boardNumber" aria-describedby="boardNumberHelp" placeholder="Enter board number" v-model="userSettings.BoardNumber">
<small id="boardNumberHelp" class="form-text text-muted">A jira board setup for to list issues by status</small>
</div>
<div class="form-group">
<label for="slackGroup">Slack integration group</label>
<input v-if="userSettings !== undefined" type="text" class="form-control" id="slackGroup" aria-describedby="slackGroupHelp" placeholder="Slack Group" v-model="userSettings.SlackGroup">
<input type="text" class="form-control" id="slackGroup" aria-describedby="slackGroupHelp" placeholder="Slack Group" v-model="userSettings.SlackGroup">
<small id="slackGroupHelp" class="form-text text-muted">A slack group for use when generating a slack message: I.e. @c2c-integrators</small>
</div>
<div class="form-group">
<label for="firstPi">First PI start date</label>
<input v-if="userSettings !== undefined" type="date" class="form-control" id="firstPi" aria-describedby="firstPiHelp" placeholder="01/01/1900" v-model="userSettings.FirstPiStartDate">
<small id="firstPiHelp" class="form-text text-muted"> The start date of the first PI of the year</small>
<input type="date" class="form-control" id="firstPi" aria-describedby="firstPiHelp" placeholder="01/01/1900" v-model="userSettings.FirstPiStartDate">
<small id="firstPiHelp" class="form-text text-muted">The start date of the first PI of the year</small>
</div>
<div class="form-group">
<label for="slackGroups">Integration Teams Slack Groups</label><br/>
<small id="slackGroupsHelp" class="form-text text-muted">Slack Integration groups for each team, used to send them notifications</small>
<ul>
<li v-for="(value, key) in userSettings.SlackGroups" :key="key">
<label for="slackGroup">{{ key }}</label>
<input type="text" class="form-control" id="slackGroup" aria-describedby="slackGroupHelp" placeholder="Slack Group" v-model="userSettings.SlackGroups[key]">
</li>
</ul>
<button type="button" class="btn btn-primary btn-sm" @click="showNewTeam = true">Add New Team</button>
</div>
</div>
<div class="modal-body">
Expand All @@ -51,9 +62,14 @@
</div>
</div>
</div>
<NewSlackTeam :showNewTeam="showNewTeam" @cancelNewTeam="toggleShowNewTeam" @saveNewTeam="addNewTeam"></NewSlackTeam>
</template>
<script setup lang="ts">
import { UserSettings } from '@/UserSettings';
import { ref } from 'vue';
import NewSlackTeam from './NewSlackTeam.vue';

const showNewTeam = ref(false);

const props = defineProps({
showSettings: Boolean,
Expand All @@ -65,6 +81,15 @@
const handleSaveSettings = () => {
emits("saveSettings");
};
const addNewTeam = (newTeam: { TeamName: string, SlackGroup: string }) => {
console.log("Adding new team", newTeam);
props.userSettings!.SlackGroups[newTeam.TeamName] = newTeam.SlackGroup;
showNewTeam.value = false;
};

const toggleShowNewTeam = () => {
showNewTeam.value = !showNewTeam.value;
}
</script>
<style scoped>
.show {
Expand Down
Loading