chore: ui and filter store updates

This commit is contained in:
gurusainath 2024-02-09 22:16:17 +05:30
parent cf10e3445d
commit 0fb531e4b7
17 changed files with 554 additions and 127 deletions

View File

@ -1,7 +1,7 @@
import { FC, Fragment, useEffect, useMemo, useState } from "react"; import { FC, Fragment, useEffect, useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { CheckCircle, Pencil } from "lucide-react"; import { CheckCircle, ChevronDown, ChevronUp, Pencil } from "lucide-react";
// hooks // hooks
import { useView, useViewDetail } from "hooks/store"; import { useView, useViewDetail } from "hooks/store";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -19,7 +19,7 @@ import {
// ui // ui
import { Spinner } from "@plane/ui"; import { Spinner } from "@plane/ui";
// constants // constants
import { VIEW_TYPES, viewLocalPayload } from "constants/view"; import { viewLocalPayload } from "constants/view";
// types // types
import { TViewOperations } from "./types"; import { TViewOperations } from "./types";
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties, TViewTypes } from "@plane/types"; import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties, TViewTypes } from "@plane/types";
@ -30,6 +30,7 @@ type TAllIssuesViewRoot = {
viewId: string; viewId: string;
viewType: TViewTypes; viewType: TViewTypes;
baseRoute: string; baseRoute: string;
workspaceViewTabOptions: { key: TViewTypes; title: string; href: string }[];
}; };
type TViewOperationsToggle = { type TViewOperationsToggle = {
@ -38,7 +39,7 @@ type TViewOperationsToggle = {
}; };
export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => { export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, baseRoute } = props; const { workspaceSlug, projectId, viewId, viewType, baseRoute, workspaceViewTabOptions } = props;
// hooks // hooks
const viewStore = useView(workspaceSlug, projectId, viewType); const viewStore = useView(workspaceSlug, projectId, viewType);
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
@ -51,28 +52,22 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
const handleViewOperationsToggle = (type: TViewOperationsToggle["type"], viewId: string | undefined) => const handleViewOperationsToggle = (type: TViewOperationsToggle["type"], viewId: string | undefined) =>
setViewOperationsToggle({ type, viewId }); setViewOperationsToggle({ type, viewId });
const workspaceViewTabOptions = useMemo( const viewDetailCreateStore = useViewDetail(
() => [ workspaceSlug,
{ projectId,
key: VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS, viewOperationsToggle?.viewId || viewId,
title: "Private", viewType
href: `/${workspaceSlug}/views/private/assigned`,
},
{
key: VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/views/public/all-issues`,
},
],
[workspaceSlug]
); );
const viewOperations: TViewOperations = useMemo( const viewOperations: TViewOperations = useMemo(
() => ({ () => ({
setName: (name: string) => viewDetailStore?.setName(name), setName: (name: string) => viewDetailStore?.setName(name),
setDescription: (name: string) => viewDetailStore?.setDescription(name), setDescription: (name: string) => viewDetailStore?.setDescription(name),
setFilters: (filterKey: keyof TViewFilters, filterValue: "clear_all" | string) => setFilters: (filterKey: keyof TViewFilters | undefined, filterValue: "clear_all" | string) => {
viewDetailStore?.setFilters(filterKey, filterValue), if (viewOperationsToggle.type && ["CREATE", "EDIT"].includes(viewOperationsToggle.type))
viewDetailCreateStore?.setFilters(filterKey, filterValue);
else viewDetailStore?.setFilters(filterKey, filterValue);
},
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) =>
viewDetailStore?.setDisplayFilters(display_filters), viewDetailStore?.setDisplayFilters(display_filters),
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) =>
@ -114,17 +109,24 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
} }
}, },
}), }),
[viewStore, viewDetailStore, setToastAlert] [viewStore, viewDetailStore, setToastAlert, viewOperationsToggle, viewDetailCreateStore]
); );
// fetch all issues
useEffect(() => { useEffect(() => {
const fetchViews = async () => { const fetchViews = async () => {
await viewStore?.fetch(viewStore?.viewIds.length > 0 ? "mutation-loader" : "init-loader"); await viewStore?.fetch(viewStore?.viewIds.length > 0 ? "mutation-loader" : "init-loader");
};
if (workspaceSlug && viewType && viewStore) fetchViews();
}, [workspaceSlug, projectId, viewType, viewStore]);
// fetch view by id
useEffect(() => {
const fetchViews = async () => {
viewId && (await viewStore?.fetchById(viewId)); viewId && (await viewStore?.fetchById(viewId));
}; };
if (workspaceSlug && viewId && viewType && viewStore) fetchViews(); if (workspaceSlug && viewId && viewType && viewStore) fetchViews();
}, [workspaceSlug, viewId, viewType, viewStore]); }, [workspaceSlug, projectId, viewId, viewType, viewStore]);
return ( return (
<div className="relative w-full h-full"> <div className="relative w-full h-full">
@ -170,7 +172,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
/> />
</div> </div>
<div className="p-5 py-2 border-b border-custom-border-200 relative flex gap-2"> <div className="p-5 py-2 border-b border-custom-border-200 relative flex items-start gap-1">
<div className="w-full overflow-hidden"> <div className="w-full overflow-hidden">
<ViewAppliedFiltersRoot <ViewAppliedFiltersRoot
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
@ -178,6 +180,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
propertyVisibleCount={5}
/> />
</div> </div>
@ -198,7 +201,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
displayDropdownText={false} displayDropdownText={true}
/> />
</div> </div>
@ -209,15 +212,24 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
displayDropdownText={false} displayDropdownText={true}
/> />
</div> </div>
<div className="border border-custom-border-300 relative flex items-center gap-1 h-7 rounded px-2 transition-all text-custom-text-200 hover:text-custom-text-100 bg-custom-background-100 hover:bg-custom-background-80 cursor-pointer shadow-custom-shadow-2xs"> <div className="relative flex items-center gap-1 rounded px-2 h-7 transition-all hover:bg-custom-background-80 cursor-pointer">
<div className="w-4 h-4 relative flex justify-center items-center overflow-hidden"> <div className="w-4 h-4 relative flex justify-center items-center overflow-hidden">
<Pencil size={12} /> <Pencil size={12} />
</div> </div>
</div> </div>
<div className=" relative flex items-center rounded h-7 transition-all cursor-pointer bg-custom-primary-100/20 text-custom-primary-100">
<div className="text-sm px-3 font-medium h-full border-r border-white/50 flex justify-center items-center rounded-l transition-all hover:bg-custom-primary-100/30">
Update
</div>
<div className="flex-shrink-0 px-1.5 hover:bg-custom-primary-100/30 h-full flex justify-center items-center rounded-r transition-all">
<ChevronDown size={16} />
</div>
</div>
</div> </div>
</> </>
)} )}

View File

@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite";
import isEmpty from "lodash/isEmpty"; import isEmpty from "lodash/isEmpty";
import { X } from "lucide-react"; import { X } from "lucide-react";
// hooks // hooks
import { useViewDetail } from "hooks/store"; import { useViewDetail, useViewFilter } from "hooks/store";
// components // components
import { ViewAppliedFiltersItem } from "./filter-item"; import { ViewAppliedFiltersItem } from "./filter-item";
// types // types
@ -17,12 +17,16 @@ type TViewAppliedFilters = {
viewType: TViewTypes; viewType: TViewTypes;
filterKey: keyof TViewFilters; filterKey: keyof TViewFilters;
viewOperations: TViewOperations; viewOperations: TViewOperations;
propertyVisibleCount?: number | undefined;
}; };
export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => { export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, filterKey, viewOperations } = props; const { workspaceSlug, projectId, viewId, viewType, filterKey, viewOperations, propertyVisibleCount } = props;
// hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
const viewFilterStore = useViewFilter(workspaceSlug, projectId);
const currentDefaultFilterDetails = viewFilterStore?.propertyDefaultDetails(filterKey);
const propertyValues = const propertyValues =
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters) viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
@ -36,9 +40,12 @@ export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
<div className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 px-2 min-h-[32px]"> <div className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 px-2 min-h-[32px]">
<div className="flex-shrink-0 text-xs text-custom-text-200 capitalize">{filterKey.replaceAll("_", " ")}</div> <div className="flex-shrink-0 text-xs text-custom-text-200 capitalize">{filterKey.replaceAll("_", " ")}</div>
<div className="relative flex items-center gap-1.5 flex-wrap"> <div className="relative flex items-center gap-1.5 flex-wrap">
{propertyValues.length >= 100 ? ( {propertyVisibleCount && propertyValues.length >= propertyVisibleCount ? (
<div className="text-xs font-medium bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2"> <div className="text-xs font-medium bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2">
{propertyValues.length} {filterKey.replaceAll("_", " ")}s <div className="flex-shrink-0 w-4-h-4">{currentDefaultFilterDetails?.icon}</div>
<div className="whitespace-nowrap">
{propertyValues.length} {currentDefaultFilterDetails?.label}
</div>
</div> </div>
) : ( ) : (
<> <>

View File

@ -16,10 +16,20 @@ type TViewAppliedFiltersRoot = {
viewId: string; viewId: string;
viewType: TViewTypes; viewType: TViewTypes;
viewOperations: TViewOperations; viewOperations: TViewOperations;
propertyVisibleCount?: number | undefined;
showClearAll?: boolean;
}; };
export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => { export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props; const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
propertyVisibleCount = undefined,
showClearAll = false,
} = props;
// hooks // hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
@ -28,7 +38,7 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
? Object.keys(viewDetailStore?.appliedFilters?.filters) ? Object.keys(viewDetailStore?.appliedFilters?.filters)
: undefined; : undefined;
const clearAllFilters = () => viewDetailStore?.setFilters(undefined, "clear_all"); const clearAllFilters = () => viewOperations?.setFilters(undefined, "clear_all");
if (!filterKeys || !viewDetailStore?.isFiltersApplied) if (!filterKeys || !viewDetailStore?.isFiltersApplied)
return ( return (
@ -49,20 +59,23 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
viewType={viewType} viewType={viewType}
filterKey={filterKey} filterKey={filterKey}
viewOperations={viewOperations} viewOperations={viewOperations}
propertyVisibleCount={propertyVisibleCount}
/> />
</Fragment> </Fragment>
); );
})} })}
<div {showClearAll && (
className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 px-2 cursor-pointer transition-all hover:bg-custom-background-80 text-custom-text-200 hover:text-custom-text-100 min-h-[36px]" <div
onClick={clearAllFilters} className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 px-2 cursor-pointer transition-all hover:bg-custom-background-80 text-custom-text-200 hover:text-custom-text-100 min-h-[36px]"
> onClick={clearAllFilters}
<div className="text-xs">Clear All</div> >
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4"> <div className="text-xs">Clear All</div>
<X size={10} /> <div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4">
<X size={10} />
</div>
</div> </div>
</div> )}
</div> </div>
); );
}); });

View File

@ -2,6 +2,7 @@ import { FC, Fragment, ReactNode, useRef, useState } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Combobox } from "@headlessui/react"; import { Combobox } from "@headlessui/react";
import { usePopper } from "react-popper"; import { usePopper } from "react-popper";
import { Placement } from "@popperjs/core";
import { ListFilter, Search } from "lucide-react"; import { ListFilter, Search } from "lucide-react";
// hooks // hooks
import useOutsideClickDetector from "hooks/use-outside-click-detector"; import useOutsideClickDetector from "hooks/use-outside-click-detector";
@ -21,13 +22,24 @@ type TViewFiltersDropdown = {
viewOperations: TViewOperations; viewOperations: TViewOperations;
children?: ReactNode; children?: ReactNode;
displayDropdownText?: boolean; displayDropdownText?: boolean;
dropdownPlacement?: Placement;
}; };
export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) => { export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props; const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
children,
displayDropdownText = true,
dropdownPlacement = "bottom-start",
} = props;
// state // state
const [dropdownToggle, setDropdownToggle] = useState(false); const [dropdownToggle, setDropdownToggle] = useState(false);
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [dateCustomFilterToggle, setDateCustomFilterToggle] = useState<string | undefined>(undefined);
// refs // refs
const dropdownRef = useRef<HTMLDivElement | null>(null); const dropdownRef = useRef<HTMLDivElement | null>(null);
// popper-js refs // popper-js refs
@ -35,7 +47,7 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null); const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
// popper-js init // popper-js init
const { styles, attributes } = usePopper(referenceElement, popperElement, { const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: "bottom-start", placement: dropdownPlacement,
modifiers: [ modifiers: [
{ {
name: "preventOverflow", name: "preventOverflow",
@ -43,6 +55,12 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
padding: 12, padding: 12,
}, },
}, },
{
name: "offset",
options: {
offset: [0, 10],
},
},
], ],
}); });
@ -55,7 +73,7 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
else handleDropdownClose(); else handleDropdownClose();
}; };
useOutsideClickDetector(dropdownRef, handleDropdownClose); useOutsideClickDetector(dropdownRef, () => dateCustomFilterToggle === undefined && handleDropdownClose());
return ( return (
<Combobox as="div" ref={dropdownRef}> <Combobox as="div" ref={dropdownRef}>
@ -109,13 +127,15 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
/> />
</div> </div>
<div className="max-h-[460px] space-y-0.5 overflow-y-scroll mb-2"> <div className="max-h-[500px] space-y-0.5 overflow-y-scroll mb-2">
<ViewFiltersRoot <ViewFiltersRoot
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
dateCustomFilterToggle={dateCustomFilterToggle}
setDateCustomFilterToggle={setDateCustomFilterToggle}
/> />
</div> </div>
</div> </div>

View File

@ -1,9 +1,10 @@
import { FC, useState } from "react"; import { FC, useState } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// hooks // hooks
import { useViewFilter } from "hooks/store"; import { useViewDetail, useViewFilter } from "hooks/store";
// components // components
import { ViewFiltersItem, ViewFilterSelection } from "../"; import { ViewFiltersItem, ViewFilterSelection } from "../";
import { DateFilterModal } from "components/core";
// types // types
import { TViewOperations } from "../types"; import { TViewOperations } from "../types";
import { TViewFilters, TViewTypes } from "@plane/types"; import { TViewFilters, TViewTypes } from "@plane/types";
@ -15,11 +16,23 @@ type TViewFiltersItemRoot = {
viewType: TViewTypes; viewType: TViewTypes;
viewOperations: TViewOperations; viewOperations: TViewOperations;
filterKey: keyof TViewFilters; filterKey: keyof TViewFilters;
dateCustomFilterToggle: string | undefined;
setDateCustomFilterToggle: (value: string | undefined) => void;
}; };
export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) => { export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations, filterKey } = props; const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
filterKey,
dateCustomFilterToggle,
setDateCustomFilterToggle,
} = props;
// hooks // hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
const viewFilterHelper = useViewFilter(workspaceSlug, projectId); const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
// state // state
const [viewAll, setViewAll] = useState(false); const [viewAll, setViewAll] = useState(false);
@ -28,7 +41,24 @@ export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) =>
const filterPropertyIds = propertyIds.length > 5 ? (viewAll ? propertyIds : propertyIds.slice(0, 5)) : propertyIds; const filterPropertyIds = propertyIds.length > 5 ? (viewAll ? propertyIds : propertyIds.slice(0, 5)) : propertyIds;
const handlePropertySelection = (_propertyId: string) => viewOperations?.setFilters(filterKey, _propertyId); const handlePropertySelection = (_propertyId: string) => {
if (["start_date", "target_date"].includes(filterKey)) {
if (_propertyId === "custom") {
const _propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || [];
const selectedDates = _propertyIds.filter((id) => id.includes("-"));
if (selectedDates.length > 0)
selectedDates.forEach((date: string) => viewOperations?.setFilters(filterKey, date));
else setDateCustomFilterToggle(filterKey);
} else viewOperations?.setFilters(filterKey, _propertyId);
} else viewOperations?.setFilters(filterKey, _propertyId);
};
const handleCustomDateSelection = (selectedDates: string[]) => {
selectedDates.forEach((date: string) => {
viewOperations?.setFilters(filterKey, date);
setDateCustomFilterToggle(undefined);
});
};
if (propertyIds.length <= 0) if (propertyIds.length <= 0)
return <div className="text-xs italic py-1 text-custom-text-300">No items are available.</div>; return <div className="text-xs italic py-1 text-custom-text-300">No items are available.</div>;
@ -65,6 +95,15 @@ export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) =>
{viewAll ? "View less" : "View all"} {viewAll ? "View less" : "View all"}
</div> </div>
)} )}
{dateCustomFilterToggle === filterKey && (
<DateFilterModal
handleClose={() => setDateCustomFilterToggle(undefined)}
isOpen={dateCustomFilterToggle === filterKey ? true : false}
onSelect={handleCustomDateSelection}
title="Start date"
/>
)}
</div> </div>
); );
}); });

View File

@ -21,7 +21,14 @@ export const ViewFilterSelection: FC<TViewFilterSelection> = observer((props) =>
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
const propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || []; const propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || [];
const isSelected = propertyIds?.includes(propertyId) || false;
const isSelected = ["start_date", "target_date"].includes(filterKey)
? propertyId === "custom"
? propertyIds.filter((id) => id.includes("-")).length > 0
? true
: false
: propertyIds?.includes(propertyId)
: propertyIds?.includes(propertyId) || false;
return ( return (
<div <div

View File

@ -19,10 +19,20 @@ type TViewFiltersRoot = {
viewId: string; viewId: string;
viewType: TViewTypes; viewType: TViewTypes;
viewOperations: TViewOperations; viewOperations: TViewOperations;
dateCustomFilterToggle: string | undefined;
setDateCustomFilterToggle: (value: string | undefined) => void;
}; };
export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => { export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props; const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
dateCustomFilterToggle,
setDateCustomFilterToggle,
} = props;
// hooks // hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
// state // state
@ -62,6 +72,8 @@ export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
filterKey={filterKey} filterKey={filterKey}
dateCustomFilterToggle={dateCustomFilterToggle}
setDateCustomFilterToggle={setDateCustomFilterToggle}
/> />
)} )}
</div> </div>

View File

@ -3,7 +3,7 @@ import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties } from
export type TViewOperations = { export type TViewOperations = {
setName: (name: string) => void; setName: (name: string) => void;
setDescription: (description: string) => void; setDescription: (description: string) => void;
setFilters: (filterKey: keyof TViewFilters, filterValue: "clear_all" | string) => void; setFilters: (filterKey: keyof TViewFilters | undefined, filterValue: "clear_all" | string) => void;
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void; setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void; setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void;

View File

@ -5,7 +5,7 @@ import { Briefcase, Globe2, Plus, X } from "lucide-react";
// hooks // hooks
import { useViewDetail, useProject } from "hooks/store"; import { useViewDetail, useProject } from "hooks/store";
// components // components
import { ViewAppliedFiltersRoot } from "../"; import { ViewAppliedFiltersRoot, ViewFiltersDropdown } from "../";
// ui // ui
import { Input, Button } from "@plane/ui"; import { Input, Button } from "@plane/ui";
// types // types
@ -85,7 +85,7 @@ export const ViewCreateEditForm: FC<TViewCreateEditForm> = observer((props) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[40rem] py-5 border-[0.1px] border-custom-border-100"> <Dialog.Panel className="relative transform rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[40rem] py-5 border-[0.1px] border-custom-border-100">
<div className="p-3 px-5 relative flex items-center gap-2"> <div className="p-3 px-5 relative flex items-center gap-2">
{projectId && projectDetails ? ( {projectId && projectDetails ? (
<div className="relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80"> <div className="relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80">
@ -121,13 +121,27 @@ export const ViewCreateEditForm: FC<TViewCreateEditForm> = observer((props) => {
</div> </div>
<div className="p-3 px-5 relative flex justify-between items-center gap-2"> <div className="p-3 px-5 relative flex justify-between items-center gap-2">
<div className="cursor-pointer relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80"> <ViewFiltersDropdown
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden"> workspaceSlug={workspaceSlug}
<Plus className="w-3 h-3" /> projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
dropdownPlacement="right"
>
<div className="cursor-pointer relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80">
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden">
<Plus className="w-3 h-3" />
</div>
<div className="text-xs">Filters</div>
</div> </div>
<div className="text-xs">Filters</div> </ViewFiltersDropdown>
</div> <div
<div className="cursor-pointer relative rounded p-1.5 px-2 flex items-center gap-1 border border-dashed border-custom-border-100 bg-custom-background-80"> className="cursor-pointer relative rounded p-1.5 px-2 flex items-center gap-1 border border-dashed border-custom-border-100 bg-custom-background-80"
onClick={() => {
viewOperations.setFilters(undefined, "clear_all");
}}
>
<div className="text-xs">Clear all filters</div> <div className="text-xs">Clear all filters</div>
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden"> <div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden">
<X className="w-3 h-3" /> <X className="w-3 h-3" />
@ -135,13 +149,14 @@ export const ViewCreateEditForm: FC<TViewCreateEditForm> = observer((props) => {
</div> </div>
</div> </div>
<div className="p-3 px-5 relative bg-custom-background-80"> <div className="p-3 px-5 relative bg-custom-background-90 max-h-36 overflow-hidden overflow-y-auto">
<ViewAppliedFiltersRoot <ViewAppliedFiltersRoot
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
propertyVisibleCount={undefined}
/> />
</div> </div>

View File

@ -64,48 +64,46 @@ export const ViewRoot: FC<TViewRoot> = observer((props) => {
return ( return (
<div className="relative flex justify-between px-5 gap-2"> <div className="relative flex justify-between px-5 gap-2">
{viewStore?.viewIds && viewStore?.viewIds.length > 0 && ( <div className="w-full">
<div {viewStore?.viewIds && viewStore?.viewIds.length > 0 && (
key={`views_list_${viewId}`} <div id="tab-container" className="relative flex items-center w-full overflow-hidden">
id="tab-container" {viewIds.map((_viewId) => (
className="relative flex items-center w-full overflow-hidden" <Fragment key={_viewId}>
> <ViewItem
{viewIds.map((_viewId) => ( workspaceSlug={workspaceSlug}
<Fragment key={_viewId}> projectId={projectId}
<ViewItem viewId={viewId}
workspaceSlug={workspaceSlug} viewType={viewType}
projectId={projectId} viewItemId={_viewId}
viewId={viewId} baseRoute={baseRoute}
viewType={viewType} />
viewItemId={_viewId} </Fragment>
baseRoute={baseRoute} ))}
/>
</Fragment>
))}
<div id="tab-item-view-more" className="min-w-[90px]"> <div id="tab-item-view-more" className="min-w-[90px]">
{viewStore?.viewIds.length <= (itemsToRenderViewsCount || viewStore?.viewIds.length) ? null : ( {viewStore?.viewIds.length <= (itemsToRenderViewsCount || viewStore?.viewIds.length) ? null : (
<ViewDropdown <ViewDropdown
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
viewId={viewId} viewId={viewId}
viewType={viewType} viewType={viewType}
viewOperations={viewOperations} viewOperations={viewOperations}
baseRoute={baseRoute} baseRoute={baseRoute}
> >
<div className="text-sm font-semibold mb-1 p-2 px-2.5 text-custom-text-200 cursor-pointer hover:bg-custom-background-80 whitespace-nowrap rounded relative flex items-center gap-1"> <div className="text-sm font-semibold mb-1 p-2 px-2.5 text-custom-text-200 cursor-pointer hover:bg-custom-background-80 whitespace-nowrap rounded relative flex items-center gap-1">
<span> <span>
<Plus size={12} /> <Plus size={12} />
</span> </span>
<span> <span>
{viewStore?.viewIds.length - (itemsToRenderViewsCount || viewStore?.viewIds.length)} More... {viewStore?.viewIds.length - (itemsToRenderViewsCount || viewStore?.viewIds.length)} More...
</span> </span>
</div> </div>
</ViewDropdown> </ViewDropdown>
)} )}
</div>
</div> </div>
</div> )}
)} </div>
<div className="flex-shrink-0 my-auto pb-1"> <div className="flex-shrink-0 my-auto pb-1">
<Button size="sm" prependIcon={<Plus />} onClick={() => viewOperations?.localViewCreateEdit(undefined)}> <Button size="sm" prependIcon={<Plus />} onClick={() => viewOperations?.localViewCreateEdit(undefined)}>

View File

@ -47,10 +47,10 @@ export const DATE_PROPERTY: {
label: string; label: string;
}; };
} = { } = {
last_week: { label: "Last Week" }, "1_weeks;after;fromnow": { label: "1 week from now" },
"2_weeks_from_now": { label: "2 weeks from now" }, "2_weeks;after;fromnow": { label: "2 weeks from now" },
"1_month_from_now": { label: "1 month from now" }, "1_months;after;fromnow": { label: "1 month from now" },
"2_months_from_now": { label: "2 months from now" }, "2_months;after;fromnow": { label: "2 months from now" },
custom: { label: "Custom" }, custom: { label: "Custom" },
}; };
@ -110,7 +110,8 @@ const ALL_FILTER_PERMISSIONS: TFilterPermissions["all"] = {
"start_date", "start_date",
"target_date", "target_date",
], ],
display_filters: ["type"], // display_filters: ["type"],
display_filters: ["group_by", "sub_group_by", "order_by", "type"],
extra_options: [], extra_options: [],
display_properties: true, display_properties: true,
}, },

View File

@ -1,21 +1,35 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { Briefcase, CalendarDays, CircleUser, Tag } from "lucide-react";
// hooks // hooks
import { useProject, useModule, useCycle, useProjectState, useMember, useLabel } from "hooks/store"; import { useProject, useModule, useCycle, useProjectState, useMember, useLabel } from "hooks/store";
// ui // ui
import { Avatar, CycleGroupIcon, DiceIcon, PriorityIcon, StateGroupIcon } from "@plane/ui"; import {
Avatar,
ContrastIcon,
CycleGroupIcon,
DiceIcon,
DoubleCircleIcon,
PriorityIcon,
StateGroupIcon,
} from "@plane/ui";
// types // types
import { TIssuePriorities, TStateGroups, TViewFilters } from "@plane/types"; import { TIssuePriorities, TStateGroups, TViewFilters } from "@plane/types";
// constants // constants
import { STATE_GROUP_PROPERTY, PRIORITIES_PROPERTY, DATE_PROPERTY } from "constants/view/filters"; import { STATE_GROUP_PROPERTY, PRIORITIES_PROPERTY, DATE_PROPERTY } from "constants/view/filters";
import { Briefcase, CalendarDays } from "lucide-react";
// helpers // helpers
import { renderEmoji } from "helpers/emoji.helper"; import { renderEmoji } from "helpers/emoji.helper";
import { renderFormattedDate } from "helpers/date-time.helper";
type TFilterPropertyDetails = { type TFilterPropertyDetails = {
icon: ReactNode; icon: ReactNode;
label: string; label: string;
}; };
type TFilterPropertyDefaultDetails = {
icon: ReactNode;
label: string;
};
export const useViewFilter = (workspaceSlug: string, projectId: string | undefined) => { export const useViewFilter = (workspaceSlug: string, projectId: string | undefined) => {
const { projectMap, getProjectById } = useProject(); const { projectMap, getProjectById } = useProject();
const { getProjectModuleIds, getModuleById } = useModule(); const { getProjectModuleIds, getModuleById } = useModule();
@ -73,6 +87,80 @@ export const useViewFilter = (workspaceSlug: string, projectId: string | undefin
} }
}; };
const propertyDefaultDetails = (filterKey: keyof TViewFilters): TFilterPropertyDefaultDetails | undefined => {
if (!filterKey) return undefined;
switch (filterKey) {
case "project":
return {
icon: <Briefcase size={12} />,
label: "Projects",
};
case "module":
return {
icon: <DiceIcon className="w-3 h-3" />,
label: "Modules",
};
case "cycle":
return {
icon: <ContrastIcon className="w-3 h-3" />,
label: "Cycles",
};
case "priority":
return {
icon: <PriorityIcon priority="high" withContainer size={10} />,
label: "Priorities",
};
case "state":
return {
icon: <DoubleCircleIcon className="w-3 h-3" />,
label: "States",
};
case "state_group":
return {
icon: <DoubleCircleIcon className="w-3 h-3" />,
label: "State Groups",
};
case "assignees":
return {
icon: <CircleUser size={12} />,
label: "Assignees",
};
case "mentions":
return {
icon: <CircleUser size={12} />,
label: "Mentions",
};
case "subscriber":
return {
icon: <CircleUser size={12} />,
label: "Subscribers",
};
case "created_by":
return {
icon: <CircleUser size={12} />,
label: "Creators",
};
case "labels":
return {
icon: <Tag size={12} />,
label: "Labels",
};
case "start_date":
return {
icon: <CalendarDays size={12} />,
label: "Start Dates",
};
case "target_date":
return {
icon: <CalendarDays size={12} />,
label: "Target Dates",
};
default:
return undefined;
}
};
const propertyDetails = (filterKey: keyof TViewFilters, propertyId: string): TFilterPropertyDetails | undefined => { const propertyDetails = (filterKey: keyof TViewFilters, propertyId: string): TFilterPropertyDetails | undefined => {
if (!filterKey || !propertyId) return undefined; if (!filterKey || !propertyId) return undefined;
@ -200,19 +288,39 @@ export const useViewFilter = (workspaceSlug: string, projectId: string | undefin
label: labelPropertyDetail.name, label: labelPropertyDetail.name,
}; };
case "start_date": case "start_date":
const startDatePropertyDetail = DATE_PROPERTY?.[propertyId]; if (propertyId.includes("-")) {
if (!startDatePropertyDetail) return undefined; const customDateString = propertyId.split(";");
return { return {
icon: <CalendarDays size={12} />, icon: <CalendarDays size={12} />,
label: startDatePropertyDetail.label, label: `${customDateString[1].charAt(0).toUpperCase()}${customDateString[1].slice(1)} ${renderFormattedDate(
}; customDateString[0]
)}`,
};
} else {
const startDatePropertyDetail = DATE_PROPERTY?.[propertyId];
if (!startDatePropertyDetail) return undefined;
return {
icon: <CalendarDays size={12} />,
label: startDatePropertyDetail.label,
};
}
case "target_date": case "target_date":
const targetDatePropertyDetail = DATE_PROPERTY?.[propertyId]; if (propertyId.includes("-")) {
if (!targetDatePropertyDetail) return undefined; const customDateString = propertyId.split(";");
return { return {
icon: <CalendarDays size={12} />, icon: <CalendarDays size={12} />,
label: targetDatePropertyDetail.label, label: `${customDateString[1].charAt(0).toUpperCase()}${customDateString[1].slice(1)} ${renderFormattedDate(
}; customDateString[0]
)}`,
};
} else {
const targetDatePropertyDetail = DATE_PROPERTY?.[propertyId];
if (!targetDatePropertyDetail) return undefined;
return {
icon: <CalendarDays size={12} />,
label: targetDatePropertyDetail.label,
};
}
default: default:
return undefined; return undefined;
} }
@ -220,6 +328,7 @@ export const useViewFilter = (workspaceSlug: string, projectId: string | undefin
return { return {
filterIdsWithKey, filterIdsWithKey,
propertyDefaultDetails,
propertyDetails, propertyDetails,
}; };
}; };

View File

@ -0,0 +1,53 @@
import { ReactElement, useMemo } from "react";
import { useRouter } from "next/router";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { AllIssuesViewRoot } from "components/view";
// types
import { NextPageWithLayout } from "lib/types";
// constants
import { VIEW_TYPES } from "constants/view";
const ProjectPrivateViewPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const workspaceViewTabOptions = useMemo(
() => [
{
key: VIEW_TYPES.PROJECT_PRIVATE_VIEWS,
title: "Private",
href: `/${workspaceSlug}/projects/${projectId}/views/private`,
},
{
key: VIEW_TYPES.PROJECT_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/projects/${projectId}/views/public`,
},
],
[workspaceSlug, projectId]
);
if (!workspaceSlug || !projectId || !viewId) return <></>;
return (
<div className="h-full overflow-hidden bg-custom-background-100">
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
<AllIssuesViewRoot
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
viewId={viewId.toString()}
viewType={VIEW_TYPES.PROJECT_PRIVATE_VIEWS}
baseRoute={`/${workspaceSlug?.toString()}/projects/${projectId}/views/private`}
workspaceViewTabOptions={workspaceViewTabOptions}
/>
</div>
</div>
);
};
ProjectPrivateViewPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<></>}>{page}</AppLayout>;
};
export default ProjectPrivateViewPage;

View File

@ -0,0 +1,53 @@
import { ReactElement, useMemo } from "react";
import { useRouter } from "next/router";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { AllIssuesViewRoot } from "components/view";
// types
import { NextPageWithLayout } from "lib/types";
// constants
import { VIEW_TYPES } from "constants/view";
const ProjectPrivateViewPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const workspaceViewTabOptions = useMemo(
() => [
{
key: VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS,
title: "Private",
href: `/${workspaceSlug}/projects/${projectId}/views/private`,
},
{
key: VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/projects/${projectId}/views/public`,
},
],
[workspaceSlug, projectId]
);
if (!workspaceSlug || !projectId || !viewId) return <></>;
return (
<div className="h-full overflow-hidden bg-custom-background-100">
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
<AllIssuesViewRoot
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
viewId={viewId.toString()}
viewType={VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS}
baseRoute={`/${workspaceSlug?.toString()}/projects/${projectId}/views/private`}
workspaceViewTabOptions={workspaceViewTabOptions}
/>
</div>
</div>
);
};
ProjectPrivateViewPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<></>}>{page}</AppLayout>;
};
export default ProjectPrivateViewPage;

View File

@ -0,0 +1,54 @@
import { ReactElement, useMemo } from "react";
import { useRouter } from "next/router";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { AllIssuesViewRoot } from "components/view";
// types
import { NextPageWithLayout } from "lib/types";
// constants
import { VIEW_TYPES } from "constants/view";
const ProjectPublicViewPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const workspaceViewTabOptions = useMemo(
() => [
{
key: VIEW_TYPES.PROJECT_PRIVATE_VIEWS,
title: "Private",
href: `/${workspaceSlug}/views/private/assigned`,
},
{
key: VIEW_TYPES.PROJECT_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/views/public/all-issues`,
},
],
[workspaceSlug]
);
if (!workspaceSlug || !viewId) return <></>;
return (
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
<div className="flex-shrink-0 w-full">
<AllIssuesViewRoot
workspaceSlug={workspaceSlug.toString()}
projectId={undefined}
viewId={viewId.toString()}
viewType={VIEW_TYPES.PROJECT_PUBLIC_VIEWS}
baseRoute={`/${workspaceSlug?.toString()}/views/public`}
workspaceViewTabOptions={workspaceViewTabOptions}
/>
</div>
<div className="w-full h-full overflow-hidden">Issues render</div>
</div>
);
};
ProjectPublicViewPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<></>}>{page}</AppLayout>;
};
export default ProjectPublicViewPage;

View File

@ -1,4 +1,4 @@
import { ReactElement } from "react"; import { ReactElement, useMemo } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// layouts // layouts
import { AppLayout } from "layouts/app-layout"; import { AppLayout } from "layouts/app-layout";
@ -9,10 +9,26 @@ import { NextPageWithLayout } from "lib/types";
// constants // constants
import { VIEW_TYPES } from "constants/view"; import { VIEW_TYPES } from "constants/view";
const GlobalViewIssuesPage: NextPageWithLayout = () => { const WorkspacePrivateViewPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query; const { workspaceSlug, viewId } = router.query;
const workspaceViewTabOptions = useMemo(
() => [
{
key: VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS,
title: "Private",
href: `/${workspaceSlug}/views/private/assigned`,
},
{
key: VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/views/public/all-issues`,
},
],
[workspaceSlug]
);
if (!workspaceSlug || !viewId) return <></>; if (!workspaceSlug || !viewId) return <></>;
return ( return (
<div className="h-full overflow-hidden bg-custom-background-100"> <div className="h-full overflow-hidden bg-custom-background-100">
@ -23,14 +39,15 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
viewId={viewId.toString()} viewId={viewId.toString()}
viewType={VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS} viewType={VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS}
baseRoute={`/${workspaceSlug?.toString()}/views/private`} baseRoute={`/${workspaceSlug?.toString()}/views/private`}
workspaceViewTabOptions={workspaceViewTabOptions}
/> />
</div> </div>
</div> </div>
); );
}; };
GlobalViewIssuesPage.getLayout = function getLayout(page: ReactElement) { WorkspacePrivateViewPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<></>}>{page}</AppLayout>; return <AppLayout header={<></>}>{page}</AppLayout>;
}; };
export default GlobalViewIssuesPage; export default WorkspacePrivateViewPage;

View File

@ -1,4 +1,4 @@
import { ReactElement } from "react"; import { ReactElement, useMemo } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// layouts // layouts
import { AppLayout } from "layouts/app-layout"; import { AppLayout } from "layouts/app-layout";
@ -9,10 +9,26 @@ import { NextPageWithLayout } from "lib/types";
// constants // constants
import { VIEW_TYPES } from "constants/view"; import { VIEW_TYPES } from "constants/view";
const GlobalViewIssuesPage: NextPageWithLayout = () => { const WorkspacePublicViewPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query; const { workspaceSlug, viewId } = router.query;
const workspaceViewTabOptions = useMemo(
() => [
{
key: VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS,
title: "Private",
href: `/${workspaceSlug}/views/private/assigned`,
},
{
key: VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS,
title: "Public",
href: `/${workspaceSlug}/views/public/all-issues`,
},
],
[workspaceSlug]
);
if (!workspaceSlug || !viewId) return <></>; if (!workspaceSlug || !viewId) return <></>;
return ( return (
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col"> <div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
@ -23,6 +39,7 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
viewId={viewId.toString()} viewId={viewId.toString()}
viewType={VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS} viewType={VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS}
baseRoute={`/${workspaceSlug?.toString()}/views/public`} baseRoute={`/${workspaceSlug?.toString()}/views/public`}
workspaceViewTabOptions={workspaceViewTabOptions}
/> />
</div> </div>
<div className="w-full h-full overflow-hidden">Issues render</div> <div className="w-full h-full overflow-hidden">Issues render</div>
@ -30,8 +47,8 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
); );
}; };
GlobalViewIssuesPage.getLayout = function getLayout(page: ReactElement) { WorkspacePublicViewPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<></>}>{page}</AppLayout>; return <AppLayout header={<></>}>{page}</AppLayout>;
}; };
export default GlobalViewIssuesPage; export default WorkspacePublicViewPage;