From 529ab19747085e224f020da4fa0da3c3cac23d0a Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:13:04 +0530 Subject: [PATCH 01/70] chore: removed extra exporter function (#1953) --- apiserver/plane/api/urls.py | 5 ++++- apiserver/plane/api/views/issue.py | 25 ------------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index b8743476e..8ebf10f57 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -89,7 +89,6 @@ from plane.api.views import ( IssueCommentPublicViewSet, IssueReactionViewSet, CommentReactionViewSet, - ExportIssuesEndpoint, ## End Issues # States StateViewSet, @@ -175,6 +174,10 @@ from plane.api.views import ( InboxIssuePublicViewSet, IssueVotePublicViewSet, ## End Public Boards + ## Exporter + ExportIssuesEndpoint, + ## End Exporter + ) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 6f0f1e6ae..0b08bb14f 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -1827,28 +1827,3 @@ class IssueVotePublicViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - -class ExportIssuesEndpoint(BaseAPIView): - permission_classes = [ - WorkSpaceAdminPermission, - ] - - def post(self, request, slug): - try: - - issue_export_task.delay( - email=request.user.email, data=request.data, slug=slug ,exporter_name=request.user.first_name - ) - - return Response( - { - "message": f"Once the export is ready it will be emailed to you at {str(request.user.email)}" - }, - status=status.HTTP_200_OK, - ) - except Exception as e: - capture_exception(e) - return Response( - {"error": "Something went wrong please try again later"}, - status=status.HTTP_400_BAD_REQUEST, - ) \ No newline at end of file From 7fca01d8c9e7eca9d8d772e8add6488b97efaf4b Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:13:37 +0530 Subject: [PATCH 02/70] feat: project deploy board endpoint (#1943) --- apiserver/plane/api/urls.py | 6 +++++ apiserver/plane/api/views/__init__.py | 1 + apiserver/plane/api/views/project.py | 35 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 8ebf10f57..a6beac693 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -173,6 +173,7 @@ from plane.api.views import ( CommentReactionPublicViewSet, InboxIssuePublicViewSet, IssueVotePublicViewSet, + WorkspaceProjectDeployBoardEndpoint, ## End Public Boards ## Exporter ExportIssuesEndpoint, @@ -1617,5 +1618,10 @@ urlpatterns = [ ), name="issue-vote-project-board", ), + path( + "public/workspaces//project-boards/", + WorkspaceProjectDeployBoardEndpoint.as_view(), + name="workspace-project-boards", + ), ## End Public Boards ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 11223f90a..9572c552f 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -16,6 +16,7 @@ from .project import ( ProjectDeployBoardViewSet, ProjectDeployBoardPublicSettingsEndpoint, ProjectMemberEndpoint, + WorkspaceProjectDeployBoardEndpoint, ) from .user import ( UserEndpoint, diff --git a/apiserver/plane/api/views/project.py b/apiserver/plane/api/views/project.py index 6adee0016..a693d535b 100644 --- a/apiserver/plane/api/views/project.py +++ b/apiserver/plane/api/views/project.py @@ -1286,3 +1286,38 @@ class ProjectDeployBoardIssuesPublicEndpoint(BaseAPIView): {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) + + +class WorkspaceProjectDeployBoardEndpoint(BaseAPIView): + + permission_classes = [AllowAny,] + + def get(self, request, slug): + try: + projects = ( + Project.objects.filter(workspace__slug=slug) + .annotate( + is_public=Exists( + ProjectDeployBoard.objects.filter( + workspace__slug=slug, project_id=OuterRef("pk") + ) + ) + ) + .filter(is_public=True) + ).values( + "id", + "identifier", + "name", + "description", + "emoji", + "icon_prop", + "cover_image", + ) + + return Response(projects, status=status.HTTP_200_OK) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) From f97597958afbe90d07f6e8bd9a0d4997bc1e984a Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 24 Aug 2023 17:44:20 +0530 Subject: [PATCH 03/70] fix: workspace memebers mutate issue --- .../workspace/send-workspace-invitation-modal.tsx | 10 ++++------ apps/app/pages/[workspaceSlug]/settings/members.tsx | 11 +++++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/app/components/workspace/send-workspace-invitation-modal.tsx b/apps/app/components/workspace/send-workspace-invitation-modal.tsx index 5bffa3264..c9d090405 100644 --- a/apps/app/components/workspace/send-workspace-invitation-modal.tsx +++ b/apps/app/components/workspace/send-workspace-invitation-modal.tsx @@ -25,6 +25,7 @@ type Props = { setIsOpen: React.Dispatch>; workspace_slug: string; user: ICurrentUserResponse | undefined; + onSuccess: () => void; }; type EmailRole = { @@ -45,12 +46,8 @@ const defaultValues: FormValues = { ], }; -const SendWorkspaceInvitationModal: React.FC = ({ - isOpen, - setIsOpen, - workspace_slug, - user, -}) => { +const SendWorkspaceInvitationModal: React.FC = (props) => { + const { isOpen, setIsOpen, workspace_slug, user, onSuccess } = props; const { control, reset, @@ -88,6 +85,7 @@ const SendWorkspaceInvitationModal: React.FC = ({ title: "Success!", message: "Invitations sent successfully.", }); + onSuccess(); }) .catch((err) => { setToastAlert({ diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index 5f6fbc150..85131dab7 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -99,6 +99,11 @@ const MembersSettings: NextPage = () => { const currentUser = workspaceMembers?.find((item) => item.member?.id === user?.id); + const handleInviteModalSuccess = () => { + mutateInvitations(); + mutateMembers(); + }; + return ( { }); }) .finally(() => { - mutateMembers((prevData: any) => - prevData?.filter((item: any) => item.id !== selectedRemoveMember) + mutateMembers( + (prevData: any) => + prevData?.filter((item: any) => item.id !== selectedRemoveMember) ); }); } @@ -180,6 +186,7 @@ const MembersSettings: NextPage = () => { setIsOpen={setInviteModal} workspace_slug={workspaceSlug as string} user={user} + onSuccess={handleInviteModalSuccess} />
From bce8cae0dac8c3827d11160f1a99196a9aea3b5c Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 24 Aug 2023 18:21:57 +0530 Subject: [PATCH 04/70] fix: mutate fixes --- apps/app/pages/[workspaceSlug]/settings/members.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index 85131dab7..0801c84b7 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -101,7 +101,6 @@ const MembersSettings: NextPage = () => { const handleInviteModalSuccess = () => { mutateInvitations(); - mutateMembers(); }; return ( From 802e6b3e8ee488694fac7c6e73cd9d44b3db317e Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 24 Aug 2023 19:43:50 +0530 Subject: [PATCH 05/70] fix: project member mutate issue (#1967) --- .../project/send-project-invitation-modal.tsx | 11 +++++++---- .../projects/[projectId]/settings/members.tsx | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/app/components/project/send-project-invitation-modal.tsx b/apps/app/components/project/send-project-invitation-modal.tsx index d37ad627f..414ef7a6c 100644 --- a/apps/app/components/project/send-project-invitation-modal.tsx +++ b/apps/app/components/project/send-project-invitation-modal.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import { useRouter } from "next/router"; -import useSWR, { mutate } from "swr"; +import useSWR from "swr"; import { useForm, Controller, useFieldArray } from "react-hook-form"; @@ -28,7 +28,7 @@ import useToast from "hooks/use-toast"; // types import { ICurrentUserResponse } from "types"; // fetch-keys -import { PROJECT_MEMBERS, WORKSPACE_MEMBERS } from "constants/fetch-keys"; +import { WORKSPACE_MEMBERS } from "constants/fetch-keys"; // constants import { ROLE } from "constants/workspace"; @@ -37,6 +37,7 @@ type Props = { setIsOpen: React.Dispatch>; members: any[]; user: ICurrentUserResponse | undefined; + onSuccess: () => void; }; type member = { @@ -57,7 +58,9 @@ const defaultValues: FormValues = { ], }; -const SendProjectInvitationModal: React.FC = ({ isOpen, setIsOpen, members, user }) => { +const SendProjectInvitationModal: React.FC = (props) => { + const { isOpen, setIsOpen, members, user, onSuccess } = props; + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -98,13 +101,13 @@ const SendProjectInvitationModal: React.FC = ({ isOpen, setIsOpen, member type: "success", message: "Member added successfully", }); + onSuccess(); }) .catch((error) => { console.log(error); }) .finally(() => { reset(defaultValues); - mutate(PROJECT_MEMBERS(projectId.toString())); }); }; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx index 6fb29efb2..ec8c0c43b 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx @@ -108,6 +108,8 @@ const MembersSettings: NextPage = () => { const currentUser = projectMembers?.find((item) => item.member.id === user?.id); + const handleProjectInvitationSuccess = () => {}; + return ( { setIsOpen={setInviteModal} members={members} user={user} + onSuccess={() => mutateMembers()} />
From d18ac83909e9073f2b17996350b1b7b253a86e0b Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 24 Aug 2023 19:45:23 +0530 Subject: [PATCH 06/70] feat: start date filter added across the platform (#1955) Co-authored-by: Aaryan Khandelwal --- ...filter-modal.tsx => date-filter-modal.tsx} | 17 +++-- ...lter-select.tsx => date-filter-select.tsx} | 15 ++-- .../components/core/filters/filters-list.tsx | 28 +++++++ apps/app/components/core/filters/index.ts | 4 +- .../core/filters/issues-view-filter.tsx | 9 +-- .../app/components/core/views/issues-view.tsx | 1 + .../my-issues/my-issues-select-filters.tsx | 75 ++++++++++++++++--- .../my-issues/my-issues-view-options.tsx | 9 +-- .../issues/my-issues/my-issues-view.tsx | 1 + .../profile/profile-issues-view-options.tsx | 9 +-- .../profile/profile-issues-view.tsx | 1 + apps/app/components/views/form.tsx | 8 +- apps/app/components/views/select-filters.tsx | 75 ++++++++++++++++--- apps/app/constants/fetch-keys.ts | 18 ++++- .../constants/{due-dates.ts => filters.ts} | 2 +- apps/app/contexts/issue-view.context.tsx | 1 + apps/app/contexts/profile-issues-context.tsx | 1 + .../hooks/my-issues/use-my-issues-filter.tsx | 1 + apps/app/hooks/my-issues/use-my-issues.tsx | 1 + apps/app/hooks/use-issues-view.tsx | 1 + apps/app/hooks/use-profile-issues.tsx | 1 + apps/app/types/issues.d.ts | 1 + apps/app/types/views.d.ts | 1 + 23 files changed, 216 insertions(+), 64 deletions(-) rename apps/app/components/core/filters/{due-date-filter-modal.tsx => date-filter-modal.tsx} (90%) rename apps/app/components/core/filters/{due-date-filter-select.tsx => date-filter-select.tsx} (78%) rename apps/app/constants/{due-dates.ts => filters.ts} (96%) diff --git a/apps/app/components/core/filters/due-date-filter-modal.tsx b/apps/app/components/core/filters/date-filter-modal.tsx similarity index 90% rename from apps/app/components/core/filters/due-date-filter-modal.tsx rename to apps/app/components/core/filters/date-filter-modal.tsx index 9556bd193..abc2cc7c4 100644 --- a/apps/app/components/core/filters/due-date-filter-modal.tsx +++ b/apps/app/components/core/filters/date-filter-modal.tsx @@ -11,15 +11,18 @@ import { Dialog, Transition } from "@headlessui/react"; // hooks import useIssuesView from "hooks/use-issues-view"; // components -import { DueDateFilterSelect } from "./due-date-filter-select"; +import { DateFilterSelect } from "./date-filter-select"; // ui import { PrimaryButton, SecondaryButton } from "components/ui"; // icons import { XMarkIcon } from "@heroicons/react/20/solid"; // helpers import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { IIssueFilterOptions } from "types"; type Props = { + title: string; + field: keyof IIssueFilterOptions; isOpen: boolean; handleClose: () => void; }; @@ -36,7 +39,7 @@ const defaultValues: TFormValues = { date2: new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()), }; -export const DueDateFilterModal: React.FC = ({ isOpen, handleClose }) => { +export const DateFilterModal: React.FC = ({ title, field, isOpen, handleClose }) => { const { filters, setFilters } = useIssuesView(); const router = useRouter(); @@ -51,11 +54,11 @@ export const DueDateFilterModal: React.FC = ({ isOpen, handleClose }) => if (filterType === "range") { setFilters( - { target_date: [`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`] }, + { [field]: [`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`] }, !Boolean(viewId) ); } else { - const filteredArray = filters?.target_date?.filter((item) => { + const filteredArray = (filters?.[field] as string[])?.filter((item) => { if (item?.includes(filterType)) return false; return true; @@ -64,13 +67,13 @@ export const DueDateFilterModal: React.FC = ({ isOpen, handleClose }) => const filterOne = filteredArray && filteredArray?.length > 0 ? filteredArray[0] : null; if (filterOne) setFilters( - { target_date: [filterOne, `${renderDateFormat(date1)};${filterType}`] }, + { [field]: [filterOne, `${renderDateFormat(date1)};${filterType}`] }, !Boolean(viewId) ); else setFilters( { - target_date: [`${renderDateFormat(date1)};${filterType}`], + [field]: [`${renderDateFormat(date1)};${filterType}`], }, !Boolean(viewId) ); @@ -116,7 +119,7 @@ export const DueDateFilterModal: React.FC = ({ isOpen, handleClose }) => control={control} name="filterType" render={({ field: { value, onChange } }) => ( - + )} /> void; }; @@ -19,29 +20,31 @@ type DueDate = { const dueDateRange: DueDate[] = [ { - name: "Due date before", + name: "before", value: "before", icon: , }, { - name: "Due date after", + name: "after", value: "after", icon: , }, { - name: "Due date range", + name: "range", value: "range", icon: , }, ]; -export const DueDateFilterSelect: React.FC = ({ value, onChange }) => ( +export const DateFilterSelect: React.FC = ({ title, value, onChange }) => ( {dueDateRange.find((item) => item.value === value)?.icon} - {dueDateRange.find((item) => item.value === value)?.name} + + {title} {dueDateRange.find((item) => item.value === value)?.name} +
} onChange={onChange} @@ -50,7 +53,7 @@ export const DueDateFilterSelect: React.FC = ({ value, onChange }) => ( <> {option.icon} - {option.name} + {title} {option.name} ))} diff --git a/apps/app/components/core/filters/filters-list.tsx b/apps/app/components/core/filters/filters-list.tsx index ffe596258..8192bdf7d 100644 --- a/apps/app/components/core/filters/filters-list.tsx +++ b/apps/app/components/core/filters/filters-list.tsx @@ -240,6 +240,34 @@ export const FiltersList: React.FC = ({
); }) + : key === "start_date" + ? filters.start_date?.map((date: string) => { + if (filters.start_date && filters.start_date.length <= 0) return null; + + const splitDate = date.split(";"); + + return ( +
+
+ + {splitDate[1]} {renderShortDateWithYearFormat(splitDate[0])} + + + setFilters({ + start_date: filters.start_date?.filter((d: any) => d !== date), + }) + } + > + + +
+ ); + }) : key === "target_date" ? filters.target_date?.map((date: string) => { if (filters.target_date && filters.target_date.length <= 0) return null; diff --git a/apps/app/components/core/filters/index.ts b/apps/app/components/core/filters/index.ts index 01c371911..d6455151e 100644 --- a/apps/app/components/core/filters/index.ts +++ b/apps/app/components/core/filters/index.ts @@ -1,4 +1,4 @@ -export * from "./due-date-filter-modal"; -export * from "./due-date-filter-select"; +export * from "./date-filter-modal"; +export * from "./date-filter-select"; export * from "./filters-list"; export * from "./issues-view-filter"; diff --git a/apps/app/components/core/filters/issues-view-filter.tsx b/apps/app/components/core/filters/issues-view-filter.tsx index 2fa80c975..37aab34e9 100644 --- a/apps/app/components/core/filters/issues-view-filter.tsx +++ b/apps/app/components/core/filters/issues-view-filter.tsx @@ -119,14 +119,11 @@ export const IssuesFilterView: React.FC = () => { onSelect={(option) => { const key = option.key as keyof typeof filters; - if (key === "target_date") { - const valueExists = checkIfArraysHaveSameElements( - filters.target_date ?? [], - option.value - ); + if (key === "start_date" || key === "target_date") { + const valueExists = checkIfArraysHaveSameElements(filters[key] ?? [], option.value); setFilters({ - target_date: valueExists ? null : option.value, + [key]: valueExists ? null : option.value, }); } else { const valueExists = filters[key]?.includes(option.value); diff --git a/apps/app/components/core/views/issues-view.tsx b/apps/app/components/core/views/issues-view.tsx index d3d76f805..5f33a6cb0 100644 --- a/apps/app/components/core/views/issues-view.tsx +++ b/apps/app/components/core/views/issues-view.tsx @@ -478,6 +478,7 @@ export const IssuesView: React.FC = ({ labels: null, priority: null, state: null, + start_date: null, target_date: null, type: null, }) diff --git a/apps/app/components/issues/my-issues/my-issues-select-filters.tsx b/apps/app/components/issues/my-issues/my-issues-select-filters.tsx index e3d2cdff0..58cc63679 100644 --- a/apps/app/components/issues/my-issues/my-issues-select-filters.tsx +++ b/apps/app/components/issues/my-issues/my-issues-select-filters.tsx @@ -7,7 +7,7 @@ import useSWR from "swr"; // services import issuesService from "services/issues.service"; // components -import { DueDateFilterModal } from "components/core"; +import { DateFilterModal } from "components/core"; // ui import { MultiLevelDropdown } from "components/ui"; // icons @@ -20,7 +20,7 @@ import { IIssueFilterOptions, IQuery } from "types"; import { WORKSPACE_LABELS } from "constants/fetch-keys"; // constants import { GROUP_CHOICES, PRIORITIES } from "constants/project"; -import { DUE_DATES } from "constants/due-dates"; +import { DATE_FILTER_OPTIONS } from "constants/filters"; type Props = { filters: Partial | IQuery; @@ -35,7 +35,14 @@ export const MyIssuesSelectFilters: React.FC = ({ direction = "right", height = "md", }) => { - const [isDueDateFilterModalOpen, setIsDueDateFilterModalOpen] = useState(false); + const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); + const [dateFilterType, setDateFilterType] = useState<{ + title: string; + type: "start_date" | "target_date"; + }>({ + title: "", + type: "start_date", + }); const [fetchLabels, setFetchLabels] = useState(false); const router = useRouter(); @@ -50,10 +57,12 @@ export const MyIssuesSelectFilters: React.FC = ({ return ( <> - {isDueDateFilterModalOpen && ( - setIsDueDateFilterModalOpen(false)} + {isDateFilterModalOpen && ( + setIsDateFilterModalOpen(false)} /> )} = ({ })), }, { - id: "target_date", - label: "Due date", - value: DUE_DATES, + id: "start_date", + label: "Start date", + value: DATE_FILTER_OPTIONS, hasChildren: true, children: [ - ...(DUE_DATES?.map((option) => ({ + ...(DATE_FILTER_OPTIONS?.map((option) => ({ + id: option.name, + label: option.name, + value: { + key: "start_date", + value: option.value, + }, + selected: checkIfArraysHaveSameElements(filters?.start_date ?? [], option.value), + })) ?? []), + { + id: "custom", + label: "Custom", + value: "custom", + element: ( + + ), + }, + ], + }, + { + id: "target_date", + label: "Due date", + value: DATE_FILTER_OPTIONS, + hasChildren: true, + children: [ + ...(DATE_FILTER_OPTIONS?.map((option) => ({ id: option.name, label: option.name, value: { @@ -152,7 +197,13 @@ export const MyIssuesSelectFilters: React.FC = ({ value: "custom", element: ( + ), + }, + ], + }, + { + id: "target_date", + label: "Due date", + value: DATE_FILTER_OPTIONS, + hasChildren: true, + children: [ + ...DATE_FILTER_OPTIONS.map((option) => ({ id: option.name, label: option.name, value: { @@ -203,7 +248,13 @@ export const SelectFilters: React.FC = ({ value: "custom", element: ( + - + - -
- } - 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.due_date && ( +
+ +
+ )} + {properties.estimate && ( +
+ +
+ )} + {properties.created_on && ( +
+ {renderLongDetailDateFormat(issue.created_at)} +
+ )} + {properties.updated_on && ( +
+ {renderLongDetailDateFormat(issue.updated_at)} +
+ )} - {properties.state && ( -
- -
- )} - {properties.priority && ( -
- -
- )} - {properties.assignee && ( -
- -
- )} - {properties.labels && ( -
- -
- )} - - {properties.start_date && ( -
- -
- )} - - {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 a4f426a23..0b2e785d6 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 000000000..efa237162 --- /dev/null +++ b/apps/app/components/inbox/inbox-issue-activity.tsx @@ -0,0 +1,106 @@ +import { useRouter } from "next/router"; + +import useSWR, { mutate } from "swr"; + +// components +import { AddComment, IssueActivitySection } from "components/issues"; +// services +import issuesService from "services/issues.service"; +// hooks +import useUser from "hooks/use-user"; +import useToast from "hooks/use-toast"; +// 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 { setToastAlert } = useToast(); + + 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()); + }; + + const handleAddComment = async (formData: IIssueComment) => { + if (!workspaceSlug || !issueDetails) return; + + await issuesService + .createIssueComment( + workspaceSlug.toString(), + issueDetails.project, + issueDetails.id, + formData, + user + ) + .then(() => { + mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id)); + }) + .catch(() => + setToastAlert({ + type: "error", + title: "Error!", + message: "Comment could not be posted. Please try again.", + }) + ); + }; + + return ( +
+

Comments/Activity

+ + +
+ ); +}; diff --git a/apps/app/components/inbox/inbox-main-content.tsx b/apps/app/components/inbox/inbox-main-content.tsx index bd4f0ab01..6d4a4337c 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 38cea0348..8301d2570 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 8dd948e08..5a82907f2 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 { workspaceSlug } = 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()); - }; - - 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 ? (