Skip to content

Commit

Permalink
Add progress bar and child issues to epic detail view (#81)
Browse files Browse the repository at this point in the history
Co-authored-by: Benedict Teutsch <bene210@web.de>
Co-authored-by: Julian Rupprecht <rupprecht.julian@web.de>
Co-authored-by: ValriRod <32778515+ValsiRod@users.noreply.github.com>
  • Loading branch information
4 people authored Dec 20, 2023
1 parent 0a53975 commit 8aeffef
Show file tree
Hide file tree
Showing 19 changed files with 635 additions and 158 deletions.
38 changes: 26 additions & 12 deletions electron/providers/jira-cloud-provider/JiraCloudProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,9 @@ export class JiraCloudProvider implements IProvider {
async getIssuesByProject(project: string): Promise<Issue[]> {
return new Promise((resolve, reject) => {
this.getRestApiClient(3)
.get(`/search?jql=project=${project}&maxResults=10000`)
.get(`/search?jql=project=${project}&maxResults=10000&fields=*all`)
.then(async (response) => {
resolve(this.fetchIssues(response))
resolve(this.fetchIssues(response, false))
})
.catch((error) => {
reject(new Error(`Error fetching issues by project: ${this.handleFetchIssuesError(error)}`))
Expand All @@ -424,7 +424,7 @@ export class JiraCloudProvider implements IProvider {
this.getAgileRestApiClient('1.0')
.get(`/sprint/${sprintId}/issue`)
.then(async (response) => {
resolve(this.fetchIssues(response))
resolve(this.fetchIssues(response, true))
})
.catch((error) => {
reject(new Error(`Error fetching issues by sprint: ${this.handleFetchIssuesError(error)}`))
Expand All @@ -440,15 +440,15 @@ export class JiraCloudProvider implements IProvider {
this.getAgileRestApiClient('1.0')
.get(`/board/${boardId}/backlog?jql=project=${project}&maxResults=500`)
.then(async (response) => {
resolve(this.fetchIssues(response))
resolve(this.fetchIssues(response, true))
})
.catch((error) => {
reject(new Error(`Error fetching issues by project: ${this.handleFetchIssuesError(error)}`))
})
})
}

async fetchIssues(response: AxiosResponse): Promise<Issue[]> {
async fetchIssues(response: AxiosResponse, isAgile: boolean): Promise<Issue[]> {
const rankCustomField = this.customFields.get("Rank") || ""
return new Promise((resolve) => {
const issues: Promise<Issue[]> = Promise.all(
Expand All @@ -459,18 +459,30 @@ export class JiraCloudProvider implements IProvider {
status: element.fields.status.name,
type: element.fields.issuetype.name,
storyPointsEstimate: await this.getIssueStoryPointsEstimate(element.key),
epic: element.fields.parent?.fields.summary,
epic: {
issueKey: element.fields.parent?.key,
summary: element.fields.parent?.fields.summary,
},
labels: element.fields.labels,
assignee: {
displayName: element.fields.assignee?.displayName,
avatarUrls: element.fields.assignee?.avatarUrls,
},
rank: element.fields[rankCustomField],
description: element.fields.description,
// IMPROVE: Remove boolean flag
description: (isAgile ? element.fields.description : element.fields.description?.content),
subtasks: element.fields.subtasks,
created: element.fields.created,
updated: element.fields.updated,
comment: element.fields.comment,
comment: {
comments: element.fields.comment.comments.map((commentElement) => ({
id: commentElement.id,
body: (isAgile ? commentElement.body : commentElement.body[0]?.content[0]?.text),
author: commentElement.author,
created: commentElement.created,
updated: commentElement.updated,
})),
},
projectId: element.fields.project.id,
sprint: element.fields.sprint,
attachments: element.fields.attachment,
Expand Down Expand Up @@ -658,7 +670,7 @@ export class JiraCloudProvider implements IProvider {
{
fields: {
summary,
parent: { key: epic },
parent: { key: epic.issueKey },
issuetype: { id: type },
project: {
id: projectId,
Expand Down Expand Up @@ -756,8 +768,8 @@ export class JiraCloudProvider implements IProvider {
...(summary && {
summary,
}),
...(epic && {
parent: { key: epic },
...(epic && epic.issueKey && {
parent: { key: epic.issueKey },
}),
...(type && {
issuetype: { id: type },
Expand Down Expand Up @@ -820,7 +832,7 @@ export class JiraCloudProvider implements IProvider {
}
}

reject(new Error(`Error creating issue: ${specificError}`))
reject(new Error(`Error editing issue: ${specificError}`))
})
})
}
Expand Down Expand Up @@ -853,7 +865,9 @@ export class JiraCloudProvider implements IProvider {
response.data.issues.map(async (element: JiraIssue) => ({
issueKey: element.key,
summary: element.fields.summary,
epic: element.fields.epic,
labels: element.fields.labels,
description: element.fields.description.content[0]?.content[0]?.text,
assignee: {
displayName: element.fields.assignee?.displayName,
avatarUrls: element.fields.assignee?.avatarUrls,
Expand Down
16 changes: 11 additions & 5 deletions electron/providers/jira-server-provider/JiraServerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export class JiraServerProvider implements IProvider {
async getIssuesByProject(project: string, boardId: number): Promise<Issue[]> {
return new Promise((resolve, reject) => {
this.getAgileRestApiClient('1.0')
.get(`/board/${boardId}/issue?jql=project=${project}&maxResults=10000`)
.get(`/board/${boardId}/issue?jql=project=${project}&maxResults=10000&fields=*all`)
.then((response) => resolve(this.fetchIssues(response)))
.catch((error) => reject(new Error(`Error in fetching issues: ${error}`)))
})
Expand Down Expand Up @@ -336,7 +336,10 @@ export class JiraServerProvider implements IProvider {
status: element.fields.status.name,
type: element.fields.issuetype.name,
storyPointsEstimate: await this.getIssueStoryPointsEstimate(element.key),
epic: element.fields.parent?.fields.summary,
epic: {
issueKey: element.fields.parent?.key,
summary: element.fields.parent?.fields.summary,
},
labels: element.fields.labels,
assignee: {
displayName: element.fields.assignee?.displayName,
Expand Down Expand Up @@ -515,7 +518,7 @@ export class JiraServerProvider implements IProvider {
{
fields: {
summary,
parent: { key: epic },
parent: { key: epic.issueKey },
issuetype: { id: type },
project: {
id: projectId,
Expand Down Expand Up @@ -583,6 +586,9 @@ export class JiraServerProvider implements IProvider {
issueKey: element.key,
summary: element.fields.summary,
labels: element.fields.labels,
created: element.fields.created,
updated: element.fields.updated,
description: element.fields.description.content,
assignee: {
displayName: element.fields.assignee?.displayName,
avatarUrls: element.fields.assignee?.avatarUrls,
Expand Down Expand Up @@ -854,8 +860,8 @@ export class JiraServerProvider implements IProvider {
...(summary && {
summary,
}),
...(epic && {
parent: { key: epic },
...(epic && epic.issueKey && {
parent: { key: epic.issueKey },
}),
...(type && {
issuetype: { id: type },
Expand Down
4 changes: 2 additions & 2 deletions src/components/BacklogView/Issue/IssueCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ export function IssueCard({
>
{issueKey}
</Text>
{epic && (
{epic.issueKey && (
<Badge mr={5} color="violet">
{epic}
{epic.summary}
</Badge>
)}
{labels?.length !== 0 &&
Expand Down
2 changes: 1 addition & 1 deletion src/components/BacklogView/helpers/backlogHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const searchIssuesFilter = (
.issues.filter(
(issue: Issue) =>
issue.summary.toLowerCase().includes(currentSearch.toLowerCase()) ||
issue.epic?.toLowerCase().includes(currentSearch.toLowerCase()) ||
issue.epic.summary?.toLowerCase().includes(currentSearch.toLowerCase()) ||
issue.assignee?.displayName
?.toLowerCase()
.includes(currentSearch.toLowerCase()) ||
Expand Down
5 changes: 5 additions & 0 deletions src/components/BacklogView/helpers/queryFetchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { Issue, Sprint, SprintCreate } from "types"
export const getSprints = (boardId: number): Promise<Sprint[]> =>
window.provider.getSprints(boardId)

export const getIssuesByProject = (
projectKey: string | undefined,
boardId: number
): Promise<Issue[]> => window.provider.getIssuesByProject(projectKey || "", boardId)

export const getIssuesBySprint = (
sprintId: number | undefined
): Promise<Issue[]> => window.provider.getIssuesBySprint(sprintId || 0)
Expand Down
1 change: 1 addition & 0 deletions src/components/CreateIssue/CreateIssueModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function CreateIssueModal({
status: "To Do",
reporter: currentUser,
priority: { id: "" },
epic: { issueKey: undefined }
} as Issue,
})

Expand Down
2 changes: 1 addition & 1 deletion src/components/CreateIssue/Fields/EpicSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function EpicSelect({
searchable
clearable
withinPortal
{...form.getInputProps("epic")}
{...form.getInputProps("epic.issueKey")}
/>
</Box>
</Tooltip>
Expand Down
2 changes: 1 addition & 1 deletion src/components/CreateIssue/Fields/IssueTypeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function IssueTypeSelect({
) {
form.setFieldValue("sprintId", null as unknown as string)
form.setFieldValue("storyPointsEstimate", null as unknown as number)
form.setFieldValue("epic", null as unknown as string)
form.setFieldValue("epic.issueKey", undefined)
}
form.setFieldValue("status", "To Do")
form.setFieldValue("priority.id", null)
Expand Down
22 changes: 12 additions & 10 deletions src/components/DetailView/Components/EditableEpic/EditableEpic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ export function EditableEpic({
}: {
projectId: string
issueKey: string
epic: string
epic: {
issueKey?: string,
summary?: string,
}
}) {
const [showEpicInput, setshowEpicInput] = useState(false)
const [showEpicInput, setShowEpicInput] = useState(false)
const { data: epics } = useQuery({
queryKey: ["epics", projectId],
queryFn: () => getEpicsByProject(projectId),
})
const [selectedEpic, setselectedEpic] = useState(epic)
const [selectedEpic, setSelectedEpic] = useState(epic.issueKey)
const mutationEpic = useMutation({
mutationFn: (epicKey: string) =>
editIssue({ epic: epicKey } as Issue, issueKey),
mutationFn: (epicKey: string) => editIssue({ epic: { issueKey: epicKey } } as Issue, issueKey),
onError: () => {
showNotification({
message: `An error occured while modifing the Epic 😢`,
message: `An error occurred while modifing the Epic 😢`,
color: "red",
})
},
Expand All @@ -49,7 +51,7 @@ export function EditableEpic({
placeholder=""
nothingFound="No Options"
data={
epics && epics instanceof Array
epics
? epics.map((epicItem) => ({
value: epicItem.issueKey,
label: epicItem.summary,
Expand All @@ -61,16 +63,16 @@ export function EditableEpic({
itemComponent={SelectItem}
value={selectedEpic}
onChange={(value) => {
setselectedEpic(value!)
setSelectedEpic(value!)
mutationEpic.mutate(value!)
setshowEpicInput(false)
setShowEpicInput(false)
}}
w="300px"
/>
) : (
<Group>
<Text
onClick={() => setshowEpicInput(true)}
onClick={() => setShowEpicInput(true)}
sx={{
":hover": { textDecoration: "underline", cursor: "pointer" },
}}
Expand Down
23 changes: 0 additions & 23 deletions src/components/DetailView/Components/EditableEpic/queries.ts

This file was deleted.

Loading

0 comments on commit 8aeffef

Please sign in to comment.