forked from github/plane
fix: new project issues response (#303)
This commit is contained in:
parent
393638c700
commit
77c319c748
@ -43,10 +43,13 @@ export const BoardHeader: React.FC<Props> = ({
|
|||||||
|
|
||||||
let assignees: any;
|
let assignees: any;
|
||||||
if (selectedGroup === "assignees") {
|
if (selectedGroup === "assignees") {
|
||||||
assignees = groupTitle.split(",");
|
assignees = groupTitle && groupTitle !== "" ? groupTitle.split(",") : [];
|
||||||
assignees = assignees
|
assignees =
|
||||||
|
assignees.length > 0
|
||||||
|
? assignees
|
||||||
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
||||||
.join(", ");
|
.join(", ")
|
||||||
|
: "No assignee";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -12,10 +12,10 @@ import {
|
|||||||
DraggingStyle,
|
DraggingStyle,
|
||||||
NotDraggingStyle,
|
NotDraggingStyle,
|
||||||
} from "react-beautiful-dnd";
|
} from "react-beautiful-dnd";
|
||||||
// constants
|
|
||||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
ViewAssigneeSelect,
|
ViewAssigneeSelect,
|
||||||
@ -25,11 +25,12 @@ import {
|
|||||||
} from "components/issues/view-select";
|
} from "components/issues/view-select";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu } from "components/ui";
|
import { CustomMenu } from "components/ui";
|
||||||
|
// helpers
|
||||||
|
import { copyTextToClipboard } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import {
|
import {
|
||||||
CycleIssueResponse,
|
CycleIssueResponse,
|
||||||
IIssue,
|
IIssue,
|
||||||
IssueResponse,
|
|
||||||
ModuleIssueResponse,
|
ModuleIssueResponse,
|
||||||
NestedKeyOf,
|
NestedKeyOf,
|
||||||
Properties,
|
Properties,
|
||||||
@ -37,8 +38,6 @@ import {
|
|||||||
} from "types";
|
} from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
import { copyTextToClipboard } from "helpers/string.helper";
|
|
||||||
import useToast from "hooks/use-toast";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type?: string;
|
type?: string;
|
||||||
@ -71,7 +70,9 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const partialUpdateIssue = useCallback(
|
const partialUpdateIssue = useCallback(
|
||||||
(formData: Partial<IIssue>) => {
|
(formData: Partial<IIssue>) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
@ -118,15 +119,15 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((p) => {
|
||||||
results: (prevData?.results ?? []).map((p) => {
|
|
||||||
if (p.id === issue.id) return { ...p, ...formData };
|
if (p.id === issue.id) return { ...p, ...formData };
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import { Button } from "components/ui";
|
|||||||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||||
import { LayerDiagonalIcon } from "components/icons";
|
import { LayerDiagonalIcon } from "components/icons";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IssueResponse } from "types";
|
import { IIssue } from "types";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
|||||||
|
|
||||||
const filteredIssues: IIssue[] =
|
const filteredIssues: IIssue[] =
|
||||||
query === ""
|
query === ""
|
||||||
? issues?.results ?? []
|
? issues ?? []
|
||||||
: issues?.results.filter(
|
: issues?.filter(
|
||||||
(issue) =>
|
(issue) =>
|
||||||
issue.name.toLowerCase().includes(query.toLowerCase()) ||
|
issue.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
`${issue.project_detail.identifier}-${issue.sequence_id}`
|
`${issue.project_detail.identifier}-${issue.sequence_id}`
|
||||||
@ -101,17 +101,9 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
|||||||
message: res.message,
|
message: res.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => ({
|
(prevData) => (prevData ?? []).filter((p) => !data.delete_issue_ids.includes(p.id)),
|
||||||
...(prevData as IssueResponse),
|
|
||||||
count: (prevData?.results ?? []).filter(
|
|
||||||
(p) => !data.delete_issue_ids.some((id) => p.id === id)
|
|
||||||
).length,
|
|
||||||
results: (prevData?.results ?? []).filter(
|
|
||||||
(p) => !data.delete_issue_ids.some((id) => p.id === id)
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
handleClose();
|
handleClose();
|
||||||
|
@ -103,7 +103,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
|
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
|
||||||
<form>
|
<form>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
@ -22,7 +22,7 @@ import { TrashIcon } from "@heroicons/react/24/outline";
|
|||||||
// helpers
|
// helpers
|
||||||
import { getStatesList } from "helpers/state.helper";
|
import { getStatesList } from "helpers/state.helper";
|
||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, IIssue, IssueResponse, ModuleIssueResponse, UserAuth } from "types";
|
import { CycleIssueResponse, IIssue, ModuleIssueResponse, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import {
|
import {
|
||||||
CYCLE_ISSUES,
|
CYCLE_ISSUES,
|
||||||
@ -159,12 +159,12 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => {
|
(prevData) => {
|
||||||
if (!prevData) return prevData;
|
if (!prevData) return prevData;
|
||||||
|
|
||||||
const updatedIssues = prevData.results.map((issue) => {
|
const updatedIssues = prevData.map((issue) => {
|
||||||
if (issue.id === draggedItem.id)
|
if (issue.id === draggedItem.id)
|
||||||
return {
|
return {
|
||||||
...draggedItem,
|
...draggedItem,
|
||||||
@ -174,10 +174,7 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
return issue;
|
return issue;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return updatedIssues;
|
||||||
...prevData,
|
|
||||||
results: updatedIssues,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -248,12 +245,12 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => {
|
(prevData) => {
|
||||||
if (!prevData) return prevData;
|
if (!prevData) return prevData;
|
||||||
|
|
||||||
const updatedIssues = prevData.results.map((issue) => {
|
const updatedIssues = prevData.map((issue) => {
|
||||||
if (issue.id === draggedItem.id)
|
if (issue.id === draggedItem.id)
|
||||||
return {
|
return {
|
||||||
...draggedItem,
|
...draggedItem,
|
||||||
@ -264,10 +261,7 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
return issue;
|
return issue;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return updatedIssues;
|
||||||
...prevData,
|
|
||||||
results: updatedIssues,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,8 @@ import { mutate } from "swr";
|
|||||||
|
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
ViewAssigneeSelect,
|
ViewAssigneeSelect,
|
||||||
@ -16,19 +18,12 @@ import {
|
|||||||
} from "components/issues/view-select";
|
} from "components/issues/view-select";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu } from "components/ui";
|
import { CustomMenu } from "components/ui";
|
||||||
// types
|
// helpers
|
||||||
import {
|
|
||||||
CycleIssueResponse,
|
|
||||||
IIssue,
|
|
||||||
IssueResponse,
|
|
||||||
ModuleIssueResponse,
|
|
||||||
Properties,
|
|
||||||
UserAuth,
|
|
||||||
} from "types";
|
|
||||||
// fetch-keys
|
|
||||||
import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST, STATE_LIST } from "constants/fetch-keys";
|
|
||||||
import { copyTextToClipboard } from "helpers/string.helper";
|
import { copyTextToClipboard } from "helpers/string.helper";
|
||||||
import useToast from "hooks/use-toast";
|
// types
|
||||||
|
import { CycleIssueResponse, IIssue, ModuleIssueResponse, Properties, UserAuth } from "types";
|
||||||
|
// fetch-keys
|
||||||
|
import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type?: string;
|
type?: string;
|
||||||
@ -98,15 +93,15 @@ export const SingleListIssue: React.FC<Props> = ({
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((p) => {
|
||||||
results: (prevData?.results ?? []).map((p) => {
|
|
||||||
if (p.id === issue.id) return { ...p, ...formData };
|
if (p.id === issue.id) return { ...p, ...formData };
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -55,10 +55,13 @@ export const SingleList: React.FC<Props> = ({
|
|||||||
|
|
||||||
let assignees: any;
|
let assignees: any;
|
||||||
if (selectedGroup === "assignees") {
|
if (selectedGroup === "assignees") {
|
||||||
assignees = groupTitle.split(",");
|
assignees = groupTitle && groupTitle !== "" ? groupTitle.split(",") : [];
|
||||||
assignees = assignees
|
assignees =
|
||||||
|
assignees.length > 0
|
||||||
|
? assignees
|
||||||
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
||||||
.join(", ");
|
.join(", ")
|
||||||
|
: "No assignee";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -15,7 +15,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
|||||||
// ui
|
// ui
|
||||||
import { Button } from "components/ui";
|
import { Button } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import type { CycleIssueResponse, IIssue, IssueResponse, ModuleIssueResponse } from "types";
|
import type { CycleIssueResponse, IIssue, ModuleIssueResponse } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLE_ISSUES, PROJECT_ISSUES_LIST, MODULE_ISSUES, USER_ISSUE } from "constants/fetch-keys";
|
import { CYCLE_ISSUES, PROJECT_ISSUES_LIST, MODULE_ISSUES, USER_ISSUE } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -77,13 +77,9 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId),
|
||||||
(prevData) => ({
|
(prevData) => (prevData ?? []).filter((i) => i.id !== data.id),
|
||||||
...(prevData as IssueResponse),
|
|
||||||
results: prevData?.results.filter((i) => i.id !== data.id) ?? [],
|
|
||||||
count: (prevData?.count as number) - 1,
|
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// components
|
// components
|
||||||
import { IssueForm } from "components/issues";
|
import { IssueForm } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import type { IIssue, IssueResponse } from "types";
|
import type { IIssue } from "types";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import {
|
import {
|
||||||
PROJECT_ISSUES_DETAILS,
|
PROJECT_ISSUES_DETAILS,
|
||||||
@ -91,14 +91,12 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
} else
|
} else
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((i) => {
|
||||||
results: (prevData?.results ?? []).map((issue) => {
|
if (i.id === res.id) return { ...i, sprints: cycleId };
|
||||||
if (issue.id === res.id) return { ...issue, sprints: cycleId };
|
return i;
|
||||||
return issue;
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -126,7 +124,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
await issuesService
|
await issuesService
|
||||||
.createIssues(workspaceSlug as string, activeProject ?? "", payload)
|
.createIssues(workspaceSlug as string, activeProject ?? "", payload)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutate<IssueResponse>(PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""));
|
mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""));
|
||||||
|
|
||||||
if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);
|
if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);
|
||||||
if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module);
|
if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module);
|
||||||
@ -159,14 +157,12 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
if (isUpdatingSingleIssue) {
|
if (isUpdatingSingleIssue) {
|
||||||
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
|
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
|
||||||
} else {
|
} else {
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((i) => {
|
||||||
results: (prevData?.results ?? []).map((issue) => {
|
if (i.id === res.id) return { ...i, ...res };
|
||||||
if (issue.id === res.id) return { ...issue, ...res };
|
return i;
|
||||||
return issue;
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -232,7 +228,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
>
|
>
|
||||||
<Dialog.Panel className="relative transform rounded-lg bg-white p-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl">
|
<Dialog.Panel className="relative transform rounded-lg bg-white p-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl">
|
||||||
<IssueForm
|
<IssueForm
|
||||||
issues={issues?.results ?? []}
|
issues={issues ?? []}
|
||||||
handleFormSubmit={handleFormSubmit}
|
handleFormSubmit={handleFormSubmit}
|
||||||
initialData={prePopulateData}
|
initialData={prePopulateData}
|
||||||
createMore={createMore}
|
createMore={createMore}
|
||||||
|
@ -127,14 +127,14 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${
|
href={`/${workspaceSlug}/projects/${projectId}/issues/${
|
||||||
issues?.results.find((i) => i.id === issue)?.id
|
issues?.find((i) => i.id === issue)?.id
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<a className="flex items-center gap-1">
|
<a className="flex items-center gap-1">
|
||||||
<BlockedIcon height={10} width={10} />
|
<BlockedIcon height={10} width={10} />
|
||||||
{`${
|
{`${issues?.find((i) => i.id === issue)?.project_detail?.identifier}-${
|
||||||
issues?.results.find((i) => i.id === issue)?.project_detail?.identifier
|
issues?.find((i) => i.id === issue)?.sequence_id
|
||||||
}-${issues?.results.find((i) => i.id === issue)?.sequence_id}`}
|
}`}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="opacity-0 duration-300 group-hover:opacity-100">
|
<span className="opacity-0 duration-300 group-hover:opacity-100">
|
||||||
@ -243,8 +243,8 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<span className="flex-shrink-0 text-xs text-gray-500">
|
<span className="flex-shrink-0 text-xs text-gray-500">
|
||||||
{
|
{
|
||||||
issues?.results.find((i) => i.id === issue.id)
|
issues?.find((i) => i.id === issue.id)?.project_detail
|
||||||
?.project_detail?.identifier
|
?.identifier
|
||||||
}
|
}
|
||||||
-{issue.sequence_id}
|
-{issue.sequence_id}
|
||||||
</span>
|
</span>
|
||||||
|
@ -119,14 +119,14 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${
|
href={`/${workspaceSlug}/projects/${projectId}/issues/${
|
||||||
issues?.results.find((i) => i.id === issue)?.id
|
issues?.find((i) => i.id === issue)?.id
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<a className="flex items-center gap-1">
|
<a className="flex items-center gap-1">
|
||||||
<BlockerIcon height={10} width={10} />
|
<BlockerIcon height={10} width={10} />
|
||||||
{`${
|
{`${issues?.find((i) => i.id === issue)?.project_detail?.identifier}-${
|
||||||
issues?.results.find((i) => i.id === issue)?.project_detail?.identifier
|
issues?.find((i) => i.id === issue)?.sequence_id
|
||||||
}-${issues?.results.find((i) => i.id === issue)?.sequence_id}`}
|
}`}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span
|
<span
|
||||||
@ -244,8 +244,8 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<span className="flex-shrink-0 text-xs text-gray-500">
|
<span className="flex-shrink-0 text-xs text-gray-500">
|
||||||
{
|
{
|
||||||
issues?.results.find((i) => i.id === issue.id)
|
issues?.find((i) => i.id === issue.id)?.project_detail
|
||||||
?.project_detail?.identifier
|
?.identifier
|
||||||
}
|
}
|
||||||
-{issue.sequence_id}
|
-{issue.sequence_id}
|
||||||
</span>
|
</span>
|
||||||
|
@ -84,9 +84,9 @@ export const SidebarParentSelect: React.FC<Props> = ({
|
|||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{watch("parent") && watch("parent") !== ""
|
{watch("parent") && watch("parent") !== ""
|
||||||
? `${
|
? `${issues?.find((i) => i.id === watch("parent"))?.project_detail?.identifier}-${
|
||||||
issues?.results.find((i) => i.id === watch("parent"))?.project_detail?.identifier
|
issues?.find((i) => i.id === watch("parent"))?.sequence_id
|
||||||
}-${issues?.results.find((i) => i.id === watch("parent"))?.sequence_id}`
|
}`
|
||||||
: "Select issue"}
|
: "Select issue"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -222,7 +222,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
control={control}
|
control={control}
|
||||||
submitChanges={submitChanges}
|
submitChanges={submitChanges}
|
||||||
issuesList={
|
issuesList={
|
||||||
issues?.results.filter(
|
issues?.filter(
|
||||||
(i) =>
|
(i) =>
|
||||||
i.id !== issueDetail?.id &&
|
i.id !== issueDetail?.id &&
|
||||||
i.id !== issueDetail?.parent &&
|
i.id !== issueDetail?.parent &&
|
||||||
@ -250,13 +250,13 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<SidebarBlockerSelect
|
<SidebarBlockerSelect
|
||||||
submitChanges={submitChanges}
|
submitChanges={submitChanges}
|
||||||
issuesList={issues?.results.filter((i) => i.id !== issueDetail?.id) ?? []}
|
issuesList={issues?.filter((i) => i.id !== issueDetail?.id) ?? []}
|
||||||
watch={watchIssue}
|
watch={watchIssue}
|
||||||
userAuth={userAuth}
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
<SidebarBlockedSelect
|
<SidebarBlockedSelect
|
||||||
submitChanges={submitChanges}
|
submitChanges={submitChanges}
|
||||||
issuesList={issues?.results.filter((i) => i.id !== issueDetail?.id) ?? []}
|
issuesList={issues?.filter((i) => i.id !== issueDetail?.id) ?? []}
|
||||||
watch={watchIssue}
|
watch={watchIssue}
|
||||||
userAuth={userAuth}
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
|
@ -19,7 +19,7 @@ import { ChevronRightIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outli
|
|||||||
// helpers
|
// helpers
|
||||||
import { orderArrayBy } from "helpers/array.helper";
|
import { orderArrayBy } from "helpers/array.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IssueResponse, UserAuth } from "types";
|
import { IIssue, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_ISSUES_LIST, SUB_ISSUES } from "constants/fetch-keys";
|
import { PROJECT_ISSUES_LIST, SUB_ISSUES } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
let newSubIssues = [...(prevData as IIssue[])];
|
let newSubIssues = [...(prevData as IIssue[])];
|
||||||
|
|
||||||
data.issues.forEach((issueId: string) => {
|
data.issues.forEach((issueId: string) => {
|
||||||
const issue = issues?.results.find((i) => i.id === issueId);
|
const issue = issues?.find((i) => i.id === issueId);
|
||||||
|
|
||||||
if (issue) newSubIssues.push(issue);
|
if (issue) newSubIssues.push(issue);
|
||||||
});
|
});
|
||||||
@ -80,11 +80,10 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((p) => {
|
||||||
results: (prevData?.results ?? []).map((p) => {
|
|
||||||
if (data.issues.includes(p.id))
|
if (data.issues.includes(p.id))
|
||||||
return {
|
return {
|
||||||
...p,
|
...p,
|
||||||
@ -93,7 +92,6 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
|
|
||||||
return p;
|
return p;
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -118,11 +116,10 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutate(SUB_ISSUES(parentIssue.id ?? ""));
|
mutate(SUB_ISSUES(parentIssue.id ?? ""));
|
||||||
|
|
||||||
mutate<IssueResponse>(
|
mutate<IIssue[]>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => ({
|
(prevData) =>
|
||||||
...(prevData as IssueResponse),
|
(prevData ?? []).map((p) => {
|
||||||
results: (prevData?.results ?? []).map((p) => {
|
|
||||||
if (p.id === res.id)
|
if (p.id === res.id)
|
||||||
return {
|
return {
|
||||||
...p,
|
...p,
|
||||||
@ -131,7 +128,6 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
|
|
||||||
return p;
|
return p;
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -160,7 +156,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, userAuth }) => {
|
|||||||
isOpen={subIssuesListModal}
|
isOpen={subIssuesListModal}
|
||||||
handleClose={() => setSubIssuesListModal(false)}
|
handleClose={() => setSubIssuesListModal(false)}
|
||||||
issues={
|
issues={
|
||||||
issues?.results.filter(
|
issues?.filter(
|
||||||
(i) =>
|
(i) =>
|
||||||
(i.parent === "" || i.parent === null) &&
|
(i.parent === "" || i.parent === null) &&
|
||||||
i.id !== parentIssue?.id &&
|
i.id !== parentIssue?.id &&
|
||||||
|
@ -8,6 +8,8 @@ import { useRouter } from "next/router";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// services
|
// services
|
||||||
import cyclesService from "services/cycles.service";
|
import cyclesService from "services/cycles.service";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { Button, CustomMenu } from "components/ui";
|
import { Button, CustomMenu } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -17,12 +19,11 @@ import { CyclesIcon } from "components/icons";
|
|||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
||||||
import { groupBy } from "helpers/array.helper";
|
import { groupBy } from "helpers/array.helper";
|
||||||
|
import { copyTextToClipboard } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, ICycle } from "types";
|
import { CycleIssueResponse, ICycle } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLE_ISSUES } from "constants/fetch-keys";
|
import { CYCLE_ISSUES } from "constants/fetch-keys";
|
||||||
import { copyTextToClipboard } from "helpers/string.helper";
|
|
||||||
import useToast from "hooks/use-toast";
|
|
||||||
|
|
||||||
type TSingleStatProps = {
|
type TSingleStatProps = {
|
||||||
cycle: ICycle;
|
cycle: ICycle;
|
||||||
|
@ -74,7 +74,7 @@ export const DeleteStateModal: React.FC<Props> = ({ isOpen, onClose, data }) =>
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const groupedIssues = groupBy(issues?.results ?? [], "state");
|
const groupedIssues = groupBy(issues ?? [], "state");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) setIssuesWithThisStateExist(!!groupedIssues[data.id]);
|
if (data) setIssuesWithThisStateExist(!!groupedIssues[data.id]);
|
||||||
|
@ -118,7 +118,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
|||||||
<ExistingIssuesListModal
|
<ExistingIssuesListModal
|
||||||
isOpen={cycleIssuesListModal}
|
isOpen={cycleIssuesListModal}
|
||||||
handleClose={() => setCycleIssuesListModal(false)}
|
handleClose={() => setCycleIssuesListModal(false)}
|
||||||
issues={issues?.results.filter((i) => !i.issue_cycle) ?? []}
|
issues={issues?.filter((i) => !i.issue_cycle) ?? []}
|
||||||
handleOnSubmit={handleAddIssuesToCycle}
|
handleOnSubmit={handleAddIssuesToCycle}
|
||||||
/>
|
/>
|
||||||
<AppLayout
|
<AppLayout
|
||||||
|
@ -55,9 +55,7 @@ const ProjectIssues: NextPage<UserAuth> = (props) => {
|
|||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<IssuesFilterView
|
<IssuesFilterView issues={projectIssues?.filter((p) => p.parent === null) ?? []} />
|
||||||
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
|
||||||
/>
|
|
||||||
<HeaderButton
|
<HeaderButton
|
||||||
Icon={PlusIcon}
|
Icon={PlusIcon}
|
||||||
label="Add Issue"
|
label="Add Issue"
|
||||||
@ -72,9 +70,9 @@ const ProjectIssues: NextPage<UserAuth> = (props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{projectIssues ? (
|
{projectIssues ? (
|
||||||
projectIssues.count > 0 ? (
|
projectIssues.length > 0 ? (
|
||||||
<IssuesView
|
<IssuesView
|
||||||
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
issues={projectIssues?.filter((p) => p.parent === null) ?? []}
|
||||||
userAuth={props}
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -113,7 +113,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
|||||||
<ExistingIssuesListModal
|
<ExistingIssuesListModal
|
||||||
isOpen={moduleIssuesListModal}
|
isOpen={moduleIssuesListModal}
|
||||||
handleClose={() => setModuleIssuesListModal(false)}
|
handleClose={() => setModuleIssuesListModal(false)}
|
||||||
issues={issues?.results.filter((i) => !i.issue_module) ?? []}
|
issues={issues?.filter((i) => !i.issue_module) ?? []}
|
||||||
handleOnSubmit={handleAddIssuesToModule}
|
handleOnSubmit={handleAddIssuesToModule}
|
||||||
/>
|
/>
|
||||||
<AppLayout
|
<AppLayout
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// services
|
// services
|
||||||
import APIService from "services/api.service";
|
import APIService from "services/api.service";
|
||||||
// type
|
// type
|
||||||
import type { IIssue, IIssueActivity, IIssueComment, IssueResponse } from "types";
|
import type { IIssue, IIssueActivity, IIssueComment } from "types";
|
||||||
|
|
||||||
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ class ProjectIssuesServices extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getIssues(workspaceSlug: string, projectId: string): Promise<IssueResponse> {
|
async getIssues(workspaceSlug: string, projectId: string): Promise<IIssue[]> {
|
||||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`)
|
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`)
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
11
apps/app/types/issues.d.ts
vendored
11
apps/app/types/issues.d.ts
vendored
@ -1,16 +1,5 @@
|
|||||||
import type { IState, IUser, IProject, ICycle, IModule, IUserLite } from "./";
|
import type { IState, IUser, IProject, ICycle, IModule, IUserLite } from "./";
|
||||||
|
|
||||||
export interface IssueResponse {
|
|
||||||
next_cursor: string;
|
|
||||||
prev_cursor: string;
|
|
||||||
next_page_results: boolean;
|
|
||||||
prev_page_results: boolean;
|
|
||||||
count: number;
|
|
||||||
total_pages: number;
|
|
||||||
extra_stats: null;
|
|
||||||
results: IIssue[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IIssueCycle {
|
export interface IIssueCycle {
|
||||||
id: string;
|
id: string;
|
||||||
cycle_detail: ICycle;
|
cycle_detail: ICycle;
|
||||||
|
Loading…
Reference in New Issue
Block a user