Skip to content

Commit

Permalink
feat: Group issues by project
Browse files Browse the repository at this point in the history
- Group issues list by project
- Renamed types
  • Loading branch information
CrawlerCode committed May 4, 2023
1 parent fea607a commit 4e070bf
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 79 deletions.
4 changes: 2 additions & 2 deletions src/api/redmine.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Issue } from "../types/redmine";
import { TIssue } from "../types/redmine";
import instance from "./axios.config";

export const getAllMyOpenIssues = async (offset = 0): Promise<Issue[]> => {
export const getAllMyOpenIssues = async (offset = 0): Promise<TIssue[]> => {
return instance.get(`/issues.json?limit=100&offset=${offset}&status_id=open&assigned_to_id=me`).then((res) => res.data.issues);
};
4 changes: 2 additions & 2 deletions src/components/issues/Issue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { faCheck, faPause, faPlay, faStop } from "@fortawesome/free-solid-svg-ic
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffect, useState } from "react";
import useSettings from "../../hooks/useSettings";
import { Issue } from "../../types/redmine";
import { TIssue } from "../../types/redmine";

type PropTypes = {
issue: Issue;
issue: TIssue;
isActive: boolean;
time: number;
start?: number;
Expand Down
160 changes: 94 additions & 66 deletions src/pages/IssuesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Toast from "../components/general/Toast";
import Issue from "../components/issues/Issue";
import useSettings from "../hooks/useSettings";
import useStorage from "../hooks/useStorage";
import { TIssue, TReference } from "../types/redmine";

type IssuesData = {
[id: number]: {
Expand All @@ -18,79 +19,106 @@ const IssuesPage = () => {
const { settings } = useSettings();

const issuesQuery = useQuery(["issues"], () => getAllMyOpenIssues());
const groupedIssues = issuesQuery.data?.reduce(
(
result: {
[id: number]: {
project: TReference;
issues: TIssue[];
};
},
issue
) => {
if (!(issue.project.id in result)) {
result[issue.project.id] = {
project: issue.project,
issues: [],
};
}
result[issue.project.id].issues.push(issue);
return result;
},
{}
);

const { data: issues, setData: setIssues } = useStorage<IssuesData>("issues", {});

return (
<>
<div className="flex flex-col items-center gap-y-2">
<div className="flex flex-col gap-y-2">
{issuesQuery.isLoading && <LoadingSpinner />}
{issuesQuery.isError && <Toast type="error" message="Failed to load issues" allowClose={false} />}
{issuesQuery.data?.map((issue) => {
const issueData =
issue.id in issues
? issues[issue.id]
: {
active: false,
start: undefined,
time: 0,
};
return (
<Issue
issue={issue}
isActive={issueData.active}
time={issueData.time}
start={issueData.start}
onStart={() => {
setIssues({
...(settings.options.autoPauseOnSwitch
? Object.entries(issues).reduce((res, [id, val]) => {
// @ts-ignore
res[id] = val.active
? {
active: false,
start: undefined,
time: calcTime(val.time, val.start),
}
: val;
return res;
}, {})
: issues),
[issue.id]: {
active: true,
start: new Date().getTime(),
time: issueData.time,
},
});
}}
onStop={(time) => {
setIssues({
...issues,
[issue.id]: {
active: false,
start: undefined,
time: time,
},
});
}}
onClear={() => {
setIssues({
...issues,
[issue.id]: {
active: false,
start: undefined,
time: 0,
},
});
}}
onDone={(time) => {
const h = time / 1000 / 60 / 60;
window.open(`${settings.redmineURL}/issues/${issue.id}/time_entries/new?time_entry[hours]=${h}`);
}}
key={issue.id}
/>
);
})}
{Object.entries(groupedIssues ?? {}).map(([_, { project, issues: groupIssues }]) => (
<>
<h5 className="text-xs text-slate-500 dark:text-slate-300 truncate">{project.name}</h5>
{groupIssues.map((issue) => {
const issueData =
issue.id in issues
? issues[issue.id]
: {
active: false,
start: undefined,
time: 0,
};
return (
<Issue
issue={issue}
isActive={issueData.active}
time={issueData.time}
start={issueData.start}
onStart={() => {
setIssues({
...(settings.options.autoPauseOnSwitch
? Object.entries(issues).reduce((res, [id, val]) => {
// @ts-ignore
res[id] = val.active
? {
active: false,
start: undefined,
time: calcTime(val.time, val.start),
}
: val;
return res;
}, {})
: issues),
[issue.id]: {
active: true,
start: new Date().getTime(),
time: issueData.time,
},
});
}}
onStop={(time) => {
setIssues({
...issues,
[issue.id]: {
active: false,
start: undefined,
time: time,
},
});
}}
onClear={() => {
setIssues({
...issues,
[issue.id]: {
active: false,
start: undefined,
time: 0,
},
});
}}
onDone={(time) => {
const h = time / 1000 / 60 / 60;
window.open(`${settings.redmineURL}/issues/${issue.id}/time_entries/new?time_entry[hours]=${h}`);
}}
key={issue.id}
/>
);
})}
</>
))}

{issuesQuery.data?.length === 0 && <p>No issues</p>}
</div>
</>
Expand Down
18 changes: 9 additions & 9 deletions src/types/redmine.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
export type Reference = {
export type TReference = {
id: number;
name: string;
};

export type Status = {
export type TStatus = {
id: number;
name: string;
is_closed: boolean;
};

export type Issue = {
export type TIssue = {
id: number;
project: Reference;
tracker: Reference;
status: Status;
priority: Reference;
author: Reference;
assigned_to: Reference;
project: TReference;
tracker: TReference;
status: TStatus;
priority: TReference;
author: TReference;
assigned_to: TReference;
subject: string;
description: string;
done_ratio: number;
Expand Down

0 comments on commit 4e070bf

Please sign in to comment.