forked from github/plane
refractor: moved all local state, and component to that component,
used dynamic imports where-ever possible
This commit is contained in:
parent
f67bbbf9ff
commit
eadebd8d93
@ -1,5 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
// next
|
// next
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// swr
|
||||||
|
import useSWR from "swr";
|
||||||
|
// constants
|
||||||
|
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||||
|
import { addSpaceIfCamelCase, timeAgo } from "constants/common";
|
||||||
|
// services
|
||||||
|
import issuesServices from "lib/services/issues.service";
|
||||||
|
// hooks
|
||||||
|
import useUser from "lib/hooks/useUser";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "ui";
|
import { Spinner } from "ui";
|
||||||
// icons
|
// icons
|
||||||
@ -12,14 +23,6 @@ import {
|
|||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IssueResponse, IState } from "types";
|
import { IssueResponse, IState } from "types";
|
||||||
// constants
|
|
||||||
import { addSpaceIfCamelCase, timeAgo } from "constants/common";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
issueActivities: any[] | undefined;
|
|
||||||
states: IState[] | undefined;
|
|
||||||
issues: IssueResponse | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const activityIcons: {
|
const activityIcons: {
|
||||||
[key: string]: JSX.Element;
|
[key: string]: JSX.Element;
|
||||||
@ -32,7 +35,25 @@ const activityIcons: {
|
|||||||
parent: <UserIcon className="h-3.5 w-3.5" />,
|
parent: <UserIcon className="h-3.5 w-3.5" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
const IssueActivitySection: React.FC<Props> = ({ issueActivities, states, issues }) => {
|
const IssueActivitySection: React.FC = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { issueId, projectId } = router.query;
|
||||||
|
|
||||||
|
const { activeWorkspace, states, issues } = useUser();
|
||||||
|
|
||||||
|
const { data: issueActivities } = useSWR<any[]>(
|
||||||
|
activeWorkspace && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null,
|
||||||
|
activeWorkspace && projectId && issueId
|
||||||
|
? () =>
|
||||||
|
issuesServices.getIssueActivities(
|
||||||
|
activeWorkspace.slug,
|
||||||
|
projectId as string,
|
||||||
|
issueId as string
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{issueActivities ? (
|
{issueActivities ? (
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
// router
|
||||||
|
import { useRouter } from "next/router";
|
||||||
// swr
|
// swr
|
||||||
import { mutate } from "swr";
|
import useSWR from "swr";
|
||||||
// react hook form
|
// react hook form
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// services
|
// services
|
||||||
import issuesServices from "lib/services/issues.service";
|
import issuesServices from "lib/services/issues.service";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import { PROJECT_ISSUES_COMMENTS } from "constants/fetch-keys";
|
import { PROJECT_ISSUES_COMMENTS } from "constants/fetch-keys";
|
||||||
|
// hooks
|
||||||
|
import useUser from "lib/hooks/useUser";
|
||||||
// components
|
// components
|
||||||
import CommentCard from "components/project/issues/issue-detail/comment/IssueCommentCard";
|
import CommentCard from "components/project/issues/issue-detail/comment/IssueCommentCard";
|
||||||
// ui
|
// ui
|
||||||
@ -14,18 +18,11 @@ import { TextArea, Button, Spinner } from "ui";
|
|||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueComment } from "types";
|
||||||
|
|
||||||
type Props = {
|
|
||||||
comments?: IIssueComment[];
|
|
||||||
workspaceSlug: string;
|
|
||||||
projectId: string;
|
|
||||||
issueId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueComment> = {
|
const defaultValues: Partial<IIssueComment> = {
|
||||||
comment: "",
|
comment: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, workspaceSlug }) => {
|
const IssueCommentSection: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -34,15 +31,31 @@ const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, wo
|
|||||||
reset,
|
reset,
|
||||||
} = useForm<IIssueComment>({ defaultValues });
|
} = useForm<IIssueComment>({ defaultValues });
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
let { issueId, projectId } = router.query;
|
||||||
|
|
||||||
|
const { activeWorkspace } = useUser();
|
||||||
|
|
||||||
|
const { data: comments, mutate } = useSWR<IIssueComment[]>(
|
||||||
|
activeWorkspace && projectId && issueId ? PROJECT_ISSUES_COMMENTS(issueId as string) : null,
|
||||||
|
activeWorkspace && projectId && issueId
|
||||||
|
? () =>
|
||||||
|
issuesServices.getIssueComments(
|
||||||
|
activeWorkspace.slug,
|
||||||
|
projectId as string,
|
||||||
|
issueId as string
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
const onSubmit = async (formData: IIssueComment) => {
|
const onSubmit = async (formData: IIssueComment) => {
|
||||||
|
if (!activeWorkspace || !projectId || !issueId || isSubmitting) return;
|
||||||
await issuesServices
|
await issuesServices
|
||||||
.createIssueComment(workspaceSlug, projectId, issueId, formData)
|
.createIssueComment(activeWorkspace.slug, projectId as string, issueId as string, formData)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
mutate<IIssueComment[]>(PROJECT_ISSUES_COMMENTS(issueId), (prevData) => [
|
mutate((prevData) => [response, ...(prevData ?? [])]);
|
||||||
response,
|
|
||||||
...(prevData ?? []),
|
|
||||||
]);
|
|
||||||
reset(defaultValues);
|
reset(defaultValues);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -51,26 +64,34 @@ const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, wo
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onCommentUpdate = async (comment: IIssueComment) => {
|
const onCommentUpdate = async (comment: IIssueComment) => {
|
||||||
|
if (!activeWorkspace || !projectId || !issueId || isSubmitting) return;
|
||||||
await issuesServices
|
await issuesServices
|
||||||
.patchIssueComment(workspaceSlug, projectId, issueId, comment.id, comment)
|
.patchIssueComment(
|
||||||
|
activeWorkspace.slug,
|
||||||
|
projectId as string,
|
||||||
|
issueId as string,
|
||||||
|
comment.id,
|
||||||
|
comment
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response);
|
mutate((prevData) => {
|
||||||
mutate<IIssueComment[]>(PROJECT_ISSUES_COMMENTS(issueId), (prevData) => {
|
const updatedComments = prevData?.map((c) => {
|
||||||
const newData = prevData ?? [];
|
if (c.id === comment.id) {
|
||||||
const index = newData.findIndex((comment) => comment.id === response.id);
|
return comment;
|
||||||
newData[index] = response;
|
}
|
||||||
return [...newData];
|
return c;
|
||||||
|
});
|
||||||
|
return updatedComments;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCommentDelete = async (commentId: string) => {
|
const onCommentDelete = async (commentId: string) => {
|
||||||
|
if (!activeWorkspace || !projectId || !issueId || isSubmitting) return;
|
||||||
await issuesServices
|
await issuesServices
|
||||||
.deleteIssueComment(workspaceSlug, projectId, issueId, commentId)
|
.deleteIssueComment(activeWorkspace.slug, projectId as string, issueId as string, commentId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
mutate<IIssueComment[]>(PROJECT_ISSUES_COMMENTS(issueId), (prevData) =>
|
mutate((prevData) => (prevData ?? []).filter((c) => c.id !== commentId));
|
||||||
(prevData ?? []).filter((c) => c.id !== commentId)
|
|
||||||
);
|
|
||||||
console.log(response);
|
console.log(response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -124,7 +145,6 @@ const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, wo
|
|||||||
/>
|
/>
|
||||||
<Button type="submit" className="whitespace-nowrap" disabled={isSubmitting}>
|
<Button type="submit" className="whitespace-nowrap" disabled={isSubmitting}>
|
||||||
{isSubmitting ? "Adding comment..." : "Add comment"}
|
{isSubmitting ? "Adding comment..." : "Add comment"}
|
||||||
{/* <UploadingIcon /> */}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
// swr
|
// swr
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// react hook form
|
// react hook form
|
||||||
@ -14,6 +15,8 @@ import useToast from "lib/hooks/useToast";
|
|||||||
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
||||||
// commons
|
// commons
|
||||||
import { copyTextToClipboard } from "constants/common";
|
import { copyTextToClipboard } from "constants/common";
|
||||||
|
// components
|
||||||
|
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
||||||
// ui
|
// ui
|
||||||
import { Input, Button, Spinner } from "ui";
|
import { Input, Button, Spinner } from "ui";
|
||||||
import { Popover } from "@headlessui/react";
|
import { Popover } from "@headlessui/react";
|
||||||
@ -30,7 +33,7 @@ import {
|
|||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import type { Control } from "react-hook-form";
|
import type { Control } from "react-hook-form";
|
||||||
import type { IIssue, IIssueLabels, NestedKeyOf } from "types";
|
import type { ICycle, IIssue, IIssueLabels, NestedKeyOf } from "types";
|
||||||
import { TwitterPicker } from "react-color";
|
import { TwitterPicker } from "react-color";
|
||||||
import { positionEditorElement } from "components/lexical/helpers/editor";
|
import { positionEditorElement } from "components/lexical/helpers/editor";
|
||||||
import SelectState from "./select-state";
|
import SelectState from "./select-state";
|
||||||
@ -46,7 +49,6 @@ type Props = {
|
|||||||
submitChanges: (formData: Partial<IIssue>) => void;
|
submitChanges: (formData: Partial<IIssue>) => void;
|
||||||
issueDetail: IIssue | undefined;
|
issueDetail: IIssue | undefined;
|
||||||
watch: UseFormWatch<IIssue>;
|
watch: UseFormWatch<IIssue>;
|
||||||
setDeleteIssueModal: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueLabels> = {
|
const defaultValues: Partial<IIssueLabels> = {
|
||||||
@ -59,7 +61,6 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||||||
submitChanges,
|
submitChanges,
|
||||||
issueDetail,
|
issueDetail,
|
||||||
watch: watchIssue,
|
watch: watchIssue,
|
||||||
setDeleteIssueModal,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [createLabelForm, setCreateLabelForm] = useState(false);
|
const [createLabelForm, setCreateLabelForm] = useState(false);
|
||||||
|
|
||||||
@ -74,6 +75,8 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -97,15 +100,17 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCycleChange = (cycleId: string) => {
|
const handleCycleChange = (cycleDetail: ICycle) => {
|
||||||
if (activeWorkspace && activeProject && issueDetail)
|
if (activeWorkspace && activeProject && issueDetail) {
|
||||||
|
submitChanges({ cycle: cycleDetail.id, cycle_detail: cycleDetail });
|
||||||
issuesServices
|
issuesServices
|
||||||
.addIssueToCycle(activeWorkspace.slug, activeProject.id, cycleId, {
|
.addIssueToCycle(activeWorkspace.slug, activeProject.id, cycleDetail.id, {
|
||||||
issues: [issueDetail.id],
|
issues: [issueDetail.id],
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
submitChanges({});
|
submitChanges({});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -420,6 +425,11 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ConfirmIssueDeletion
|
||||||
|
handleClose={() => setDeleteIssueModal(false)}
|
||||||
|
isOpen={deleteIssueModal}
|
||||||
|
data={issueDetail}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,13 +9,13 @@ import { Spinner, CustomSelect } from "ui";
|
|||||||
// icons
|
// icons
|
||||||
import { ArrowPathIcon } from "@heroicons/react/24/outline";
|
import { ArrowPathIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { ICycle, IIssue } from "types";
|
||||||
// common
|
// common
|
||||||
import { classNames } from "constants/common";
|
import { classNames } from "constants/common";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
control: Control<IIssue, any>;
|
control: Control<IIssue, any>;
|
||||||
handleCycleChange: (cycleId: string) => void;
|
handleCycleChange: (cycle: ICycle) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectCycle: React.FC<Props> = ({ control, handleCycleChange }) => {
|
const SelectCycle: React.FC<Props> = ({ control, handleCycleChange }) => {
|
||||||
@ -46,7 +46,7 @@ const SelectCycle: React.FC<Props> = ({ control, handleCycleChange }) => {
|
|||||||
}
|
}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(value: any) => {
|
onChange={(value: any) => {
|
||||||
handleCycleChange(value);
|
handleCycleChange(cycles?.find((c) => c.id === value) as any);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{cycles ? (
|
{cycles ? (
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
// next
|
// next
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
// swr
|
// swr
|
||||||
import useSWR, { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
// react hook form
|
// react hook form
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Disclosure, Menu, Tab, Transition } from "@headlessui/react";
|
import { Disclosure, Menu, Tab, Transition } from "@headlessui/react";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import {
|
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
PROJECT_ISSUES_ACTIVITY,
|
|
||||||
PROJECT_ISSUES_COMMENTS,
|
|
||||||
PROJECT_ISSUES_LIST,
|
|
||||||
} from "constants/fetch-keys";
|
|
||||||
// services
|
// services
|
||||||
import issuesServices from "lib/services/issues.service";
|
import issuesServices from "lib/services/issues.service";
|
||||||
// common
|
// common
|
||||||
@ -27,14 +23,23 @@ import withAuth from "lib/hoc/withAuthWrapper";
|
|||||||
// layouts
|
// layouts
|
||||||
import AppLayout from "layouts/app-layout";
|
import AppLayout from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import CreateUpdateIssuesModal from "components/project/issues/create-update-issue-modal";
|
|
||||||
import IssueCommentSection from "components/project/issues/issue-detail/comment/IssueCommentSection";
|
|
||||||
import AddAsSubIssue from "components/project/issues/issue-detail/add-as-sub-issue";
|
import AddAsSubIssue from "components/project/issues/issue-detail/add-as-sub-issue";
|
||||||
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
import CreateUpdateIssuesModal from "components/project/issues/create-update-issue-modal";
|
||||||
import IssueDetailSidebar from "components/project/issues/issue-detail/issue-detail-sidebar";
|
import IssueDetailSidebar from "components/project/issues/issue-detail/issue-detail-sidebar";
|
||||||
import IssueActivitySection from "components/project/issues/issue-detail/activity";
|
import IssueCommentSection from "components/project/issues/issue-detail/comment/IssueCommentSection";
|
||||||
|
const IssueActivitySection = dynamic(
|
||||||
|
() => import("components/project/issues/issue-detail/activity"),
|
||||||
|
{
|
||||||
|
loading: () => (
|
||||||
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
ssr: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
// ui
|
// ui
|
||||||
import { Spinner, TextArea, HeaderButton, Breadcrumbs, BreadcrumbItem, CustomMenu } from "ui";
|
import { Spinner, TextArea, HeaderButton, Breadcrumbs } from "ui";
|
||||||
// icons
|
// icons
|
||||||
import {
|
import {
|
||||||
ChevronLeftIcon,
|
ChevronLeftIcon,
|
||||||
@ -43,32 +48,51 @@ import {
|
|||||||
PlusIcon,
|
PlusIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueComment, IssueResponse } from "types";
|
import { IIssue, IssueResponse } from "types";
|
||||||
|
|
||||||
const RichTextEditor = dynamic(() => import("components/lexical/editor"), {
|
const RichTextEditor = dynamic(() => import("components/lexical/editor"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
state: "",
|
||||||
|
assignees_list: [],
|
||||||
|
priority: "low",
|
||||||
|
blockers_list: [],
|
||||||
|
blocked_list: [],
|
||||||
|
target_date: new Date().toString(),
|
||||||
|
issue_cycle: null,
|
||||||
|
labels_list: [],
|
||||||
|
};
|
||||||
|
|
||||||
const IssueDetail: NextPage = () => {
|
const IssueDetail: NextPage = () => {
|
||||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { issueId, projectId } = router.query;
|
||||||
|
|
||||||
|
const { activeWorkspace, activeProject, issues, mutateIssues } = useUser();
|
||||||
|
|
||||||
|
const issueDetail = issues?.results?.find((issue) => issue.id === issueId);
|
||||||
|
|
||||||
|
const prevIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) - 1];
|
||||||
|
const nextIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) + 1];
|
||||||
|
|
||||||
|
const subIssues = (issues && issues.results.filter((i) => i.parent === issueId)) ?? [];
|
||||||
|
const siblingIssues =
|
||||||
|
issueDetail &&
|
||||||
|
issues?.results.filter((i) => i.parent === issueDetail.parent && i.id !== issueId);
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isAddAsSubIssueOpen, setIsAddAsSubIssueOpen] = useState(false);
|
const [isAddAsSubIssueOpen, setIsAddAsSubIssueOpen] = useState(false);
|
||||||
|
|
||||||
const [issueDetail, setIssueDetail] = useState<IIssue | undefined>(undefined);
|
|
||||||
|
|
||||||
const [preloadedData, setPreloadedData] = useState<
|
const [preloadedData, setPreloadedData] = useState<
|
||||||
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
const [issueDescriptionValue, setIssueDescriptionValue] = useState("");
|
const [issueDescriptionValue, setIssueDescriptionValue] = useState("");
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { issueId, projectId } = router.query;
|
|
||||||
|
|
||||||
const { activeWorkspace, activeProject, issues, mutateIssues, states } = useUser();
|
|
||||||
|
|
||||||
const handleDescriptionChange: any = (value: any) => {
|
const handleDescriptionChange: any = (value: any) => {
|
||||||
console.log(value);
|
console.log(value);
|
||||||
setIssueDescriptionValue(value);
|
setIssueDescriptionValue(value);
|
||||||
@ -82,44 +106,9 @@ const IssueDetail: NextPage = () => {
|
|||||||
control,
|
control,
|
||||||
watch,
|
watch,
|
||||||
} = useForm<IIssue>({
|
} = useForm<IIssue>({
|
||||||
defaultValues: {
|
defaultValues,
|
||||||
name: "",
|
|
||||||
description: "",
|
|
||||||
state: "",
|
|
||||||
assignees_list: [],
|
|
||||||
priority: "low",
|
|
||||||
blockers_list: [],
|
|
||||||
blocked_list: [],
|
|
||||||
target_date: new Date().toString(),
|
|
||||||
issue_cycle: null,
|
|
||||||
labels_list: [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: issueActivities } = useSWR<any[]>(
|
|
||||||
activeWorkspace && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null,
|
|
||||||
activeWorkspace && projectId && issueId
|
|
||||||
? () =>
|
|
||||||
issuesServices.getIssueActivities(
|
|
||||||
activeWorkspace.slug,
|
|
||||||
projectId as string,
|
|
||||||
issueId as string
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: issueComments } = useSWR<IIssueComment[]>(
|
|
||||||
activeWorkspace && projectId && issueId ? PROJECT_ISSUES_COMMENTS(issueId as string) : null,
|
|
||||||
activeWorkspace && projectId && issueId
|
|
||||||
? () =>
|
|
||||||
issuesServices.getIssueComments(
|
|
||||||
activeWorkspace.slug,
|
|
||||||
projectId as string,
|
|
||||||
issueId as string
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const submitChanges = useCallback(
|
const submitChanges = useCallback(
|
||||||
(formData: Partial<IIssue>) => {
|
(formData: Partial<IIssue>) => {
|
||||||
if (!activeWorkspace || !activeProject || !issueId) return;
|
if (!activeWorkspace || !activeProject || !issueId) return;
|
||||||
@ -180,19 +169,6 @@ const IssueDetail: NextPage = () => {
|
|||||||
});
|
});
|
||||||
}, [issueDetail, reset]);
|
}, [issueDetail, reset]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const issueDetail = issues?.results.find((issue) => issue.id === issueId);
|
|
||||||
setIssueDetail(issueDetail);
|
|
||||||
}, [issueId, issues]);
|
|
||||||
|
|
||||||
const prevIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) - 1];
|
|
||||||
const nextIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) + 1];
|
|
||||||
|
|
||||||
const subIssues = (issues && issues.results.filter((i) => i.parent === issueId)) ?? [];
|
|
||||||
const siblingIssues =
|
|
||||||
issueDetail &&
|
|
||||||
issues?.results.filter((i) => i.parent === issueDetail.parent && i.id !== issueId);
|
|
||||||
|
|
||||||
const handleSubIssueRemove = (issueId: string) => {
|
const handleSubIssueRemove = (issueId: string) => {
|
||||||
if (activeWorkspace && activeProject) {
|
if (activeWorkspace && activeProject) {
|
||||||
issuesServices
|
issuesServices
|
||||||
@ -215,19 +191,17 @@ const IssueDetail: NextPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Issue detail", issueDetail);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
noPadding={true}
|
noPadding={true}
|
||||||
bg="secondary"
|
bg="secondary"
|
||||||
breadcrumbs={
|
breadcrumbs={
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
title={`${activeProject?.name ?? "Project"} Issues`}
|
title={`${activeProject?.name ?? "Project"} Issues`}
|
||||||
link={`/projects/${activeProject?.id}/issues`}
|
link={`/projects/${activeProject?.id}/issues`}
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
title={`Issue ${activeProject?.identifier ?? "Project"}-${
|
title={`Issue ${activeProject?.identifier ?? "Project"}-${
|
||||||
issueDetail?.sequence_id ?? "..."
|
issueDetail?.sequence_id ?? "..."
|
||||||
} Details`}
|
} Details`}
|
||||||
@ -259,6 +233,7 @@ const IssueDetail: NextPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
{isOpen && (
|
||||||
<CreateUpdateIssuesModal
|
<CreateUpdateIssuesModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
setIsOpen={setIsOpen}
|
setIsOpen={setIsOpen}
|
||||||
@ -267,16 +242,14 @@ const IssueDetail: NextPage = () => {
|
|||||||
...preloadedData,
|
...preloadedData,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConfirmIssueDeletion
|
)}
|
||||||
handleClose={() => setDeleteIssueModal(false)}
|
{isAddAsSubIssueOpen && (
|
||||||
isOpen={deleteIssueModal}
|
|
||||||
data={issueDetail}
|
|
||||||
/>
|
|
||||||
<AddAsSubIssue
|
<AddAsSubIssue
|
||||||
isOpen={isAddAsSubIssueOpen}
|
isOpen={isAddAsSubIssueOpen}
|
||||||
setIsOpen={setIsAddAsSubIssueOpen}
|
setIsOpen={setIsAddAsSubIssueOpen}
|
||||||
parent={issueDetail}
|
parent={issueDetail}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{issueDetail && activeProject ? (
|
{issueDetail && activeProject ? (
|
||||||
<div className="h-full flex gap-5">
|
<div className="h-full flex gap-5">
|
||||||
<div className="basis-2/3 space-y-5 p-5">
|
<div className="basis-2/3 space-y-5 p-5">
|
||||||
@ -590,19 +563,10 @@ const IssueDetail: NextPage = () => {
|
|||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels>
|
<Tab.Panels>
|
||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<IssueCommentSection
|
<IssueCommentSection />
|
||||||
comments={issueComments}
|
|
||||||
workspaceSlug={activeWorkspace?.slug as string}
|
|
||||||
projectId={projectId as string}
|
|
||||||
issueId={issueId as string}
|
|
||||||
/>
|
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<IssueActivitySection
|
<IssueActivitySection />
|
||||||
issueActivities={issueActivities}
|
|
||||||
states={states}
|
|
||||||
issues={issues}
|
|
||||||
/>
|
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
@ -615,7 +579,6 @@ const IssueDetail: NextPage = () => {
|
|||||||
issueDetail={issueDetail}
|
issueDetail={issueDetail}
|
||||||
submitChanges={submitChanges}
|
submitChanges={submitChanges}
|
||||||
watch={watch}
|
watch={watch}
|
||||||
setDeleteIssueModal={setDeleteIssueModal}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
1
apps/app/types/issues.d.ts
vendored
1
apps/app/types/issues.d.ts
vendored
@ -72,6 +72,7 @@ export interface IIssue {
|
|||||||
blocked_issue_details: any[];
|
blocked_issue_details: any[];
|
||||||
sprints: string | null;
|
sprints: string | null;
|
||||||
cycle: string | null;
|
cycle: string | null;
|
||||||
|
cycle_detail: ICycle | null;
|
||||||
|
|
||||||
issue_cycle: IIssueCycle;
|
issue_cycle: IIssueCycle;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ type BreadcrumbsProps = {
|
|||||||
children: any;
|
children: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ children }: BreadcrumbsProps) => {
|
const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -54,4 +54,6 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ title, link, icon }) =>
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Breadcrumbs.BreadcrumbItem = BreadcrumbItem;
|
||||||
|
|
||||||
export { Breadcrumbs, BreadcrumbItem };
|
export { Breadcrumbs, BreadcrumbItem };
|
||||||
|
Loading…
Reference in New Issue
Block a user