From b66f07845a7f5ae86c35ed4f69a4d65d0bee25f8 Mon Sep 17 00:00:00 2001
From: guru_sainath
Date: Wed, 24 Jan 2024 20:33:54 +0530
Subject: [PATCH] chore: inbox issue restructure the components and store
(#3456)
* chore: inbox-issues store and type updates
* chore: issue inbox payload change for GET and POST
* chore: issue inbox payload change for PATCH
* chore: inbox-issue new hooks and store updates
* chore: update inbox issue template.
* chore: UI root
* chore: sidebar issues render
* chore: inbox issue details page layout.
* chore: inbox issue filters
* chore: inbox issue status card.
* chore: add loader.
* chore: active inbox issue styles.
* chore: inbox filters
* chore: inbox applied filters UI
* chore: inbox issue approval header
* chore: inbox issue approval header operations
* chore: issue reaction and activity fetch in issue_inbox store
* chore: posthog enabled
---------
Co-authored-by: NarayanBavisetti
Co-authored-by: Prateek Shourya
---
apiserver/plane/app/serializers/__init__.py | 1 +
apiserver/plane/app/serializers/base.py | 8 +-
apiserver/plane/app/views/inbox.py | 80 ++--
apiserver/plane/app/views/project.py | 7 +-
packages/types/src/inbox/inbox-issue.d.ts | 65 +++
packages/types/src/inbox/inbox.d.ts | 27 ++
packages/types/src/inbox/root.d.ts | 2 +
packages/types/src/index.d.ts | 4 +
web/components/headers/project-inbox.tsx | 14 +-
web/components/headers/project-issues.tsx | 15 +-
web/components/inbox/actions-header.tsx | 247 -----------
web/components/inbox/inbox-issue-actions.tsx | 329 ++++++++++++++
web/components/inbox/inbox-issue-status.tsx | 55 +++
web/components/inbox/index.ts | 18 +-
web/components/inbox/issue-activity.tsx | 130 ------
web/components/inbox/issue-card.tsx | 96 -----
web/components/inbox/issues-list-sidebar.tsx | 45 --
web/components/inbox/main-content.tsx | 292 -------------
.../inbox/modals/accept-issue-modal.tsx | 6 +-
.../inbox/modals/create-issue-modal.tsx | 8 +-
.../inbox/modals/decline-issue-modal.tsx | 6 +-
.../inbox/modals/delete-issue-modal.tsx | 76 +---
.../filter/applied-filters.tsx} | 76 ++--
.../filter/filter-selection.tsx} | 43 +-
.../inbox/sidebar/inbox-list-item.tsx | 100 +++++
web/components/inbox/sidebar/inbox-list.tsx | 27 ++
web/components/inbox/sidebar/root.tsx | 38 ++
.../issues/issue-detail/cycle-select.tsx | 4 +-
.../issues/issue-detail/inbox/index.ts | 3 +
.../issue-detail/inbox/main-content.tsx | 84 ++++
.../issues/issue-detail/inbox/root.tsx | 150 +++++++
.../issues/issue-detail/inbox/sidebar.tsx | 149 +++++++
.../issues/issue-detail/module-select.tsx | 4 +-
web/components/issues/issue-detail/root.tsx | 13 +-
web/constants/inbox.ts | 44 --
web/constants/inbox.tsx | 86 ++++
web/hooks/store/index.ts | 5 +-
web/hooks/store/use-inbox-filters.ts | 11 -
web/hooks/store/use-inbox-issues.ts | 10 +-
web/hooks/store/use-inbox.ts | 6 +-
web/layouts/auth-layout/project-wrapper.tsx | 14 +-
.../projects/[projectId]/inbox/[inboxId].tsx | 106 ++++-
.../projects/[projectId]/inbox/index.tsx | 49 +++
web/services/inbox/inbox-issue.service.ts | 112 +++++
web/services/inbox/inbox.service.ts | 35 ++
web/services/inbox/index.ts | 2 +
web/store/inbox/inbox.store.ts | 149 +++----
web/store/inbox/inbox_filter.store.ts | 147 ++++---
web/store/inbox/inbox_issue.store.ts | 400 ++++++++++--------
web/store/inbox/index.ts | 25 --
web/store/inbox/root.store.ts | 26 ++
web/store/root.store.ts | 6 +-
52 files changed, 1998 insertions(+), 1457 deletions(-)
create mode 100644 packages/types/src/inbox/inbox-issue.d.ts
create mode 100644 packages/types/src/inbox/inbox.d.ts
create mode 100644 packages/types/src/inbox/root.d.ts
delete mode 100644 web/components/inbox/actions-header.tsx
create mode 100644 web/components/inbox/inbox-issue-actions.tsx
create mode 100644 web/components/inbox/inbox-issue-status.tsx
delete mode 100644 web/components/inbox/issue-activity.tsx
delete mode 100644 web/components/inbox/issue-card.tsx
delete mode 100644 web/components/inbox/issues-list-sidebar.tsx
delete mode 100644 web/components/inbox/main-content.tsx
rename web/components/inbox/{filters-list.tsx => sidebar/filter/applied-filters.tsx} (71%)
rename web/components/inbox/{filters-dropdown.tsx => sidebar/filter/filter-selection.tsx} (68%)
create mode 100644 web/components/inbox/sidebar/inbox-list-item.tsx
create mode 100644 web/components/inbox/sidebar/inbox-list.tsx
create mode 100644 web/components/inbox/sidebar/root.tsx
create mode 100644 web/components/issues/issue-detail/inbox/index.ts
create mode 100644 web/components/issues/issue-detail/inbox/main-content.tsx
create mode 100644 web/components/issues/issue-detail/inbox/root.tsx
create mode 100644 web/components/issues/issue-detail/inbox/sidebar.tsx
delete mode 100644 web/constants/inbox.ts
create mode 100644 web/constants/inbox.tsx
delete mode 100644 web/hooks/store/use-inbox-filters.ts
create mode 100644 web/pages/[workspaceSlug]/projects/[projectId]/inbox/index.tsx
create mode 100644 web/services/inbox/inbox-issue.service.ts
create mode 100644 web/services/inbox/inbox.service.ts
create mode 100644 web/services/inbox/index.ts
delete mode 100644 web/store/inbox/index.ts
create mode 100644 web/store/inbox/root.store.ts
diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py
index c35737cb5..0d72f9192 100644
--- a/apiserver/plane/app/serializers/__init__.py
+++ b/apiserver/plane/app/serializers/__init__.py
@@ -111,6 +111,7 @@ from .inbox import (
InboxSerializer,
InboxIssueSerializer,
IssueStateInboxSerializer,
+ InboxIssueLiteSerializer,
)
from .analytic import AnalyticViewSerializer
diff --git a/apiserver/plane/app/serializers/base.py b/apiserver/plane/app/serializers/base.py
index 89683ffe5..446fdb6d5 100644
--- a/apiserver/plane/app/serializers/base.py
+++ b/apiserver/plane/app/serializers/base.py
@@ -60,6 +60,7 @@ class DynamicBaseSerializer(BaseSerializer):
CycleIssueSerializer,
IssueFlatSerializer,
IssueRelationSerializer,
+ InboxIssueLiteSerializer
)
# Expansion mapper
@@ -80,9 +81,10 @@ class DynamicBaseSerializer(BaseSerializer):
"issue_cycle": CycleIssueSerializer,
"parent": IssueSerializer,
"issue_relation": IssueRelationSerializer,
+ "issue_inbox" : InboxIssueLiteSerializer,
}
- self.fields[field] = expansion[field](many=True if field in ["members", "assignees", "labels", "issue_cycle", "issue_relation"] else False)
+ self.fields[field] = expansion[field](many=True if field in ["members", "assignees", "labels", "issue_cycle", "issue_relation", "issue_inbox"] else False)
return self.fields
@@ -103,6 +105,7 @@ class DynamicBaseSerializer(BaseSerializer):
LabelSerializer,
CycleIssueSerializer,
IssueRelationSerializer,
+ InboxIssueLiteSerializer
)
# Expansion mapper
@@ -122,7 +125,8 @@ class DynamicBaseSerializer(BaseSerializer):
"labels": LabelSerializer,
"issue_cycle": CycleIssueSerializer,
"parent": IssueSerializer,
- "issue_relation": IssueRelationSerializer
+ "issue_relation": IssueRelationSerializer,
+ "issue_inbox" : InboxIssueLiteSerializer,
}
# Check if field in expansion then expand the field
if expand in expansion:
diff --git a/apiserver/plane/app/views/inbox.py b/apiserver/plane/app/views/inbox.py
index 0f8e68656..3bacdae4c 100644
--- a/apiserver/plane/app/views/inbox.py
+++ b/apiserver/plane/app/views/inbox.py
@@ -88,39 +88,24 @@ class InboxIssueViewSet(BaseViewSet):
]
def get_queryset(self):
- return self.filter_queryset(
- super()
- .get_queryset()
- .filter(
- Q(snoozed_till__gte=timezone.now())
- | Q(snoozed_till__isnull=True),
- workspace__slug=self.kwargs.get("slug"),
- project_id=self.kwargs.get("project_id"),
- inbox_id=self.kwargs.get("inbox_id"),
- )
- .select_related("issue", "workspace", "project")
- )
-
- def list(self, request, slug, project_id, inbox_id):
- filters = issue_filters(request.query_params, "GET")
- issues = (
+ return (
Issue.objects.filter(
- issue_inbox__inbox_id=inbox_id,
- workspace__slug=slug,
- project_id=project_id,
+ project_id=self.kwargs.get("project_id"),
+ workspace__slug=self.kwargs.get("slug"),
+ issue_inbox__inbox_id=self.kwargs.get("inbox_id")
)
- .filter(**filters)
.select_related("workspace", "project", "state", "parent")
- .prefetch_related("assignees", "labels")
- .order_by("issue_inbox__snoozed_till", "issue_inbox__status")
- .annotate(
- sub_issues_count=Issue.issue_objects.filter(
- parent=OuterRef("id")
+ .prefetch_related("labels", "assignees")
+ .prefetch_related(
+ Prefetch(
+ "issue_inbox",
+ queryset=InboxIssue.objects.only(
+ "status", "duplicate_to", "snoozed_till", "source"
+ ),
)
- .order_by()
- .annotate(count=Func(F("id"), function="Count"))
- .values("count")
)
+ .annotate(cycle_id=F("issue_cycle__cycle_id"))
+ .annotate(module_id=F("issue_module__module_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()
@@ -135,16 +120,20 @@ class InboxIssueViewSet(BaseViewSet):
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
- .prefetch_related(
- Prefetch(
- "issue_inbox",
- queryset=InboxIssue.objects.only(
- "status", "duplicate_to", "snoozed_till", "source"
- ),
+ .annotate(
+ sub_issues_count=Issue.issue_objects.filter(
+ parent=OuterRef("id")
)
+ .order_by()
+ .annotate(count=Func(F("id"), function="Count"))
+ .values("count")
)
- )
- issues_data = IssueStateInboxSerializer(issues, many=True).data
+ ).distinct()
+
+ def list(self, request, slug, project_id, inbox_id):
+ filters = issue_filters(request.query_params, "GET")
+ issue_queryset = self.get_queryset().filter(**filters).order_by("issue_inbox__snoozed_till", "issue_inbox__status")
+ issues_data = IssueSerializer(issue_queryset, expand=self.expand, many=True).data
return Response(
issues_data,
status=status.HTTP_200_OK,
@@ -211,7 +200,8 @@ class InboxIssueViewSet(BaseViewSet):
source=request.data.get("source", "in-app"),
)
- serializer = IssueStateInboxSerializer(issue)
+ issue = (self.get_queryset().filter(pk=issue.id).first())
+ serializer = IssueSerializer(issue ,expand=self.expand)
return Response(serializer.data, status=status.HTTP_200_OK)
def partial_update(self, request, slug, project_id, inbox_id, issue_id):
@@ -331,22 +321,20 @@ class InboxIssueViewSet(BaseViewSet):
if state is not None:
issue.state = state
issue.save()
-
+ issue = (self.get_queryset().filter(pk=issue_id).first())
+ serializer = IssueSerializer(issue, expand=self.expand)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
else:
- return Response(
- InboxIssueSerializer(inbox_issue).data,
- status=status.HTTP_200_OK,
- )
+ issue = (self.get_queryset().filter(pk=issue_id).first())
+ serializer = IssueSerializer(issue ,expand=self.expand)
+ return Response(serializer.data, status=status.HTTP_200_OK)
def retrieve(self, request, slug, project_id, inbox_id, issue_id):
- issue = Issue.objects.get(
- pk=issue_id, workspace__slug=slug, project_id=project_id
- )
- serializer = IssueStateInboxSerializer(issue)
+ issue = self.get_queryset().filter(pk=issue_id).first()
+ serializer = IssueSerializer(issue, expand=self.expand,)
return Response(serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, slug, project_id, inbox_id, issue_id):
diff --git a/apiserver/plane/app/views/project.py b/apiserver/plane/app/views/project.py
index 2895661f8..e411e0e39 100644
--- a/apiserver/plane/app/views/project.py
+++ b/apiserver/plane/app/views/project.py
@@ -68,7 +68,7 @@ from plane.bgtasks.project_invitation_task import project_invitation
class ProjectViewSet(WebhookMixin, BaseViewSet):
- serializer_class = ProjectSerializer
+ serializer_class = ProjectListSerializer
model = Project
webhook_event = "project"
@@ -76,11 +76,6 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
ProjectBasePermission,
]
- def get_serializer_class(self, *args, **kwargs):
- if self.action in ["update", "partial_update"]:
- return ProjectSerializer
- return ProjectDetailSerializer
-
def get_queryset(self):
return self.filter_queryset(
super()
diff --git a/packages/types/src/inbox/inbox-issue.d.ts b/packages/types/src/inbox/inbox-issue.d.ts
new file mode 100644
index 000000000..c7d33f75b
--- /dev/null
+++ b/packages/types/src/inbox/inbox-issue.d.ts
@@ -0,0 +1,65 @@
+import { TIssue } from "../issues/base";
+
+export enum EInboxStatus {
+ PENDING = -2,
+ REJECT = -1,
+ SNOOZED = 0,
+ ACCEPTED = 1,
+ DUPLICATE = 2,
+}
+
+export type TInboxStatus =
+ | EInboxStatus.PENDING
+ | EInboxStatus.REJECT
+ | EInboxStatus.SNOOZED
+ | EInboxStatus.ACCEPTED
+ | EInboxStatus.DUPLICATE;
+
+export type TInboxIssueDetail = {
+ id?: string;
+ source: "in-app";
+ status: TInboxStatus;
+ duplicate_to: string | undefined;
+ snoozed_till: Date | undefined;
+};
+
+export type TInboxIssueDetailMap = Record<
+ string,
+ Record
+>; // inbox_id -> issue_id -> TInboxIssueDetail
+
+export type TInboxIssueDetailIdMap = Record; // inbox_id -> issue_id[]
+
+export type TInboxIssueExtendedDetail = TIssue & {
+ issue_inbox: TInboxIssueDetail[];
+};
+
+// property type checks
+export type TInboxPendingStatus = {
+ status: EInboxStatus.PENDING;
+};
+
+export type TInboxRejectStatus = {
+ status: EInboxStatus.REJECT;
+};
+
+export type TInboxSnoozedStatus = {
+ status: EInboxStatus.SNOOZED;
+ snoozed_till: Date;
+};
+
+export type TInboxAcceptedStatus = {
+ status: EInboxStatus.ACCEPTED;
+};
+
+export type TInboxDuplicateStatus = {
+ status: EInboxStatus.DUPLICATE;
+ duplicate_to: string; // issue_id
+};
+
+export type TInboxDetailedStatus =
+ | TInboxPendingStatus
+ | TInboxRejectStatus
+ | TInboxSnoozedStatus
+ | TInboxAcceptedStatus
+ | TInboxDuplicateStatus;
diff --git a/packages/types/src/inbox/inbox.d.ts b/packages/types/src/inbox/inbox.d.ts
new file mode 100644
index 000000000..1b4e23e0f
--- /dev/null
+++ b/packages/types/src/inbox/inbox.d.ts
@@ -0,0 +1,27 @@
+export type TInboxIssueFilterOptions = {
+ priority: string[];
+ inbox_status: number[];
+};
+
+export type TInboxIssueQueryParams = "priority" | "inbox_status";
+
+export type TInboxIssueFilters = { filters: TInboxIssueFilterOptions };
+
+export type TInbox = {
+ id: string;
+ name: string;
+ description: string;
+ workspace: string;
+ project: string;
+ is_default: boolean;
+ view_props: TInboxIssueFilters;
+ created_by: string;
+ updated_by: string;
+ created_at: Date;
+ updated_at: Date;
+ pending_issue_count: number;
+};
+
+export type TInboxDetailMap = Record; // inbox_id -> TInbox
+
+export type TInboxDetailIdMap = Record; // project_id -> inbox_id[]
diff --git a/packages/types/src/inbox/root.d.ts b/packages/types/src/inbox/root.d.ts
new file mode 100644
index 000000000..2f10c088d
--- /dev/null
+++ b/packages/types/src/inbox/root.d.ts
@@ -0,0 +1,2 @@
+export * from "./inbox";
+export * from "./inbox-issue";
diff --git a/packages/types/src/index.d.ts b/packages/types/src/index.d.ts
index 209aa6794..6e8ded942 100644
--- a/packages/types/src/index.d.ts
+++ b/packages/types/src/index.d.ts
@@ -13,7 +13,11 @@ export * from "./pages";
export * from "./ai";
export * from "./estimate";
export * from "./importer";
+
+// FIXME: Remove this after development and the refactor/mobx-store-issue branch is stable
export * from "./inbox";
+export * from "./inbox/root";
+
export * from "./analytics";
export * from "./calendar";
export * from "./notifications";
diff --git a/web/components/headers/project-inbox.tsx b/web/components/headers/project-inbox.tsx
index ae16eff89..b09aa9dc3 100644
--- a/web/components/headers/project-inbox.tsx
+++ b/web/components/headers/project-inbox.tsx
@@ -51,12 +51,14 @@ export const ProjectInboxHeader: FC = observer(() => {
-
- setCreateIssueModal(false)} />
- } size="sm" onClick={() => setCreateIssueModal(true)}>
- Add Issue
-
-
+ {currentProjectDetails?.inbox_view && (
+
+ setCreateIssueModal(false)} />
+ } size="sm" onClick={() => setCreateIssueModal(true)}>
+ Add Issue
+
+
+ )}
);
});
diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx
index c756b4410..6929da92f 100644
--- a/web/components/headers/project-issues.tsx
+++ b/web/components/headers/project-issues.tsx
@@ -32,7 +32,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
const {
issuesFilter: { issueFilters, updateFilters },
} = useIssues(EIssuesStoreType.PROJECT);
- const { inboxesList, isInboxEnabled, getInboxId } = useInbox();
const {
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
@@ -43,6 +42,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const { projectLabels } = useLabel();
+ const { getInboxesByProjectId, getInboxById } = useInbox();
const activeLayout = issueFilters?.displayFilters?.layout;
@@ -89,7 +89,9 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
[workspaceSlug, projectId, updateFilters]
);
- const inboxDetails = projectId ? inboxesList?.[projectId]?.[0] : undefined;
+ const inboxesMap = currentProjectDetails?.inbox_view ? getInboxesByProjectId(currentProjectDetails.id) : undefined;
+ const inboxDetails = inboxesMap && inboxesMap.length > 0 ? getInboxById(inboxesMap[0]) : undefined;
+
const deployUrl = process.env.NEXT_PUBLIC_DEPLOY_URL;
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
@@ -190,14 +192,15 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
handleDisplayPropertiesUpdate={handleDisplayProperties}
/>
- {projectId && isInboxEnabled && inboxDetails && (
-
+
+ {currentProjectDetails?.inbox_view && inboxDetails && (
+
diff --git a/web/components/inbox/actions-header.tsx b/web/components/inbox/actions-header.tsx
deleted file mode 100644
index cab4be600..000000000
--- a/web/components/inbox/actions-header.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import { useEffect, useState } from "react";
-import { useRouter } from "next/router";
-import { observer } from "mobx-react-lite";
-import DatePicker from "react-datepicker";
-import { Popover } from "@headlessui/react";
-// hooks
-import { useUser, useInboxIssues } from "hooks/store";
-import useToast from "hooks/use-toast";
-// components
-import {
- AcceptIssueModal,
- DeclineIssueModal,
- DeleteInboxIssueModal,
- FiltersDropdown,
- SelectDuplicateInboxIssueModal,
-} from "components/inbox";
-// ui
-import { Button } from "@plane/ui";
-// icons
-import { CheckCircle2, Clock, FileStack, Inbox, Trash2, XCircle } from "lucide-react";
-// types
-import type { TInboxStatus } from "@plane/types";
-import { EUserProjectRoles } from "constants/project";
-
-export const InboxActionsHeader = observer(() => {
- // states
- const [date, setDate] = useState(new Date());
- const [selectDuplicateIssue, setSelectDuplicateIssue] = useState(false);
- const [acceptIssueModal, setAcceptIssueModal] = useState(false);
- const [declineIssueModal, setDeclineIssueModal] = useState(false);
- const [deleteIssueModal, setDeleteIssueModal] = useState(false);
- // router
- const router = useRouter();
- const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query;
- // store hooks
- const { updateIssueStatus, getIssueById } = useInboxIssues();
- const {
- currentUser,
- membership: { currentProjectRole },
- } = useUser();
- // toast
- const { setToastAlert } = useToast();
- // derived values
- const issue = getIssueById(inboxId as string, inboxIssueId as string);
-
- const markInboxStatus = async (data: TInboxStatus) => {
- if (!workspaceSlug || !projectId || !inboxId || !inboxIssueId || !issue) return;
-
- await updateIssueStatus(
- workspaceSlug.toString(),
- projectId.toString(),
- inboxId.toString(),
- issue.issue_inbox[0].id!,
- data
- ).catch(() =>
- setToastAlert({
- type: "error",
- title: "Error!",
- message: "Something went wrong while updating inbox status. Please try again.",
- })
- );
- };
-
- // const currentIssueIndex = issuesList?.findIndex((issue) => issue.issue_inbox[0].id === inboxIssueId) ?? 0;
-
- useEffect(() => {
- if (!issue?.issue_inbox[0].snoozed_till) return;
-
- setDate(new Date(issue.issue_inbox[0].snoozed_till));
- }, [issue]);
-
- const issueStatus = issue?.issue_inbox[0].status;
- const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
-
- const today = new Date();
- const tomorrow = new Date(today);
-
- tomorrow.setDate(today.getDate() + 1);
-
- return (
- <>
- {issue && (
- <>
- setSelectDuplicateIssue(false)}
- value={issue?.issue_inbox[0].duplicate_to}
- onSubmit={(dupIssueId) => {
- markInboxStatus({
- status: 2,
- duplicate_to: dupIssueId,
- }).finally(() => setSelectDuplicateIssue(false));
- }}
- />
- setAcceptIssueModal(false)}
- onSubmit={async () => {
- await markInboxStatus({
- status: 1,
- }).finally(() => setAcceptIssueModal(false));
- }}
- />
- setDeclineIssueModal(false)}
- onSubmit={async () => {
- await markInboxStatus({
- status: -1,
- }).finally(() => setDeclineIssueModal(false));
- }}
- />
- setDeleteIssueModal(false)} />
- >
- )}
-
-
- {inboxIssueId && (
-
- {/*
-
-
-
- {currentIssueIndex + 1}/{issuesList?.length ?? 0}
-
-
*/}
-
- {isAllowed && (issueStatus === 0 || issueStatus === -2) && (
-
-
-
- } size="sm">
- Snooze
-
-
-
- {({ close }) => (
-
- {
- if (!val) return;
- setDate(val);
- }}
- dateFormat="dd-MM-yyyy"
- minDate={tomorrow}
- inline
- />
-
-
- )}
-
-
-
- )}
- {isAllowed && issueStatus === -2 && (
-
- }
- onClick={() => setSelectDuplicateIssue(true)}
- >
- Mark as duplicate
-
-
- )}
- {isAllowed && (issueStatus === 0 || issueStatus === -2) && (
-
- }
- onClick={() => setAcceptIssueModal(true)}
- >
- Accept
-
-
- )}
- {isAllowed && issueStatus === -2 && (
-
- }
- onClick={() => setDeclineIssueModal(true)}
- >
- Decline
-
-
- )}
- {(isAllowed || currentUser?.id === issue?.created_by) && (
-
- }
- onClick={() => setDeleteIssueModal(true)}
- >
- Delete
-
-
- )}
-
-
- )}
-
- >
- );
-});
diff --git a/web/components/inbox/inbox-issue-actions.tsx b/web/components/inbox/inbox-issue-actions.tsx
new file mode 100644
index 000000000..25b444de0
--- /dev/null
+++ b/web/components/inbox/inbox-issue-actions.tsx
@@ -0,0 +1,329 @@
+import { FC, useEffect, useMemo, useState } from "react";
+import { useRouter } from "next/router";
+import { observer } from "mobx-react-lite";
+import DatePicker from "react-datepicker";
+import { Popover } from "@headlessui/react";
+// hooks
+import { useApplication, useUser, useInboxIssues, useIssueDetail, useWorkspace } from "hooks/store";
+import useToast from "hooks/use-toast";
+// components
+import {
+ AcceptIssueModal,
+ DeclineIssueModal,
+ DeleteInboxIssueModal,
+ SelectDuplicateInboxIssueModal,
+} from "components/inbox";
+// ui
+import { Button } from "@plane/ui";
+// icons
+import { CheckCircle2, ChevronDown, ChevronUp, Clock, FileStack, Inbox, Trash2, XCircle } from "lucide-react";
+// types
+import type { TInboxStatus, TInboxDetailedStatus } from "@plane/types";
+import { EUserProjectRoles } from "constants/project";
+
+type TInboxIssueActionsHeader = {
+ workspaceSlug: string;
+ projectId: string;
+ inboxId: string;
+ inboxIssueId: string | undefined;
+};
+
+type TInboxIssueOperations = {
+ updateInboxIssueStatus: (data: TInboxStatus) => Promise;
+ removeInboxIssue: () => Promise;
+};
+
+export const InboxIssueActionsHeader: FC = observer((props) => {
+ const { workspaceSlug, projectId, inboxId, inboxIssueId } = props;
+ // router
+ const router = useRouter();
+ // hooks
+ const {
+ eventTracker: { postHogEventTracker },
+ } = useApplication();
+ const { currentWorkspace } = useWorkspace();
+ const {
+ issues: { getInboxIssuesByInboxId, getInboxIssueByIssueId, updateInboxIssueStatus, removeInboxIssue },
+ } = useInboxIssues();
+ const {
+ issue: { getIssueById },
+ } = useIssueDetail();
+ const {
+ currentUser,
+ membership: { currentProjectRole },
+ } = useUser();
+ const { setToastAlert } = useToast();
+
+ // states
+ const [date, setDate] = useState(new Date());
+ const [selectDuplicateIssue, setSelectDuplicateIssue] = useState(false);
+ const [acceptIssueModal, setAcceptIssueModal] = useState(false);
+ const [declineIssueModal, setDeclineIssueModal] = useState(false);
+ const [deleteIssueModal, setDeleteIssueModal] = useState(false);
+
+ // derived values
+ const inboxIssues = getInboxIssuesByInboxId(inboxId);
+ const issueStatus = (inboxIssueId && inboxId && getInboxIssueByIssueId(inboxId, inboxIssueId)) || undefined;
+ const issue = (inboxIssueId && getIssueById(inboxIssueId)) || undefined;
+
+ const currentIssueIndex = inboxIssues?.findIndex((issue) => issue === inboxIssueId) ?? 0;
+
+ const inboxIssueOperations: TInboxIssueOperations = useMemo(
+ () => ({
+ updateInboxIssueStatus: async (data: TInboxDetailedStatus) => {
+ try {
+ if (!workspaceSlug || !projectId || !inboxId || !inboxIssueId) throw new Error("Missing required parameters");
+ await updateInboxIssueStatus(workspaceSlug, projectId, inboxId, inboxIssueId, data);
+ } catch (error) {
+ setToastAlert({
+ type: "error",
+ title: "Error!",
+ message: "Something went wrong while updating inbox status. Please try again.",
+ });
+ }
+ },
+ removeInboxIssue: async () => {
+ try {
+ if (!workspaceSlug || !projectId || !inboxId || !inboxIssueId || !currentWorkspace)
+ throw new Error("Missing required parameters");
+ await removeInboxIssue(workspaceSlug, projectId, inboxId, inboxIssueId);
+ postHogEventTracker(
+ "ISSUE_DELETED",
+ {
+ state: "SUCCESS",
+ },
+ {
+ isGrouping: true,
+ groupType: "Workspace_metrics",
+ groupId: currentWorkspace?.id!,
+ }
+ );
+ router.push({
+ pathname: `/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}`,
+ });
+ } catch (error) {
+ setToastAlert({
+ type: "error",
+ title: "Error!",
+ message: "Something went wrong while deleting inbox issue. Please try again.",
+ });
+ postHogEventTracker(
+ "ISSUE_DELETED",
+ {
+ state: "FAILED",
+ },
+ {
+ isGrouping: true,
+ groupType: "Workspace_metrics",
+ groupId: currentWorkspace?.id!,
+ }
+ );
+ }
+ },
+ }),
+ [
+ currentWorkspace,
+ workspaceSlug,
+ projectId,
+ inboxId,
+ inboxIssueId,
+ updateInboxIssueStatus,
+ removeInboxIssue,
+ setToastAlert,
+ postHogEventTracker,
+ router,
+ ]
+ );
+
+ const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
+
+ const today = new Date();
+ const tomorrow = new Date(today);
+ tomorrow.setDate(today.getDate() + 1);
+ useEffect(() => {
+ if (!issueStatus || !issueStatus.snoozed_till) return;
+ setDate(new Date(issueStatus.snoozed_till));
+ }, [issueStatus]);
+
+ if (!issueStatus || !issue || !inboxIssues) return <>>;
+ return (
+ <>
+ {issue && (
+ <>
+ setSelectDuplicateIssue(false)}
+ value={issueStatus.duplicate_to}
+ onSubmit={(dupIssueId) => {
+ inboxIssueOperations
+ .updateInboxIssueStatus({
+ status: 2,
+ duplicate_to: dupIssueId,
+ })
+ .finally(() => setSelectDuplicateIssue(false));
+ }}
+ />
+
+ setAcceptIssueModal(false)}
+ onSubmit={async () => {
+ await inboxIssueOperations
+ .updateInboxIssueStatus({
+ status: 1,
+ })
+ .finally(() => setAcceptIssueModal(false));
+ }}
+ />
+
+ setDeclineIssueModal(false)}
+ onSubmit={async () => {
+ await inboxIssueOperations
+ .updateInboxIssueStatus({
+ status: -1,
+ })
+ .finally(() => setDeclineIssueModal(false));
+ }}
+ />
+
+ setDeleteIssueModal(false)}
+ onSubmit={async () => {
+ await inboxIssueOperations.removeInboxIssue().finally(() => setDeclineIssueModal(false));
+ }}
+ />
+ >
+ )}
+
+ {inboxIssueId && (
+
+
+
+
+
+ {currentIssueIndex + 1}/{inboxIssues?.length ?? 0}
+
+
+
+
+ {isAllowed && (issueStatus.status === 0 || issueStatus.status === -2) && (
+
+
+
+ } size="sm">
+ Snooze
+
+
+
+ {({ close }) => (
+
+ {
+ if (!val) return;
+ setDate(val);
+ }}
+ dateFormat="dd-MM-yyyy"
+ minDate={tomorrow}
+ inline
+ />
+
+
+ )}
+
+
+
+ )}
+
+ {isAllowed && issueStatus.status === -2 && (
+
+ }
+ onClick={() => setSelectDuplicateIssue(true)}
+ >
+ Mark as duplicate
+
+
+ )}
+
+ {isAllowed && (issueStatus.status === 0 || issueStatus.status === -2) && (
+
+ }
+ onClick={() => setAcceptIssueModal(true)}
+ >
+ Accept
+
+
+ )}
+
+ {isAllowed && issueStatus.status === -2 && (
+
+ }
+ onClick={() => setDeclineIssueModal(true)}
+ >
+ Decline
+
+
+ )}
+
+ {(isAllowed || currentUser?.id === issue?.created_by) && (
+
+ }
+ onClick={() => setDeleteIssueModal(true)}
+ >
+ Delete
+
+
+ )}
+
+
+ )}
+ >
+ );
+});
diff --git a/web/components/inbox/inbox-issue-status.tsx b/web/components/inbox/inbox-issue-status.tsx
new file mode 100644
index 000000000..301583b4b
--- /dev/null
+++ b/web/components/inbox/inbox-issue-status.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+// hooks
+import { useInboxIssues } from "hooks/store";
+// constants
+import { INBOX_STATUS } from "constants/inbox";
+
+type Props = {
+ workspaceSlug: string;
+ projectId: string;
+ inboxId: string;
+ issueId: string;
+ iconSize?: number;
+ showDescription?: boolean;
+};
+
+export const InboxIssueStatus: React.FC = (props) => {
+ const { workspaceSlug, projectId, inboxId, issueId, iconSize = 18, showDescription = false } = props;
+ // hooks
+ const {
+ issues: { getInboxIssueByIssueId },
+ } = useInboxIssues();
+
+ const inboxIssueDetail = getInboxIssueByIssueId(inboxId, issueId);
+ if (!inboxIssueDetail) return <>>;
+
+ const inboxIssueStatusDetail = INBOX_STATUS.find((s) => s.status === inboxIssueDetail.status);
+ if (!inboxIssueStatusDetail) return <>>;
+
+ const isSnoozedDatePassed =
+ inboxIssueDetail.status === 0 && new Date(inboxIssueDetail.snoozed_till ?? "") < new Date();
+
+ return (
+
+
+ {showDescription ? (
+ inboxIssueStatusDetail.description(
+ workspaceSlug,
+ projectId,
+ inboxIssueDetail.duplicate_to ?? "",
+ new Date(inboxIssueDetail.snoozed_till ?? "")
+ )
+ ) : (
+ {inboxIssueStatusDetail.title}
+ )}
+
+ );
+};
diff --git a/web/components/inbox/index.ts b/web/components/inbox/index.ts
index ef1a9e92d..ae267f54c 100644
--- a/web/components/inbox/index.ts
+++ b/web/components/inbox/index.ts
@@ -1,8 +1,12 @@
export * from "./modals";
-export * from "./actions-header";
-export * from "./filters-dropdown";
-export * from "./filters-list";
-export * from "./issue-activity";
-export * from "./issue-card";
-export * from "./issues-list-sidebar";
-export * from "./main-content";
+
+export * from "./inbox-issue-actions";
+export * from "./inbox-issue-status";
+
+export * from "./sidebar/root";
+
+export * from "./sidebar/filter/filter-selection";
+export * from "./sidebar/filter/applied-filters";
+
+export * from "./sidebar/inbox-list";
+export * from "./sidebar/inbox-list-item";
diff --git a/web/components/inbox/issue-activity.tsx b/web/components/inbox/issue-activity.tsx
deleted file mode 100644
index a3b3978e5..000000000
--- a/web/components/inbox/issue-activity.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import { useRouter } from "next/router";
-import useSWR, { mutate } from "swr";
-import { observer } from "mobx-react-lite";
-// hooks
-import { useApplication, useUser, useWorkspace } from "hooks/store";
-// components
-import { AddComment, IssueActivitySection } from "components/issues";
-// services
-import { IssueService, IssueCommentService } from "services/issue";
-// hooks
-import useToast from "hooks/use-toast";
-// types
-import { TIssue, IIssueActivity } from "@plane/types";
-// fetch-keys
-import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
-
-type Props = { issueDetails: TIssue };
-
-// services
-const issueService = new IssueService();
-const issueCommentService = new IssueCommentService();
-
-export const InboxIssueActivity: React.FC = observer(({ issueDetails }) => {
- // router
- const router = useRouter();
- const { workspaceSlug, projectId } = router.query;
- // store hooks
- const {
- eventTracker: { postHogEventTracker },
- } = useApplication();
- const { currentUser } = useUser();
- const { currentWorkspace } = useWorkspace();
-
- const { setToastAlert } = useToast();
-
- const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
- workspaceSlug && projectId && issueDetails ? PROJECT_ISSUES_ACTIVITY(issueDetails.id) : null,
- workspaceSlug && projectId && issueDetails
- ? () => issueService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), issueDetails.id)
- : null
- );
-
- const handleCommentUpdate = async (commentId: string, data: Partial) => {
- if (!workspaceSlug || !projectId || !issueDetails.id || !currentUser) return;
-
- await issueCommentService
- .patchIssueComment(workspaceSlug.toString(), projectId.toString(), issueDetails.id, commentId, data)
- .then((res) => {
- mutateIssueActivity();
- postHogEventTracker(
- "COMMENT_UPDATED",
- {
- ...res,
- state: "SUCCESS",
- },
- {
- isGrouping: true,
- groupType: "Workspace_metrics",
- groupId: currentWorkspace?.id!,
- }
- );
- });
- };
-
- const handleCommentDelete = async (commentId: string) => {
- if (!workspaceSlug || !projectId || !issueDetails.id || !currentUser) return;
-
- mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
-
- await issueCommentService
- .deleteIssueComment(workspaceSlug.toString(), projectId.toString(), issueDetails.id, commentId)
- .then(() => {
- mutateIssueActivity();
- postHogEventTracker(
- "COMMENT_DELETED",
- {
- state: "SUCCESS",
- },
- {
- isGrouping: true,
- groupType: "Workspace_metrics",
- groupId: currentWorkspace?.id!,
- }
- );
- });
- };
-
- const handleAddComment = async (formData: IIssueActivity) => {
- if (!workspaceSlug || !issueDetails || !currentUser) return;
-
- /* FIXME: Replace this with the new issue activity component --issue-detail-- */
- // await issueCommentService
- // .createIssueComment(workspaceSlug.toString(), issueDetails.project_id, issueDetails.id, formData)
- // .then((res) => {
- // mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
- // postHogEventTracker(
- // "COMMENT_ADDED",
- // {
- // ...res,
- // state: "SUCCESS",
- // },
- // {
- // isGrouping: true,
- // groupType: "Workspace_metrics",
- // groupId: currentWorkspace?.id!,
- // }
- // );
- // })
- // .catch(() =>
- // setToastAlert({
- // type: "error",
- // title: "Error!",
- // message: "Comment could not be posted. Please try again.",
- // })
- // );
- };
-
- return (
-
- {/* FIXME: Replace this with the new issue activity component --issue-detail-- */}
- {/*
Comments/Activity
-
-
*/}
-
- );
-});
diff --git a/web/components/inbox/issue-card.tsx b/web/components/inbox/issue-card.tsx
deleted file mode 100644
index b6adeb2a1..000000000
--- a/web/components/inbox/issue-card.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { useRouter } from "next/router";
-import Link from "next/link";
-import { AlertTriangle, CalendarDays, CheckCircle2, Clock, Copy, XCircle } from "lucide-react";
-// ui
-import { Tooltip, PriorityIcon } from "@plane/ui";
-// hooks
-import { useInboxIssues, useProject } from "hooks/store";
-// helpers
-import { renderFormattedDate } from "helpers/date-time.helper";
-// constants
-import { INBOX_STATUS } from "constants/inbox";
-
-type Props = {
- active: boolean;
- issueId: string;
-};
-
-export const InboxIssueCard: React.FC = (props) => {
- const { active } = props;
- // router
- const router = useRouter();
- const { workspaceSlug, projectId, inboxId } = router.query;
- // store hooks
- const { getIssueById } = useInboxIssues();
- const { getProjectById } = useProject();
- // derived values
- const issue = getIssueById(inboxId as string, props.issueId);
- const issueStatus = issue?.issue_inbox[0].status;
-
- if (!issue) return null;
-
- return (
-
-
-
-
- {getProjectById(issue.project_id)?.identifier}-{issue.sequence_id}
-
-
{issue.name}
-
-
-
-
-
-
-
-
- {renderFormattedDate(issue.created_at ?? "")}
-
-
-
-
s.value === issueStatus)?.textColor
- }`}
- >
- {issueStatus === -2 ? (
- <>
-
-
Pending
- >
- ) : issueStatus === -1 ? (
- <>
-
-
Declined
- >
- ) : issueStatus === 0 ? (
- <>
-
-
- {new Date(issue.issue_inbox[0].snoozed_till ?? "") < new Date() ? "Snoozed date passed" : "Snoozed"}
-
- >
- ) : issueStatus === 1 ? (
- <>
-
-
Accepted
- >
- ) : (
- <>
-
-
Duplicate
- >
- )}
-
-
-
- );
-};
diff --git a/web/components/inbox/issues-list-sidebar.tsx b/web/components/inbox/issues-list-sidebar.tsx
deleted file mode 100644
index 18d2f3d5d..000000000
--- a/web/components/inbox/issues-list-sidebar.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { useRouter } from "next/router";
-import { observer } from "mobx-react-lite";
-
-// mobx store
-import { useInboxIssues } from "hooks/store";
-// components
-import { InboxIssueCard, InboxFiltersList } from "components/inbox";
-// ui
-import { Loader } from "@plane/ui";
-
-export const InboxIssuesListSidebar = observer(() => {
- const router = useRouter();
- const { inboxIssueId } = router.query;
-
- const { currentInboxIssueIds: currentInboxIssues } = useInboxIssues();
-
- const issuesList = currentInboxIssues;
-
- return (
-
-
- {issuesList ? (
- issuesList.length > 0 ? (
-
- {issuesList.map((id) => (
-
- ))}
-
- ) : (
-
- {/* TODO: add filtersLength logic here */}
- {/* {filtersLength > 0 && "No issues found for the selected filters. Try changing the filters."} */}
-
- )
- ) : (
-
-
-
-
-
-
- )}
-
- );
-});
diff --git a/web/components/inbox/main-content.tsx b/web/components/inbox/main-content.tsx
deleted file mode 100644
index 97346d0a0..000000000
--- a/web/components/inbox/main-content.tsx
+++ /dev/null
@@ -1,292 +0,0 @@
-import { useCallback, useEffect, useState } from "react";
-import { useRouter } from "next/router";
-import { observer } from "mobx-react-lite";
-import useSWR from "swr";
-import { useForm } from "react-hook-form";
-import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, XCircle } from "lucide-react";
-// hooks
-import { useProjectState, useUser, useInboxIssues } from "hooks/store";
-// components
-import {
- IssueDescriptionForm,
- // FIXME: have to replace this once the issue details page is ready --issue-detail--
- // IssueDetailsSidebar,
- // IssueReaction,
- IssueUpdateStatus,
-} from "components/issues";
-import { InboxIssueActivity } from "components/inbox";
-// ui
-import { Loader, StateGroupIcon } from "@plane/ui";
-// helpers
-import { renderFormattedDate } from "helpers/date-time.helper";
-// types
-import { IInboxIssue, TIssue } from "@plane/types";
-import { EUserProjectRoles } from "constants/project";
-
-const defaultValues: Partial = {
- name: "",
- description_html: "",
- assignee_ids: [],
- priority: "low",
- target_date: new Date().toString(),
- label_ids: [],
-};
-
-export const InboxMainContent: React.FC = observer(() => {
- // states
- const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
- // router
- const router = useRouter();
- const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query;
- // store hooks
- const { currentInboxIssueIds: currentInboxIssues, fetchIssueDetails, getIssueById, updateIssue } = useInboxIssues();
- const {
- currentUser,
- membership: { currentProjectRole },
- } = useUser();
- const { projectStates } = useProjectState();
- // form info
- const { reset, control, watch } = useForm({
- defaultValues,
- });
-
- useSWR(
- workspaceSlug && projectId && inboxId && inboxIssueId ? `INBOX_ISSUE_DETAILS_${inboxIssueId.toString()}` : null,
- workspaceSlug && projectId && inboxId && inboxIssueId
- ? () =>
- fetchIssueDetails(workspaceSlug.toString(), projectId.toString(), inboxId.toString(), inboxIssueId.toString())
- : null
- );
-
- const issuesList = currentInboxIssues;
- const issueDetails = inboxIssueId ? getIssueById(inboxId as string, inboxIssueId.toString()) : undefined;
- const currentIssueState = projectStates?.find((s) => s.id === issueDetails?.state_id);
-
- const submitChanges = useCallback(
- async (formData: Partial) => {
- if (!workspaceSlug || !projectId || !inboxIssueId || !inboxId || !issueDetails) return;
-
- await updateIssue(
- workspaceSlug.toString(),
- projectId.toString(),
- inboxId.toString(),
- issueDetails.issue_inbox[0].id,
- formData
- );
- },
- [workspaceSlug, inboxIssueId, projectId, inboxId, issueDetails, updateIssue]
- );
-
- // const onKeyDown = useCallback(
- // (e: KeyboardEvent) => {
- // if (!issuesList || !inboxIssueId) return;
-
- // const currentIssueIndex = issuesList.findIndex((issue) => issue.issue_inbox[0].id === inboxIssueId);
-
- // switch (e.key) {
- // case "ArrowUp":
- // Router.push({
- // pathname: `/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}`,
- // query: {
- // inboxIssueId:
- // currentIssueIndex === 0
- // ? issuesList[issuesList.length - 1].issue_inbox[0].id
- // : issuesList[currentIssueIndex - 1].issue_inbox[0].id,
- // },
- // });
- // break;
- // case "ArrowDown":
- // Router.push({
- // pathname: `/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}`,
- // query: {
- // inboxIssueId:
- // currentIssueIndex === issuesList.length - 1
- // ? issuesList[0].issue_inbox[0].id
- // : issuesList[currentIssueIndex + 1].issue_inbox[0].id,
- // },
- // });
- // break;
- // default:
- // break;
- // }
- // },
- // [workspaceSlug, projectId, inboxIssueId, inboxId, issuesList]
- // );
-
- // useEffect(() => {
- // document.addEventListener("keydown", onKeyDown);
-
- // return () => {
- // document.removeEventListener("keydown", onKeyDown);
- // };
- // }, [onKeyDown]);
-
- useEffect(() => {
- if (!issueDetails || !inboxIssueId) return;
-
- reset({
- ...issueDetails,
- assignee_ids: issueDetails.assignee_ids ?? issueDetails.assignee_ids,
- label_ids: issueDetails.label_ids ?? issueDetails.label_ids,
- });
- }, [issueDetails, reset, inboxIssueId]);
-
- const issueStatus = issueDetails?.issue_inbox[0].status;
-
- if (!inboxIssueId)
- return (
-
-
-
-
- {issuesList && issuesList.length > 0 ? (
-
- {issuesList?.length} issues found. Select an issue from the sidebar to view its details.
-
- ) : (
- No issues found
- )}
-
-
-
- );
-
- const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
-
- return (
- <>
- {issueDetails ? (
-
-
-
- {issueStatus === -2 ? (
- <>
-
-
This issue is still pending.
- >
- ) : issueStatus === -1 ? (
- <>
-
-
This issue has been declined.
- >
- ) : issueStatus === 0 ? (
- <>
-
- {new Date(issueDetails.issue_inbox[0].snoozed_till ?? "") < new Date() ? (
-
- This issue was snoozed till {renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
-
- ) : (
-
- This issue has been snoozed till{" "}
- {renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
-
- )}
- >
- ) : issueStatus === 1 ? (
- <>
-
-
This issue has been accepted.
- >
- ) : issueStatus === 2 ? (
- <>
-
-
- This issue has been marked as a duplicate of
-
- this issue
-
- .
-
- >
- ) : null}
-
-
- {currentIssueState && (
-
- )}
-
-
-
- {/* FIXME: have to replace this once the issue details page is ready --issue-detail-- */}
- {/*
- setIsSubmitting(value)}
- isSubmitting={isSubmitting}
- workspaceSlug={workspaceSlug as string}
- issue={{
- name: issueDetails.name,
- description_html: issueDetails.description_html,
- id: issueDetails.id,
- }}
- handleFormSubmit={submitChanges}
- isAllowed={isAllowed || currentUser?.id === issueDetails.created_by}
- />
-
*/}
-
- {/* FIXME: have to replace this once the issue details page is ready --issue-detail-- */}
- {/* {workspaceSlug && projectId && (
-
- )} */}
-
-
-
-
- {/* FIXME: have to replace this once the issue details page is ready --issue-detail-- */}
- {/* */}
-
-
- ) : (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
- >
- );
-});
diff --git a/web/components/inbox/modals/accept-issue-modal.tsx b/web/components/inbox/modals/accept-issue-modal.tsx
index bffeffed1..5ec63ea8a 100644
--- a/web/components/inbox/modals/accept-issue-modal.tsx
+++ b/web/components/inbox/modals/accept-issue-modal.tsx
@@ -1,16 +1,15 @@
import React, { useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
-
// icons
import { CheckCircle } from "lucide-react";
// ui
import { Button } from "@plane/ui";
// types
-import type { IInboxIssue } from "@plane/types";
+import type { TIssue } from "@plane/types";
import { useProject } from "hooks/store";
type Props = {
- data: IInboxIssue;
+ data: TIssue;
isOpen: boolean;
onClose: () => void;
onSubmit: () => Promise;
@@ -28,7 +27,6 @@ export const AcceptIssueModal: React.FC = ({ isOpen, onClose, data, onSub
const handleAccept = () => {
setIsAccepting(true);
-
onSubmit().finally(() => setIsAccepting(false));
};
diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx
index 358a6f1ec..e152c1b03 100644
--- a/web/components/inbox/modals/create-issue-modal.tsx
+++ b/web/components/inbox/modals/create-issue-modal.tsx
@@ -58,7 +58,9 @@ export const CreateInboxIssueModal: React.FC = observer((props) => {
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
// store hooks
- const { createIssue } = useInboxIssues();
+ const {
+ issues: { createInboxIssue },
+ } = useInboxIssues();
const {
config: { envConfig },
eventTracker: { postHogEventTracker },
@@ -85,10 +87,10 @@ export const CreateInboxIssueModal: React.FC = observer((props) => {
const handleFormSubmit = async (formData: Partial) => {
if (!workspaceSlug || !projectId || !inboxId) return;
- await createIssue(workspaceSlug.toString(), projectId.toString(), inboxId.toString(), formData)
+ await createInboxIssue(workspaceSlug.toString(), projectId.toString(), inboxId.toString(), formData)
.then((res) => {
if (!createMore) {
- router.push(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}`);
+ router.push(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${res.id}`);
handleClose();
} else reset(defaultValues);
postHogEventTracker(
diff --git a/web/components/inbox/modals/decline-issue-modal.tsx b/web/components/inbox/modals/decline-issue-modal.tsx
index ec4a06f2a..a69c8d0e1 100644
--- a/web/components/inbox/modals/decline-issue-modal.tsx
+++ b/web/components/inbox/modals/decline-issue-modal.tsx
@@ -1,16 +1,15 @@
import React, { useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
-
// icons
import { AlertTriangle } from "lucide-react";
// ui
import { Button } from "@plane/ui";
// types
-import type { IInboxIssue } from "@plane/types";
+import type { TIssue } from "@plane/types";
import { useProject } from "hooks/store";
type Props = {
- data: IInboxIssue;
+ data: TIssue;
isOpen: boolean;
onClose: () => void;
onSubmit: () => Promise;
@@ -28,7 +27,6 @@ export const DeclineIssueModal: React.FC = ({ isOpen, onClose, data, onSu
const handleDecline = () => {
setIsDeclining(true);
-
onSubmit().finally(() => setIsDeclining(false));
};
diff --git a/web/components/inbox/modals/delete-issue-modal.tsx b/web/components/inbox/modals/delete-issue-modal.tsx
index 1dbf2541c..c06621c03 100644
--- a/web/components/inbox/modals/delete-issue-modal.tsx
+++ b/web/components/inbox/modals/delete-issue-modal.tsx
@@ -1,39 +1,27 @@
import React, { useState } from "react";
-import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Dialog, Transition } from "@headlessui/react";
// hooks
-import { useApplication, useProject, useWorkspace } from "hooks/store";
-import useToast from "hooks/use-toast";
+import { useProject } from "hooks/store";
// icons
import { AlertTriangle } from "lucide-react";
// ui
import { Button } from "@plane/ui";
// types
-import type { IInboxIssue } from "@plane/types";
-import { useInboxIssues } from "hooks/store/use-inbox-issues";
+import type { TIssue } from "@plane/types";
type Props = {
- data: IInboxIssue;
+ data: TIssue;
isOpen: boolean;
onClose: () => void;
+ onSubmit: () => Promise;
};
-export const DeleteInboxIssueModal: React.FC = observer(({ isOpen, onClose, data }) => {
+export const DeleteInboxIssueModal: React.FC = observer(({ isOpen, onClose, onSubmit, data }) => {
// states
const [isDeleting, setIsDeleting] = useState(false);
- // router
- const router = useRouter();
- const { workspaceSlug, projectId, inboxId } = router.query;
- // store hooks
- const { deleteIssue } = useInboxIssues();
- const {
- eventTracker: { postHogEventTracker },
- } = useApplication();
- const { currentWorkspace } = useWorkspace();
- const { getProjectById } = useProject();
- const { setToastAlert } = useToast();
+ const { getProjectById } = useProject();
const handleClose = () => {
setIsDeleting(false);
@@ -41,59 +29,13 @@ export const DeleteInboxIssueModal: React.FC = observer(({ isOpen, onClos
};
const handleDelete = () => {
- if (!workspaceSlug || !projectId || !inboxId) return;
-
setIsDeleting(true);
-
- deleteIssue(workspaceSlug.toString(), projectId.toString(), inboxId.toString(), data.issue_inbox[0].id)
- .then(() => {
- setToastAlert({
- type: "success",
- title: "Success!",
- message: "Issue deleted successfully.",
- });
- postHogEventTracker(
- "ISSUE_DELETED",
- {
- state: "SUCCESS",
- },
- {
- isGrouping: true,
- groupType: "Workspace_metrics",
- groupId: currentWorkspace?.id!,
- }
- );
- // remove inboxIssueId from the url
- router.push({
- pathname: `/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}`,
- });
-
- handleClose();
- })
- .catch(() => {
- setToastAlert({
- type: "error",
- title: "Error!",
- message: "Issue could not be deleted. Please try again.",
- });
- postHogEventTracker(
- "ISSUE_DELETED",
- {
- state: "FAILED",
- },
- {
- isGrouping: true,
- groupType: "Workspace_metrics",
- groupId: currentWorkspace?.id!,
- }
- );
- })
- .finally(() => setIsDeleting(false));
+ onSubmit().finally(() => setIsDeleting(false));
};
return (
-
-