mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
refactor: layout roots (#2517)
This commit is contained in:
parent
05a76c5ee3
commit
38421e8106
@ -1,11 +1,15 @@
|
|||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
|
// ui
|
||||||
|
import { BreadcrumbItem, Breadcrumbs, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
||||||
|
// helpers
|
||||||
|
import { truncateText } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -19,6 +23,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
issueFilter: issueFilterStore,
|
issueFilter: issueFilterStore,
|
||||||
projectViewFilters: projectViewFiltersStore,
|
projectViewFilters: projectViewFiltersStore,
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
|
projectViews: projectViewsStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
|
const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
|
||||||
@ -82,32 +87,78 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
[issueFilterStore, projectId, workspaceSlug]
|
[issueFilterStore, projectId, workspaceSlug]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const projectDetails =
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const viewsList = projectId ? projectViewsStore.viewsList[projectId.toString()] : undefined;
|
||||||
|
const viewDetails = viewId ? projectViewsStore.viewDetails[viewId.toString()] : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="relative w-full flex items-center z-10 justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<LayoutSelection
|
<div className="flex items-center gap-2">
|
||||||
layouts={["list", "kanban", "calendar", "spreadsheet", "gantt_chart"]}
|
<Breadcrumbs onBack={() => router.back()}>
|
||||||
onChange={(layout) => handleLayoutChange(layout)}
|
<BreadcrumbItem
|
||||||
selectedLayout={activeLayout}
|
link={
|
||||||
/>
|
<Link href={`/${workspaceSlug}/projects/${projectDetails?.id}/cycles`}>
|
||||||
<FiltersDropdown title="Filters">
|
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
||||||
<FilterSelection
|
<p className="truncate">{`${projectDetails?.name ?? "Project"} Views`}</p>
|
||||||
filters={storedFilters ?? {}}
|
</a>
|
||||||
handleFiltersUpdate={handleFiltersUpdate}
|
</Link>
|
||||||
layoutDisplayFiltersOptions={activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined}
|
}
|
||||||
labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? undefined}
|
/>
|
||||||
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
</Breadcrumbs>
|
||||||
states={projectStore.states?.[projectId?.toString() ?? ""] ?? undefined}
|
<CustomMenu
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<PhotoFilterIcon height={12} width={12} />
|
||||||
|
{viewDetails?.name && truncateText(viewDetails.name, 40)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
className="ml-1.5"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
{viewsList?.map((view) => (
|
||||||
|
<CustomMenu.MenuItem
|
||||||
|
key={view.id}
|
||||||
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/views/${view.id}`)}
|
||||||
|
>
|
||||||
|
{truncateText(view.name, 40)}
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
))}
|
||||||
|
</CustomMenu>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<LayoutSelection
|
||||||
|
layouts={["list", "kanban", "calendar", "spreadsheet", "gantt_chart"]}
|
||||||
|
onChange={(layout) => handleLayoutChange(layout)}
|
||||||
|
selectedLayout={activeLayout}
|
||||||
/>
|
/>
|
||||||
</FiltersDropdown>
|
<FiltersDropdown title="Filters">
|
||||||
<FiltersDropdown title="Display">
|
<FilterSelection
|
||||||
<DisplayFiltersSelection
|
filters={storedFilters ?? {}}
|
||||||
displayFilters={issueFilterStore.userDisplayFilters}
|
handleFiltersUpdate={handleFiltersUpdate}
|
||||||
displayProperties={issueFilterStore.userDisplayProperties}
|
layoutDisplayFiltersOptions={
|
||||||
handleDisplayFiltersUpdate={handleDisplayFiltersUpdate}
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||||
handleDisplayPropertiesUpdate={handleDisplayPropertiesUpdate}
|
}
|
||||||
layoutDisplayFiltersOptions={activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined}
|
labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? undefined}
|
||||||
/>
|
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
||||||
</FiltersDropdown>
|
states={projectStore.states?.[projectId?.toString() ?? ""] ?? undefined}
|
||||||
|
/>
|
||||||
|
</FiltersDropdown>
|
||||||
|
<FiltersDropdown title="Display">
|
||||||
|
<DisplayFiltersSelection
|
||||||
|
displayFilters={issueFilterStore.userDisplayFilters}
|
||||||
|
displayProperties={issueFilterStore.userDisplayProperties}
|
||||||
|
handleDisplayFiltersUpdate={handleDisplayFiltersUpdate}
|
||||||
|
handleDisplayPropertiesUpdate={handleDisplayPropertiesUpdate}
|
||||||
|
layoutDisplayFiltersOptions={
|
||||||
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FiltersDropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { FC, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// icons
|
import Link from "next/link";
|
||||||
import { ArrowLeft, Link, Plus } from "lucide-react";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { ArrowLeft, Plus } from "lucide-react";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateProjectViewModal } from "components/views";
|
import { CreateUpdateProjectViewModal } from "components/views";
|
||||||
// components
|
// components
|
||||||
@ -11,18 +14,20 @@ import { PrimaryButton } from "components/ui";
|
|||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
|
|
||||||
interface IProjectViewsHeader {
|
export const ProjectViewsHeader: React.FC = observer(() => {
|
||||||
title: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
|
|
||||||
const { title } = props;
|
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// states
|
// states
|
||||||
const [createViewModal, setCreateViewModal] = useState(false);
|
const [createViewModal, setCreateViewModal] = useState(false);
|
||||||
|
|
||||||
|
const { project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
|
const projectDetails =
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{workspaceSlug && projectId && (
|
{workspaceSlug && projectId && (
|
||||||
@ -43,7 +48,7 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
|
|||||||
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
|
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
>
|
>
|
||||||
<ArrowLeft fontSize={14} strokeWidth={2} />
|
<ArrowLeft className="h-3 w-3" strokeWidth={2} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -57,20 +62,13 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(title ?? "Project", 32)} Cycles`} />
|
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Views`} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
<div>
|
<div>
|
||||||
<PrimaryButton
|
<PrimaryButton type="button" className="flex items-center gap-2" onClick={() => setCreateViewModal(true)}>
|
||||||
type="button"
|
|
||||||
className="flex items-center gap-2"
|
|
||||||
onClick={() => {
|
|
||||||
const e = new KeyboardEvent("keydown", { key: "v" });
|
|
||||||
document.dispatchEvent(e);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Plus size={14} strokeWidth={2} />
|
<Plus size={14} strokeWidth={2} />
|
||||||
Create View
|
Create View
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
@ -79,4 +77,4 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
// next imports
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// swr
|
|
||||||
import useSWR from "swr";
|
|
||||||
// mobx react lite
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import useSWR from "swr";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
CycleAppliedFiltersRoot,
|
CycleAppliedFiltersRoot,
|
||||||
@ -14,59 +13,63 @@ import {
|
|||||||
CycleListLayout,
|
CycleListLayout,
|
||||||
CycleSpreadsheetLayout,
|
CycleSpreadsheetLayout,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
// mobx store
|
import { TransferIssues, TransferIssuesModal } from "components/cycles";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
// helpers
|
||||||
|
import { getDateRangeStatus } from "helpers/date-time.helper";
|
||||||
|
|
||||||
export const CycleLayoutRoot: React.FC = observer(() => {
|
export const CycleLayoutRoot: React.FC = observer(() => {
|
||||||
|
const [transferIssuesModal, setTransferIssuesModal] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
workspaceSlug: string;
|
|
||||||
projectId: string;
|
|
||||||
cycleId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: projectStore,
|
|
||||||
issueFilter: issueFilterStore,
|
issueFilter: issueFilterStore,
|
||||||
|
cycle: cycleStore,
|
||||||
cycleIssue: cycleIssueStore,
|
cycleIssue: cycleIssueStore,
|
||||||
cycleIssueFilter: cycleIssueFilterStore,
|
cycleIssueFilter: cycleIssueFilterStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId && cycleId ? `CYCLE_ISSUES` : null, async () => {
|
useSWR(workspaceSlug && projectId && cycleId ? `CYCLE_FILTERS_AND_ISSUES_${cycleId.toString()}` : null, async () => {
|
||||||
if (workspaceSlug && projectId && cycleId) {
|
if (workspaceSlug && projectId && cycleId) {
|
||||||
// fetching the project display filters and display properties
|
// fetching the project display filters and display properties
|
||||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
|
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||||
// fetching the cycle filters
|
// fetching the cycle filters
|
||||||
await cycleIssueFilterStore.fetchCycleFilters(workspaceSlug, projectId, cycleId);
|
await cycleIssueFilterStore.fetchCycleFilters(workspaceSlug.toString(), projectId.toString(), cycleId.toString());
|
||||||
|
|
||||||
// fetching the project state, labels and members
|
|
||||||
await projectStore.fetchProjectStates(workspaceSlug, projectId);
|
|
||||||
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
|
|
||||||
await projectStore.fetchProjectMembers(workspaceSlug, projectId);
|
|
||||||
|
|
||||||
// fetching the cycle issues
|
// fetching the cycle issues
|
||||||
await cycleIssueStore.fetchIssues(workspaceSlug, projectId, cycleId);
|
await cycleIssueStore.fetchIssues(workspaceSlug.toString(), projectId.toString(), cycleId.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
|
||||||
|
const cycleDetails = cycleId ? cycleStore.cycle_details[cycleId.toString()] : undefined;
|
||||||
|
const cycleStatus =
|
||||||
|
cycleDetails?.start_date && cycleDetails?.end_date
|
||||||
|
? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date)
|
||||||
|
: "draft";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full flex flex-col overflow-hidden">
|
<>
|
||||||
<CycleAppliedFiltersRoot />
|
<TransferIssuesModal handleClose={() => setTransferIssuesModal(false)} isOpen={transferIssuesModal} />
|
||||||
<div className="w-full h-full overflow-auto">
|
<div className="relative w-full h-full flex flex-col overflow-hidden">
|
||||||
{activeLayout === "list" ? (
|
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
|
||||||
<CycleListLayout />
|
<CycleAppliedFiltersRoot />
|
||||||
) : activeLayout === "kanban" ? (
|
<div className="w-full h-full overflow-auto">
|
||||||
<CycleKanBanLayout />
|
{activeLayout === "list" ? (
|
||||||
) : activeLayout === "calendar" ? (
|
<CycleListLayout />
|
||||||
<CycleCalendarLayout />
|
) : activeLayout === "kanban" ? (
|
||||||
) : activeLayout === "gantt_chart" ? (
|
<CycleKanBanLayout />
|
||||||
<CycleGanttLayout />
|
) : activeLayout === "calendar" ? (
|
||||||
) : activeLayout === "spreadsheet" ? (
|
<CycleCalendarLayout />
|
||||||
<CycleSpreadsheetLayout />
|
) : activeLayout === "gantt_chart" ? (
|
||||||
) : null}
|
<CycleGanttLayout />
|
||||||
|
) : activeLayout === "spreadsheet" ? (
|
||||||
|
<CycleSpreadsheetLayout />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2,15 +2,12 @@ import React, { useCallback } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { GlobalViewsAppliedFiltersRoot, SpreadsheetView } from "components/issues";
|
import { GlobalViewsAppliedFiltersRoot, SpreadsheetView } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayFilterOptions, TStaticViewTypes } from "types";
|
import { IIssue, IIssueDisplayFilterOptions, TStaticViewTypes } from "types";
|
||||||
// fetch-keys
|
|
||||||
import { GLOBAL_VIEW_ISSUES } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type?: TStaticViewTypes;
|
type?: TStaticViewTypes;
|
||||||
@ -35,7 +32,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
|
|||||||
const storedFilters = globalViewId ? globalViewFiltersStore.storedFilters[globalViewId.toString()] : undefined;
|
const storedFilters = globalViewId ? globalViewFiltersStore.storedFilters[globalViewId.toString()] : undefined;
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && globalViewId && viewDetails ? GLOBAL_VIEW_ISSUES(globalViewId.toString()) : null,
|
workspaceSlug && globalViewId && viewDetails ? `GLOBAL_VIEW_ISSUES_${globalViewId.toString()}` : null,
|
||||||
workspaceSlug && globalViewId && viewDetails
|
workspaceSlug && globalViewId && viewDetails
|
||||||
? () => {
|
? () => {
|
||||||
globalViewIssuesStore.fetchViewIssues(workspaceSlug.toString(), globalViewId.toString(), storedFilters ?? {});
|
globalViewIssuesStore.fetchViewIssues(workspaceSlug.toString(), globalViewId.toString(), storedFilters ?? {});
|
||||||
@ -44,7 +41,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && type ? GLOBAL_VIEW_ISSUES(type) : null,
|
workspaceSlug && type ? `GLOBAL_VIEW_ISSUES_${type.toString()}` : null,
|
||||||
workspaceSlug && type
|
workspaceSlug && type
|
||||||
? () => {
|
? () => {
|
||||||
globalViewIssuesStore.fetchStaticIssues(workspaceSlug.toString(), type);
|
globalViewIssuesStore.fetchStaticIssues(workspaceSlug.toString(), type);
|
||||||
|
@ -24,28 +24,25 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: projectStore,
|
|
||||||
issueFilter: issueFilterStore,
|
issueFilter: issueFilterStore,
|
||||||
moduleIssue: moduleIssueStore,
|
moduleIssue: moduleIssueStore,
|
||||||
moduleFilter: moduleIssueFilterStore,
|
moduleFilter: moduleIssueFilterStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId && moduleId ? `MODULE_INFORMATION_${moduleId.toString()}` : null, async () => {
|
useSWR(
|
||||||
if (workspaceSlug && projectId && moduleId) {
|
workspaceSlug && projectId && moduleId ? `MODULE_FILTERS_AND_ISSUES_${moduleId.toString()}` : null,
|
||||||
// fetching the project display filters and display properties
|
async () => {
|
||||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
|
if (workspaceSlug && projectId && moduleId) {
|
||||||
// fetching the module filters
|
// fetching the project display filters and display properties
|
||||||
await moduleIssueFilterStore.fetchModuleFilters(workspaceSlug, projectId, moduleId);
|
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
|
||||||
|
// fetching the module filters
|
||||||
|
await moduleIssueFilterStore.fetchModuleFilters(workspaceSlug, projectId, moduleId);
|
||||||
|
|
||||||
// fetching the project state, labels and members
|
// fetching the module issues
|
||||||
await projectStore.fetchProjectStates(workspaceSlug, projectId);
|
await moduleIssueStore.fetchIssues(workspaceSlug, projectId, moduleId);
|
||||||
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
|
}
|
||||||
await projectStore.fetchProjectMembers(workspaceSlug, projectId);
|
|
||||||
|
|
||||||
// fetching the module issues
|
|
||||||
await moduleIssueStore.fetchIssues(workspaceSlug, projectId, moduleId);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
|
||||||
|
@ -18,24 +18,15 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore } = useMobxStore();
|
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||||
|
|
||||||
useSWR(
|
useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
||||||
workspaceSlug && projectId ? `REVALIDATE_PROJECT_ISSUES_${projectId.toString()}` : null,
|
if (workspaceSlug && projectId) {
|
||||||
async () => {
|
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||||
if (workspaceSlug && projectId) {
|
|
||||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
|
||||||
|
|
||||||
await projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString());
|
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
||||||
await projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString());
|
}
|
||||||
await projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString());
|
});
|
||||||
await projectStore.fetchProjectEstimates(workspaceSlug.toString(), projectId.toString());
|
|
||||||
|
|
||||||
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ revalidateOnFocus: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
|
||||||
|
@ -20,34 +20,31 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
const { workspaceSlug, projectId, viewId } = router.query;
|
const { workspaceSlug, projectId, viewId } = router.query;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: projectStore,
|
|
||||||
issueFilter: issueFilterStore,
|
issueFilter: issueFilterStore,
|
||||||
projectViews: projectViewsStore,
|
projectViews: projectViewsStore,
|
||||||
projectViewIssues: projectViewIssuesStore,
|
projectViewIssues: projectViewIssuesStore,
|
||||||
projectViewFilters: projectViewFiltersStore,
|
projectViewFilters: projectViewFiltersStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId && viewId ? `PROJECT_VIEW_INFORMATION_${viewId.toString()}` : null, async () => {
|
useSWR(
|
||||||
if (workspaceSlug && projectId && viewId) {
|
workspaceSlug && projectId && viewId ? `PROJECT_VIEW_FILTERS_AND_ISSUES_${viewId.toString()}` : null,
|
||||||
// fetching the project display filters and display properties
|
async () => {
|
||||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
if (workspaceSlug && projectId && viewId) {
|
||||||
|
// fetching the project display filters and display properties
|
||||||
|
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||||
|
|
||||||
// fetching the project state, labels and members
|
// fetching the view details
|
||||||
await projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString());
|
await projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString());
|
||||||
await projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString());
|
// fetching the view issues
|
||||||
await projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString());
|
await projectViewIssuesStore.fetchViewIssues(
|
||||||
|
workspaceSlug.toString(),
|
||||||
// fetching the view details
|
projectId.toString(),
|
||||||
await projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString());
|
viewId.toString(),
|
||||||
// fetching the view issues
|
projectViewFiltersStore.storedFilters[viewId.toString()] ?? {}
|
||||||
await projectViewIssuesStore.fetchViewIssues(
|
);
|
||||||
workspaceSlug.toString(),
|
}
|
||||||
projectId.toString(),
|
|
||||||
viewId.toString(),
|
|
||||||
projectViewFiltersStore.storedFilters[viewId.toString()] ?? {}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||||||
? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString())
|
? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString())
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
// TODO: fetch project estimates
|
||||||
// fetching project cycles
|
// fetching project cycles
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null,
|
workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null,
|
||||||
|
@ -10,18 +10,15 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
// components
|
// components
|
||||||
import { CycleIssuesHeader } from "components/headers";
|
import { CycleIssuesHeader } from "components/headers";
|
||||||
import { ExistingIssuesListModal } from "components/core";
|
import { ExistingIssuesListModal } from "components/core";
|
||||||
import { CycleDetailsSidebar, TransferIssues, TransferIssuesModal } from "components/cycles";
|
import { CycleDetailsSidebar } from "components/cycles";
|
||||||
import { CycleLayoutRoot } from "components/issues/issue-layouts";
|
import { CycleLayoutRoot } from "components/issues/issue-layouts";
|
||||||
// ui
|
// ui
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// assets
|
// assets
|
||||||
import emptyCycle from "public/empty-state/cycle.svg";
|
import emptyCycle from "public/empty-state/cycle.svg";
|
||||||
// helpers
|
|
||||||
import { getDateRangeStatus } from "helpers/date-time.helper";
|
|
||||||
|
|
||||||
const SingleCycle: React.FC = () => {
|
const SingleCycle: React.FC = () => {
|
||||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||||
const [transferIssuesModal, setTransferIssuesModal] = useState(false);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
@ -31,18 +28,13 @@ const SingleCycle: React.FC = () => {
|
|||||||
const { storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
const { storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
||||||
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
|
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
|
||||||
|
|
||||||
const { data: cycleDetails, error } = useSWR(
|
const { error } = useSWR(
|
||||||
workspaceSlug && projectId && cycleId ? `CURRENT_CYCLE_DETAILS_${cycleId.toString()}` : null,
|
workspaceSlug && projectId && cycleId ? `CURRENT_CYCLE_DETAILS_${cycleId.toString()}` : null,
|
||||||
workspaceSlug && projectId && cycleId
|
workspaceSlug && projectId && cycleId
|
||||||
? () => cycleStore.fetchCycleWithId(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
? () => cycleStore.fetchCycleWithId(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
const cycleStatus =
|
|
||||||
cycleDetails?.start_date && cycleDetails?.end_date
|
|
||||||
? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date)
|
|
||||||
: "draft";
|
|
||||||
|
|
||||||
// TODO: add this function to bulk add issues to cycle
|
// TODO: add this function to bulk add issues to cycle
|
||||||
// const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => {
|
// const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => {
|
||||||
// if (!workspaceSlug || !projectId) return;
|
// if (!workspaceSlug || !projectId) return;
|
||||||
@ -83,13 +75,9 @@ const SingleCycle: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<TransferIssuesModal handleClose={() => setTransferIssuesModal(false)} isOpen={transferIssuesModal} />
|
|
||||||
<div className="relative w-full h-full flex overflow-auto">
|
<div className="relative w-full h-full flex overflow-auto">
|
||||||
<div className={`flex flex-col h-full w-full ${isSidebarCollapsed ? "" : "mr-[24rem]"} duration-300`}>
|
<div className={`h-full w-full ${isSidebarCollapsed ? "" : "mr-[24rem]"} duration-300`}>
|
||||||
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
|
<CycleLayoutRoot />
|
||||||
<div className="h-full w-full">
|
|
||||||
<CycleLayoutRoot />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{cycleId && <CycleDetailsSidebar isOpen={!isSidebarCollapsed} cycleId={cycleId.toString()} />}
|
{cycleId && <CycleDetailsSidebar isOpen={!isSidebarCollapsed} cycleId={cycleId.toString()} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import useSWR from "swr";
|
|
||||||
// mobx
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { ProjectLayoutRoot } from "components/issues";
|
import { ProjectLayoutRoot } from "components/issues";
|
||||||
import { ProjectIssuesHeader } from "components/headers";
|
import { ProjectIssuesHeader } from "components/headers";
|
||||||
@ -10,27 +6,12 @@ import type { NextPage } from "next";
|
|||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
|
|
||||||
const ProjectIssues: NextPage = () => {
|
const ProjectIssues: NextPage = () => (
|
||||||
const router = useRouter();
|
<AppLayout header={<ProjectIssuesHeader />} withProjectWrapper>
|
||||||
const { workspaceSlug, projectId } = router.query;
|
<div className="h-full w-full">
|
||||||
|
<ProjectLayoutRoot />
|
||||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
</div>
|
||||||
|
</AppLayout>
|
||||||
// TODO: update the fetch keys
|
);
|
||||||
useSWR(
|
|
||||||
workspaceSlug && projectId ? "REVALIDATE_USER_PROJECT_FILTERS" : null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppLayout header={<ProjectIssuesHeader />} withProjectWrapper>
|
|
||||||
<div className="h-full w-full flex flex-col">
|
|
||||||
<ProjectLayoutRoot />
|
|
||||||
</div>
|
|
||||||
</AppLayout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectIssues;
|
export default ProjectIssues;
|
||||||
|
@ -1,90 +1,34 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
// mobx store
|
||||||
// services
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { ProjectService } from "services/project";
|
|
||||||
import { ViewService } from "services/view.service";
|
|
||||||
// layouts
|
// layouts
|
||||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { ProjectViewLayoutRoot } from "components/issues";
|
import { ProjectViewLayoutRoot } from "components/issues";
|
||||||
// ui
|
|
||||||
import { BreadcrumbItem, Breadcrumbs, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
|
||||||
import { EmptyState } from "components/common";
|
|
||||||
// icons
|
|
||||||
// images
|
|
||||||
import emptyView from "public/empty-state/view.svg";
|
|
||||||
// helpers
|
|
||||||
import { truncateText } from "helpers/string.helper";
|
|
||||||
// fetch-keys
|
|
||||||
import { PROJECT_DETAILS, VIEWS_LIST, VIEW_DETAILS } from "constants/fetch-keys";
|
|
||||||
import { ProjectViewIssuesHeader } from "components/headers";
|
import { ProjectViewIssuesHeader } from "components/headers";
|
||||||
|
// ui
|
||||||
|
import { EmptyState } from "components/common";
|
||||||
|
// assets
|
||||||
|
import emptyView from "public/empty-state/view.svg";
|
||||||
|
// types
|
||||||
|
import { NextPage } from "next";
|
||||||
|
|
||||||
// services
|
const ProjectViewIssues: NextPage = () => {
|
||||||
const projectService = new ProjectService();
|
|
||||||
const viewService = new ViewService();
|
|
||||||
|
|
||||||
const SingleView: React.FC = () => {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, viewId } = router.query;
|
const { workspaceSlug, projectId, viewId } = router.query;
|
||||||
|
|
||||||
const { data: activeProject } = useSWR(
|
const { projectViews: projectViewsStore } = useMobxStore();
|
||||||
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
|
|
||||||
workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: views } = useSWR(
|
const { error } = useSWR(
|
||||||
workspaceSlug && projectId ? VIEWS_LIST(projectId as string) : null,
|
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
|
||||||
workspaceSlug && projectId ? () => viewService.getViews(workspaceSlug as string, projectId as string) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: viewDetails, error } = useSWR(
|
|
||||||
workspaceSlug && projectId && viewId ? VIEW_DETAILS(viewId as string) : null,
|
|
||||||
workspaceSlug && projectId && viewId
|
workspaceSlug && projectId && viewId
|
||||||
? () => viewService.getViewDetails(workspaceSlug as string, projectId as string, viewId as string)
|
? () => projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString())
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProjectAuthorizationWrapper
|
<AppLayout header={<ProjectViewIssuesHeader />} withProjectWrapper>
|
||||||
breadcrumbs={
|
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
|
||||||
<BreadcrumbItem
|
|
||||||
link={
|
|
||||||
<Link href={`/${workspaceSlug}/projects/${activeProject?.id}/cycles`}>
|
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
|
||||||
<p className="truncate">{`${activeProject?.name ?? "Project"} Views`}</p>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Breadcrumbs>
|
|
||||||
}
|
|
||||||
left={
|
|
||||||
<CustomMenu
|
|
||||||
label={
|
|
||||||
<>
|
|
||||||
<PhotoFilterIcon height={12} width={12} />
|
|
||||||
{viewDetails?.name && truncateText(viewDetails.name, 40)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
className="ml-1.5"
|
|
||||||
width="auto"
|
|
||||||
>
|
|
||||||
{views?.map((view) => (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={view.id}
|
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/views/${view.id}`)}
|
|
||||||
>
|
|
||||||
{truncateText(view.name, 40)}
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
))}
|
|
||||||
</CustomMenu>
|
|
||||||
}
|
|
||||||
right={<ProjectViewIssuesHeader />}
|
|
||||||
>
|
|
||||||
{error ? (
|
{error ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
image={emptyView}
|
image={emptyView}
|
||||||
@ -98,8 +42,8 @@ const SingleView: React.FC = () => {
|
|||||||
) : (
|
) : (
|
||||||
<ProjectViewLayoutRoot />
|
<ProjectViewLayoutRoot />
|
||||||
)}
|
)}
|
||||||
</ProjectAuthorizationWrapper>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SingleView;
|
export default ProjectViewIssues;
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// hooks
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { ProjectViewsHeader } from "components/headers";
|
import { ProjectViewsHeader } from "components/headers";
|
||||||
@ -14,14 +14,7 @@ const ProjectViews: NextPage = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const { project: projectStore, projectViews: projectViewsStore } = useMobxStore();
|
const { projectViews: projectViewsStore } = useMobxStore();
|
||||||
|
|
||||||
useSWR(
|
|
||||||
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId.toString()}` : null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && projectId ? `PROJECT_VIEWS_LIST_${workspaceSlug.toString()}_${projectId.toString()}` : null,
|
workspaceSlug && projectId ? `PROJECT_VIEWS_LIST_${workspaceSlug.toString()}_${projectId.toString()}` : null,
|
||||||
@ -30,13 +23,8 @@ const ProjectViews: NextPage = () => {
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
const projectDetails =
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<ProjectViewsHeader title={projectDetails?.name} />}>
|
<AppLayout header={<ProjectViewsHeader />}>
|
||||||
<ProjectViewsList />
|
<ProjectViewsList />
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user