@@ -27,7 +27,7 @@ export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
{
- issueStore.setPeekId(issue.id);
+ issueDetailStore.setPeekId(issue.id);
}}
className="text-[0.825rem] font-medium text-sm truncate text-custom-text-100"
>
diff --git a/apps/space/components/issues/peek-overview/full-screen-peek-view.tsx b/apps/space/components/issues/peek-overview/full-screen-peek-view.tsx
index 27080d37f..291bcc887 100644
--- a/apps/space/components/issues/peek-overview/full-screen-peek-view.tsx
+++ b/apps/space/components/issues/peek-overview/full-screen-peek-view.tsx
@@ -1,70 +1,52 @@
import { useEffect } from "react";
-
-// mobx
import { observer } from "mobx-react-lite";
+// lib
import { useMobxStore } from "lib/mobx/store-provider";
-
+// components
import {
PeekOverviewHeader,
PeekOverviewIssueActivity,
PeekOverviewIssueDetails,
PeekOverviewIssueProperties,
- TPeekOverviewModes,
} from "components/issues/peek-overview";
+// types
+import { IIssue } from "types/issue";
type Props = {
- issueId: string;
- workspaceSlug: string;
- projectId: string;
handleClose: () => void;
- mode: TPeekOverviewModes;
- setMode: (mode: TPeekOverviewModes) => void;
+ issueDetails: IIssue;
};
export const FullScreenPeekView: React.FC = observer((props) => {
- const { handleClose, issueId, mode, setMode, workspaceSlug , projectId } = props;
+ const { handleClose, issueDetails } = props;
- const { issue: issueStore } = useMobxStore();
-
- const issue = issueStore.issue_detail[issueId]?.issue;
-
- useEffect(() => {
- if (!workspaceSlug || !projectId || !issueId) return;
-
- issueStore.getIssueByIdAsync(workspaceSlug, projectId, issueId);
- }, [workspaceSlug, projectId, issueId, issueStore]);
+ const { issueDetails: issueDetailStore } = useMobxStore();
return (
-
-
-
- {/* issue title and description */}
-
- {/* divider */}
-
- {/* issue activity/comments */}
-
-
-
-
- {/* issue properties */}
-
-
+
+
+
+ {/* issue title and description */}
+
- )
-})
+ {/* divider */}
+
+ {/* issue activity/comments */}
+
+
+
+
+ {/* issue properties */}
+
+
+
+ );
+});
diff --git a/apps/space/components/issues/peek-overview/header.tsx b/apps/space/components/issues/peek-overview/header.tsx
index 86b7fdffc..b809f5438 100644
--- a/apps/space/components/issues/peek-overview/header.tsx
+++ b/apps/space/components/issues/peek-overview/header.tsx
@@ -1,23 +1,26 @@
+import { useRouter } from "next/router";
+import { ArrowRightAlt, CloseFullscreen, East, OpenInFull } from "@mui/icons-material";
// hooks
import useToast from "hooks/use-toast";
// ui
import { Icon } from "components/ui";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
+// store
+import { IPeekMode } from "store/issue_details";
+import { RootStore } from "store/root";
+// lib
+import { useMobxStore } from "lib/mobx/store-provider";
// types
-import { TPeekOverviewModes } from "./layout";
-import { ArrowRightAlt, CloseFullscreen, East, OpenInFull } from "@mui/icons-material";
+import { IIssue } from "types";
type Props = {
handleClose: () => void;
- issue: any;
- mode: TPeekOverviewModes;
- setMode: (mode: TPeekOverviewModes) => void;
- workspaceSlug: string;
+ issueDetails: IIssue;
};
const peekModes: {
- key: TPeekOverviewModes;
+ key: IPeekMode;
icon: string;
label: string;
}[] = [
@@ -34,13 +37,20 @@ const peekModes: {
},
];
-export const PeekOverviewHeader: React.FC = ({ issue, handleClose, mode, setMode, workspaceSlug }) => {
+export const PeekOverviewHeader: React.FC = (props) => {
+ const { issueDetails, handleClose } = props;
+
+ const { issueDetails: issueDetailStore }: RootStore = useMobxStore();
+
+ const router = useRouter();
+ const { workspaceSlug } = router.query;
+
const { setToastAlert } = useToast();
const handleCopyLink = () => {
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
- copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${issue.project}/`).then(() => {
+ copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${issueDetails.project}/`).then(() => {
setToastAlert({
type: "success",
title: "Link copied!",
@@ -52,7 +62,7 @@ export const PeekOverviewHeader: React.FC = ({ issue, handleClose, mode,
return (
- {mode === "side" && (
+ {issueDetailStore.peekMode === "side" && (
)}
- {mode === "modal" || mode === "full" ? (
-
- {(mode === "side" || mode === "modal") && (
+ {(issueDetailStore.peekMode === "side" || issueDetailStore.peekMode === "modal") && (
diff --git a/apps/space/components/issues/peek-overview/issue-activity.tsx b/apps/space/components/issues/peek-overview/issue-activity.tsx
index 266f2ed3c..67de5fb54 100644
--- a/apps/space/components/issues/peek-overview/issue-activity.tsx
+++ b/apps/space/components/issues/peek-overview/issue-activity.tsx
@@ -1,42 +1,41 @@
import React, { useEffect } from "react";
-// mobx
+import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
+// lib
import { useMobxStore } from "lib/mobx/store-provider";
-
+// components
import { CommentCard, AddComment } from "components/issues/peek-overview";
+// types
+import { IIssue } from "types";
type Props = {
- workspaceSlug: string;
+ issueDetails: IIssue;
};
export const PeekOverviewIssueActivity: React.FC = observer((props) => {
- const { workspaceSlug } = props;
+ const router = useRouter();
+ const { workspaceSlug } = router.query;
- const { issue: issueStore, user: userStore } = useMobxStore();
+ const { issueDetails: issueDetailStore, user: userStore } = useMobxStore();
- const issueId = issueStore?.activePeekOverviewIssueId;
- const comments = issueStore?.issue_detail[issueId ?? ""]?.comments ?? [];
-
- useEffect(() => {
- if (userStore.currentUser) return;
-
- userStore.getUserAsync();
- }, [userStore]);
+ const issueId = issueDetailStore?.peekId;
+ const comments = issueDetailStore?.details[issueId ?? ""]?.comments ?? [];
return (
Activity
-
-
-
- {comments.map((comment) => (
-
- ))}
-
+ {workspaceSlug && (
-
+
+ {comments.map((comment: any) => (
+
+ ))}
+
+
-
+ )}
);
});
diff --git a/apps/space/components/issues/peek-overview/issue-details.tsx b/apps/space/components/issues/peek-overview/issue-details.tsx
index ffbae2bde..bbe982a69 100644
--- a/apps/space/components/issues/peek-overview/issue-details.tsx
+++ b/apps/space/components/issues/peek-overview/issue-details.tsx
@@ -4,15 +4,15 @@ import { IssueReactions } from "components/issues/peek-overview";
import { IIssue } from "types";
type Props = {
- issue: IIssue;
+ issueDetails: IIssue;
};
-export const PeekOverviewIssueDetails: React.FC = ({ issue }) => (
+export const PeekOverviewIssueDetails: React.FC = ({ issueDetails }) => (
- {issue.project_detail.identifier}-{issue.sequence_id}
+ {issueDetails.project_detail.identifier}-{issueDetails.sequence_id}
- {issue.name}
+ {issueDetails.name}
);
diff --git a/apps/space/components/issues/peek-overview/issue-properties.tsx b/apps/space/components/issues/peek-overview/issue-properties.tsx
index 10af5f71e..d91defc84 100644
--- a/apps/space/components/issues/peek-overview/issue-properties.tsx
+++ b/apps/space/components/issues/peek-overview/issue-properties.tsx
@@ -3,24 +3,23 @@ import { Disclosure } from "@headlessui/react";
// import { getStateGroupIcon } from "components/icons";
// hooks
import useToast from "hooks/use-toast";
-// components
-import { TPeekOverviewModes } from "components/issues/peek-overview";
// icons
import { Icon } from "components/ui";
import { copyTextToClipboard, addSpaceIfCamelCase } from "helpers/string.helper";
-
// types
import { IIssue } from "types";
-
// constants
import { issueGroupFilter, issuePriorityFilter } from "constants/data";
import { useEffect } from "react";
import { renderDateFormat } from "constants/helpers";
+import { IPeekMode } from "store/issue_details";
+import { useRouter } from "next/router";
+import { RootStore } from "store/root";
+import { useMobxStore } from "lib/mobx/store-provider";
type Props = {
- issue: IIssue;
- mode: TPeekOverviewModes;
- workspaceSlug: string;
+ issueDetails: IIssue;
+ mode?: IPeekMode;
};
const validDate = (date: any, state: string): string => {
@@ -35,11 +34,16 @@ const validDate = (date: any, state: string): string => {
}
};
-export const PeekOverviewIssueProperties: React.FC = ({ issue, mode, workspaceSlug }) => {
+export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mode }) => {
const { setToastAlert } = useToast();
- const startDate = issue.start_date;
- const targetDate = issue.target_date;
+ const { issueDetails: issueDetailStore }: RootStore = useMobxStore();
+
+ const router = useRouter();
+ const { workspaceSlug } = router.query;
+
+ const startDate = issueDetails.start_date;
+ const targetDate = issueDetails.target_date;
const minDate = startDate ? new Date(startDate) : null;
minDate?.setDate(minDate.getDate());
@@ -47,15 +51,17 @@ export const PeekOverviewIssueProperties: React.FC = ({ issue, mode, work
const maxDate = targetDate ? new Date(targetDate) : null;
maxDate?.setDate(maxDate.getDate());
- const state = issue.state_detail;
+ const state = issueDetails.state_detail;
const stateGroup = issueGroupFilter(state.group);
- const priority = issue.priority ? issuePriorityFilter(issue.priority) : null;
+ const priority = issueDetails.priority ? issuePriorityFilter(issueDetails.priority) : null;
const handleCopyLink = () => {
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
- copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => {
+ copyTextToClipboard(
+ `${originURL}/${workspaceSlug}/projects/${issueDetails.project}/issues/${issueDetails.id}`
+ ).then(() => {
setToastAlert({
type: "success",
title: "Link copied!",
@@ -70,7 +76,7 @@ export const PeekOverviewIssueProperties: React.FC = ({ issue, mode, work
{/* {getStateGroupIcon(issue.state_detail.group, "16", "16", issue.state_detail.color)} */}
- {issue.project_detail.identifier}-{issue.sequence_id}
+ {issueDetails.project_detail.identifier}-{issueDetails.sequence_id}
@@ -131,12 +137,12 @@ export const PeekOverviewIssueProperties: React.FC = ({ issue, mode, work
Due date
- {issue.target_date ? (
+ {issueDetails.target_date ? (
- {renderDateFormat(issue.target_date)}
+ {renderDateFormat(issueDetails.target_date)}
) : (
Empty
diff --git a/apps/space/components/issues/peek-overview/layout.tsx b/apps/space/components/issues/peek-overview/layout.tsx
index 2d30cff68..ee763d148 100644
--- a/apps/space/components/issues/peek-overview/layout.tsx
+++ b/apps/space/components/issues/peek-overview/layout.tsx
@@ -1,36 +1,48 @@
-import React, { useState } from "react";
-
-// headless ui
+import React, { useEffect, useState } from "react";
+import { useRouter } from "next/router";
import { Dialog, Transition } from "@headlessui/react";
+import { observer } from "mobx-react-lite";
+// components
import { FullScreenPeekView, SidePeekView } from "components/issues/peek-overview";
-
// types
-import type { IIssue } from "types";
+import type { IIssue } from "types/issue";
+// lib
+import { useMobxStore } from "lib/mobx/store-provider";
type Props = {
- issue: IIssue | null;
isOpen: boolean;
onClose: () => void;
- workspaceSlug: string;
};
-export type TPeekOverviewModes = "side" | "modal" | "full";
+export const IssuePeekOverview: React.FC
= observer((props) => {
+ const { isOpen, onClose } = props;
+ // router
+ const router = useRouter();
+ const { workspace_slug, project_slug } = router.query;
+ // store
+ const { issueDetails: issueDetailStore } = useMobxStore();
-export const IssuePeekOverview: React.FC = ({ issue, isOpen, onClose, workspaceSlug }) => {
- const [peekOverviewMode, setPeekOverviewMode] = useState("side");
+ const issueDetails = issueDetailStore.peekId ? issueDetailStore.details[issueDetailStore.peekId] : null;
+ console.log("issueDetails", issueDetails);
+
+ useEffect(() => {
+ if (workspace_slug && project_slug && issueDetailStore.peekId) {
+ if (!issueDetails) {
+ issueDetailStore.fetchIssueDetails(workspace_slug.toString(), project_slug.toString(), issueDetailStore.peekId);
+ }
+ }
+ }, [workspace_slug, project_slug, issueDetailStore, issueDetails]);
const handleClose = () => {
onClose();
- setPeekOverviewMode("side");
+ issueDetailStore.setPeekMode("side");
};
- if (!issue || !isOpen) return null;
-
return (
);
-};
+});
diff --git a/apps/space/components/issues/peek-overview/side-peek-view.tsx b/apps/space/components/issues/peek-overview/side-peek-view.tsx
index 7547f91e9..094565c93 100644
--- a/apps/space/components/issues/peek-overview/side-peek-view.tsx
+++ b/apps/space/components/issues/peek-overview/side-peek-view.tsx
@@ -1,64 +1,45 @@
-// mobx
+import { useEffect } from "react";
import { observer } from "mobx-react-lite";
+// lib
import { useMobxStore } from "lib/mobx/store-provider";
-
+// components
import {
PeekOverviewHeader,
PeekOverviewIssueActivity,
PeekOverviewIssueDetails,
PeekOverviewIssueProperties,
- TPeekOverviewModes,
} from "components/issues/peek-overview";
-import { useEffect } from "react";
+// types
+import { IIssue } from "types/issue";
type Props = {
- issueId: string;
- projectId: string;
- workspaceSlug: string;
handleClose: () => void;
- mode: TPeekOverviewModes;
- setMode: (mode: TPeekOverviewModes) => void;
+ issueDetails: IIssue;
};
export const SidePeekView: React.FC = observer((props) => {
- const { handleClose, issueId, mode, setMode, workspaceSlug, projectId } = props;
-
- const { issue: issueStore } = useMobxStore();
-
- const issue = issueStore.issue_detail[issueId]?.issue;
-
- useEffect(() => {
- if (!workspaceSlug || !projectId || !issueId) return;
-
- issueStore.getIssueByIdAsync(workspaceSlug, projectId, issueId);
- }, [workspaceSlug, projectId, issueId, issueStore]);
+ const { handleClose, issueDetails } = props;
return (
- {issue && (
+ {issueDetails && (
{/* issue title and description */}
{/* issue properties */}
{/* divider */}
{/* issue activity/comments */}
)}
diff --git a/apps/space/components/views/project-details.tsx b/apps/space/components/views/project-details.tsx
index b1a857b98..1ef870dc4 100644
--- a/apps/space/components/views/project-details.tsx
+++ b/apps/space/components/views/project-details.tsx
@@ -16,11 +16,9 @@ export const ProjectDetailsView = observer(() => {
const router = useRouter();
const { workspace_slug, project_slug, states, labels, priorities } = router.query;
- const { issue: issueStore, project: projectStore }: RootStore = useMobxStore();
+ const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
- console.log("projectStore?.activeBoard", projectStore?.activeBoard);
-
- const activeIssueId = issueStore.peekId;
+ const activeIssueId = issueDetailStore.peekId;
useEffect(() => {
if (workspace_slug && project_slug) {
@@ -36,12 +34,7 @@ export const ProjectDetailsView = observer(() => {
return (
{workspace_slug && (
- issueStore.setPeekId(null)}
- issue={issueStore?.issues?.find((_issue) => _issue.id === activeIssueId) || null}
- workspaceSlug={workspace_slug.toString()}
- />
+ issueDetailStore.setPeekId(null)} />
)}
{issueStore?.loader && !issueStore.issues ? (
diff --git a/apps/space/store/issue.ts b/apps/space/store/issue.ts
index c15a9a2e9..c14175beb 100644
--- a/apps/space/store/issue.ts
+++ b/apps/space/store/issue.ts
@@ -18,13 +18,10 @@ export interface IIssueStore {
filteredStates: string[];
filteredLabels: string[];
filteredPriorities: string[];
- // peek info
- peekId: string | null;
// service
issueService: any;
// actions
fetchPublicIssues: (workspace_slug: string, project_slug: string, params: any) => void;
- setPeekId: (issueId: string | null) => void;
getFilteredIssuesByState: (state: string) => IIssue[];
}
@@ -42,8 +39,6 @@ class IssueStore implements IIssueStore {
issues: IIssue[] | null = [];
issue_detail: any = {};
- peekId: string | null = null;
-
rootStore: RootStore;
issueService: any;
@@ -62,11 +57,8 @@ class IssueStore implements IIssueStore {
// issues
issues: observable.ref,
issue_detail: observable.ref,
- // peek
- peekId: observable.ref,
// actions
fetchPublicIssues: action,
- setPeekId: action,
getFilteredIssuesByState: action,
});
@@ -98,10 +90,6 @@ class IssueStore implements IIssueStore {
}
};
- setPeekId = (issueId: string | null) => {
- this.peekId = issueId;
- };
-
getFilteredIssuesByState = (state_id: string): IIssue[] | [] =>
this.issues?.filter((issue) => issue.state == state_id) || [];
}
diff --git a/apps/space/store/issue_details.ts b/apps/space/store/issue_details.ts
new file mode 100644
index 000000000..4da937bff
--- /dev/null
+++ b/apps/space/store/issue_details.ts
@@ -0,0 +1,100 @@
+import { makeObservable, observable, action, runInAction } from "mobx";
+// store
+import { RootStore } from "./root";
+// services
+import IssueService from "services/issue.service";
+
+export type IPeekMode = "side" | "modal" | "full";
+
+export interface IIssueDetailStore {
+ loader: boolean;
+ error: any;
+ // peek info
+ peekId: string | null;
+ peekMode: IPeekMode;
+ details: any;
+ // actions
+ setPeekId: (issueId: string | null) => void;
+ setPeekMode: (mode: IPeekMode) => void;
+ fetchIssueDetails: (workspaceId: string, projectId: string, issueId: string) => void;
+}
+
+class IssueDetailStore implements IssueDetailStore {
+ loader: boolean = false;
+ error: any = null;
+ peekId: string | null = null;
+ peekMode: IPeekMode = "side";
+ details: any = {};
+ issueService: any;
+ rootStore: RootStore;
+
+ constructor(_rootStore: RootStore) {
+ makeObservable(this, {
+ loader: observable.ref,
+ error: observable.ref,
+ // peek
+ peekId: observable.ref,
+ peekMode: observable.ref,
+ details: observable.ref,
+ // actions
+ setPeekId: action,
+ fetchIssueDetails: action,
+ setPeekMode: action,
+ });
+ this.issueService = new IssueService();
+ this.rootStore = _rootStore;
+ }
+
+ setPeekId = (issueId: string | null) => {
+ this.peekId = issueId;
+ };
+
+ setPeekMode = (mode: IPeekMode) => {
+ this.peekMode = mode;
+ };
+
+ fetchIssueDetails = async (workspaceSlug: string, projectId: string, issueId: string) => {
+ try {
+ this.loader = true;
+ this.error = null;
+
+ const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
+ const reactionsResponse = await this.issueService.getIssueReactions(workspaceSlug, projectId, issueId);
+ const commentsResponse = await this.issueService.getIssueComments(workspaceSlug, projectId, issueId);
+ const votesResponse = await this.issueService.getIssueVotes(workspaceSlug, projectId, issueId);
+
+ if (issueDetails) {
+ runInAction(() => {
+ this.details = {
+ ...this.details,
+ [issueId]: {
+ ...issueDetails,
+ comments: commentsResponse,
+ reactions: reactionsResponse,
+ votes: votesResponse,
+ },
+ };
+ });
+ }
+ } catch (error) {
+ this.loader = false;
+ this.error = error;
+
+ const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
+
+ runInAction(() => {
+ this.details = {
+ ...this.details,
+ [issueId]: {
+ ...issueDetails,
+ comments: [],
+ reactions: [],
+ votes: [],
+ },
+ };
+ });
+ }
+ };
+}
+
+export default IssueDetailStore;
diff --git a/apps/space/store/root.ts b/apps/space/store/root.ts
index c6bdfc6aa..cf097a1c6 100644
--- a/apps/space/store/root.ts
+++ b/apps/space/store/root.ts
@@ -5,6 +5,7 @@ import UserStore from "./user";
import ThemeStore from "./theme";
import IssueStore, { IIssueStore } from "./issue";
import ProjectStore, { IProjectStore } from "./project";
+import IssueDetailStore, { IIssueDetailStore } from "./issue_details";
// types
import { IThemeStore } from "../types";
@@ -14,6 +15,7 @@ export class RootStore {
user: UserStore;
theme: IThemeStore;
issue: IIssueStore;
+ issueDetails: IIssueDetailStore;
project: IProjectStore;
constructor() {
@@ -21,5 +23,6 @@ export class RootStore {
this.theme = new ThemeStore(this);
this.issue = new IssueStore(this);
this.project = new ProjectStore(this);
+ this.issueDetails = new IssueDetailStore(this);
}
}