From 10f145f85c616ceb3df9b695641ce16d02c9c5b0 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:39:42 +0530 Subject: [PATCH] feat: user profile analytics, views and filters (#1698) * feat: user profile overview * chore: profile sidebar designed * feat: user issues filters and view options * refactor: filters * refactor: mutation logic * fix: percentage calculation logic and sidebar shadow --- .../core/views/board-view/single-issue.tsx | 39 +-- .../app/components/core/views/issues-view.tsx | 2 + .../core/views/list-view/single-issue.tsx | 48 +-- apps/app/components/issues/activity.tsx | 305 +---------------- apps/app/components/issues/modal.tsx | 24 +- .../issues/my-issues/my-issues-view.tsx | 25 +- .../issues/view-select/assignee.tsx | 17 +- apps/app/components/profile/index.ts | 5 + apps/app/components/profile/navbar.tsx | 54 +++ apps/app/components/profile/overview/index.ts | 4 + .../overview/priority-distribution.tsx | 75 +++++ .../profile/overview/state-distribution.tsx | 81 +++++ .../app/components/profile/overview/stats.tsx | 66 ++++ .../components/profile/overview/workload.tsx | 32 ++ .../profile/profile-issues-view-options.tsx | 298 +++++++++++++++++ .../profile/profile-issues-view.tsx | 273 +++++++++++++++ apps/app/components/profile/sidebar.tsx | 256 ++++++++++++++ .../workspace/create-workspace-form.tsx | 1 + apps/app/constants/fetch-keys.ts | 21 +- apps/app/constants/state.ts | 10 +- apps/app/contexts/profile-issues-context.tsx | 310 +++++++++++++++++ apps/app/helpers/activity.helper.tsx | 312 ++++++++++++++++++ apps/app/hooks/use-issues-view.tsx | 19 +- apps/app/hooks/use-profile-issues.tsx | 126 +++++++ apps/app/hooks/use-project-members.tsx | 12 +- .../pages/[workspaceSlug]/me/my-issues.tsx | 2 +- .../profile/[userId]/assigned.tsx | 48 +++ .../profile/[userId]/created.tsx | 48 +++ .../profile/[userId]/index.tsx | 152 +++++++++ .../profile/[userId]/subscribed.tsx | 48 +++ apps/app/services/user.service.ts | 51 +++ apps/app/types/issues.d.ts | 7 + apps/app/types/users.d.ts | 52 ++- 33 files changed, 2396 insertions(+), 427 deletions(-) create mode 100644 apps/app/components/profile/index.ts create mode 100644 apps/app/components/profile/navbar.tsx create mode 100644 apps/app/components/profile/overview/index.ts create mode 100644 apps/app/components/profile/overview/priority-distribution.tsx create mode 100644 apps/app/components/profile/overview/state-distribution.tsx create mode 100644 apps/app/components/profile/overview/stats.tsx create mode 100644 apps/app/components/profile/overview/workload.tsx create mode 100644 apps/app/components/profile/profile-issues-view-options.tsx create mode 100644 apps/app/components/profile/profile-issues-view.tsx create mode 100644 apps/app/components/profile/sidebar.tsx create mode 100644 apps/app/contexts/profile-issues-context.tsx create mode 100644 apps/app/helpers/activity.helper.tsx create mode 100644 apps/app/hooks/use-profile-issues.tsx create mode 100644 apps/app/pages/[workspaceSlug]/profile/[userId]/assigned.tsx create mode 100644 apps/app/pages/[workspaceSlug]/profile/[userId]/created.tsx create mode 100644 apps/app/pages/[workspaceSlug]/profile/[userId]/index.tsx create mode 100644 apps/app/pages/[workspaceSlug]/profile/[userId]/subscribed.tsx diff --git a/apps/app/components/core/views/board-view/single-issue.tsx b/apps/app/components/core/views/board-view/single-issue.tsx index 5deaabe06..533d4ffe7 100644 --- a/apps/app/components/core/views/board-view/single-issue.tsx +++ b/apps/app/components/core/views/board-view/single-issue.tsx @@ -46,16 +46,7 @@ import { copyTextToClipboard } from "helpers/string.helper"; // types import { ICurrentUserResponse, IIssue, IIssueViewProps, ISubIssueResponse, UserAuth } from "types"; // fetch-keys -import { - CYCLE_DETAILS, - CYCLE_ISSUES_WITH_PARAMS, - MODULE_DETAILS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - SUB_ISSUES, - USER_ISSUES, - VIEW_ISSUES, -} from "constants/fetch-keys"; +import { CYCLE_DETAILS, MODULE_DETAILS, SUB_ISSUES } from "constants/fetch-keys"; type Props = { type?: string; @@ -99,10 +90,10 @@ export const SingleBoardIssue: React.FC = ({ const actionSectionRef = useRef(null); - const { groupByProperty: selectedGroup, orderBy, params, properties } = viewProps; + const { groupByProperty: selectedGroup, orderBy, properties, mutateIssues } = viewProps; const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { setToastAlert } = useToast(); @@ -110,16 +101,6 @@ export const SingleBoardIssue: React.FC = ({ (formData: Partial, issue: IIssue) => { if (!workspaceSlug || !issue) return; - const fetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) - : viewId - ? VIEW_ISSUES(viewId.toString(), params) - : router.pathname.includes("my-issues") - ? USER_ISSUES(workspaceSlug.toString(), params) - : PROJECT_ISSUES_LIST_WITH_PARAMS(issue.project.toString(), params); - if (issue.parent) { mutate( SUB_ISSUES(issue.parent.toString()), @@ -142,13 +123,7 @@ export const SingleBoardIssue: React.FC = ({ false ); } else { - mutate< - | { - [key: string]: IIssue[]; - } - | IIssue[] - >( - fetchKey, + mutateIssues( (prevData) => handleIssuesMutation( formData, @@ -165,7 +140,7 @@ export const SingleBoardIssue: React.FC = ({ issuesService .patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user) .then(() => { - mutate(fetchKey); + mutateIssues(); if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); @@ -175,13 +150,11 @@ export const SingleBoardIssue: React.FC = ({ workspaceSlug, cycleId, moduleId, - viewId, groupTitle, index, selectedGroup, + mutateIssues, orderBy, - params, - router, user, ] ); diff --git a/apps/app/components/core/views/issues-view.tsx b/apps/app/components/core/views/issues-view.tsx index 04c6ad0f7..96b7e395c 100644 --- a/apps/app/components/core/views/issues-view.tsx +++ b/apps/app/components/core/views/issues-view.tsx @@ -79,6 +79,7 @@ export const IssuesView: React.FC = ({ const { groupedByIssues, + mutateIssues, issueView, groupByProperty: selectedGroup, orderBy, @@ -525,6 +526,7 @@ export const IssuesView: React.FC = ({ groupedIssues: groupedByIssues, isEmpty, issueView, + mutateIssues, orderBy, params, properties, diff --git a/apps/app/components/core/views/list-view/single-issue.tsx b/apps/app/components/core/views/list-view/single-issue.tsx index 11d3c0c75..aa6c4caff 100644 --- a/apps/app/components/core/views/list-view/single-issue.tsx +++ b/apps/app/components/core/views/list-view/single-issue.tsx @@ -35,25 +35,9 @@ import { LayerDiagonalIcon } from "components/icons"; import { copyTextToClipboard, truncateText } from "helpers/string.helper"; import { handleIssuesMutation } from "constants/issue"; // types -import { - ICurrentUserResponse, - IIssue, - IIssueViewProps, - ISubIssueResponse, - Properties, - UserAuth, -} from "types"; +import { ICurrentUserResponse, IIssue, IIssueViewProps, ISubIssueResponse, UserAuth } from "types"; // fetch-keys -import { - CYCLE_DETAILS, - CYCLE_ISSUES_WITH_PARAMS, - MODULE_DETAILS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - SUB_ISSUES, - USER_ISSUES, - VIEW_ISSUES, -} from "constants/fetch-keys"; +import { CYCLE_DETAILS, MODULE_DETAILS, SUB_ISSUES } from "constants/fetch-keys"; type Props = { type?: string; @@ -89,27 +73,17 @@ export const SingleListIssue: React.FC = ({ const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 }); const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const isArchivedIssues = router.pathname.includes("archived-issues"); const { setToastAlert } = useToast(); - const { groupByProperty: selectedGroup, orderBy, params, properties } = viewProps; + const { groupByProperty: selectedGroup, orderBy, properties, mutateIssues } = viewProps; const partialUpdateIssue = useCallback( (formData: Partial, issue: IIssue) => { if (!workspaceSlug || !issue) return; - const fetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) - : viewId - ? VIEW_ISSUES(viewId.toString(), params) - : router.pathname.includes("my-issues") - ? USER_ISSUES(workspaceSlug.toString(), params) - : PROJECT_ISSUES_LIST_WITH_PARAMS(issue.project.toString(), params); - if (issue.parent) { mutate( SUB_ISSUES(issue.parent.toString()), @@ -132,13 +106,7 @@ export const SingleListIssue: React.FC = ({ false ); } else { - mutate< - | { - [key: string]: IIssue[]; - } - | IIssue[] - >( - fetchKey, + mutateIssues( (prevData) => handleIssuesMutation( formData, @@ -155,7 +123,7 @@ export const SingleListIssue: React.FC = ({ issuesService .patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user) .then(() => { - mutate(fetchKey); + mutateIssues(); if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); @@ -165,13 +133,11 @@ export const SingleListIssue: React.FC = ({ workspaceSlug, cycleId, moduleId, - viewId, groupTitle, index, selectedGroup, + mutateIssues, orderBy, - params, - router, user, ] ); diff --git a/apps/app/components/issues/activity.tsx b/apps/app/components/issues/activity.tsx index 10b9d450f..2780d3ba1 100644 --- a/apps/app/components/issues/activity.tsx +++ b/apps/app/components/issues/activity.tsx @@ -10,313 +10,14 @@ import issuesService from "services/issues.service"; import { CommentCard } from "components/issues/comment"; // ui import { Icon, Loader } from "components/ui"; -// icons -import { Squares2X2Icon } from "@heroicons/react/24/outline"; -import { BlockedIcon, BlockerIcon } from "components/icons"; // helpers -import { renderShortDateWithYearFormat, timeAgo } from "helpers/date-time.helper"; -import { capitalizeFirstLetter } from "helpers/string.helper"; +import { timeAgo } from "helpers/date-time.helper"; +import { activityDetails } from "helpers/activity.helper"; // types -import { ICurrentUserResponse, IIssueActivity, IIssueComment } from "types"; +import { ICurrentUserResponse, IIssueComment } from "types"; // fetch-keys import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; -const activityDetails: { - [key: string]: { - message: (activity: IIssueActivity) => React.ReactNode; - icon: React.ReactNode; - }; -} = { - assignees: { - message: (activity) => { - if (activity.old_value === "") - return ( - <> - added a new assignee{" "} - {activity.new_value}. - - ); - else - return ( - <> - removed the assignee{" "} - {activity.old_value}. - - ); - }, - icon: