diff --git a/apps/app/components/ui/tooltip.tsx b/apps/app/components/ui/tooltip.tsx
index 11504facd..86ca39e54 100644
--- a/apps/app/components/ui/tooltip.tsx
+++ b/apps/app/components/ui/tooltip.tsx
@@ -42,7 +42,7 @@ export const Tooltip: React.FC
= ({
disabled={disabled}
content={
diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts
index 5625b710d..7e77e6dc2 100644
--- a/apps/app/constants/fetch-keys.ts
+++ b/apps/app/constants/fetch-keys.ts
@@ -1,7 +1,7 @@
import { IAnalyticsParams, IJiraMetadata } from "types";
const paramsToKey = (params: any) => {
- const { state, priority, assignees, created_by, labels, target_date } = params;
+ const { state, priority, assignees, created_by, labels, target_date, sub_issue } = params;
let stateKey = state ? state.split(",") : [];
let priorityKey = priority ? priority.split(",") : [];
@@ -12,6 +12,7 @@ const paramsToKey = (params: any) => {
const type = params.type ? params.type.toUpperCase() : "NULL";
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
const orderBy = params.order_by ? params.order_by.toUpperCase() : "NULL";
+ const subIssue = sub_issue ? sub_issue.toUpperCase() : "NULL";
// sorting each keys in ascending order
stateKey = stateKey.sort().join("_");
@@ -20,7 +21,7 @@ const paramsToKey = (params: any) => {
createdByKey = createdByKey.sort().join("_");
labelsKey = labelsKey.sort().join("_");
- return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}`;
+ return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}_${subIssue}`;
};
const inboxParamsToKey = (params: any) => {
diff --git a/apps/app/constants/spreadsheet.ts b/apps/app/constants/spreadsheet.ts
new file mode 100644
index 000000000..5ef60f40d
--- /dev/null
+++ b/apps/app/constants/spreadsheet.ts
@@ -0,0 +1,60 @@
+import {
+ CalendarDaysIcon,
+ PlayIcon,
+ Squares2X2Icon,
+ TagIcon,
+ UserGroupIcon,
+} from "@heroicons/react/24/outline";
+
+export const SPREADSHEET_COLUMN = [
+ {
+ propertyName: "title",
+ colName: "Title",
+ colSize: "440px",
+ },
+ {
+ propertyName: "state",
+ colName: "State",
+ colSize: "128px",
+ icon: Squares2X2Icon,
+ ascendingOrder: "state__name",
+ descendingOrder: "-state__name",
+ },
+ {
+ propertyName: "priority",
+ colName: "Priority",
+ colSize: "128px",
+ },
+ {
+ propertyName: "assignee",
+ colName: "Assignees",
+ colSize: "128px",
+ icon: UserGroupIcon,
+ ascendingOrder: "assignees__name",
+ descendingOrder: "-assignees__name",
+ },
+ {
+ propertyName: "labels",
+ colName: "Labels",
+ colSize: "128px",
+ icon: TagIcon,
+ ascendingOrder: "labels__name",
+ descendingOrder: "-labels__name",
+ },
+ {
+ propertyName: "due_date",
+ colName: "Due Date",
+ colSize: "128px",
+ icon: CalendarDaysIcon,
+ ascendingOrder: "target_date",
+ descendingOrder: "-target_date",
+ },
+ {
+ propertyName: "estimate",
+ colName: "Estimate",
+ colSize: "128px",
+ icon: PlayIcon,
+ ascendingOrder: "estimate_point",
+ descendingOrder: "-estimate_point",
+ },
+];
diff --git a/apps/app/hooks/use-spreadsheet-issues-view.tsx b/apps/app/hooks/use-spreadsheet-issues-view.tsx
new file mode 100644
index 000000000..6e7b66bec
--- /dev/null
+++ b/apps/app/hooks/use-spreadsheet-issues-view.tsx
@@ -0,0 +1,125 @@
+import { useContext } from "react";
+
+import { useRouter } from "next/router";
+
+import useSWR from "swr";
+
+// contexts
+import { issueViewContext } from "contexts/issue-view.context";
+// services
+import issuesService from "services/issues.service";
+import cyclesService from "services/cycles.service";
+import modulesService from "services/modules.service";
+// types
+import { IIssue } from "types";
+// fetch-keys
+import {
+ CYCLE_ISSUES_WITH_PARAMS,
+ MODULE_ISSUES_WITH_PARAMS,
+ PROJECT_ISSUES_LIST_WITH_PARAMS,
+ VIEW_ISSUES,
+} from "constants/fetch-keys";
+
+const useSpreadsheetIssuesView = () => {
+ const {
+ issueView,
+ orderBy,
+ setOrderBy,
+ filters,
+ setFilters,
+ resetFilterToDefault,
+ setNewFilterDefaultView,
+ setIssueView,
+ } = useContext(issueViewContext);
+
+ const router = useRouter();
+ const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
+
+ const params: any = {
+ order_by: orderBy,
+ assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
+ state: filters?.state ? filters?.state.join(",") : undefined,
+ priority: filters?.priority ? filters?.priority.join(",") : undefined,
+ type: filters?.type ? filters?.type : undefined,
+ labels: filters?.labels ? filters?.labels.join(",") : undefined,
+ issue__assignees__id: filters?.issue__assignees__id
+ ? filters?.issue__assignees__id.join(",")
+ : undefined,
+ issue__labels__id: filters?.issue__labels__id
+ ? filters?.issue__labels__id.join(",")
+ : undefined,
+ created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
+ sub_issue: "false",
+ };
+
+ const { data: projectSpreadsheetIssues } = useSWR(
+ workspaceSlug && projectId
+ ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params)
+ : null,
+ workspaceSlug && projectId
+ ? () =>
+ issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
+ : null
+ );
+
+ const { data: cycleSpreadsheetIssues } = useSWR(
+ workspaceSlug && projectId && cycleId
+ ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
+ : null,
+ workspaceSlug && projectId && cycleId
+ ? () =>
+ cyclesService.getCycleIssuesWithParams(
+ workspaceSlug.toString(),
+ projectId.toString(),
+ cycleId.toString(),
+ params
+ )
+ : null
+ );
+
+ const { data: moduleSpreadsheetIssues } = useSWR(
+ workspaceSlug && projectId && moduleId
+ ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
+ : null,
+ workspaceSlug && projectId && moduleId
+ ? () =>
+ modulesService.getModuleIssuesWithParams(
+ workspaceSlug.toString(),
+ projectId.toString(),
+ moduleId.toString(),
+ params
+ )
+ : null
+ );
+
+ const { data: viewSpreadsheetIssues } = useSWR(
+ workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
+ workspaceSlug && projectId && viewId && params
+ ? () =>
+ issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
+ : null
+ );
+
+ const spreadsheetIssues = cycleId
+ ? (cycleSpreadsheetIssues as IIssue[])
+ : moduleId
+ ? (moduleSpreadsheetIssues as IIssue[])
+ : viewId
+ ? (viewSpreadsheetIssues as IIssue[])
+ : (projectSpreadsheetIssues as IIssue[]);
+
+ return {
+ issueView,
+ spreadsheetIssues: spreadsheetIssues ?? [],
+ orderBy,
+ setOrderBy,
+ filters,
+ setFilters,
+ params,
+ resetFilterToDefault,
+ setNewFilterDefaultView,
+ setIssueView,
+ } as const;
+};
+
+export default useSpreadsheetIssuesView;
diff --git a/apps/app/hooks/use-sub-issue.tsx b/apps/app/hooks/use-sub-issue.tsx
new file mode 100644
index 000000000..8eb30fd0b
--- /dev/null
+++ b/apps/app/hooks/use-sub-issue.tsx
@@ -0,0 +1,34 @@
+import { useEffect, useState } from "react";
+
+import { useRouter } from "next/router";
+
+import useSWR from "swr";
+
+// services
+import issuesService from "services/issues.service";
+// types
+import { ISubIssueResponse } from "types";
+// fetch-keys
+import { SUB_ISSUES } from "constants/fetch-keys";
+
+const useSubIssue = (issueId: string, isExpanded: boolean) => {
+ const router = useRouter();
+ const { workspaceSlug, projectId } = router.query;
+
+ const shouldFetch = workspaceSlug && projectId && issueId && isExpanded;
+
+ const { data: subIssuesResponse, isLoading } = useSWR(
+ shouldFetch ? SUB_ISSUES(issueId as string) : null,
+ shouldFetch
+ ? () =>
+ issuesService.subIssues(workspaceSlug as string, projectId as string, issueId as string)
+ : null
+ );
+
+ return {
+ subIssues: subIssuesResponse?.sub_issues ?? [],
+ isLoading,
+ };
+};
+
+export default useSubIssue;
diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts
index a8881924b..a33a04ffc 100644
--- a/apps/app/types/issues.d.ts
+++ b/apps/app/types/issues.d.ts
@@ -247,11 +247,25 @@ export interface IIssueFilterOptions {
created_by: string[] | null;
}
-export type TIssueViewOptions = "list" | "kanban" | "calendar" | "gantt_chart";
+export type TIssueViewOptions = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt_chart";
export type TIssueGroupByOptions = "state" | "priority" | "labels" | "created_by" | null;
-export type TIssueOrderByOptions = "-created_at" | "-updated_at" | "priority" | "sort_order";
+export type TIssueOrderByOptions =
+ | "-created_at"
+ | "-updated_at"
+ | "priority"
+ | "sort_order"
+ | "state__name"
+ | "-state__name"
+ | "assignees__name"
+ | "-assignees__name"
+ | "labels__name"
+ | "-labels__name"
+ | "target_date"
+ | "-target_date"
+ | "estimate__point"
+ | "-estimate__point";
export interface IIssueViewOptions {
group_by: TIssueGroupByOptions;