diff --git a/web/components/core/views/list-view/single-issue.tsx b/web/components/core/views/list-view/single-issue.tsx index aad6af6c8..62d35e73f 100644 --- a/web/components/core/views/list-view/single-issue.tsx +++ b/web/components/core/views/list-view/single-issue.tsx @@ -198,6 +198,8 @@ export const SingleListIssue: React.FC = ({ const isNotAllowed = userAuth.isGuest || userAuth.isViewer || disableUserActions || isArchivedIssues; + console.log("properties", properties); + return ( <> = (props) => { + const { groupId, groupBy } = props; + + const { issueView: issueViewStore, issueFilters: issueFilterStore }: RootStore = useMobxStore(); + + return ( +
+ {groupBy === "state" && <>{issueFilterStore.getProjectStateById(groupId)?.name}} + {groupBy === "state_detail.group" && <>{groupId}} + {groupBy === "priority" && <>{groupId}} + {groupBy === "project" && ( + <>{issueFilterStore.workspaceProjects?.find((p) => (p.id = groupId))} + )} + {groupBy === "labels" && ( + <>{issueFilterStore.projectLabels?.find((p) => p.id === groupId)?.name || " None"} + )} + {groupBy === "assignees" && ( + <> + {issueFilterStore.projectMembers?.find((p) => p?.member?.id === groupId)?.member + ?.display_name || " None"} + + )} + {groupBy === "created_by" && ( + <> + {issueFilterStore.projectMembers?.find((p) => p?.member?.id === groupId)?.member + ?.display_name || " None"} + + )} +
+ ); +}; diff --git a/web/components/issue-layouts/list/index.ts b/web/components/issue-layouts/list/index.ts new file mode 100644 index 000000000..a4e3561c6 --- /dev/null +++ b/web/components/issue-layouts/list/index.ts @@ -0,0 +1,4 @@ +export * from "./root"; +export * from "./list"; +export * from "./item"; +export * from "./group-header"; diff --git a/web/components/issue-layouts/list/index.tsx b/web/components/issue-layouts/list/index.tsx deleted file mode 100644 index 826f1b884..000000000 --- a/web/components/issue-layouts/list/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from "react"; - -export const IssueListViewRoot = () => { - console.log(); - return
IssueListViewRoot
; -}; diff --git a/web/components/issue-layouts/list/item.tsx b/web/components/issue-layouts/list/item.tsx new file mode 100644 index 000000000..accd79fd8 --- /dev/null +++ b/web/components/issue-layouts/list/item.tsx @@ -0,0 +1,234 @@ +import React, { FC, useState } from "react"; +import { Tooltip, CustomMenu, ContextMenu } from "components/ui"; +// lib +import { useMobxStore } from "lib/mobx/store-provider"; +import { IIssue } from "types"; +import useUserAuth from "hooks/use-user-auth"; +// icons +import { + ClipboardDocumentCheckIcon, + LinkIcon, + PencilIcon, + TrashIcon, + XMarkIcon, + ArrowTopRightOnSquareIcon, + PaperClipIcon, +} from "@heroicons/react/24/outline"; +// components +import { LayerDiagonalIcon } from "components/icons"; +import { + ViewAssigneeSelect, + ViewDueDateSelect, + ViewEstimateSelect, + ViewIssueLabel, + ViewPrioritySelect, + ViewStartDateSelect, + ViewStateSelect, +} from "components/issues"; + +export interface IIssueListItem { + issue: IIssue; +} + +export const IssueListItem: FC = (props) => { + const { issue } = props; + // store + const { user: userStore, issueFilters: issueFilterStore } = useMobxStore(); + const displayProperties = issueFilterStore.userFilters?.display_properties; + console.log("userStore", userStore); + // context menu + const [contextMenu, setContextMenu] = useState(false); + const [contextMenuPosition, setContextMenuPosition] = useState(null); + const { user: userAuth } = useUserAuth(); + + // const isNotAllowed = + // userAuth?.isGuest || userAuth?.isViewer || disableUserActions || isArchivedIssues; + + return ( +
+ <> + + {/* {!isNotAllowed && ( + <> + + Edit issue + + + Make a copy... + + handleDeleteIssue(issue)}> + Delete issue + + + )} + + Copy issue link + + + + Open issue in new tab + + */} + +
{ + e.preventDefault(); + setContextMenu(true); + setContextMenuPosition(e); + }} + > +
+
+ {/* {properties.key && ( + + + {issue.project_detail?.identifier}-{issue.sequence_id} + + + )} */} + + + +
+
+ +
+ {displayProperties?.priority && ( + + )} + {displayProperties?.state && ( + + )} + {displayProperties?.start_date && issue.start_date && ( + + )} + {displayProperties?.due_date && issue.target_date && ( + + )} + {displayProperties?.labels && ( + + )} + {displayProperties?.assignee && ( + + )} + {displayProperties?.estimate && issue.estimate_point !== null && ( + + )} + {displayProperties?.sub_issue_count && issue.sub_issues_count > 0 && ( +
+ +
+ + {issue.sub_issues_count} +
+
+
+ )} + {displayProperties?.link && issue.link_count > 0 && ( +
+ +
+ + {issue.link_count} +
+
+
+ )} + {displayProperties?.attachment_count && issue.attachment_count > 0 && ( +
+ +
+ + {issue.attachment_count} +
+
+
+ )} + {/* {type && !isNotAllowed && ( + + +
+ + Edit issue +
+
+ {type !== "issue" && removeIssue && ( + +
+ + Remove from {type} +
+
+ )} + handleDeleteIssue(issue)}> +
+ + Delete issue +
+
+ +
+ + Copy issue link +
+
+
+ )} */} +
+
+ +
+ ); +}; diff --git a/web/components/issue-layouts/list/list.tsx b/web/components/issue-layouts/list/list.tsx new file mode 100644 index 000000000..8ece7b1c8 --- /dev/null +++ b/web/components/issue-layouts/list/list.tsx @@ -0,0 +1,18 @@ +import React, { FC } from "react"; +import { IIssue } from "types"; +import { IssueListItem } from "./item"; + +export interface IIssueListView { + issues: IIssue[]; +} + +export const IssueListView: FC = (props) => { + const { issues = [] } = props; + return ( +
+ {issues.map((issue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issue-layouts/list/root.tsx b/web/components/issue-layouts/list/root.tsx new file mode 100644 index 000000000..6b136cf40 --- /dev/null +++ b/web/components/issue-layouts/list/root.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { Disclosure } from "@headlessui/react"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; +// components +import { IssueListView } from "./list"; +import { IssueListGroupHeader } from "./group-header"; + +export const IssueListViewRoot = observer(() => { + const { issueView: issueViewStore, issueFilters: issueFilterStore }: RootStore = useMobxStore(); + console.log("issueViewStore", issueViewStore); + console.log("userFilters", issueFilterStore.userFilters); + console.log("issueFilterStore", issueFilterStore); + + return ( +
+ {issueViewStore.loader || issueViewStore?.getIssues === null ? ( +
Loading...
+ ) : ( + <> + {Object.keys(issueViewStore?.getIssues).map((groupId) => ( + + {({ open }) => ( + <> + + + + + + + + )} + + ))} + + )} +
+ ); +}); diff --git a/web/components/issue-layouts/root.tsx b/web/components/issue-layouts/root.tsx index 897c47121..92cbf5493 100644 --- a/web/components/issue-layouts/root.tsx +++ b/web/components/issue-layouts/root.tsx @@ -7,7 +7,7 @@ import { DisplayFiltersSelection } from "./display-filters"; import { FilterPreview } from "./filters-preview"; -import { IssueListViewRoot } from "./list"; +import { IssueListViewRoot } from "./list/root"; import { IssueKanBanViewRoot } from "./kanban"; import { IssueCalendarViewRoot } from "./calendar"; import { IssueSpreadsheetViewRoot } from "./spreadsheet"; @@ -19,8 +19,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; export const IssuesRoot = observer(() => { - const store: RootStore = useMobxStore(); - const { issueFilters: issueFilterStore } = store; + const { issueFilters: issueFilterStore }: RootStore = useMobxStore(); return (
diff --git a/web/store/issue-views/issue_data.ts b/web/store/issue-views/issue_data.ts index 7c870e5b5..d6d12743a 100644 --- a/web/store/issue-views/issue_data.ts +++ b/web/store/issue-views/issue_data.ts @@ -45,7 +45,7 @@ export const displayPropertyGroupBy: { key: string; title: string }[] = [ { key: "state", title: "States" }, { key: "state_detail.group", title: "State Groups" }, { key: "priority", title: "Priority" }, - { key: "Project", title: "Project" }, // required this on my issues + { key: "project", title: "Project" }, // required this on my issues { key: "labels", title: "Labels" }, { key: "assignees", title: "Assignees" }, { key: "created_by", title: "Created By" }, diff --git a/web/store/issue-views/issue_filters.ts b/web/store/issue-views/issue_filters.ts index dda2f0dac..c0f5a2552 100644 --- a/web/store/issue-views/issue_filters.ts +++ b/web/store/issue-views/issue_filters.ts @@ -21,6 +21,7 @@ import { displayProperties, extraProperties, } from "./issue_data"; +import { IIssueState } from "./Issues"; export type TIssueViews = "my_issues" | "issues" | "modules" | "views" | "cycles"; export type TIssueLayouts = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt_chart"; @@ -211,6 +212,8 @@ export interface IIssueFilterStore { viewId: string, data: any ) => Promise; + + getProjectStateById: (state_id: string) => any; } class IssueFilterStore implements IIssueFilterStore { @@ -281,6 +284,9 @@ class IssueFilterStore implements IIssueFilterStore { userFilters: computed, // actions + + getProjectStateById: action, + getComputedFilters: action, handleUserFilter: action, @@ -345,6 +351,23 @@ class IssueFilterStore implements IIssueFilterStore { this.projectId ]?.states; } + + getProjectStateById = (stateId: string) => { + if (!this.workspaceId || !this.projectId) return null; + const states = + this.issueRenderFilters?.workspace_properties?.[this.workspaceId]?.project_properties?.[ + this.projectId + ]?.states; + + let stateInfo: any = null; + Object.keys(states).forEach((stateGroupName) => { + if (states[stateGroupName].find((state: any) => state.id === stateId)) { + stateInfo = states[stateGroupName].find((state: any) => state.id === stateId); + } + }); + return stateInfo; + }; + get projectLabels() { if (!this.workspaceId || !this.projectId) return null; return this.issueRenderFilters?.workspace_properties?.[this.workspaceId]?.project_properties?.[ diff --git a/web/store/theme.ts b/web/store/theme.ts index ecd02ec97..c4b52c6e5 100644 --- a/web/store/theme.ts +++ b/web/store/theme.ts @@ -40,7 +40,7 @@ class ThemeStore { setTheme = async (_theme: { theme: ICurrentUserSettings }) => { try { - const currentTheme: string = _theme.theme.theme.toString(); + const currentTheme: string = _theme?.theme?.theme?.toString(); // updating the local storage theme value localStorage.setItem("theme", currentTheme);