mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: updated ui and store
This commit is contained in:
parent
1ce7f20c2d
commit
f90595ca31
@ -10,10 +10,8 @@ import {
|
|||||||
ViewRoot,
|
ViewRoot,
|
||||||
ViewCreateEditForm,
|
ViewCreateEditForm,
|
||||||
ViewLayoutRoot,
|
ViewLayoutRoot,
|
||||||
ViewFiltersRoot,
|
|
||||||
ViewFiltersDropdown,
|
ViewFiltersDropdown,
|
||||||
ViewDisplayFiltersDropdown,
|
ViewDisplayFiltersDropdown,
|
||||||
ViewDisplayPropertiesRoot,
|
|
||||||
ViewAppliedFiltersRoot,
|
ViewAppliedFiltersRoot,
|
||||||
ViewDuplicateConfirmationModal,
|
ViewDuplicateConfirmationModal,
|
||||||
ViewDeleteConfirmationModal,
|
ViewDeleteConfirmationModal,
|
||||||
@ -73,11 +71,12 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
() => ({
|
() => ({
|
||||||
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: (filters: Partial<TViewFilters>) => viewDetailStore?.setFilters(filters),
|
setFilters: (filterKey: keyof TViewFilters, filterValue: "clear_all" | string) =>
|
||||||
|
viewDetailStore?.setFilters(filterKey, filterValue),
|
||||||
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) =>
|
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) =>
|
||||||
viewDetailStore?.setDisplayFilters(display_filters),
|
viewDetailStore?.setDisplayFilters(display_filters),
|
||||||
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) =>
|
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) =>
|
||||||
viewDetailStore?.setDisplayProperties(display_properties),
|
viewDetailStore?.setDisplayProperties(displayPropertyKey),
|
||||||
localViewCreateEdit: (viewId: string | undefined) => {
|
localViewCreateEdit: (viewId: string | undefined) => {
|
||||||
if (viewId === undefined) {
|
if (viewId === undefined) {
|
||||||
const viewPayload = viewLocalPayload;
|
const viewPayload = viewLocalPayload;
|
||||||
@ -171,29 +170,8 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-5 border-b border-custom-border-300">
|
<div className="p-5 py-2 border-b border-custom-border-200 relative flex gap-2">
|
||||||
<ViewDisplayPropertiesRoot
|
<div className="w-full overflow-hidden">
|
||||||
workspaceSlug={workspaceSlug}
|
|
||||||
projectId={projectId}
|
|
||||||
viewId={viewId}
|
|
||||||
viewType={viewType}
|
|
||||||
viewOperations={viewOperations}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <div className="m-5 px-2 rounded border border-custom-border-300 w-[400px] max-h-[500px] overflow-hidden overflow-y-auto mx-auto bg-custom-background-100">
|
|
||||||
<ViewFiltersRoot
|
|
||||||
workspaceSlug={workspaceSlug}
|
|
||||||
projectId={projectId}
|
|
||||||
viewId={viewId}
|
|
||||||
viewType={viewType}
|
|
||||||
viewOperations={viewOperations}
|
|
||||||
baseRoute={baseRoute}
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div className="p-5 border-b border-custom-border-200 relative flex gap-2">
|
|
||||||
{/* <div className="w-full">
|
|
||||||
<ViewAppliedFiltersRoot
|
<ViewAppliedFiltersRoot
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
@ -201,9 +179,9 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0 h-full">
|
<div className="flex-shrink-0">
|
||||||
<ViewLayoutRoot
|
<ViewLayoutRoot
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
@ -220,7 +198,6 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
baseRoute={baseRoute}
|
|
||||||
displayDropdownText={false}
|
displayDropdownText={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -232,12 +209,11 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
baseRoute={baseRoute}
|
|
||||||
displayDropdownText={false}
|
displayDropdownText={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border border-custom-border-300 relative flex items-center gap-1 h-8 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="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="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>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { User, X } from "lucide-react";
|
import { ImagePlus, X } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useViewDetail } from "hooks/store";
|
import { useViewFilter } from "hooks/store";
|
||||||
// types
|
// types
|
||||||
import { TViewFilters, TViewTypes } from "@plane/types";
|
import { TViewFilters, TViewTypes } from "@plane/types";
|
||||||
|
import { TViewOperations } from "../types";
|
||||||
|
|
||||||
type TViewAppliedFiltersItem = {
|
type TViewAppliedFiltersItem = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -11,33 +12,30 @@ type TViewAppliedFiltersItem = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
filterKey: keyof TViewFilters;
|
filterKey: keyof TViewFilters;
|
||||||
filterId: string;
|
propertyId: string;
|
||||||
|
viewOperations: TViewOperations;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewAppliedFiltersItem: FC<TViewAppliedFiltersItem> = (props) => {
|
export const ViewAppliedFiltersItem: FC<TViewAppliedFiltersItem> = (props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, filterKey, filterId } = props;
|
const { workspaceSlug, projectId, filterKey, propertyId, viewOperations } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
||||||
|
|
||||||
|
const propertyDetail = viewFilterHelper?.propertyDetails(filterKey, propertyId) || undefined;
|
||||||
|
|
||||||
const removeFilterOption = () => {
|
const removeFilterOption = () => {
|
||||||
const filters = viewDetailStore?.appliedFilters?.filters;
|
viewOperations?.setFilters(filterKey, propertyId);
|
||||||
if (!filters) return;
|
|
||||||
const filterValues = filters[filterKey];
|
|
||||||
const updatedFilterValues = filterValues.filter((value) => value !== filterId);
|
|
||||||
viewDetailStore?.setFilters({ [filterKey]: updatedFilterValues });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`filter_value_${filterKey}_${filterId}`}
|
key={`filter_value_${filterKey}_${propertyId}`}
|
||||||
className="border border-custom-border-200 rounded relative flex items-center gap-1 px-1 py-0.5"
|
className="bg-custom-background-80 rounded relative flex items-center gap-1.5 p-1.5 py-1"
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center overflow-hidden">
|
<div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center overflow-hidden">
|
||||||
<User size={12} />
|
{propertyDetail?.icon || <ImagePlus size={14} />}
|
||||||
</div>
|
|
||||||
<div className="text-xs">
|
|
||||||
{filterKey} - {filterId}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-xs">{propertyDetail?.label || propertyId}</div>
|
||||||
<div
|
<div
|
||||||
className="flex-shrink-0 w-3.5 h-3.5 relative flex justify-center items-center overflow-hidden rounded-full transition-all cursor-pointer bg-custom-background-80 hover:bg-custom-background-90 text-custom-text-300 hover:text-custom-text-200"
|
className="flex-shrink-0 w-3.5 h-3.5 relative flex justify-center items-center overflow-hidden rounded-full transition-all cursor-pointer bg-custom-background-80 hover:bg-custom-background-90 text-custom-text-300 hover:text-custom-text-200"
|
||||||
onClick={removeFilterOption}
|
onClick={removeFilterOption}
|
||||||
|
@ -6,10 +6,9 @@ import { X } from "lucide-react";
|
|||||||
import { useViewDetail } from "hooks/store";
|
import { useViewDetail } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { ViewAppliedFiltersItem } from "./filter-item";
|
import { ViewAppliedFiltersItem } from "./filter-item";
|
||||||
// helpers
|
|
||||||
import { generateTitle } from "./helper";
|
|
||||||
// types
|
// types
|
||||||
import { TViewFilters, TViewTypes } from "@plane/types";
|
import { TViewFilters, TViewTypes } from "@plane/types";
|
||||||
|
import { TViewOperations } from "../types";
|
||||||
|
|
||||||
type TViewAppliedFilters = {
|
type TViewAppliedFilters = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -17,41 +16,51 @@ type TViewAppliedFilters = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
filterKey: keyof TViewFilters;
|
filterKey: keyof TViewFilters;
|
||||||
|
viewOperations: TViewOperations;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
|
export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, filterKey } = props;
|
const { workspaceSlug, projectId, viewId, viewType, filterKey, viewOperations } = props;
|
||||||
|
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
const filterKeyValue =
|
const propertyValues =
|
||||||
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
|
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
|
||||||
? viewDetailStore?.appliedFilters?.filters?.[filterKey] || undefined
|
? viewDetailStore?.appliedFilters?.filters?.[filterKey] || undefined
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const clearFilter = () => viewDetailStore?.setFilters({ [filterKey]: [] });
|
const clearPropertyFilter = () => viewDetailStore?.setFilters(filterKey, "clear_all");
|
||||||
|
|
||||||
if (!filterKeyValue || filterKeyValue.length <= -1) return <></>;
|
if (!propertyValues || propertyValues.length <= 0) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 py-1 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">{generateTitle(filterKey)}</div>
|
<div className="flex-shrink-0 text-xs text-custom-text-200 capitalize">{filterKey.replaceAll("_", " ")}</div>
|
||||||
<div className="relative flex items-center gap-1 flex-wrap">
|
<div className="relative flex items-center gap-1.5 flex-wrap">
|
||||||
{["1", "2", "3", "4"].map((filterId) => (
|
{propertyValues.length >= 100 ? (
|
||||||
<Fragment key={filterId}>
|
<div className="text-xs font-medium bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2">
|
||||||
<ViewAppliedFiltersItem
|
{propertyValues.length} {filterKey.replaceAll("_", " ")}s
|
||||||
workspaceSlug={workspaceSlug}
|
</div>
|
||||||
projectId={projectId}
|
) : (
|
||||||
viewId={viewId}
|
<>
|
||||||
viewType={viewType}
|
{propertyValues.map((propertyId) => (
|
||||||
filterKey={filterKey}
|
<Fragment key={propertyId}>
|
||||||
filterId={filterId}
|
<ViewAppliedFiltersItem
|
||||||
/>
|
workspaceSlug={workspaceSlug}
|
||||||
</Fragment>
|
projectId={projectId}
|
||||||
))}
|
viewId={viewId}
|
||||||
|
viewType={viewType}
|
||||||
|
filterKey={filterKey}
|
||||||
|
propertyId={propertyId}
|
||||||
|
viewOperations={viewOperations}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 rounded-full cursor-pointer transition-all bg-custom-background-80 hover:bg-custom-background-90 text-custom-text-300 hover:text-custom-text-200"
|
className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 rounded-full cursor-pointer transition-all bg-custom-background-80 hover:bg-custom-background-90 text-custom-text-300 hover:text-custom-text-200"
|
||||||
onClick={clearFilter}
|
onClick={clearPropertyFilter}
|
||||||
>
|
>
|
||||||
<X size={10} />
|
<X size={10} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
import { ReactNode } from "react";
|
|
||||||
import isEmpty from "lodash/isEmpty";
|
|
||||||
// types
|
|
||||||
import { TViewFilters } from "@plane/types";
|
|
||||||
|
|
||||||
type TComputedAppliedFilters = {
|
|
||||||
key: string;
|
|
||||||
title: string;
|
|
||||||
selectedOptions?: { id: string; icon: ""; title: ""; component: ReactNode }[];
|
|
||||||
dropdownOptions?: { id: string; icon: ""; title: ""; component: ReactNode }[];
|
|
||||||
}[];
|
|
||||||
|
|
||||||
export const filterOptions = (key: keyof TViewFilters, selectedFilters: string[]) => {
|
|
||||||
switch (key) {
|
|
||||||
case "project":
|
|
||||||
return [];
|
|
||||||
case "priority":
|
|
||||||
return [];
|
|
||||||
case "state":
|
|
||||||
return [];
|
|
||||||
case "state_group":
|
|
||||||
return [];
|
|
||||||
case "assignees":
|
|
||||||
return [];
|
|
||||||
case "mentions":
|
|
||||||
return [];
|
|
||||||
case "subscriber":
|
|
||||||
return [];
|
|
||||||
case "created_by":
|
|
||||||
return [];
|
|
||||||
case "labels":
|
|
||||||
return [];
|
|
||||||
case "start_date":
|
|
||||||
return [];
|
|
||||||
case "target_date":
|
|
||||||
return [];
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateTitle = (title: string) =>
|
|
||||||
title
|
|
||||||
.split("_")
|
|
||||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
export const constructAppliedFilters = (filters: TViewFilters): TComputedAppliedFilters => {
|
|
||||||
const appliedFilters: TComputedAppliedFilters = [];
|
|
||||||
|
|
||||||
if (filters && !isEmpty(filters)) {
|
|
||||||
Object.keys(filters).forEach((_filterKey) => {
|
|
||||||
const _key = _filterKey as keyof TViewFilters;
|
|
||||||
const _value = filters[_key];
|
|
||||||
|
|
||||||
if (_value && !isEmpty(_value)) {
|
|
||||||
appliedFilters.push({
|
|
||||||
key: _key,
|
|
||||||
title: generateTitle(_key),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return appliedFilters;
|
|
||||||
};
|
|
@ -19,7 +19,7 @@ type TViewAppliedFiltersRoot = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => {
|
export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType } = props;
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
@ -28,16 +28,14 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
|
|||||||
? Object.keys(viewDetailStore?.appliedFilters?.filters)
|
? Object.keys(viewDetailStore?.appliedFilters?.filters)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const clearAllFilters = () => {
|
const clearAllFilters = () => viewDetailStore?.setFilters(undefined, "clear_all");
|
||||||
const clearedFilters: Partial<Record<keyof TViewFilters, string[]>> = {};
|
|
||||||
filterKeys?.forEach((key) => {
|
|
||||||
const _key = key as keyof TViewFilters;
|
|
||||||
clearedFilters[_key] = [];
|
|
||||||
});
|
|
||||||
viewDetailStore?.setFilters(clearedFilters);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!filterKeys) return <></>;
|
if (!filterKeys || !viewDetailStore?.isFiltersApplied)
|
||||||
|
return (
|
||||||
|
<div className="relative w-full text-sm text-custom-text-200 inline-block truncate line-clamp-1 pt-1.5">
|
||||||
|
No filters applied. Apply filters to create views.
|
||||||
|
</div>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center gap-2 flex-wrap">
|
<div className="relative flex items-center gap-2 flex-wrap">
|
||||||
{filterKeys.map((key) => {
|
{filterKeys.map((key) => {
|
||||||
@ -50,12 +48,14 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
filterKey={filterKey}
|
filterKey={filterKey}
|
||||||
|
viewOperations={viewOperations}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 py-1 cursor-pointer transition-all hover:bg-custom-background-80 text-custom-text-200 hover:text-custom-text-100 min-h-[32px]"
|
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}
|
onClick={clearAllFilters}
|
||||||
>
|
>
|
||||||
<div className="text-xs">Clear All</div>
|
<div className="text-xs">Clear All</div>
|
||||||
|
@ -7,6 +7,8 @@ import { MonitorDot } from "lucide-react";
|
|||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// components
|
// components
|
||||||
import { ViewDisplayPropertiesRoot } from "../";
|
import { ViewDisplayPropertiesRoot } from "../";
|
||||||
|
// ui
|
||||||
|
import { Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { TViewOperations } from "../types";
|
import { TViewOperations } from "../types";
|
||||||
import { TViewTypes } from "@plane/types";
|
import { TViewTypes } from "@plane/types";
|
||||||
@ -17,22 +19,12 @@ type TViewDisplayFiltersDropdown = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
baseRoute: string;
|
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
displayDropdownText?: boolean;
|
displayDropdownText?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = observer((props) => {
|
export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = observer((props) => {
|
||||||
const {
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props;
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
viewId,
|
|
||||||
viewType,
|
|
||||||
viewOperations,
|
|
||||||
baseRoute,
|
|
||||||
children,
|
|
||||||
displayDropdownText = true,
|
|
||||||
} = props;
|
|
||||||
// state
|
// state
|
||||||
const [dropdownToggle, setDropdownToggle] = useState(false);
|
const [dropdownToggle, setDropdownToggle] = useState(false);
|
||||||
// refs
|
// refs
|
||||||
@ -74,22 +66,24 @@ export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = obser
|
|||||||
onClick={handleDropdownToggle}
|
onClick={handleDropdownToggle}
|
||||||
>
|
>
|
||||||
{children ? (
|
{children ? (
|
||||||
<span className="relative inline-block">{children}</span>
|
<div className="relative inline-block">{children}</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<Tooltip tooltipContent={"Display"} position="bottom">
|
||||||
className={`relative flex items-center gap-1 h-8 rounded px-2 transition-all
|
<div
|
||||||
|
className={`relative flex items-center gap-1 h-7 rounded px-2 transition-all
|
||||||
${
|
${
|
||||||
displayDropdownText
|
displayDropdownText
|
||||||
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
|
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
|
||||||
: `hover:bg-custom-background-80`
|
: `hover:bg-custom-background-80`
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<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">
|
||||||
<MonitorDot size={14} />
|
<MonitorDot size={14} />
|
||||||
|
</div>
|
||||||
|
{displayDropdownText && <div className="text-sm whitespace-nowrap">Display</div>}
|
||||||
</div>
|
</div>
|
||||||
{displayDropdownText && <div className="text-sm whitespace-nowrap">Display</div>}
|
</Tooltip>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// hooks
|
||||||
|
import { useViewDetail } from "hooks/store";
|
||||||
|
// types
|
||||||
|
import { TViewDisplayProperties, TViewTypes } from "@plane/types";
|
||||||
|
import { TViewOperations } from "../types";
|
||||||
|
|
||||||
|
type TViewDisplayPropertySelection = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string | undefined;
|
||||||
|
viewId: string;
|
||||||
|
viewType: TViewTypes;
|
||||||
|
viewOperations: TViewOperations;
|
||||||
|
property: keyof TViewDisplayProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewDisplayPropertySelection: FC<TViewDisplayPropertySelection> = observer((props) => {
|
||||||
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations, property } = props;
|
||||||
|
// hooks
|
||||||
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
|
const propertyIsSelected = viewDetailStore?.appliedFilters?.display_properties?.[property];
|
||||||
|
|
||||||
|
const handlePropertySelection = () => viewOperations?.setDisplayProperties(property);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative flex items-center gap-1 text-xs rounded p-0.5 px-2 border transition-all capitalize cursor-pointer
|
||||||
|
${
|
||||||
|
propertyIsSelected
|
||||||
|
? `border-custom-primary-100 bg-custom-primary-100`
|
||||||
|
: `border-custom-border-300 hover:bg-custom-background-80`
|
||||||
|
}`}
|
||||||
|
onClick={handlePropertySelection}
|
||||||
|
>
|
||||||
|
{["key"].includes(property) ? "ID" : property.replaceAll("_", " ")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -1,4 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC, Fragment } from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// components
|
||||||
|
import { ViewDisplayPropertySelection } from "../";
|
||||||
// types
|
// types
|
||||||
import { TViewDisplayProperties, TViewTypes } from "@plane/types";
|
import { TViewDisplayProperties, TViewTypes } from "@plane/types";
|
||||||
import { TViewOperations } from "../types";
|
import { TViewOperations } from "../types";
|
||||||
@ -11,7 +14,7 @@ type TViewDisplayPropertiesRoot = {
|
|||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = (props) => {
|
export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
|
||||||
|
|
||||||
const displayProperties: Partial<keyof TViewDisplayProperties>[] = [
|
const displayProperties: Partial<keyof TViewDisplayProperties>[] = [
|
||||||
@ -31,20 +34,17 @@ export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = (props)
|
|||||||
return (
|
return (
|
||||||
<div className="relative flex items-center flex-wrap gap-2">
|
<div className="relative flex items-center flex-wrap gap-2">
|
||||||
{displayProperties.map((property) => (
|
{displayProperties.map((property) => (
|
||||||
<div
|
<Fragment key={property}>
|
||||||
key={property}
|
<ViewDisplayPropertySelection
|
||||||
className={`relative flex items-center gap-1 text-xs rounded p-0.5 px-2 border transition-all capitalize cursor-pointer
|
workspaceSlug={workspaceSlug}
|
||||||
${
|
projectId={projectId}
|
||||||
false
|
viewId={viewId}
|
||||||
? `border-custom-primary-100 bg-custom-primary-100`
|
viewType={viewType}
|
||||||
: `border-custom-border-300 hover:bg-custom-background-80`
|
viewOperations={viewOperations}
|
||||||
}
|
property={property}
|
||||||
`}
|
/>
|
||||||
onClick={() => {}}
|
</Fragment>
|
||||||
>
|
|
||||||
{["key"].includes(property) ? "ID" : property.replaceAll("_", " ")}
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -7,6 +7,8 @@ import { ListFilter, Search } from "lucide-react";
|
|||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// components
|
// components
|
||||||
import { ViewFiltersRoot } from "../";
|
import { ViewFiltersRoot } from "../";
|
||||||
|
// ui
|
||||||
|
import { Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { TViewOperations } from "../types";
|
import { TViewOperations } from "../types";
|
||||||
import { TViewTypes } from "@plane/types";
|
import { TViewTypes } from "@plane/types";
|
||||||
@ -17,22 +19,12 @@ type TViewFiltersDropdown = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
baseRoute: string;
|
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
displayDropdownText?: boolean;
|
displayDropdownText?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) => {
|
export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) => {
|
||||||
const {
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props;
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
viewId,
|
|
||||||
viewType,
|
|
||||||
viewOperations,
|
|
||||||
baseRoute,
|
|
||||||
children,
|
|
||||||
displayDropdownText = true,
|
|
||||||
} = props;
|
|
||||||
// state
|
// state
|
||||||
const [dropdownToggle, setDropdownToggle] = useState(false);
|
const [dropdownToggle, setDropdownToggle] = useState(false);
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@ -77,20 +69,22 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
|
|||||||
{children ? (
|
{children ? (
|
||||||
<span className="relative inline-block">{children}</span>
|
<span className="relative inline-block">{children}</span>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<Tooltip tooltipContent={"Filters"} position="bottom">
|
||||||
className={`relative flex items-center gap-1 h-8 rounded px-2 transition-all
|
<div
|
||||||
|
className={`relative flex items-center gap-1 h-7 rounded px-2 transition-all
|
||||||
${
|
${
|
||||||
displayDropdownText
|
displayDropdownText
|
||||||
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
|
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
|
||||||
: `hover:bg-custom-background-80`
|
: `hover:bg-custom-background-80`
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<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">
|
||||||
<ListFilter size={14} />
|
<ListFilter size={14} />
|
||||||
|
</div>
|
||||||
|
{displayDropdownText && <div className="text-sm whitespace-nowrap">Filters</div>}
|
||||||
</div>
|
</div>
|
||||||
{displayDropdownText && <div className="text-sm whitespace-nowrap">Filters</div>}
|
</Tooltip>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
@ -122,7 +116,6 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
baseRoute={baseRoute}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import concat from "lodash/concat";
|
|
||||||
import pull from "lodash/pull";
|
|
||||||
import uniq from "lodash/uniq";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useViewFilter, useViewDetail } from "hooks/store";
|
import { useViewFilter } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { ViewFiltersItem, ViewFilterSelection } from "../";
|
import { ViewFiltersItem, ViewFilterSelection } from "../";
|
||||||
// types
|
// types
|
||||||
@ -17,15 +14,13 @@ type TViewFiltersItemRoot = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
baseRoute: string;
|
|
||||||
filterKey: keyof TViewFilters;
|
filterKey: keyof TViewFilters;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) => {
|
export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute, filterKey } = props;
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations, filterKey } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
|
||||||
// state
|
// state
|
||||||
const [viewAll, setViewAll] = useState(false);
|
const [viewAll, setViewAll] = useState(false);
|
||||||
|
|
||||||
@ -33,13 +28,7 @@ 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) => {
|
const handlePropertySelection = (_propertyId: string) => viewOperations?.setFilters(filterKey, _propertyId);
|
||||||
const _propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || [];
|
|
||||||
const isSelected = _propertyIds?.includes(_propertyId) || false;
|
|
||||||
viewOperations?.setFilters({
|
|
||||||
[filterKey]: isSelected ? pull(_propertyIds, _propertyId) : uniq(concat(_propertyIds, [_propertyId])),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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>;
|
||||||
@ -56,18 +45,12 @@ export const ViewFiltersItemRoot: FC<TViewFiltersItemRoot> = observer((props) =>
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
|
||||||
baseRoute={baseRoute}
|
|
||||||
filterKey={filterKey}
|
filterKey={filterKey}
|
||||||
propertyId={propertyId}
|
propertyId={propertyId}
|
||||||
/>
|
/>
|
||||||
<ViewFiltersItem
|
<ViewFiltersItem
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
viewId={viewId}
|
|
||||||
viewType={viewType}
|
|
||||||
viewOperations={viewOperations}
|
|
||||||
baseRoute={baseRoute}
|
|
||||||
filterKey={filterKey}
|
filterKey={filterKey}
|
||||||
propertyId={propertyId}
|
propertyId={propertyId}
|
||||||
/>
|
/>
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
import { FC, Fragment } from "react";
|
import { FC, Fragment } from "react";
|
||||||
import { CheckSquare } from "lucide-react";
|
import { ImagePlus } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useViewFilter } from "hooks/store";
|
import { useViewFilter } from "hooks/store";
|
||||||
// types
|
// types
|
||||||
import { TViewFilters, TViewTypes } from "@plane/types";
|
import { TViewFilters } from "@plane/types";
|
||||||
import { TViewOperations } from "../types";
|
|
||||||
// helpers
|
|
||||||
// import { filterPropertyItemByFilterKeyAndId } from "../helpers/filters";
|
|
||||||
|
|
||||||
type TViewFiltersItem = {
|
type TViewFiltersItem = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
viewId: string;
|
|
||||||
viewType: TViewTypes;
|
|
||||||
viewOperations: TViewOperations;
|
|
||||||
baseRoute: string;
|
|
||||||
filterKey: keyof TViewFilters;
|
filterKey: keyof TViewFilters;
|
||||||
propertyId: string;
|
propertyId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
|
export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute, filterKey, propertyId } = props;
|
const { workspaceSlug, projectId, filterKey, propertyId } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
||||||
|
|
||||||
@ -30,7 +24,7 @@ export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="flex-shrink-0 w-4 h-4 flex justify-center items-center">
|
<div className="flex-shrink-0 w-4 h-4 flex justify-center items-center">
|
||||||
{propertyDetail?.icon || <CheckSquare size={14} />}
|
{propertyDetail?.icon || <ImagePlus size={14} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs block truncate line-clamp-1 text-custom-text-200 group-hover:text-custom-text-100">
|
<div className="text-xs block truncate line-clamp-1 text-custom-text-200 group-hover:text-custom-text-100">
|
||||||
{propertyDetail?.label || propertyId}
|
{propertyDetail?.label || propertyId}
|
||||||
|
@ -5,21 +5,18 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useViewDetail } from "hooks/store";
|
import { useViewDetail } from "hooks/store";
|
||||||
// types
|
// types
|
||||||
import { TViewFilters, TViewTypes } from "@plane/types";
|
import { TViewFilters, TViewTypes } from "@plane/types";
|
||||||
import { TViewOperations } from "../types";
|
|
||||||
|
|
||||||
type TViewFilterSelection = {
|
type TViewFilterSelection = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
viewOperations: TViewOperations;
|
|
||||||
baseRoute: string;
|
|
||||||
filterKey: keyof TViewFilters;
|
filterKey: keyof TViewFilters;
|
||||||
propertyId: string;
|
propertyId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewFilterSelection: FC<TViewFilterSelection> = observer((props) => {
|
export const ViewFilterSelection: FC<TViewFilterSelection> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute, filterKey, propertyId } = props;
|
const { workspaceSlug, projectId, viewId, viewType, filterKey, propertyId } = props;
|
||||||
|
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ChevronDown, ChevronUp, Search } from "lucide-react";
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||||
import concat from "lodash/concat";
|
import concat from "lodash/concat";
|
||||||
import uniq from "lodash/uniq";
|
import uniq from "lodash/uniq";
|
||||||
import filter from "lodash/filter";
|
import filter from "lodash/filter";
|
||||||
@ -19,11 +19,10 @@ type TViewFiltersRoot = {
|
|||||||
viewId: string;
|
viewId: string;
|
||||||
viewType: TViewTypes;
|
viewType: TViewTypes;
|
||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
baseRoute: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
|
export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute } = props;
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
// state
|
// state
|
||||||
@ -62,7 +61,6 @@ export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
baseRoute={baseRoute}
|
|
||||||
filterKey={filterKey}
|
filterKey={filterKey}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -25,6 +25,7 @@ export * from "./display-filters/root";
|
|||||||
|
|
||||||
// view display properties
|
// view display properties
|
||||||
export * from "./display-properties/root";
|
export * from "./display-properties/root";
|
||||||
|
export * from "./display-properties/property-selection";
|
||||||
|
|
||||||
// view applied filters
|
// view applied filters
|
||||||
export * from "./applied-filters/root";
|
export * from "./applied-filters/root";
|
||||||
|
@ -31,7 +31,7 @@ export const ViewLayoutRoot: FC<TViewLayoutRoot> = observer((props) => {
|
|||||||
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex gap-0.5 items-center bg-custom-background-80 rounded p-1 h-8 shadow-custom-shadow-2xs">
|
<div className="relative flex gap-0.5 items-center bg-custom-background-80 rounded p-1 h-7 shadow-custom-shadow-2xs">
|
||||||
{LAYOUTS_DATA.map((layout) => (
|
{LAYOUTS_DATA.map((layout) => (
|
||||||
<Fragment key={layout.key}>
|
<Fragment key={layout.key}>
|
||||||
<Tooltip tooltipContent={layout.title} position="bottom">
|
<Tooltip tooltipContent={layout.title} position="bottom">
|
||||||
|
6
web/components/view/types.d.ts
vendored
6
web/components/view/types.d.ts
vendored
@ -1,11 +1,11 @@
|
|||||||
import { TView } from "@plane/types";
|
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties } from "@plane/types";
|
||||||
|
|
||||||
export type TViewOperations = {
|
export type TViewOperations = {
|
||||||
setName: (name: string) => void;
|
setName: (name: string) => void;
|
||||||
setDescription: (description: string) => void;
|
setDescription: (description: string) => void;
|
||||||
setFilters: (filters: Partial<TViewFilters>) => void;
|
setFilters: (filterKey: keyof TViewFilters, filterValue: "clear_all" | string) => void;
|
||||||
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
||||||
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
|
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void;
|
||||||
|
|
||||||
localViewCreateEdit: (viewId: string | undefined) => void;
|
localViewCreateEdit: (viewId: string | undefined) => void;
|
||||||
localViewCreateEditClear: (viewId: string | undefined) => Promise<void>;
|
localViewCreateEditClear: (viewId: string | undefined) => Promise<void>;
|
||||||
|
@ -15,8 +15,8 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
if (!workspaceSlug || !viewId) return <></>;
|
if (!workspaceSlug || !viewId) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
|
||||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
<div className="flex-shrink-0 w-full">
|
||||||
<AllIssuesViewRoot
|
<AllIssuesViewRoot
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={undefined}
|
projectId={undefined}
|
||||||
@ -25,6 +25,7 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
|
|||||||
baseRoute={`/${workspaceSlug?.toString()}/views/public`}
|
baseRoute={`/${workspaceSlug?.toString()}/views/public`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full h-full overflow-hidden">Issues render</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import isEmpty from "lodash/isEmpty";
|
import isEmpty from "lodash/isEmpty";
|
||||||
|
import get from "lodash/get";
|
||||||
// types
|
// types
|
||||||
import {
|
import {
|
||||||
TViewFilters,
|
TViewFilters,
|
||||||
@ -11,19 +12,19 @@ import {
|
|||||||
export class FiltersHelper {
|
export class FiltersHelper {
|
||||||
// computed filters
|
// computed filters
|
||||||
computedFilters = (filters: TViewFilters, defaultValues?: Partial<TViewFilters>): TViewFilters => ({
|
computedFilters = (filters: TViewFilters, defaultValues?: Partial<TViewFilters>): TViewFilters => ({
|
||||||
project: defaultValues?.project || filters?.project || [],
|
project: get(defaultValues, "project", get(filters, "project", [])),
|
||||||
module: defaultValues?.module || filters?.module || [],
|
module: get(defaultValues, "module", get(filters, "module", [])),
|
||||||
cycle: defaultValues?.cycle || filters?.cycle || [],
|
cycle: get(defaultValues, "cycle", get(filters, "cycle", [])),
|
||||||
priority: defaultValues?.priority || filters?.priority || [],
|
priority: get(defaultValues, "priority", get(filters, "priority", [])),
|
||||||
state: defaultValues?.state || filters?.state || [],
|
state: get(defaultValues, "state", get(filters, "state", [])),
|
||||||
state_group: defaultValues?.state_group || filters?.state_group || [],
|
state_group: get(defaultValues, "state_group", get(filters, "state_group", [])),
|
||||||
assignees: defaultValues?.assignees || filters?.assignees || [],
|
assignees: get(defaultValues, "assignees", get(filters, "assignees", [])),
|
||||||
mentions: defaultValues?.mentions || filters?.mentions || [],
|
mentions: get(defaultValues, "mentions", get(filters, "mentions", [])),
|
||||||
subscriber: defaultValues?.subscriber || filters?.subscriber || [],
|
subscriber: get(defaultValues, "subscriber", get(filters, "subscriber", [])),
|
||||||
created_by: defaultValues?.created_by || filters?.created_by || [],
|
created_by: get(defaultValues, "created_by", get(filters, "created_by", [])),
|
||||||
labels: defaultValues?.labels || filters?.labels || [],
|
labels: get(defaultValues, "labels", get(filters, "labels", [])),
|
||||||
start_date: defaultValues?.start_date || filters?.start_date || [],
|
start_date: get(defaultValues, "start_date", get(filters, "start_date", [])),
|
||||||
target_date: defaultValues?.target_date || filters?.target_date || [],
|
target_date: get(defaultValues, "target_date", get(filters, "target_date", [])),
|
||||||
});
|
});
|
||||||
|
|
||||||
// computed display filters
|
// computed display filters
|
||||||
@ -49,19 +50,19 @@ export class FiltersHelper {
|
|||||||
displayProperties: TViewDisplayProperties,
|
displayProperties: TViewDisplayProperties,
|
||||||
defaultValues?: Partial<TViewDisplayProperties>
|
defaultValues?: Partial<TViewDisplayProperties>
|
||||||
): TViewDisplayProperties => ({
|
): TViewDisplayProperties => ({
|
||||||
assignee: defaultValues?.assignee || displayProperties?.assignee || true,
|
assignee: get(defaultValues, "assignee", get(displayProperties, "assignee", true)),
|
||||||
start_date: defaultValues?.start_date || displayProperties?.start_date || true,
|
start_date: get(defaultValues, "start_date", get(displayProperties, "start_date", true)),
|
||||||
due_date: defaultValues?.due_date || displayProperties?.due_date || true,
|
due_date: get(defaultValues, "due_date", get(displayProperties, "due_date", true)),
|
||||||
labels: defaultValues?.labels || displayProperties?.labels || true,
|
labels: get(defaultValues, "labels", get(displayProperties, "labels", true)),
|
||||||
priority: defaultValues?.priority || displayProperties?.priority || true,
|
priority: get(defaultValues, "priority", get(displayProperties, "priority", true)),
|
||||||
state: defaultValues?.state || displayProperties?.state || true,
|
state: get(defaultValues, "state", get(displayProperties, "state", true)),
|
||||||
sub_issue_count: defaultValues?.sub_issue_count || displayProperties?.sub_issue_count || true,
|
sub_issue_count: get(defaultValues, "sub_issue_count", get(displayProperties, "sub_issue_count", true)),
|
||||||
attachment_count: defaultValues?.attachment_count || displayProperties?.attachment_count || true,
|
attachment_count: get(defaultValues, "attachment_count", get(displayProperties, "attachment_count", true)),
|
||||||
link: defaultValues?.link || displayProperties?.link || true,
|
link: get(defaultValues, "link", get(displayProperties, "link", true)),
|
||||||
estimate: defaultValues?.estimate || displayProperties?.estimate || true,
|
estimate: get(defaultValues, "estimate", get(displayProperties, "estimate", true)),
|
||||||
key: defaultValues?.key || displayProperties?.key || true,
|
key: get(defaultValues, "key", get(displayProperties, "key", true)),
|
||||||
created_on: defaultValues?.created_on || displayProperties?.created_on || true,
|
created_on: get(defaultValues, "created_on", get(displayProperties, "created_on", true)),
|
||||||
updated_on: defaultValues?.updated_on || displayProperties?.updated_on || true,
|
updated_on: get(defaultValues, "updated_on", get(displayProperties, "updated_on", true)),
|
||||||
});
|
});
|
||||||
|
|
||||||
// compute filters and display_filters issue query parameters
|
// compute filters and display_filters issue query parameters
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// stores
|
// stores
|
||||||
import { autorun, makeObservable, observable } from "mobx";
|
|
||||||
import { ViewRootStore } from "./view-root.store";
|
import { ViewRootStore } from "./view-root.store";
|
||||||
// services
|
// services
|
||||||
import {
|
import {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
|
import update from "lodash/update";
|
||||||
|
import concat from "lodash/concat";
|
||||||
|
import pull from "lodash/pull";
|
||||||
// store
|
// store
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
// types
|
// types
|
||||||
@ -24,12 +27,13 @@ export type TViewStore = TView & {
|
|||||||
// computed
|
// computed
|
||||||
appliedFilters: TViewFilterProps | undefined;
|
appliedFilters: TViewFilterProps | undefined;
|
||||||
appliedFiltersQueryParams: string | undefined;
|
appliedFiltersQueryParams: string | undefined;
|
||||||
|
isFiltersApplied: boolean;
|
||||||
// helper actions
|
// helper actions
|
||||||
setName: (name: string) => void;
|
setName: (name: string) => void;
|
||||||
setDescription: (description: string) => void;
|
setDescription: (description: string) => void;
|
||||||
setFilters: (filters: Partial<TViewFilters>) => void;
|
setFilters: (filterKey: keyof TViewFilters | undefined, filterValue: "clear_all" | string) => void;
|
||||||
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
setDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
||||||
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
|
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void;
|
||||||
resetChanges: () => void;
|
resetChanges: () => void;
|
||||||
saveChanges: () => Promise<void>;
|
saveChanges: () => Promise<void>;
|
||||||
// actions
|
// actions
|
||||||
@ -132,6 +136,7 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
// computed
|
// computed
|
||||||
appliedFilters: computed,
|
appliedFilters: computed,
|
||||||
appliedFiltersQueryParams: computed,
|
appliedFiltersQueryParams: computed,
|
||||||
|
isFiltersApplied: computed,
|
||||||
// helper actions
|
// helper actions
|
||||||
setName: action,
|
setName: action,
|
||||||
setFilters: action,
|
setFilters: action,
|
||||||
@ -164,6 +169,16 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
return this.computeAppliedFiltersQueryParameters(filters, [])?.query || undefined;
|
return this.computeAppliedFiltersQueryParameters(filters, [])?.query || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isFiltersApplied() {
|
||||||
|
const filters = this.appliedFilters?.filters;
|
||||||
|
let isFiltersApplied = false;
|
||||||
|
Object.keys(filters).forEach((key) => {
|
||||||
|
const _key = key as keyof TViewFilters;
|
||||||
|
if (filters[_key]?.length > 0) isFiltersApplied = true;
|
||||||
|
});
|
||||||
|
return isFiltersApplied;
|
||||||
|
}
|
||||||
|
|
||||||
// helper actions
|
// helper actions
|
||||||
setName = (name: string) => {
|
setName = (name: string) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
@ -177,13 +192,18 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
setFilters = (filters: Partial<TViewFilters>) => {
|
setFilters = (filterKey: keyof TViewFilters | undefined = undefined, filterValue: "clear_all" | string) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = "submit";
|
this.loader = "submit";
|
||||||
Object.keys(filters).forEach((key) => {
|
if (filterKey === undefined) {
|
||||||
const _key = key as keyof TViewFilters;
|
if (filterValue === "clear_all") set(this.filtersToUpdate, ["filters"], {});
|
||||||
set(this.filtersToUpdate, ["filters", _key], filters[_key]);
|
this.loader = undefined;
|
||||||
});
|
} else
|
||||||
|
update(this.filtersToUpdate, ["filters", filterKey], (_values = []) => {
|
||||||
|
if (filterValue === "clear_all") return [];
|
||||||
|
if (_values.includes(filterValue)) return pull(_values, filterValue);
|
||||||
|
return concat(_values, filterValue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,12 +230,9 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
setDisplayProperties = async (display_properties: Partial<TViewDisplayProperties>) => {
|
setDisplayProperties = async (displayPropertyKey: keyof TViewDisplayProperties) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
Object.keys(display_properties).forEach((key) => {
|
update(this.filtersToUpdate, ["display_properties", displayPropertyKey], (_value: boolean = true) => !_value);
|
||||||
const _key = key as keyof TViewDisplayProperties;
|
|
||||||
set(this.filtersToUpdate, ["display_properties", _key], display_properties[_key]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user