From ec01d1baeacedca301da14f99490f878143f3e02 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 24 Aug 2023 20:15:41 +0530 Subject: [PATCH 1/7] feat: peak overview for issues --- .../app/components/core/views/issues-view.tsx | 2 +- .../views/spreadsheet-view/single-issue.tsx | 374 +++++++++--------- .../spreadsheet-view/spreadsheet-view.tsx | 2 +- .../components/inbox/inbox-issue-activity.tsx | 80 ++++ .../components/inbox/inbox-main-content.tsx | 17 +- apps/app/components/inbox/index.ts | 1 + apps/app/components/issues/activity.tsx | 70 +--- .../components/issues/description-form.tsx | 81 ++-- apps/app/components/issues/index.ts | 1 + apps/app/components/issues/main-content.tsx | 67 +++- .../issues/peak-overview/header.tsx | 78 ++++ .../components/issues/peak-overview/index.ts | 5 + .../issues/peak-overview/issue-activity.tsx | 66 ++++ .../issues/peak-overview/issue-details.tsx | 34 ++ .../issues/peak-overview/issue-properties.tsx | 186 +++++++++ .../issues/peak-overview/layout.tsx | 112 ++++++ .../issues/sidebar-select/assignee.tsx | 60 +-- .../issues/sidebar-select/estimate.tsx | 86 ++-- .../issues/sidebar-select/priority.tsx | 75 ++-- .../issues/sidebar-select/state.tsx | 109 +++-- apps/app/components/issues/sidebar.tsx | 129 +++--- 21 files changed, 1086 insertions(+), 549 deletions(-) create mode 100644 apps/app/components/inbox/inbox-issue-activity.tsx create mode 100644 apps/app/components/issues/peak-overview/header.tsx create mode 100644 apps/app/components/issues/peak-overview/index.ts create mode 100644 apps/app/components/issues/peak-overview/issue-activity.tsx create mode 100644 apps/app/components/issues/peak-overview/issue-details.tsx create mode 100644 apps/app/components/issues/peak-overview/issue-properties.tsx create mode 100644 apps/app/components/issues/peak-overview/layout.tsx diff --git a/apps/app/components/core/views/issues-view.tsx b/apps/app/components/core/views/issues-view.tsx index d3d76f80552..48b47d0818a 100644 --- a/apps/app/components/core/views/issues-view.tsx +++ b/apps/app/components/core/views/issues-view.tsx @@ -546,7 +546,7 @@ export const IssuesView: React.FC = ({ }} handleOnDragEnd={handleOnDragEnd} handleIssueAction={handleIssueAction} - openIssuesListModal={openIssuesListModal ? openIssuesListModal : null} + openIssuesListModal={openIssuesListModal ?? null} removeIssue={cycleId ? removeIssueFromCycle : moduleId ? removeIssueFromModule : null} trashBox={trashBox} setTrashBox={setTrashBox} diff --git a/apps/app/components/core/views/spreadsheet-view/single-issue.tsx b/apps/app/components/core/views/spreadsheet-view/single-issue.tsx index 7e8fd0d26bc..37a7817bf55 100644 --- a/apps/app/components/core/views/spreadsheet-view/single-issue.tsx +++ b/apps/app/components/core/views/spreadsheet-view/single-issue.tsx @@ -7,6 +7,7 @@ import { mutate } from "swr"; // components import { + IssuePeakOverview, ViewAssigneeSelect, ViewDueDateSelect, ViewEstimateSelect, @@ -75,6 +76,11 @@ export const SingleSpreadsheetIssue: React.FC = ({ nestingLevel, }) => { const [isOpen, setIsOpen] = useState(false); + + // issue peak overview + const [issuePeakOverview, setIssuePeakOverview] = useState(false); + const [issuePeakOverviewData, setIssuePeakOverviewData] = useState(null); + const router = useRouter(); const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; @@ -95,7 +101,7 @@ export const SingleSpreadsheetIssue: React.FC = ({ ? VIEW_ISSUES(viewId.toString(), params) : PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params); - if (issue.parent) { + if (issue.parent) mutate( SUB_ISSUES(issue.parent.toString()), (prevData) => { @@ -116,7 +122,7 @@ export const SingleSpreadsheetIssue: React.FC = ({ }, false ); - } else { + else mutate( fetchKey, (prevData) => @@ -131,7 +137,6 @@ export const SingleSpreadsheetIssue: React.FC = ({ }), false ); - } issuesService .patchIssue( @@ -158,6 +163,11 @@ export const SingleSpreadsheetIssue: React.FC = ({ [workspaceSlug, projectId, cycleId, moduleId, viewId, params, user] ); + const openPeakOverview = useCallback((issue: IIssue) => { + setIssuePeakOverviewData(issue); + setIssuePeakOverview(true); + }, []); + const handleCopyText = () => { const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; @@ -179,190 +189,202 @@ export const SingleSpreadsheetIssue: React.FC = ({ const isNotAllowed = userAuth.isGuest || userAuth.isViewer; return ( -
-
-
-
- {properties.key && ( - - {issue.project_detail?.identifier}-{issue.sequence_id} - - )} - {!isNotAllowed && !disableUserActions && ( -
- setIsOpen(nextOpenState)} - content={ -
- + - + - -
- } - placement="bottom-start" + +
+ } + placement="bottom-start" + > + + +
+ )} +
+ + {issue.sub_issues_count > 0 && ( +
+
)}
- {issue.sub_issues_count > 0 && ( -
- -
- )} -
- - - + - )} + {properties.state && ( +
+ +
+ )} + {properties.priority && ( +
+ +
+ )} + {properties.assignee && ( +
+ +
+ )} + {properties.labels && ( +
+ +
+ )} - {properties.start_date && ( -
- -
- )} + {properties.start_date && ( +
+ +
+ )} - {properties.due_date && ( -
- -
- )} - {properties.estimate && ( -
- -
- )} - {properties.created_on && ( -
- {renderLongDetailDateFormat(issue.created_at)} -
- )} - {properties.updated_on && ( -
- {renderLongDetailDateFormat(issue.updated_at)} -
- )} - + {properties.due_date && ( +
+ +
+ )} + {properties.estimate && ( +
+ +
+ )} + {properties.created_on && ( +
+ {renderLongDetailDateFormat(issue.created_at)} +
+ )} + {properties.updated_on && ( +
+ {renderLongDetailDateFormat(issue.updated_at)} +
+ )} + + ); }; diff --git a/apps/app/components/core/views/spreadsheet-view/spreadsheet-view.tsx b/apps/app/components/core/views/spreadsheet-view/spreadsheet-view.tsx index a4f426a2369..0b2e785d68f 100644 --- a/apps/app/components/core/views/spreadsheet-view/spreadsheet-view.tsx +++ b/apps/app/components/core/views/spreadsheet-view/spreadsheet-view.tsx @@ -5,7 +5,7 @@ import { useRouter } from "next/router"; // components import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { CustomMenu, Icon, Spinner } from "components/ui"; +import { CustomMenu, Spinner } from "components/ui"; // hooks import useIssuesProperties from "hooks/use-issue-properties"; import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; diff --git a/apps/app/components/inbox/inbox-issue-activity.tsx b/apps/app/components/inbox/inbox-issue-activity.tsx new file mode 100644 index 00000000000..6d586449cee --- /dev/null +++ b/apps/app/components/inbox/inbox-issue-activity.tsx @@ -0,0 +1,80 @@ +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// components +import { AddComment, IssueActivitySection } from "components/issues"; +// services +import issuesService from "services/issues.service"; +// hooks +import useUser from "hooks/use-user"; +// types +import { IIssue, IIssueComment } from "types"; +// fetch-keys +import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; + +type Props = { issueDetails: IIssue }; + +export const InboxIssueActivity: React.FC = ({ issueDetails }) => { + const router = useRouter(); + const { workspaceSlug, projectId, inboxIssueId } = router.query; + + const { user } = useUser(); + + const { data: issueActivity, mutate: mutateIssueActivity } = useSWR( + workspaceSlug && projectId && inboxIssueId + ? PROJECT_ISSUES_ACTIVITY(inboxIssueId.toString()) + : null, + workspaceSlug && projectId && inboxIssueId + ? () => + issuesService.getIssueActivities( + workspaceSlug.toString(), + projectId.toString(), + inboxIssueId.toString() + ) + : null + ); + + const handleCommentUpdate = async (comment: IIssueComment) => { + if (!workspaceSlug || !projectId || !inboxIssueId) return; + + await issuesService + .patchIssueComment( + workspaceSlug as string, + projectId as string, + inboxIssueId as string, + comment.id, + comment, + user + ) + .then(() => mutateIssueActivity()); + }; + + const handleCommentDelete = async (commentId: string) => { + if (!workspaceSlug || !projectId || !inboxIssueId) return; + + mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false); + + await issuesService + .deleteIssueComment( + workspaceSlug as string, + projectId as string, + inboxIssueId as string, + commentId, + user + ) + .then(() => mutateIssueActivity()); + }; + + return ( +
+

Comments/Activity

+ + +
+ ); +}; diff --git a/apps/app/components/inbox/inbox-main-content.tsx b/apps/app/components/inbox/inbox-main-content.tsx index bd4f0ab01fd..6d4a4337c0f 100644 --- a/apps/app/components/inbox/inbox-main-content.tsx +++ b/apps/app/components/inbox/inbox-main-content.tsx @@ -14,13 +14,8 @@ import inboxServices from "services/inbox.service"; import useInboxView from "hooks/use-inbox-view"; import useUserAuth from "hooks/use-user-auth"; // components -import { - AddComment, - IssueActivitySection, - IssueDescriptionForm, - IssueDetailsSidebar, - IssueReaction, -} from "components/issues"; +import { IssueDescriptionForm, IssueDetailsSidebar, IssueReaction } from "components/issues"; +import { InboxIssueActivity } from "components/inbox"; // ui import { Loader } from "components/ui"; // icons @@ -42,7 +37,6 @@ import { INBOX_ISSUES, INBOX_ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "cons const defaultValues = { name: "", - description: "", description_html: "", estimate_point: null, assignees_list: [], @@ -296,7 +290,6 @@ export const InboxMainContent: React.FC = () => { workspaceSlug={workspaceSlug as string} issue={{ name: issueDetails.name, - description: issueDetails.description, description_html: issueDetails.description_html, }} handleFormSubmit={submitChanges} @@ -312,11 +305,7 @@ export const InboxMainContent: React.FC = () => { issueId={issueDetails.id} /> -
-

Comments/Activity

- - -
+
diff --git a/apps/app/components/inbox/index.ts b/apps/app/components/inbox/index.ts index 38cea0348af..8301d25700c 100644 --- a/apps/app/components/inbox/index.ts +++ b/apps/app/components/inbox/index.ts @@ -4,6 +4,7 @@ export * from "./delete-issue-modal"; export * from "./filters-dropdown"; export * from "./filters-list"; export * from "./inbox-action-headers"; +export * from "./inbox-issue-activity"; export * from "./inbox-issue-card"; export * from "./inbox-main-content"; export * from "./issues-list-sidebar"; diff --git a/apps/app/components/issues/activity.tsx b/apps/app/components/issues/activity.tsx index 8dd948e083e..5a82907f2f8 100644 --- a/apps/app/components/issues/activity.tsx +++ b/apps/app/components/issues/activity.tsx @@ -3,10 +3,6 @@ import React from "react"; import Link from "next/link"; import { useRouter } from "next/router"; -import useSWR from "swr"; - -// services -import issuesService from "services/issues.service"; // components import { ActivityIcon, ActivityMessage } from "components/core"; import { CommentCard } from "components/issues/comment"; @@ -15,62 +11,23 @@ import { Icon, Loader } from "components/ui"; // helpers import { timeAgo } from "helpers/date-time.helper"; // types -import { ICurrentUserResponse, IIssueComment } from "types"; -// fetch-keys -import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; +import { IIssueActivity, IIssueComment } from "types"; type Props = { - issueId: string; - user: ICurrentUserResponse | undefined; + activity: IIssueActivity[] | undefined; + handleCommentUpdate: (comment: IIssueComment) => Promise; + handleCommentDelete: (commentId: string) => Promise; }; -export const IssueActivitySection: React.FC = ({ issueId, user }) => { +export const IssueActivitySection: React.FC = ({ + activity, + handleCommentUpdate, + handleCommentDelete, +}) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { data: issueActivities, mutate: mutateIssueActivities } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_ACTIVITY(issueId) : null, - workspaceSlug && projectId - ? () => - issuesService.getIssueActivities(workspaceSlug as string, projectId as string, issueId) - : null - ); - - const handleCommentUpdate = async (comment: IIssueComment) => { - if (!workspaceSlug || !projectId || !issueId) return; - - await issuesService - .patchIssueComment( - workspaceSlug as string, - projectId as string, - issueId as string, - comment.id, - comment, - user - ) - .then((res) => mutateIssueActivities()); - }; - - const handleCommentDelete = async (commentId: string) => { - if (!workspaceSlug || !projectId || !issueId) return; - - mutateIssueActivities( - (prevData: any) => prevData?.filter((p: any) => p.id !== commentId), - false - ); - - await issuesService - .deleteIssueComment( - workspaceSlug as string, - projectId as string, - issueId as string, - commentId, - user - ) - .then(() => mutateIssueActivities()); - }; + const { workspaceSlug } = router.query; - if (!issueActivities) { + if (!activity) return (
@@ -87,12 +44,11 @@ export const IssueActivitySection: React.FC = ({ issueId, user }) => {
); - } return (
    - {issueActivities.map((activityItem, index) => { + {activity.map((activityItem, index) => { // determines what type of action is performed const message = activityItem.field ? ( @@ -104,7 +60,7 @@ export const IssueActivitySection: React.FC = ({ issueId, user }) => { return (
  • - {issueActivities.length > 1 && index !== issueActivities.length - 1 ? ( + {activity.length > 1 && index !== activity.length - 1 ? (