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 Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
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
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
// constants
@ -19,6 +23,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
issueFilter: issueFilterStore,
projectViewFilters: projectViewFiltersStore,
project: projectStore,
projectViews: projectViewsStore,
} = useMobxStore();
const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
@ -82,32 +87,78 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
[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 (
<div className="flex items-center gap-2">
<LayoutSelection
layouts={["list", "kanban", "calendar", "spreadsheet", "gantt_chart"]}
onChange={(layout) => handleLayoutChange(layout)}
selectedLayout={activeLayout}
/>
<FiltersDropdown title="Filters">
<FilterSelection
filters={storedFilters ?? {}}
handleFiltersUpdate={handleFiltersUpdate}
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)}
states={projectStore.states?.[projectId?.toString() ?? ""] ?? undefined}
<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">
<div className="flex items-center gap-2">
<Breadcrumbs onBack={() => router.back()}>
<BreadcrumbItem
link={
<Link href={`/${workspaceSlug}/projects/${projectDetails?.id}/cycles`}>
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
<p className="truncate">{`${projectDetails?.name ?? "Project"} Views`}</p>
</a>
</Link>
}
/>
</Breadcrumbs>
<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="Display">
<DisplayFiltersSelection
displayFilters={issueFilterStore.userDisplayFilters}
displayProperties={issueFilterStore.userDisplayProperties}
handleDisplayFiltersUpdate={handleDisplayFiltersUpdate}
handleDisplayPropertiesUpdate={handleDisplayPropertiesUpdate}
layoutDisplayFiltersOptions={activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined}
/>
</FiltersDropdown>
<FiltersDropdown title="Filters">
<FilterSelection
filters={storedFilters ?? {}}
handleFiltersUpdate={handleFiltersUpdate}
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)}
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>
);
});

View File

@ -1,7 +1,10 @@
import { FC, useState } from "react";
import { useState } from "react";
import { useRouter } from "next/router";
// icons
import { ArrowLeft, Link, Plus } from "lucide-react";
import Link from "next/link";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Plus } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { CreateUpdateProjectViewModal } from "components/views";
// components
@ -11,18 +14,20 @@ import { PrimaryButton } from "components/ui";
// helpers
import { truncateText } from "helpers/string.helper";
interface IProjectViewsHeader {
title: string | undefined;
}
export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
const { title } = props;
export const ProjectViewsHeader: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// states
const [createViewModal, setCreateViewModal] = useState(false);
const { project: projectStore } = useMobxStore();
const projectDetails =
workspaceSlug && projectId
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
: undefined;
return (
<>
{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"
onClick={() => router.back()}
>
<ArrowLeft fontSize={14} strokeWidth={2} />
<ArrowLeft className="h-3 w-3" strokeWidth={2} />
</button>
</div>
<div>
@ -57,20 +62,13 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
</Link>
}
/>
<BreadcrumbItem title={`${truncateText(title ?? "Project", 32)} Cycles`} />
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Views`} />
</Breadcrumbs>
</div>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 flex-shrink-0">
<div>
<PrimaryButton
type="button"
className="flex items-center gap-2"
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "v" });
document.dispatchEvent(e);
}}
>
<PrimaryButton type="button" className="flex items-center gap-2" onClick={() => setCreateViewModal(true)}>
<Plus size={14} strokeWidth={2} />
Create View
</PrimaryButton>
@ -79,4 +77,4 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
</div>
</>
);
};
});

View File

@ -1,10 +1,9 @@
import React from "react";
// next imports
import React, { useState } from "react";
import { useRouter } from "next/router";
// swr
import useSWR from "swr";
// mobx react lite
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import {
CycleAppliedFiltersRoot,
@ -14,59 +13,63 @@ import {
CycleListLayout,
CycleSpreadsheetLayout,
} from "components/issues";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { TransferIssues, TransferIssuesModal } from "components/cycles";
// helpers
import { getDateRangeStatus } from "helpers/date-time.helper";
export const CycleLayoutRoot: React.FC = observer(() => {
const [transferIssuesModal, setTransferIssuesModal] = useState(false);
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query as {
workspaceSlug: string;
projectId: string;
cycleId: string;
};
const { workspaceSlug, projectId, cycleId } = router.query;
const {
project: projectStore,
issueFilter: issueFilterStore,
cycle: cycleStore,
cycleIssue: cycleIssueStore,
cycleIssueFilter: cycleIssueFilterStore,
} = 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) {
// fetching the project display filters and display properties
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
// fetching the cycle filters
await cycleIssueFilterStore.fetchCycleFilters(workspaceSlug, projectId, cycleId);
// fetching the project state, labels and members
await projectStore.fetchProjectStates(workspaceSlug, projectId);
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
await projectStore.fetchProjectMembers(workspaceSlug, projectId);
await cycleIssueFilterStore.fetchCycleFilters(workspaceSlug.toString(), projectId.toString(), cycleId.toString());
// 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 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 (
<div className="relative w-full h-full flex flex-col overflow-hidden">
<CycleAppliedFiltersRoot />
<div className="w-full h-full overflow-auto">
{activeLayout === "list" ? (
<CycleListLayout />
) : activeLayout === "kanban" ? (
<CycleKanBanLayout />
) : activeLayout === "calendar" ? (
<CycleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<CycleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<CycleSpreadsheetLayout />
) : null}
<>
<TransferIssuesModal handleClose={() => setTransferIssuesModal(false)} isOpen={transferIssuesModal} />
<div className="relative w-full h-full flex flex-col overflow-hidden">
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
<CycleAppliedFiltersRoot />
<div className="w-full h-full overflow-auto">
{activeLayout === "list" ? (
<CycleListLayout />
) : activeLayout === "kanban" ? (
<CycleKanBanLayout />
) : activeLayout === "calendar" ? (
<CycleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<CycleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<CycleSpreadsheetLayout />
) : null}
</div>
</div>
</div>
</>
);
});

View File

@ -2,15 +2,12 @@ import React, { useCallback } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { GlobalViewsAppliedFiltersRoot, SpreadsheetView } from "components/issues";
// types
import { IIssue, IIssueDisplayFilterOptions, TStaticViewTypes } from "types";
// fetch-keys
import { GLOBAL_VIEW_ISSUES } from "constants/fetch-keys";
type Props = {
type?: TStaticViewTypes;
@ -35,7 +32,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
const storedFilters = globalViewId ? globalViewFiltersStore.storedFilters[globalViewId.toString()] : undefined;
useSWR(
workspaceSlug && globalViewId && viewDetails ? GLOBAL_VIEW_ISSUES(globalViewId.toString()) : null,
workspaceSlug && globalViewId && viewDetails ? `GLOBAL_VIEW_ISSUES_${globalViewId.toString()}` : null,
workspaceSlug && globalViewId && viewDetails
? () => {
globalViewIssuesStore.fetchViewIssues(workspaceSlug.toString(), globalViewId.toString(), storedFilters ?? {});
@ -44,7 +41,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
);
useSWR(
workspaceSlug && type ? GLOBAL_VIEW_ISSUES(type) : null,
workspaceSlug && type ? `GLOBAL_VIEW_ISSUES_${type.toString()}` : null,
workspaceSlug && type
? () => {
globalViewIssuesStore.fetchStaticIssues(workspaceSlug.toString(), type);

View File

@ -24,28 +24,25 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
};
const {
project: projectStore,
issueFilter: issueFilterStore,
moduleIssue: moduleIssueStore,
moduleFilter: moduleIssueFilterStore,
} = useMobxStore();
useSWR(workspaceSlug && projectId && moduleId ? `MODULE_INFORMATION_${moduleId.toString()}` : null, async () => {
if (workspaceSlug && projectId && moduleId) {
// fetching the project display filters and display properties
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
// fetching the module filters
await moduleIssueFilterStore.fetchModuleFilters(workspaceSlug, projectId, moduleId);
useSWR(
workspaceSlug && projectId && moduleId ? `MODULE_FILTERS_AND_ISSUES_${moduleId.toString()}` : null,
async () => {
if (workspaceSlug && projectId && moduleId) {
// fetching the project display filters and display properties
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
// fetching the module filters
await moduleIssueFilterStore.fetchModuleFilters(workspaceSlug, projectId, moduleId);
// fetching the project state, labels and members
await projectStore.fetchProjectStates(workspaceSlug, projectId);
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
await projectStore.fetchProjectMembers(workspaceSlug, projectId);
// fetching the module issues
await moduleIssueStore.fetchIssues(workspaceSlug, projectId, moduleId);
// fetching the module issues
await moduleIssueStore.fetchIssues(workspaceSlug, projectId, moduleId);
}
}
});
);
const activeLayout = issueFilterStore.userDisplayFilters.layout;

View File

@ -18,24 +18,15 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore } = useMobxStore();
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
useSWR(
workspaceSlug && projectId ? `REVALIDATE_PROJECT_ISSUES_${projectId.toString()}` : null,
async () => {
if (workspaceSlug && projectId) {
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
if (workspaceSlug && projectId) {
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
await projectStore.fetchProjectStates(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 }
);
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
}
});
const activeLayout = issueFilterStore.userDisplayFilters.layout;

View File

@ -20,34 +20,31 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
const { workspaceSlug, projectId, viewId } = router.query;
const {
project: projectStore,
issueFilter: issueFilterStore,
projectViews: projectViewsStore,
projectViewIssues: projectViewIssuesStore,
projectViewFilters: projectViewFiltersStore,
} = useMobxStore();
useSWR(workspaceSlug && projectId && viewId ? `PROJECT_VIEW_INFORMATION_${viewId.toString()}` : null, async () => {
if (workspaceSlug && projectId && viewId) {
// fetching the project display filters and display properties
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
useSWR(
workspaceSlug && projectId && viewId ? `PROJECT_VIEW_FILTERS_AND_ISSUES_${viewId.toString()}` : null,
async () => {
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
await projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString());
await projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString());
await projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString());
// fetching the view details
await projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString());
// fetching the view issues
await projectViewIssuesStore.fetchViewIssues(
workspaceSlug.toString(),
projectId.toString(),
viewId.toString(),
projectViewFiltersStore.storedFilters[viewId.toString()] ?? {}
);
// fetching the view details
await projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString());
// fetching the view issues
await projectViewIssuesStore.fetchViewIssues(
workspaceSlug.toString(),
projectId.toString(),
viewId.toString(),
projectViewFiltersStore.storedFilters[viewId.toString()] ?? {}
);
}
}
});
);
const activeLayout = issueFilterStore.userDisplayFilters.layout;

View File

@ -65,6 +65,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString())
: null
);
// TODO: fetch project estimates
// fetching project cycles
useSWR(
workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null,

View File

@ -10,18 +10,15 @@ import { AppLayout } from "layouts/app-layout";
// components
import { CycleIssuesHeader } from "components/headers";
import { ExistingIssuesListModal } from "components/core";
import { CycleDetailsSidebar, TransferIssues, TransferIssuesModal } from "components/cycles";
import { CycleDetailsSidebar } from "components/cycles";
import { CycleLayoutRoot } from "components/issues/issue-layouts";
// ui
import { EmptyState } from "components/common";
// assets
import emptyCycle from "public/empty-state/cycle.svg";
// helpers
import { getDateRangeStatus } from "helpers/date-time.helper";
const SingleCycle: React.FC = () => {
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
const [transferIssuesModal, setTransferIssuesModal] = useState(false);
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query;
@ -31,18 +28,13 @@ const SingleCycle: React.FC = () => {
const { storedValue } = useLocalStorage("cycle_sidebar_collapsed", "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
? () => cycleStore.fetchCycleWithId(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
: 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
// const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => {
// 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={`flex flex-col h-full w-full ${isSidebarCollapsed ? "" : "mr-[24rem]"} duration-300`}>
{cycleStatus === "completed" && <TransferIssues handleClick={() => setTransferIssuesModal(true)} />}
<div className="h-full w-full">
<CycleLayoutRoot />
</div>
<div className={`h-full w-full ${isSidebarCollapsed ? "" : "mr-[24rem]"} duration-300`}>
<CycleLayoutRoot />
</div>
{cycleId && <CycleDetailsSidebar isOpen={!isSidebarCollapsed} cycleId={cycleId.toString()} />}
</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
import { ProjectLayoutRoot } from "components/issues";
import { ProjectIssuesHeader } from "components/headers";
@ -10,27 +6,12 @@ import type { NextPage } from "next";
// layouts
import { AppLayout } from "layouts/app-layout";
const ProjectIssues: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { issueFilter: issueFilterStore } = useMobxStore();
// 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>
);
};
const ProjectIssues: NextPage = () => (
<AppLayout header={<ProjectIssuesHeader />} withProjectWrapper>
<div className="h-full w-full">
<ProjectLayoutRoot />
</div>
</AppLayout>
);
export default ProjectIssues;

View File

@ -1,90 +1,34 @@
import { useRouter } from "next/router";
import Link from "next/link";
import useSWR from "swr";
// services
import { ProjectService } from "services/project";
import { ViewService } from "services/view.service";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy";
import { AppLayout } from "layouts/app-layout";
// components
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";
// ui
import { EmptyState } from "components/common";
// assets
import emptyView from "public/empty-state/view.svg";
// types
import { NextPage } from "next";
// services
const projectService = new ProjectService();
const viewService = new ViewService();
const SingleView: React.FC = () => {
const ProjectViewIssues: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const { data: activeProject } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null
);
const { projectViews: projectViewsStore } = useMobxStore();
const { data: views } = useSWR(
workspaceSlug && projectId ? VIEWS_LIST(projectId as string) : 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,
const { error } = useSWR(
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
workspaceSlug && projectId && viewId
? () => viewService.getViewDetails(workspaceSlug as string, projectId as string, viewId as string)
? () => projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString())
: null
);
return (
<ProjectAuthorizationWrapper
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 />}
>
<AppLayout header={<ProjectViewIssuesHeader />} withProjectWrapper>
{error ? (
<EmptyState
image={emptyView}
@ -98,8 +42,8 @@ const SingleView: React.FC = () => {
) : (
<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 { useRouter } from "next/router";
import useSWR from "swr";
// hooks
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { ProjectViewsHeader } from "components/headers";
@ -14,14 +14,7 @@ const ProjectViews: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { project: projectStore, projectViews: projectViewsStore } = useMobxStore();
useSWR(
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId.toString()}` : null,
workspaceSlug && projectId
? () => projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString())
: null
);
const { projectViews: projectViewsStore } = useMobxStore();
useSWR(
workspaceSlug && projectId ? `PROJECT_VIEWS_LIST_${workspaceSlug.toString()}_${projectId.toString()}` : null,
@ -30,13 +23,8 @@ const ProjectViews: NextPage = () => {
: null
);
const projectDetails =
workspaceSlug && projectId
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
: undefined;
return (
<AppLayout header={<ProjectViewsHeader title={projectDetails?.name} />}>
<AppLayout header={<ProjectViewsHeader />}>
<ProjectViewsList />
</AppLayout>
);