refactor: layout roots (#2517)

This commit is contained in:
Aaryan Khandelwal 2023-10-23 15:06:28 +05:30 committed by GitHub
parent 05a76c5ee3
commit 38421e8106
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 205 additions and 269 deletions

View File

@ -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>
); );
}); });

View File

@ -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>
</> </>
); );
}; });

View File

@ -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> </>
); );
}); });

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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>
); );