mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: implemented extra options in the display filter properties dropdown and hanlded the arrow functions with useCallback
This commit is contained in:
parent
6bde956166
commit
c35d650de0
2
packages/types/src/view/filter.d.ts
vendored
2
packages/types/src/view/filter.d.ts
vendored
@ -52,6 +52,8 @@ export type TViewDisplayFiltersOrderBy =
|
||||
| "sub_issues_count"
|
||||
| "-sub_issues_count";
|
||||
|
||||
export type TViewDisplayFiltersExtraOptions = "sub_issue" | "show_empty_groups";
|
||||
|
||||
export type TViewDisplayFiltersType = "active" | "backlog";
|
||||
|
||||
export type TViewCalendarLayouts = "month" | "week";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useCallback } from "react";
|
||||
import { ImagePlus, X } from "lucide-react";
|
||||
// hooks
|
||||
import { useViewDetail, useViewFilter } from "hooks/store";
|
||||
@ -22,9 +22,10 @@ export const ViewAppliedFiltersItem: FC<TViewAppliedFiltersItem> = (props) => {
|
||||
|
||||
const propertyDetail = viewFilterHelper?.propertyDetails(filterKey, propertyId) || undefined;
|
||||
|
||||
const removeFilterOption = () => {
|
||||
viewDetailStore?.setFilters(filterKey, propertyId);
|
||||
};
|
||||
const removeFilterOption = useCallback(
|
||||
() => viewDetailStore?.setFilters(filterKey, propertyId),
|
||||
[viewDetailStore, filterKey, propertyId]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment } from "react";
|
||||
import { FC, Fragment, useCallback, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { X } from "lucide-react";
|
||||
@ -24,14 +24,23 @@ export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
const viewFilterStore = useViewFilter(workspaceSlug, projectId);
|
||||
|
||||
const currentDefaultFilterDetails = viewFilterStore?.propertyDefaultDetails(filterKey);
|
||||
const currentDefaultFilterDetails = useMemo(
|
||||
() => viewFilterStore?.propertyDefaultDetails(filterKey),
|
||||
[viewFilterStore, filterKey]
|
||||
);
|
||||
|
||||
const propertyValues =
|
||||
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
|
||||
? viewDetailStore?.appliedFilters?.filters?.[filterKey] || undefined
|
||||
: undefined;
|
||||
const propertyValues = useMemo(
|
||||
() =>
|
||||
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
|
||||
? viewDetailStore?.appliedFilters?.filters?.[filterKey] || undefined
|
||||
: undefined,
|
||||
[filterKey, viewDetailStore?.appliedFilters?.filters]
|
||||
);
|
||||
|
||||
const clearPropertyFilter = () => viewDetailStore?.setFilters(filterKey, "clear_all");
|
||||
const clearPropertyFilter = useCallback(
|
||||
() => viewDetailStore?.setFilters(filterKey, "clear_all"),
|
||||
[viewDetailStore, filterKey]
|
||||
);
|
||||
|
||||
if (!propertyValues || propertyValues.length <= 0) return <></>;
|
||||
return (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment } from "react";
|
||||
import { FC, Fragment, useCallback, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { X } from "lucide-react";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
@ -23,12 +23,15 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
|
||||
// hooks
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
|
||||
const filterKeys =
|
||||
viewDetailStore?.filtersToUpdate && !isEmpty(viewDetailStore?.filtersToUpdate?.filters)
|
||||
? Object.keys(viewDetailStore?.filtersToUpdate?.filters)
|
||||
: undefined;
|
||||
const filterKeys = useMemo(
|
||||
() =>
|
||||
viewDetailStore?.filtersToUpdate && !isEmpty(viewDetailStore?.filtersToUpdate?.filters)
|
||||
? Object.keys(viewDetailStore?.filtersToUpdate?.filters)
|
||||
: undefined,
|
||||
[viewDetailStore?.filtersToUpdate]
|
||||
);
|
||||
|
||||
const clearAllFilters = () => viewDetailStore?.setFilters(undefined, "clear_all");
|
||||
const clearAllFilters = useCallback(() => viewDetailStore?.setFilters(undefined, "clear_all"), [viewDetailStore]);
|
||||
|
||||
if (!filterKeys || !viewDetailStore?.isFiltersApplied)
|
||||
return (
|
||||
|
54
web/components/view/display-filters/extra-options.tsx
Normal file
54
web/components/view/display-filters/extra-options.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { FC, Fragment, useCallback, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Check } from "lucide-react";
|
||||
// hooks
|
||||
import { useViewDetail } from "hooks/store";
|
||||
// types
|
||||
import { TViewDisplayFiltersExtraOptions, TViewTypes } from "@plane/types";
|
||||
// constants
|
||||
import { EXTRA_OPTIONS_PROPERTY } from "constants/view";
|
||||
|
||||
type TDisplayFilterExtraOptions = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string;
|
||||
viewType: TViewTypes;
|
||||
filterKey: TViewDisplayFiltersExtraOptions;
|
||||
};
|
||||
|
||||
export const DisplayFilterExtraOptions: FC<TDisplayFilterExtraOptions> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewType, filterKey } = props;
|
||||
// hooks
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
|
||||
const optionTitle = useMemo(() => EXTRA_OPTIONS_PROPERTY[filterKey].label, [filterKey]);
|
||||
|
||||
const isSelected = viewDetailStore?.appliedFilters?.display_filters?.[filterKey] ? true : false;
|
||||
|
||||
const handlePropertySelection = useCallback(
|
||||
() => viewDetailStore?.setDisplayFilters({ [filterKey]: !isSelected }),
|
||||
[viewDetailStore, filterKey, isSelected]
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div
|
||||
className="relative w-full flex items-center overflow-hidden gap-2.5 cursor-pointer p-1 py-1.5 rounded hover:bg-custom-background-80 transition-all group"
|
||||
onClick={handlePropertySelection}
|
||||
>
|
||||
<div
|
||||
className={`flex-shrink-0 w-3 h-3 flex justify-center items-center border rounded text-bold ${
|
||||
isSelected
|
||||
? "border-custom-primary-100 bg-custom-primary-100"
|
||||
: "border-custom-border-400 bg-custom-background-100"
|
||||
}`}
|
||||
>
|
||||
{isSelected && <Check size={14} />}
|
||||
</div>
|
||||
<div className="text-xs block truncate line-clamp-1 text-custom-text-200 group-hover:text-custom-text-100">
|
||||
{optionTitle || "Extra Option"}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import { FC, useState } from "react";
|
||||
import { FC, useCallback, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ChevronUp, ChevronDown } from "lucide-react";
|
||||
import filter from "lodash/filter";
|
||||
@ -7,9 +7,9 @@ import uniq from "lodash/uniq";
|
||||
// hooks
|
||||
import { useViewDetail } from "hooks/store";
|
||||
// components
|
||||
import { ViewDisplayPropertiesRoot, ViewDisplayFiltersItemRoot } from "../";
|
||||
import { ViewDisplayPropertiesRoot, ViewDisplayFiltersItemRoot, DisplayFilterExtraOptions } from "../";
|
||||
// types
|
||||
import { TViewDisplayFilters, TViewTypes } from "@plane/types";
|
||||
import { TViewDisplayFilters, TViewDisplayFiltersExtraOptions, TViewTypes } from "@plane/types";
|
||||
// constants
|
||||
import { EViewPageType, viewDefaultFilterParametersByViewTypeAndLayout } from "constants/view";
|
||||
|
||||
@ -29,18 +29,22 @@ export const ViewDisplayFiltersRoot: FC<TViewDisplayFiltersRoot> = observer((pro
|
||||
const [filterVisibility, setFilterVisibility] = useState<(Partial<keyof TViewDisplayFilters> | "display_property")[]>(
|
||||
[]
|
||||
);
|
||||
const handleFilterVisibility = (key: keyof TViewDisplayFilters | "display_property") => {
|
||||
const handleFilterVisibility = useCallback((key: keyof TViewDisplayFilters | "display_property") => {
|
||||
setFilterVisibility((prevData = []) => {
|
||||
if (prevData.includes(key)) return filter(prevData, (item) => item !== key);
|
||||
return uniq(concat(prevData, [key]));
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const layout = viewDetailStore?.appliedFilters?.display_filters?.layout;
|
||||
const filtersProperties = useMemo(() => {
|
||||
const layout = viewDetailStore?.appliedFilters?.display_filters?.layout;
|
||||
return layout ? viewDefaultFilterParametersByViewTypeAndLayout(viewPageType, layout, "display_filters") : [];
|
||||
}, [viewDetailStore, viewPageType]);
|
||||
|
||||
const filtersProperties = layout
|
||||
? viewDefaultFilterParametersByViewTypeAndLayout(viewPageType, layout, "display_filters")
|
||||
: [];
|
||||
const filtersExtraProperties = useMemo(() => {
|
||||
const layout = viewDetailStore?.appliedFilters?.display_filters?.layout;
|
||||
return layout ? viewDefaultFilterParametersByViewTypeAndLayout(viewPageType, layout, "extra_options") : [];
|
||||
}, [viewDetailStore, viewPageType]);
|
||||
|
||||
return (
|
||||
<div className="space-y-1 divide-y divide-custom-border-300">
|
||||
@ -67,7 +71,7 @@ export const ViewDisplayFiltersRoot: FC<TViewDisplayFiltersRoot> = observer((pro
|
||||
</div>
|
||||
|
||||
{filtersProperties.map((filterKey) => (
|
||||
<div key={filterKey} className="relative py-1 last:pb-0">
|
||||
<div key={filterKey} className="relative py-1">
|
||||
<div className="sticky top-0 z-20 flex justify-between items-center gap-2 bg-custom-background-100 select-none">
|
||||
<div className="font-medium text-xs text-custom-text-300 capitalize py-1">
|
||||
{filterKey.replaceAll("_", " ")}
|
||||
@ -91,10 +95,16 @@ export const ViewDisplayFiltersRoot: FC<TViewDisplayFiltersRoot> = observer((pro
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* extra options */}
|
||||
<div>
|
||||
<div>Show sub issues</div>
|
||||
<div>Show Empty groups</div>
|
||||
<div className="pt-1 pb-0">
|
||||
{filtersExtraProperties.map((option) => (
|
||||
<DisplayFilterExtraOptions
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={viewId}
|
||||
viewType={viewType}
|
||||
filterKey={option as TViewDisplayFiltersExtraOptions}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useViewDetail } from "hooks/store";
|
||||
@ -18,9 +18,12 @@ export const ViewDisplayPropertySelection: FC<TViewDisplayPropertySelection> = o
|
||||
// hooks
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
|
||||
const propertyIsSelected = viewDetailStore?.appliedFilters?.display_properties?.[property];
|
||||
const handlePropertySelection = useCallback(
|
||||
() => viewDetailStore?.setDisplayProperties(property),
|
||||
[viewDetailStore, property]
|
||||
);
|
||||
|
||||
const handlePropertySelection = () => viewDetailStore?.setDisplayProperties(property);
|
||||
const propertyIsSelected = viewDetailStore?.appliedFilters?.display_properties?.[property];
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment, ReactNode, useRef, useState } from "react";
|
||||
import { FC, Fragment, ReactNode, useCallback, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
import { usePopper } from "react-popper";
|
||||
@ -64,14 +64,17 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
|
||||
],
|
||||
});
|
||||
|
||||
const handleDropdownOpen = () => setDropdownToggle(true);
|
||||
const handleDropdownClose = () => setDropdownToggle(false);
|
||||
const handleDropdownToggle = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!dropdownToggle) handleDropdownOpen();
|
||||
else handleDropdownClose();
|
||||
};
|
||||
const handleDropdownOpen = useCallback(() => setDropdownToggle(true), []);
|
||||
const handleDropdownClose = useCallback(() => setDropdownToggle(false), []);
|
||||
const handleDropdownToggle = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!dropdownToggle) handleDropdownOpen();
|
||||
else handleDropdownClose();
|
||||
},
|
||||
[dropdownToggle, handleDropdownOpen, handleDropdownClose]
|
||||
);
|
||||
|
||||
useOutsideClickDetector(dropdownRef, () => dateCustomFilterToggle === undefined && handleDropdownClose());
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, useState } from "react";
|
||||
import { FC, useCallback, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useViewDetail, useViewFilter } from "hooks/store";
|
||||
@ -27,28 +27,37 @@ export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) =>
|
||||
// state
|
||||
const [viewAll, setViewAll] = useState(false);
|
||||
|
||||
const propertyIds = viewFilterHelper?.filterIdsWithKey(filterKey) || [];
|
||||
const propertyIds = useMemo(() => viewFilterHelper?.filterIdsWithKey(filterKey) || [], [viewFilterHelper, filterKey]);
|
||||
|
||||
const filterPropertyIds = propertyIds.length > 5 ? (viewAll ? propertyIds : propertyIds.slice(0, 5)) : propertyIds;
|
||||
const filterPropertyIds = useMemo(
|
||||
() => (propertyIds.length > 5 ? (viewAll ? propertyIds : propertyIds.slice(0, 5)) : propertyIds),
|
||||
[propertyIds, viewAll]
|
||||
);
|
||||
|
||||
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) => viewDetailStore?.setFilters(filterKey, date));
|
||||
else setDateCustomFilterToggle(filterKey);
|
||||
const handlePropertySelection = useCallback(
|
||||
(_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) => viewDetailStore?.setFilters(filterKey, date));
|
||||
else setDateCustomFilterToggle(filterKey);
|
||||
} else viewDetailStore?.setFilters(filterKey, _propertyId);
|
||||
} else viewDetailStore?.setFilters(filterKey, _propertyId);
|
||||
} else viewDetailStore?.setFilters(filterKey, _propertyId);
|
||||
};
|
||||
},
|
||||
[filterKey, viewDetailStore, setDateCustomFilterToggle]
|
||||
);
|
||||
|
||||
const handleCustomDateSelection = (selectedDates: string[]) => {
|
||||
selectedDates.forEach((date: string) => {
|
||||
viewDetailStore?.setFilters(filterKey, date);
|
||||
setDateCustomFilterToggle(undefined);
|
||||
});
|
||||
};
|
||||
const handleCustomDateSelection = useCallback(
|
||||
(selectedDates: string[]) => {
|
||||
selectedDates.forEach((date: string) => {
|
||||
viewDetailStore?.setFilters(filterKey, date);
|
||||
setDateCustomFilterToggle(undefined);
|
||||
});
|
||||
},
|
||||
[filterKey, viewDetailStore, setDateCustomFilterToggle]
|
||||
);
|
||||
|
||||
if (propertyIds.length <= 0)
|
||||
return <div className="text-xs italic py-1 text-custom-text-300">No items are available.</div>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { Check } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
@ -20,7 +20,10 @@ export const ViewFilterSelection: FC<TViewFilterSelection> = observer((props) =>
|
||||
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
|
||||
const propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || [];
|
||||
const propertyIds = useMemo(
|
||||
() => viewDetailStore?.appliedFilters?.filters?.[filterKey] || [],
|
||||
[viewDetailStore?.appliedFilters?.filters, filterKey]
|
||||
);
|
||||
|
||||
const isSelected = ["start_date", "target_date"].includes(filterKey)
|
||||
? propertyId === "custom"
|
||||
@ -30,6 +33,18 @@ export const ViewFilterSelection: FC<TViewFilterSelection> = observer((props) =>
|
||||
: propertyIds?.includes(propertyId)
|
||||
: propertyIds?.includes(propertyId) || false;
|
||||
|
||||
// const isSelected = useMemo(
|
||||
// () =>
|
||||
// ["start_date", "target_date"].includes(filterKey)
|
||||
// ? propertyId === "custom"
|
||||
// ? propertyIds.filter((id) => id.includes("-")).length > 0
|
||||
// ? true
|
||||
// : false
|
||||
// : propertyIds?.includes(propertyId)
|
||||
// : propertyIds?.includes(propertyId) || false,
|
||||
// [filterKey, propertyId, propertyIds]
|
||||
// );
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex-shrink-0 w-3 h-3 flex justify-center items-center border rounded text-bold ${
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, useState } from "react";
|
||||
import { FC, useCallback, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
import concat from "lodash/concat";
|
||||
@ -36,20 +36,19 @@ export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
// state
|
||||
const [filterVisibility, setFilterVisibility] = useState<Partial<keyof TViewFilters>[]>([]);
|
||||
const handleFilterVisibility = (key: keyof TViewFilters) => {
|
||||
const handleFilterVisibility = useCallback((key: keyof TViewFilters) => {
|
||||
setFilterVisibility((prevData = []) => {
|
||||
if (prevData.includes(key)) return filter(prevData, (item) => item !== key);
|
||||
return uniq(concat(prevData, [key]));
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const layout = viewDetailStore?.appliedFilters?.display_filters?.layout;
|
||||
const filtersProperties = useMemo(() => {
|
||||
const layout = viewDetailStore?.appliedFilters?.display_filters?.layout;
|
||||
return layout ? viewDefaultFilterParametersByViewTypeAndLayout(viewPageType, layout, "filters") : [];
|
||||
}, [viewDetailStore?.appliedFilters?.display_filters?.layout, viewPageType]);
|
||||
|
||||
const filtersProperties = layout
|
||||
? viewDefaultFilterParametersByViewTypeAndLayout(viewPageType, layout, "filters")
|
||||
: [];
|
||||
|
||||
if (!layout || filtersProperties.length <= 0) return <></>;
|
||||
if (filtersProperties.length <= 0) return <></>;
|
||||
return (
|
||||
<div className="space-y-1 divide-y divide-custom-border-300">
|
||||
{filtersProperties.map((filterKey) => (
|
||||
|
74
web/components/view/header-tabs.tsx
Normal file
74
web/components/view/header-tabs.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { FC, Fragment, ReactNode, useMemo } from "react";
|
||||
import Link from "next/link";
|
||||
import { Briefcase, CheckCircle, ChevronRight } from "lucide-react";
|
||||
// hooks
|
||||
import { useProject } from "hooks/store";
|
||||
// types
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
type TViewHeader = {
|
||||
projectId: string | undefined;
|
||||
viewType: TViewTypes;
|
||||
titleIcon: ReactNode;
|
||||
title: string;
|
||||
workspaceViewTabOptions: { key: TViewTypes; title: string; href: string }[];
|
||||
};
|
||||
|
||||
export const ViewHeader: FC<TViewHeader> = (props) => {
|
||||
const { projectId, viewType, titleIcon, title, workspaceViewTabOptions } = props;
|
||||
// hooks
|
||||
const { getProjectById } = useProject();
|
||||
|
||||
const projectDetails = useMemo(
|
||||
() => (projectId ? getProjectById(projectId) : undefined),
|
||||
[projectId, getProjectById]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-2">
|
||||
{projectDetails && (
|
||||
<Fragment>
|
||||
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||
<div className="flex-shrink-0 w-6 h-6 rounded relative flex justify-center items-center bg-custom-background-80">
|
||||
{projectDetails?.icon_prop ? projectDetails?.icon_prop.toString() : <Briefcase size={12} />}
|
||||
</div>
|
||||
<div className="font-medium inline-block whitespace-nowrap overflow-hidden truncate line-clamp-1 text-sm">
|
||||
{projectDetails?.name ? projectDetails?.name : "Project Issues"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-custom-text-200">
|
||||
<ChevronRight size={12} />
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||
<div className="flex-shrink-0 w-6 h-6 rounded relative flex justify-center items-center bg-custom-background-80">
|
||||
{titleIcon ? titleIcon : <CheckCircle size={12} />}
|
||||
</div>
|
||||
<div className="font-medium inline-block whitespace-nowrap overflow-hidden truncate line-clamp-1 text-sm">
|
||||
{title ? title : "All Issues"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ml-auto relative flex items-center gap-3">
|
||||
<div className="relative flex items-center rounded border border-custom-border-200 bg-custom-background-80">
|
||||
{workspaceViewTabOptions.map((tab) => (
|
||||
<Link
|
||||
key={tab.key}
|
||||
href={tab.href}
|
||||
className={`p-4 py-1.5 rounded text-sm transition-all cursor-pointer font-medium
|
||||
${
|
||||
viewType === tab.key
|
||||
? "text-custom-text-100 bg-custom-background-100"
|
||||
: "text-custom-text-200 bg-custom-background-80 hover:text-custom-text-100"
|
||||
}`}
|
||||
>
|
||||
{tab.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,5 +1,7 @@
|
||||
export * from "./root";
|
||||
|
||||
export * from "./header-tabs";
|
||||
|
||||
// views
|
||||
export * from "./views/root";
|
||||
export * from "./views/view-item";
|
||||
@ -25,6 +27,7 @@ export * from "./display-filters/root";
|
||||
export * from "./display-filters/display-filter-item-root";
|
||||
export * from "./display-filters/display-filter-item";
|
||||
export * from "./display-filters/display-filter-selection";
|
||||
export * from "./display-filters/extra-options";
|
||||
|
||||
// view display properties
|
||||
export * from "./display-properties/root";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment } from "react";
|
||||
import { FC, Fragment, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { LucideIcon, List, Kanban, Calendar, Sheet, GanttChartSquare } from "lucide-react";
|
||||
// hooks
|
||||
@ -18,20 +18,23 @@ type TViewLayoutRoot = {
|
||||
viewPageType: EViewPageType;
|
||||
};
|
||||
|
||||
const LAYOUTS_DATA: { key: EViewLayouts; title: string; icon: LucideIcon }[] = [
|
||||
{ key: EViewLayouts.LIST, title: "List Layout", icon: List },
|
||||
{ key: EViewLayouts.KANBAN, title: "Kanban Layout", icon: Kanban },
|
||||
{ key: EViewLayouts.CALENDAR, title: "Calendar Layout", icon: Calendar },
|
||||
{ key: EViewLayouts.SPREADSHEET, title: "Spreadsheet Layout", icon: Sheet },
|
||||
{ key: EViewLayouts.GANTT, title: "Gantt Chart layout", icon: GanttChartSquare },
|
||||
];
|
||||
|
||||
export const ViewLayoutRoot: FC<TViewLayoutRoot> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewType, viewPageType } = props;
|
||||
// hooks
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
|
||||
const validLayouts = viewPageDefaultLayoutsByPageType(viewPageType);
|
||||
const LAYOUTS_DATA: { key: EViewLayouts; title: string; icon: LucideIcon }[] = useMemo(
|
||||
() => [
|
||||
{ key: EViewLayouts.LIST, title: "List Layout", icon: List },
|
||||
{ key: EViewLayouts.KANBAN, title: "Kanban Layout", icon: Kanban },
|
||||
{ key: EViewLayouts.CALENDAR, title: "Calendar Layout", icon: Calendar },
|
||||
{ key: EViewLayouts.SPREADSHEET, title: "Spreadsheet Layout", icon: Sheet },
|
||||
{ key: EViewLayouts.GANTT, title: "Gantt Chart layout", icon: GanttChartSquare },
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const validLayouts = useMemo(() => viewPageDefaultLayoutsByPageType(viewPageType), [viewPageType]);
|
||||
|
||||
if (!viewDetailStore || validLayouts.length <= 1) return <></>;
|
||||
return (
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { FC, Fragment, useEffect, useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { FC, Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
// hooks
|
||||
@ -35,7 +33,6 @@ type TGlobalViewRoot = {
|
||||
viewType: TViewTypes;
|
||||
viewPageType: EViewPageType;
|
||||
baseRoute: string;
|
||||
workspaceViewTabOptions: { key: TViewTypes; title: string; href: string }[];
|
||||
};
|
||||
|
||||
type TViewOperationsToggle = {
|
||||
@ -44,7 +41,7 @@ type TViewOperationsToggle = {
|
||||
};
|
||||
|
||||
export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewType, viewPageType, baseRoute, workspaceViewTabOptions } = props;
|
||||
const { workspaceSlug, projectId, viewId, viewType, viewPageType, baseRoute } = props;
|
||||
// hooks
|
||||
const viewStore = useView(workspaceSlug, projectId, viewType);
|
||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||
@ -54,9 +51,11 @@ export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
|
||||
type: undefined,
|
||||
viewId: undefined,
|
||||
});
|
||||
const handleViewOperationsToggle = (type: TViewOperationsToggle["type"], viewId: string | undefined) =>
|
||||
setViewOperationsToggle({ type, viewId });
|
||||
|
||||
const handleViewOperationsToggle = useCallback(
|
||||
(type: TViewOperationsToggle["type"], viewId: string | undefined) => setViewOperationsToggle({ type, viewId }),
|
||||
[]
|
||||
);
|
||||
// hooks
|
||||
const viewDetailCreateEditStore = useViewDetail(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
@ -153,7 +152,7 @@ export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
|
||||
}
|
||||
},
|
||||
}),
|
||||
[viewStore, viewDetailStore, setToastAlert, viewDetailCreateEditStore]
|
||||
[viewStore, viewDetailStore, setToastAlert, viewDetailCreateEditStore, handleViewOperationsToggle]
|
||||
);
|
||||
|
||||
// fetch all views
|
||||
@ -174,43 +173,13 @@ export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
<div className="relative flex items-center gap-2 px-5 py-4">
|
||||
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||
<div className="flex-shrink-0 w-6 h-6 rounded relative flex justify-center items-center bg-custom-background-80">
|
||||
<CheckCircle size={12} />
|
||||
</div>
|
||||
<div className="font-medium inline-block whitespace-nowrap overflow-hidden truncate line-clamp-1">
|
||||
All Issues
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ml-auto relative flex items-center gap-3">
|
||||
<div className="relative flex items-center rounded border border-custom-border-200 bg-custom-background-80">
|
||||
{workspaceViewTabOptions.map((tab) => (
|
||||
<Link
|
||||
key={tab.key}
|
||||
href={tab.href}
|
||||
className={`p-4 py-1.5 rounded text-sm transition-all cursor-pointer font-medium
|
||||
${
|
||||
viewType === tab.key
|
||||
? "text-custom-text-100 bg-custom-background-100"
|
||||
: "text-custom-text-200 bg-custom-background-80 hover:text-custom-text-100"
|
||||
}`}
|
||||
>
|
||||
{tab.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{viewStore?.loader && viewStore?.loader === "init-loader" ? (
|
||||
<div className="relative w-full h-full flex justify-center items-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="border-b border-custom-border-200">
|
||||
<div className="border-b border-custom-border-200 pt-2">
|
||||
<ViewRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment, useEffect, useState } from "react";
|
||||
import { FC, Fragment, useEffect, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Plus } from "lucide-react";
|
||||
// hooks
|
||||
@ -31,7 +31,7 @@ export const ViewRoot: FC<TViewRoot> = observer((props) => {
|
||||
const handleViewTabsVisibility = () => {
|
||||
const tabContainer = document.getElementById("tab-container");
|
||||
const tabItemViewMore = document.getElementById("tab-item-view-more");
|
||||
const itemWidth = 116;
|
||||
const itemWidth = 124;
|
||||
if (!tabContainer || !tabItemViewMore) return;
|
||||
|
||||
const containerWidth = tabContainer.clientWidth;
|
||||
@ -49,12 +49,14 @@ export const ViewRoot: FC<TViewRoot> = observer((props) => {
|
||||
return () => window.removeEventListener("resize", () => handleViewTabsVisibility());
|
||||
}, [viewStore?.viewIds]);
|
||||
|
||||
const viewIds = viewStore?.viewIds?.slice(0, itemsToRenderViewsCount || viewStore?.viewIds.length) || [];
|
||||
|
||||
if (!viewIds.includes(viewId)) {
|
||||
viewIds.pop();
|
||||
viewIds.push(viewId);
|
||||
}
|
||||
const viewIds = useMemo(() => {
|
||||
const ids = viewStore?.viewIds?.slice(0, itemsToRenderViewsCount || viewStore?.viewIds.length) || [];
|
||||
if (!ids.includes(viewId)) {
|
||||
ids.pop();
|
||||
ids.push(viewId);
|
||||
}
|
||||
return ids;
|
||||
}, [viewId, viewStore, itemsToRenderViewsCount]);
|
||||
|
||||
return (
|
||||
<div className="relative flex justify-between px-5 gap-2">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, Fragment, ReactNode, useRef, useState } from "react";
|
||||
import { FC, Fragment, ReactNode, useCallback, useRef, useState } from "react";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
import { usePopper } from "react-popper";
|
||||
import { Placement } from "@popperjs/core";
|
||||
@ -7,7 +7,7 @@ import { Plus, Search } from "lucide-react";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
import { useView } from "hooks/store";
|
||||
// components
|
||||
import { ViewDropdownItem } from "..";
|
||||
import { ViewDropdownItem } from "../";
|
||||
// types
|
||||
import { TViewTypes } from "@plane/types";
|
||||
import { TViewOperations } from "../types";
|
||||
@ -63,14 +63,17 @@ export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
||||
],
|
||||
});
|
||||
|
||||
const handleDropdownOpen = () => setDropdownToggle(true);
|
||||
const handleDropdownClose = () => setDropdownToggle(false);
|
||||
const handleDropdownToggle = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!dropdownToggle) handleDropdownOpen();
|
||||
else handleDropdownClose();
|
||||
};
|
||||
const handleDropdownOpen = useCallback(() => setDropdownToggle(true), []);
|
||||
const handleDropdownClose = useCallback(() => setDropdownToggle(false), []);
|
||||
const handleDropdownToggle = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!dropdownToggle) handleDropdownOpen();
|
||||
else handleDropdownClose();
|
||||
},
|
||||
[dropdownToggle, handleDropdownOpen, handleDropdownClose]
|
||||
);
|
||||
|
||||
useOutsideClickDetector(dropdownRef, handleDropdownClose);
|
||||
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
TViewDisplayFiltersGrouped,
|
||||
TViewDisplayFiltersOrderBy,
|
||||
TViewDisplayFiltersType,
|
||||
TViewDisplayFiltersExtraOptions,
|
||||
} from "@plane/types";
|
||||
|
||||
// filters constants
|
||||
@ -61,7 +62,7 @@ export const TYPE_PROPERTY: Record<TViewDisplayFiltersType | "null", { label: st
|
||||
backlog: { label: "Backlog issues" },
|
||||
};
|
||||
|
||||
export const EXTRA_OPTIONS_PROPERTY: Record<string, { label: string }> = {
|
||||
export const EXTRA_OPTIONS_PROPERTY: Record<TViewDisplayFiltersExtraOptions, { label: string }> = {
|
||||
sub_issue: { label: "Sub Issues" },
|
||||
show_empty_groups: { label: "Show Empty Groups" },
|
||||
};
|
||||
@ -111,8 +112,9 @@ const ALL_FILTER_PERMISSIONS: TFilterPermissions["all"] = {
|
||||
layouts: [EViewLayouts.SPREADSHEET],
|
||||
[EViewLayouts.SPREADSHEET]: {
|
||||
filters: ["project", "priority", "state_group", "assignees", "created_by", "labels", "start_date", "target_date"],
|
||||
display_filters: ["type"],
|
||||
// display_filters: ["type"],
|
||||
// extra_options: [],
|
||||
display_filters: ["group_by", "sub_group_by", "order_by", "type"],
|
||||
extra_options: ["sub_issue", "show_empty_groups"],
|
||||
display_properties: true,
|
||||
},
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
GROUP_BY_PROPERTY,
|
||||
ORDER_BY_PROPERTY,
|
||||
TYPE_PROPERTY,
|
||||
EViewLayouts,
|
||||
} from "constants/view/filters";
|
||||
// helpers
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
@ -346,12 +347,19 @@ export const useViewFilter = (workspaceSlug: string, projectId: string | undefin
|
||||
}
|
||||
};
|
||||
|
||||
const displayFilterIdsWithKey = (displayFilterKey: keyof TViewDisplayFilters): string[] | undefined => {
|
||||
const displayFilterIdsWithKey = (
|
||||
displayFilterKey: keyof TViewDisplayFilters,
|
||||
layout?: EViewLayouts
|
||||
): string[] | undefined => {
|
||||
if (!displayFilterKey) return undefined;
|
||||
|
||||
switch (displayFilterKey) {
|
||||
case "group_by":
|
||||
return Object.keys(GROUP_BY_PROPERTY) || undefined;
|
||||
return (
|
||||
Object.keys(GROUP_BY_PROPERTY).filter((property) =>
|
||||
layout === EViewLayouts.KANBAN ? (property !== "null" ? false : true) : true
|
||||
) || undefined
|
||||
);
|
||||
case "sub_group_by":
|
||||
return Object.keys(GROUP_BY_PROPERTY) || undefined;
|
||||
case "order_by":
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ReactElement, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// components
|
||||
import { GlobalViewRoot } from "components/view";
|
||||
import { GlobalViewRoot, ViewHeader } from "components/view";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
// constants
|
||||
@ -31,8 +32,20 @@ const WorkspacePrivateViewPage: NextPageWithLayout = () => {
|
||||
|
||||
if (!workspaceSlug || !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">
|
||||
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
|
||||
<div className="flex-shrink-0 w-full">
|
||||
{/* header */}
|
||||
<div className="px-5 pt-4 pb-2 border-b border-custom-border-200">
|
||||
<ViewHeader
|
||||
projectId={undefined}
|
||||
viewType={VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS}
|
||||
titleIcon={<CheckCircle size={12} />}
|
||||
title="All Issues"
|
||||
workspaceViewTabOptions={workspaceViewTabOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* content */}
|
||||
<GlobalViewRoot
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={undefined}
|
||||
@ -40,9 +53,12 @@ const WorkspacePrivateViewPage: NextPageWithLayout = () => {
|
||||
viewType={VIEW_TYPES.WORKSPACE_PRIVATE_VIEWS}
|
||||
viewPageType={EViewPageType.ALL}
|
||||
baseRoute={`/${workspaceSlug?.toString()}/views/private`}
|
||||
workspaceViewTabOptions={workspaceViewTabOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full h-full overflow-hidden relative flex justify-center items-center text-sm text-custom-text-300">
|
||||
Issues render placeholder
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ReactElement, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// components
|
||||
import { GlobalViewRoot } from "components/view";
|
||||
import { GlobalViewRoot, ViewHeader } from "components/view";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
// constants
|
||||
@ -33,6 +34,18 @@ const WorkspacePublicViewPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
|
||||
<div className="flex-shrink-0 w-full">
|
||||
{/* header */}
|
||||
<div className="px-5 pt-4 pb-2 border-b border-custom-border-200">
|
||||
<ViewHeader
|
||||
projectId={undefined}
|
||||
viewType={VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS}
|
||||
titleIcon={<CheckCircle size={12} />}
|
||||
title="All Issues"
|
||||
workspaceViewTabOptions={workspaceViewTabOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* content */}
|
||||
<GlobalViewRoot
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={undefined}
|
||||
@ -40,10 +53,12 @@ const WorkspacePublicViewPage: NextPageWithLayout = () => {
|
||||
viewType={VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS}
|
||||
viewPageType={EViewPageType.ALL}
|
||||
baseRoute={`/${workspaceSlug?.toString()}/views/public`}
|
||||
workspaceViewTabOptions={workspaceViewTabOptions}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full h-full overflow-hidden">Issues render</div>
|
||||
|
||||
<div className="w-full h-full overflow-hidden relative flex justify-center items-center text-sm text-custom-text-300">
|
||||
Issues render placeholder
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -14,19 +14,19 @@ import { EViewPageType, viewPageDefaultLayoutsByPageType } from "constants/view"
|
||||
export class FiltersHelper {
|
||||
// computed filters
|
||||
computedFilters = (filters: TViewFilters, defaultValues?: Partial<TViewFilters>): TViewFilters => ({
|
||||
project: get(defaultValues, "project", get(filters, "project", [])),
|
||||
module: get(defaultValues, "module", get(filters, "module", [])),
|
||||
cycle: get(defaultValues, "cycle", get(filters, "cycle", [])),
|
||||
priority: get(defaultValues, "priority", get(filters, "priority", [])),
|
||||
state: get(defaultValues, "state", get(filters, "state", [])),
|
||||
state_group: get(defaultValues, "state_group", get(filters, "state_group", [])),
|
||||
assignees: get(defaultValues, "assignees", get(filters, "assignees", [])),
|
||||
mentions: get(defaultValues, "mentions", get(filters, "mentions", [])),
|
||||
subscriber: get(defaultValues, "subscriber", get(filters, "subscriber", [])),
|
||||
created_by: get(defaultValues, "created_by", get(filters, "created_by", [])),
|
||||
labels: get(defaultValues, "labels", get(filters, "labels", [])),
|
||||
start_date: get(defaultValues, "start_date", get(filters, "start_date", [])),
|
||||
target_date: get(defaultValues, "target_date", get(filters, "target_date", [])),
|
||||
project: defaultValues?.project || filters?.project || [],
|
||||
module: defaultValues?.module || filters?.module || [],
|
||||
cycle: defaultValues?.cycle || filters?.cycle || [],
|
||||
priority: defaultValues?.priority || filters?.priority || [],
|
||||
state: defaultValues?.state || filters?.state || [],
|
||||
state_group: defaultValues?.state_group || filters?.state_group || [],
|
||||
assignees: defaultValues?.assignees || filters?.assignees || [],
|
||||
mentions: defaultValues?.mentions || filters?.mentions || [],
|
||||
subscriber: defaultValues?.subscriber || filters?.subscriber || [],
|
||||
created_by: defaultValues?.created_by || filters?.created_by || [],
|
||||
labels: defaultValues?.labels || filters?.labels || [],
|
||||
start_date: defaultValues?.start_date || filters?.start_date || [],
|
||||
target_date: defaultValues?.target_date || filters?.target_date || [],
|
||||
});
|
||||
|
||||
// computed display filters
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
// helpers
|
||||
import { FiltersHelper } from "./helpers/filters_helpers";
|
||||
// constants
|
||||
import { EViewPageType, viewDefaultFilterParametersByViewTypeAndLayout } from "constants/view";
|
||||
import { EViewLayouts, EViewPageType, viewDefaultFilterParametersByViewTypeAndLayout } from "constants/view";
|
||||
|
||||
type TLoader = "updating" | undefined;
|
||||
|
||||
@ -250,11 +250,11 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
||||
const sub_issue = appliedFilters?.display_filters?.sub_issue;
|
||||
|
||||
if (group_by === undefined && display_filters.sub_group_by) display_filters.sub_group_by = undefined;
|
||||
if (layout === "kanban") {
|
||||
if (layout === EViewLayouts.KANBAN) {
|
||||
if (sub_group_by === group_by) display_filters.group_by = undefined;
|
||||
if (group_by === null) display_filters.group_by = "state";
|
||||
}
|
||||
if (layout === "spreadsheet" && sub_issue === true) display_filters.sub_issue = false;
|
||||
if (layout === EViewLayouts.SPREADSHEET && sub_issue === true) display_filters.sub_issue = false;
|
||||
|
||||
runInAction(() => {
|
||||
Object.keys(display_filters).forEach((key) => {
|
||||
|
Loading…
Reference in New Issue
Block a user