forked from github/plane
chore: breadcrumbs ui revamp and refactor (#2634)
This commit is contained in:
parent
7eeac188d7
commit
8919b724c5
@ -1,58 +1,80 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
import { MoveLeft } from "lucide-react";
|
import { ChevronRight } from "lucide-react";
|
||||||
|
// components
|
||||||
|
import { Tooltip } from "../tooltip";
|
||||||
|
|
||||||
type BreadcrumbsProps = {
|
type BreadcrumbsProps = {
|
||||||
onBack: () => void;
|
|
||||||
children: any;
|
children: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Breadcrumbs = ({ onBack, children }: BreadcrumbsProps) => (
|
const Breadcrumbs = ({ children }: BreadcrumbsProps) => (
|
||||||
<>
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex w-full flex-grow items-center overflow-hidden overflow-ellipsis whitespace-nowrap">
|
{React.Children.map(children, (child, index) => (
|
||||||
<button
|
<div key={index} className="flex items-center flex-wrap gap-2.5">
|
||||||
type="button"
|
{child}
|
||||||
className="group grid h-7 w-7 flex-shrink-0 cursor-pointer place-items-center rounded border border-custom-sidebar-border-200 text-center text-sm hover:bg-custom-sidebar-background-90"
|
{index !== React.Children.count(children) - 1 && (
|
||||||
onClick={onBack}
|
<ChevronRight
|
||||||
>
|
className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-400"
|
||||||
<MoveLeft className="h-4 w-4 text-custom-sidebar-text-200 group-hover:text-custom-sidebar-text-100" />
|
aria-hidden="true"
|
||||||
</button>
|
/>
|
||||||
{children}
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
type BreadcrumbItemProps = {
|
type Props = {
|
||||||
title?: string;
|
type?: "text" | "component";
|
||||||
link?: JSX.Element;
|
component?: React.ReactNode;
|
||||||
icon?: any;
|
label?: string;
|
||||||
unshrinkTitle?: boolean;
|
icon?: React.ReactNode;
|
||||||
|
link?: string;
|
||||||
};
|
};
|
||||||
|
const BreadcrumbItem: React.FC<Props> = (props) => {
|
||||||
const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({
|
const { type = "text", component, label, icon, link } = props;
|
||||||
title,
|
return (
|
||||||
link,
|
|
||||||
icon,
|
|
||||||
unshrinkTitle = false,
|
|
||||||
}) => (
|
|
||||||
<>
|
<>
|
||||||
{link ? (
|
{type != "text" ? (
|
||||||
link
|
<div className="flex items-center space-x-2">{component}</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<Tooltip tooltipContent={label} position="bottom">
|
||||||
className={`truncate px-3 text-sm ${
|
<li className="flex items-center space-x-2">
|
||||||
unshrinkTitle ? "flex-shrink-0" : ""
|
<div className="flex items-center flex-wrap gap-2.5">
|
||||||
}`}
|
{link ? (
|
||||||
|
<a
|
||||||
|
className="flex items-center gap-1 text-sm font-medium text-custom-text-300 hover:text-custom-text-100"
|
||||||
|
href={link}
|
||||||
>
|
>
|
||||||
<p className={`truncate ${icon ? "flex items-center gap-2" : ""}`}>
|
{icon && (
|
||||||
|
<div className="overflow-hidden w-5 h-5 flex justify-center items-center !text-[1rem]">
|
||||||
{icon}
|
{icon}
|
||||||
<span className="break-words">{title}</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="relative block overflow-hidden truncate line-clamp-1 max-w-[150px]">
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-1 text-sm font-medium text-custom-text-100 cursor-default">
|
||||||
|
{icon && (
|
||||||
|
<div className="overflow-hidden w-5 h-5 flex justify-center items-center">
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="relative block overflow-hidden truncate line-clamp-1 max-w-[150px]">
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Breadcrumbs.BreadcrumbItem = BreadcrumbItem;
|
Breadcrumbs.BreadcrumbItem = BreadcrumbItem;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
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";
|
||||||
@ -10,11 +9,12 @@ import useLocalStorage from "hooks/use-local-storage";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, CustomMenu } from "@plane/ui";
|
import { Breadcrumbs, Button, ContrastIcon, CustomMenu } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ArrowRight, ContrastIcon, Plus } from "lucide-react";
|
import { ArrowRight, Plus } from "lucide-react";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -32,6 +32,8 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
cycleIssueFilter: cycleIssueFilterStore,
|
cycleIssueFilter: cycleIssueFilterStore,
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
|
||||||
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
||||||
@ -110,18 +112,34 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
<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="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">
|
<div className="flex items-center gap-2">
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p className="truncate">{`${truncateText(cycleDetails?.project_detail.name ?? "", 32)} Cycles`}</p>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</a>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
</Link>
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center justify-center h-4 w-4 rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Cycles"
|
||||||
|
link={`/${workspaceSlug}/projects/${projectId}/cycles`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="component"
|
||||||
|
component={
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
|
placement="bottom-start"
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
<ContrastIcon className="h-3 w-3" />
|
<ContrastIcon className="h-3 w-3" />
|
||||||
@ -140,6 +158,9 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<LayoutSelection
|
<LayoutSelection
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button } from "@plane/ui";
|
import { Breadcrumbs, Button, ContrastIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
|
||||||
export interface ICyclesHeader {}
|
export interface ICyclesHeader {}
|
||||||
|
|
||||||
export const CyclesHeader: FC<ICyclesHeader> = (props) => {
|
export const CyclesHeader: FC<ICyclesHeader> = (props) => {
|
||||||
@ -25,17 +25,28 @@ export const CyclesHeader: FC<ICyclesHeader> = (props) => {
|
|||||||
>
|
>
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p>Projects</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</Link>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center justify-center h-4 w-4 rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Cycles"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(currentProjectDetails?.name ?? "Project Title", 32)} Cycles`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,9 +10,9 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection } from "components/issues";
|
||||||
import { CreateUpdateWorkspaceViewModal } from "components/workspace";
|
import { CreateUpdateWorkspaceViewModal } from "components/workspace";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Tooltip } from "@plane/ui";
|
import { Breadcrumbs, Button, LayersIcon, PhotoFilterIcon, Tooltip } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { CheckCircle, List, PlusIcon, Sheet } from "lucide-react";
|
import { List, PlusIcon, Sheet } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TStaticViewTypes } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TStaticViewTypes } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -99,9 +99,20 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
<>
|
<>
|
||||||
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
||||||
<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="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 gap-2 items-center">
|
<div>
|
||||||
{activeLayout === "spreadsheet" && <CheckCircle size={16} strokeWidth={2} />}
|
<Breadcrumbs>
|
||||||
<span className="text-sm font-medium">Workspace {activeLayout === "spreadsheet" ? "Issues" : "Views"}</span>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={
|
||||||
|
activeLayout === "spreadsheet" ? (
|
||||||
|
<LayersIcon className="h-4 w-4 text-custom-text-300" />
|
||||||
|
) : (
|
||||||
|
<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label={`Workspace ${activeLayout === "spreadsheet" ? "Issues" : "Views"}`}
|
||||||
|
/>
|
||||||
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-1 p-1 rounded bg-custom-background-80">
|
<div className="flex items-center gap-1 p-1 rounded bg-custom-background-80">
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
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";
|
||||||
@ -10,11 +9,12 @@ import useLocalStorage from "hooks/use-local-storage";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, CustomMenu } from "@plane/ui";
|
import { Breadcrumbs, Button, CustomMenu, DiceIcon } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ArrowRight, ContrastIcon, Plus } from "lucide-react";
|
import { ArrowRight, ContrastIcon, Plus } from "lucide-react";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -33,6 +33,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
project: projectStore,
|
project: projectStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
|
|
||||||
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
|
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
|
||||||
|
|
||||||
@ -110,20 +111,32 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
<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="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">
|
<div className="flex items-center gap-2">
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/modules`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p className="truncate">{`${truncateText(
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
moduleDetails?.project_detail.name ?? "",
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
32
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
)} Modules`}</p>
|
) : (
|
||||||
</a>
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
</Link>
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Modules"
|
||||||
|
link={`/${workspaceSlug}/projects/${projectId}/modules`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="component"
|
||||||
|
component={
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
@ -143,6 +156,9 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<LayoutSelection
|
<LayoutSelection
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { GanttChartSquare, LayoutGrid, List, Plus } from "lucide-react";
|
import { GanttChartSquare, LayoutGrid, List, Plus } from "lucide-react";
|
||||||
// mobx store
|
// mobx store
|
||||||
@ -7,9 +6,10 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// hooks
|
// hooks
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button, Tooltip } from "@plane/ui";
|
import { Breadcrumbs, Button, Tooltip, DiceIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
const moduleViewOptions: { type: "list" | "grid" | "gantt_chart"; icon: any }[] = [
|
const moduleViewOptions: { type: "list" | "grid" | "gantt_chart"; icon: any }[] = [
|
||||||
{
|
{
|
||||||
@ -42,17 +42,28 @@ export const ModulesListHeader: React.FC = observer(() => {
|
|||||||
>
|
>
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p>Projects</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</Link>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Modules"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(currentProjectDetails?.name ?? "Project", 32)} Modules`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
92
web/components/headers/page-details.tsx
Normal file
92
web/components/headers/page-details.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { FileText, Plus } from "lucide-react";
|
||||||
|
// services
|
||||||
|
import { PageService } from "services/page.service";
|
||||||
|
|
||||||
|
// constants
|
||||||
|
import { PAGE_DETAILS } from "constants/fetch-keys";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// ui
|
||||||
|
import { Breadcrumbs, Button } from "@plane/ui";
|
||||||
|
// helper
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
export interface IPagesHeaderProps {
|
||||||
|
showButton?: boolean;
|
||||||
|
}
|
||||||
|
const pageService = new PageService();
|
||||||
|
|
||||||
|
export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
|
||||||
|
const { showButton = false } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, pageId } = router.query;
|
||||||
|
|
||||||
|
const { project: projectStore } = useMobxStore();
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
|
|
||||||
|
const { data: pageDetails } = useSWR(
|
||||||
|
workspaceSlug && currentProjectDetails?.id && pageId ? PAGE_DETAILS(pageId as string) : null,
|
||||||
|
workspaceSlug && currentProjectDetails?.id
|
||||||
|
? () => pageService.getPageDetails(workspaceSlug as string, currentProjectDetails.id, pageId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
|
<div>
|
||||||
|
<Breadcrumbs>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
icon={
|
||||||
|
currentProjectDetails?.emoji ? (
|
||||||
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Pages"
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label={pageDetails?.name ?? "Page"}
|
||||||
|
/>
|
||||||
|
</Breadcrumbs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{showButton && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
prependIcon={<Plus />}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
const e = new KeyboardEvent("keydown", { key: "d" });
|
||||||
|
document.dispatchEvent(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -1,14 +1,13 @@
|
|||||||
import { FC } from "react";
|
import { FC } 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";
|
||||||
import { Plus } from "lucide-react";
|
import { FileText, Plus } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button } from "@plane/ui";
|
import { Breadcrumbs, Button } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export interface IPagesHeaderProps {
|
export interface IPagesHeaderProps {
|
||||||
showButton?: boolean;
|
showButton?: boolean;
|
||||||
@ -17,30 +16,37 @@ export interface IPagesHeaderProps {
|
|||||||
export const PagesHeader: FC<IPagesHeaderProps> = observer((props) => {
|
export const PagesHeader: FC<IPagesHeaderProps> = observer((props) => {
|
||||||
const { showButton = false } = props;
|
const { showButton = false } = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
const projectDetails =
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p>Projects</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</Link>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Pages"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Pages`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
|
||||||
export const ProfilePreferencesHeader = () => {
|
export const ProfilePreferencesHeader = () => (
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
<div
|
||||||
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4`}
|
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem title="My Profile Preferences" />
|
<Breadcrumbs.BreadcrumbItem type="text" label="My Profile Preferences" />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
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";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
|
||||||
import { truncateText } from "helpers/string.helper";
|
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||||
// services
|
// services
|
||||||
import { IssueArchiveService } from "services/issue";
|
import { IssueArchiveService } from "services/issue";
|
||||||
|
// helpers
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
const issueArchiveService = new IssueArchiveService();
|
const issueArchiveService = new IssueArchiveService();
|
||||||
|
|
||||||
@ -24,10 +23,7 @@ export const ProjectArchivedIssueDetailsHeader: FC = observer(() => {
|
|||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
const projectDetails =
|
const { currentProjectDetails } = projectStore;
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const { data: issueDetails } = useSWR<IIssue | undefined>(
|
const { data: issueDetails } = useSWR<IIssue | undefined>(
|
||||||
workspaceSlug && projectId && archivedIssueId ? ISSUE_DETAILS(archivedIssueId as string) : null,
|
workspaceSlug && projectId && archivedIssueId ? ISSUE_DETAILS(archivedIssueId as string) : null,
|
||||||
@ -45,17 +41,35 @@ export const ProjectArchivedIssueDetailsHeader: FC = observer(() => {
|
|||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId as string}/issues`}>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
type="text"
|
||||||
<p className="truncate">{`${truncateText(
|
icon={
|
||||||
issueDetails?.project_detail.name ?? "Project",
|
currentProjectDetails?.emoji ? (
|
||||||
32
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
)} Issues`}</p>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
</a>
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
</Link>
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
|
/>
|
||||||
|
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Archived Issues`} />
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Archived Issues"
|
||||||
|
link={`/${workspaceSlug}/projects/${projectId}/archived-issues`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
label={`${issueDetails?.project_detail.identifier}-${issueDetails?.sequence_id}` ?? "..."}
|
||||||
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,37 +1,48 @@
|
|||||||
import { FC } from "react";
|
import { FC } 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";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
const projectDetails =
|
const { currentProjectDetails } = projectStore;
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
type="text"
|
||||||
<p>Projects</p>
|
icon={
|
||||||
</a>
|
currentProjectDetails?.emoji ? (
|
||||||
</Link>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
|
/>
|
||||||
|
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Archived Issues`} />
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Archived Issues"
|
||||||
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,41 +1,47 @@
|
|||||||
import { FC } from "react";
|
import { FC } 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";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export const ProjectDraftIssueHeader: FC = observer(() => {
|
export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
const projectDetails =
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p>Projects</p>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</a>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
</Link>
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Draft Issues`} />
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Draft Issues"
|
||||||
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,44 +1,52 @@
|
|||||||
import { FC, useState } from "react";
|
import { FC, useState } 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";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button } from "@plane/ui";
|
import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { CreateInboxIssueModal } from "components/inbox";
|
import { CreateInboxIssueModal } from "components/inbox";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export const ProjectInboxHeader: FC = observer(() => {
|
export const ProjectInboxHeader: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
const projectDetails =
|
const { currentProjectDetails } = projectStore;
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p>Projects</p>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</a>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
</Link>
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Inbox Issues"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Inbox`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
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";
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// services
|
// services
|
||||||
import { IssueService } from "services/issue";
|
import { IssueService } from "services/issue";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const issueService = new IssueService();
|
const issueService = new IssueService();
|
||||||
@ -20,6 +20,10 @@ export const ProjectIssueDetailsHeader: FC = observer(() => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, issueId } = router.query;
|
const { workspaceSlug, projectId, issueId } = router.query;
|
||||||
|
|
||||||
|
const { project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
|
|
||||||
const { data: issueDetails } = useSWR(
|
const { data: issueDetails } = useSWR(
|
||||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||||
workspaceSlug && projectId && issueId
|
workspaceSlug && projectId && issueId
|
||||||
@ -31,24 +35,34 @@ export const ProjectIssueDetailsHeader: FC = observer(() => {
|
|||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId as string}/issues`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p className="truncate">{`${truncateText(
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
issueDetails?.project_detail.name ?? "Project",
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
32
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
)} Issues`}</p>
|
) : (
|
||||||
</a>
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
</Link>
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
title={`Issue ${issueDetails?.project_detail.identifier ?? "Project"}-${
|
type="text"
|
||||||
issueDetails?.sequence_id ?? "..."
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
} Details`}
|
label="Issues"
|
||||||
unshrinkTitle
|
link={`/${workspaceSlug}/projects/${projectId}/issues`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
label={`${issueDetails?.project_detail.identifier}-${issueDetails?.sequence_id}` ?? "..."}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,13 +9,13 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button } from "@plane/ui";
|
import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export const ProjectIssuesHeader: React.FC = observer(() => {
|
export const ProjectIssuesHeader: React.FC = observer(() => {
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -85,11 +85,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
},
|
},
|
||||||
[issueFilterStore, projectId, workspaceSlug]
|
[issueFilterStore, projectId, workspaceSlug]
|
||||||
);
|
);
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
const projectDetails =
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const inboxDetails = projectId ? inboxStore.inboxesList?.[projectId.toString()]?.[0] : undefined;
|
const inboxDetails = projectId ? inboxStore.inboxesList?.[projectId.toString()]?.[0] : undefined;
|
||||||
|
|
||||||
@ -100,7 +96,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
<ProjectAnalyticsModal
|
<ProjectAnalyticsModal
|
||||||
isOpen={analyticsModal}
|
isOpen={analyticsModal}
|
||||||
onClose={() => setAnalyticsModal(false)}
|
onClose={() => setAnalyticsModal(false)}
|
||||||
projectDetails={projectDetails ?? undefined}
|
projectDetails={currentProjectDetails ?? undefined}
|
||||||
/>
|
/>
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
@ -114,22 +110,38 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
icon={
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
currentProjectDetails?.emoji ? (
|
||||||
<p>Projects</p>
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
|
||||||
</a>
|
{renderEmoji(currentProjectDetails.emoji)}
|
||||||
</Link>
|
</span>
|
||||||
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
<div className="h-7 w-7 flex-shrink-0 grid place-items-center">
|
||||||
|
{renderEmoji(currentProjectDetails.icon_prop)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
link={`/${workspaceSlug}/projects`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Issues"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Issues`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
{projectDetails?.is_deployed && deployUrl && (
|
{currentProjectDetails?.is_deployed && deployUrl && (
|
||||||
<a
|
<a
|
||||||
href={`${deployUrl}/${workspaceSlug}/${projectDetails?.id}`}
|
href={`${deployUrl}/${workspaceSlug}/${currentProjectDetails?.id}`}
|
||||||
className="group bg-custom-primary-100/10 text-custom-primary-100 px-2.5 py-1 text-xs flex items-center gap-1.5 rounded font-medium"
|
className="group bg-custom-primary-100/10 text-custom-primary-100 px-2.5 py-1 text-xs flex items-center gap-1.5 rounded font-medium"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
@ -28,17 +27,24 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p className="truncate">{`${truncateText(currentProjectDetails?.name ?? "Project", 32)}`}</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
</Link>
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
renderEmoji(currentProjectDetails.icon_prop)
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={title} unshrinkTitle />
|
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
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
|
||||||
@ -7,9 +6,10 @@ 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
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
import { Breadcrumbs, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -26,6 +26,8 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
projectViews: projectViewsStore,
|
projectViews: projectViewsStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
|
|
||||||
const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
|
const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
|
||||||
|
|
||||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||||
@ -87,28 +89,42 @@ 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 viewsList = projectId ? projectViewsStore.viewsList[projectId.toString()] : undefined;
|
||||||
const viewDetails = viewId ? projectViewsStore.viewDetails[viewId.toString()] : undefined;
|
const viewDetails = viewId ? projectViewsStore.viewDetails[viewId.toString()] : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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="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">
|
<div className="flex items-center gap-2">
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectDetails?.id}/cycles`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p className="truncate">{`${projectDetails?.name ?? "Project"} Views`}</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
|
||||||
</Link>
|
{renderEmoji(currentProjectDetails.emoji)}
|
||||||
|
</span>
|
||||||
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
<div className="h-7 w-7 flex-shrink-0 grid place-items-center">
|
||||||
|
{renderEmoji(currentProjectDetails.icon_prop)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Views"
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/views`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="component"
|
||||||
|
component={
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
@ -128,6 +144,9 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<LayoutSelection
|
<LayoutSelection
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
// mobx store
|
// mobx store
|
||||||
@ -8,9 +7,9 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { CreateUpdateProjectViewModal } from "components/views";
|
import { CreateUpdateProjectViewModal } from "components/views";
|
||||||
// components
|
// components
|
||||||
import { Breadcrumbs, BreadcrumbItem, Button } from "@plane/ui";
|
import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
export const ProjectViewsHeader: React.FC = observer(() => {
|
export const ProjectViewsHeader: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -20,11 +19,7 @@ export const ProjectViewsHeader: React.FC = observer(() => {
|
|||||||
const [createViewModal, setCreateViewModal] = useState(false);
|
const [createViewModal, setCreateViewModal] = useState(false);
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
|
const { currentProjectDetails } = projectStore;
|
||||||
const projectDetails =
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString())
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -41,17 +36,32 @@ export const ProjectViewsHeader: React.FC = observer(() => {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}/projects`}>
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={
|
||||||
<p>Projects</p>
|
currentProjectDetails?.emoji ? (
|
||||||
</a>
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
|
||||||
</Link>
|
{renderEmoji(currentProjectDetails.emoji)}
|
||||||
|
</span>
|
||||||
|
) : currentProjectDetails?.icon_prop ? (
|
||||||
|
<div className="h-7 w-7 flex-shrink-0 grid place-items-center">
|
||||||
|
{renderEmoji(currentProjectDetails.icon_prop)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||||
|
{currentProjectDetails?.name.charAt(0)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Views"
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Views`} />
|
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { Search, Plus } from "lucide-react";
|
import { Search, Plus, Briefcase } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs, Button } from "@plane/ui";
|
import { Breadcrumbs, Button } from "@plane/ui";
|
||||||
// helper
|
|
||||||
import { truncateText } from "helpers/string.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
@ -13,8 +11,7 @@ export const ProjectsHeader = observer(() => {
|
|||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { project: projectStore, workspace: workspaceStore } = useMobxStore();
|
const { project: projectStore } = useMobxStore();
|
||||||
const currentWorkspace = workspaceStore.currentWorkspace;
|
|
||||||
|
|
||||||
const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : [];
|
const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : [];
|
||||||
|
|
||||||
@ -24,10 +21,11 @@ export const ProjectsHeader = observer(() => {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
title={`${truncateText(currentWorkspace?.name ?? "Workspace", 32)} Projects`}
|
type="text"
|
||||||
unshrinkTitle={false}
|
icon={<Briefcase className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Projects"
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,35 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
import { UserCircle2 } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
export const UserProfileHeader: FC = observer(() => {
|
export interface IUserProfileHeader {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserProfileHeader: FC<IUserProfileHeader> = observer((props) => {
|
||||||
|
const { title } = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4`}
|
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem title="User Profile" unshrinkTitle />
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
label="Profile"
|
||||||
|
icon={<UserCircle2 className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
link={`/${workspaceSlug}/me/profile`}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { ArrowLeft } from "lucide-react";
|
import { ArrowLeft, BarChart2 } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
|
||||||
export const WorkspaceAnalyticsHeader = () => {
|
export const WorkspaceAnalyticsHeader = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -22,8 +22,12 @@ export const WorkspaceAnalyticsHeader = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem title="Workspace Analytics" />
|
<Breadcrumbs.BreadcrumbItem
|
||||||
|
type="text"
|
||||||
|
icon={<BarChart2 className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
label="Analytics"
|
||||||
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,13 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
import { Settings } from "lucide-react";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// services
|
|
||||||
import { WorkspaceService } from "services/workspace.service";
|
|
||||||
// helpers
|
|
||||||
import { truncateText } from "helpers/string.helper";
|
|
||||||
// constant
|
|
||||||
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
const workspaceService = new WorkspaceService();
|
|
||||||
|
|
||||||
export interface IWorkspaceSettingHeader {
|
export interface IWorkspaceSettingHeader {
|
||||||
title: string;
|
title: string;
|
||||||
@ -27,27 +19,20 @@ export const WorkspaceSettingHeader: FC<IWorkspaceSettingHeader> = observer((pro
|
|||||||
|
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { data: activeWorkspace } = useSWR(workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null, () =>
|
|
||||||
workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4`}
|
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center 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 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs onBack={() => router.back()}>
|
<Breadcrumbs>
|
||||||
<BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
link={
|
type="text"
|
||||||
<Link href={`/${workspaceSlug}`}>
|
label="Settings"
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
|
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
||||||
<p className="truncate">{`${truncateText(activeWorkspace?.name ?? "Workspace", 32)}`}</p>
|
link={`/${workspaceSlug}/settings`}
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<BreadcrumbItem title={title} unshrinkTitle />
|
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,7 +65,7 @@ const ProfileAssignedIssuesPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<UserProfileHeader />}>
|
<AppLayout header={<UserProfileHeader title="Assigned" />}>
|
||||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
@ -61,7 +61,7 @@ const ProfileCreatedIssuesPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<UserProfileHeader />}>
|
<AppLayout header={<UserProfileHeader title="Created" />}>
|
||||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
@ -56,7 +56,7 @@ const ProfileOverviewPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
ProfileOverviewPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileOverviewPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<UserProfileHeader />}>
|
<AppLayout header={<UserProfileHeader title="Overview" />}>
|
||||||
<ProfileAuthWrapper>{page}</ProfileAuthWrapper>
|
<ProfileAuthWrapper>{page}</ProfileAuthWrapper>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
@ -61,7 +61,7 @@ const ProfileSubscribedIssuesPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<UserProfileHeader />}>
|
<AppLayout header={<UserProfileHeader title="Subscribed" />}>
|
||||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
|||||||
import { CreateUpdateBlockInline, SinglePageBlock } from "components/pages";
|
import { CreateUpdateBlockInline, SinglePageBlock } from "components/pages";
|
||||||
import { CreateLabelModal } from "components/labels";
|
import { CreateLabelModal } from "components/labels";
|
||||||
import { CreateBlock } from "components/pages/create-block";
|
import { CreateBlock } from "components/pages/create-block";
|
||||||
import { PagesHeader } from "components/headers";
|
import { PageDetailsHeader } from "components/headers/page-details";
|
||||||
// ui
|
// ui
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
import { CustomSearchSelect, TextArea, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
import { CustomSearchSelect, TextArea, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
||||||
@ -626,7 +626,7 @@ const PageDetailsPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
PageDetailsPage.getLayout = function getLayout(page: ReactElement) {
|
PageDetailsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<PagesHeader />} withProjectWrapper>
|
<AppLayout header={<PageDetailsHeader />} withProjectWrapper>
|
||||||
{page}
|
{page}
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user