chore: updated ui and store

This commit is contained in:
gurusainath 2024-02-09 14:33:22 +05:30
parent 1ce7f20c2d
commit f90595ca31
20 changed files with 221 additions and 286 deletions

View File

@ -10,10 +10,8 @@ import {
ViewRoot,
ViewCreateEditForm,
ViewLayoutRoot,
ViewFiltersRoot,
ViewFiltersDropdown,
ViewDisplayFiltersDropdown,
ViewDisplayPropertiesRoot,
ViewAppliedFiltersRoot,
ViewDuplicateConfirmationModal,
ViewDeleteConfirmationModal,
@ -73,11 +71,12 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
() => ({
setName: (name: string) => viewDetailStore?.setName(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>) =>
viewDetailStore?.setDisplayFilters(display_filters),
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) =>
viewDetailStore?.setDisplayProperties(display_properties),
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) =>
viewDetailStore?.setDisplayProperties(displayPropertyKey),
localViewCreateEdit: (viewId: string | undefined) => {
if (viewId === undefined) {
const viewPayload = viewLocalPayload;
@ -171,29 +170,8 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
/>
</div>
<div className="p-5 border-b border-custom-border-300">
<ViewDisplayPropertiesRoot
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">
<div className="p-5 py-2 border-b border-custom-border-200 relative flex gap-2">
<div className="w-full overflow-hidden">
<ViewAppliedFiltersRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
@ -201,9 +179,9 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewType={viewType}
viewOperations={viewOperations}
/>
</div> */}
</div>
<div className="flex-shrink-0 h-full">
<div className="flex-shrink-0">
<ViewLayoutRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
@ -220,7 +198,6 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
displayDropdownText={false}
/>
</div>
@ -232,12 +209,11 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
displayDropdownText={false}
/>
</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">
<Pencil size={12} />
</div>

View File

@ -1,9 +1,10 @@
import { FC } from "react";
import { User, X } from "lucide-react";
import { ImagePlus, X } from "lucide-react";
// hooks
import { useViewDetail } from "hooks/store";
import { useViewFilter } from "hooks/store";
// types
import { TViewFilters, TViewTypes } from "@plane/types";
import { TViewOperations } from "../types";
type TViewAppliedFiltersItem = {
workspaceSlug: string;
@ -11,33 +12,30 @@ type TViewAppliedFiltersItem = {
viewId: string;
viewType: TViewTypes;
filterKey: keyof TViewFilters;
filterId: string;
propertyId: string;
viewOperations: TViewOperations;
};
export const ViewAppliedFiltersItem: FC<TViewAppliedFiltersItem> = (props) => {
const { workspaceSlug, projectId, viewId, viewType, filterKey, filterId } = props;
const { workspaceSlug, projectId, filterKey, propertyId, viewOperations } = props;
// hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
const propertyDetail = viewFilterHelper?.propertyDetails(filterKey, propertyId) || undefined;
const removeFilterOption = () => {
const filters = viewDetailStore?.appliedFilters?.filters;
if (!filters) return;
const filterValues = filters[filterKey];
const updatedFilterValues = filterValues.filter((value) => value !== filterId);
viewDetailStore?.setFilters({ [filterKey]: updatedFilterValues });
viewOperations?.setFilters(filterKey, propertyId);
};
return (
<div
key={`filter_value_${filterKey}_${filterId}`}
className="border border-custom-border-200 rounded relative flex items-center gap-1 px-1 py-0.5"
key={`filter_value_${filterKey}_${propertyId}`}
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">
<User size={12} />
</div>
<div className="text-xs">
{filterKey} - {filterId}
{propertyDetail?.icon || <ImagePlus size={14} />}
</div>
<div className="text-xs">{propertyDetail?.label || propertyId}</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"
onClick={removeFilterOption}

View File

@ -6,10 +6,9 @@ import { X } from "lucide-react";
import { useViewDetail } from "hooks/store";
// components
import { ViewAppliedFiltersItem } from "./filter-item";
// helpers
import { generateTitle } from "./helper";
// types
import { TViewFilters, TViewTypes } from "@plane/types";
import { TViewOperations } from "../types";
type TViewAppliedFilters = {
workspaceSlug: string;
@ -17,41 +16,51 @@ type TViewAppliedFilters = {
viewId: string;
viewType: TViewTypes;
filterKey: keyof TViewFilters;
viewOperations: TViewOperations;
};
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 filterKeyValue =
const propertyValues =
viewDetailStore?.appliedFilters?.filters && !isEmpty(viewDetailStore?.appliedFilters?.filters)
? viewDetailStore?.appliedFilters?.filters?.[filterKey] || 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 (
<div className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 py-1 min-h-[32px]">
<div className="flex-shrink-0 text-xs text-custom-text-200">{generateTitle(filterKey)}</div>
<div className="relative flex items-center gap-1 flex-wrap">
{["1", "2", "3", "4"].map((filterId) => (
<Fragment key={filterId}>
<ViewAppliedFiltersItem
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
filterKey={filterKey}
filterId={filterId}
/>
</Fragment>
))}
<div className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5 px-2 min-h-[32px]">
<div className="flex-shrink-0 text-xs text-custom-text-200 capitalize">{filterKey.replaceAll("_", " ")}</div>
<div className="relative flex items-center gap-1.5 flex-wrap">
{propertyValues.length >= 100 ? (
<div className="text-xs font-medium bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2">
{propertyValues.length} {filterKey.replaceAll("_", " ")}s
</div>
) : (
<>
{propertyValues.map((propertyId) => (
<Fragment key={propertyId}>
<ViewAppliedFiltersItem
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
filterKey={filterKey}
propertyId={propertyId}
viewOperations={viewOperations}
/>
</Fragment>
))}
</>
)}
</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"
onClick={clearFilter}
onClick={clearPropertyFilter}
>
<X size={10} />
</div>

View File

@ -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;
};

View File

@ -19,7 +19,7 @@ type TViewAppliedFiltersRoot = {
};
export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType } = props;
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
// hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
@ -28,16 +28,14 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
? Object.keys(viewDetailStore?.appliedFilters?.filters)
: undefined;
const clearAllFilters = () => {
const clearedFilters: Partial<Record<keyof TViewFilters, string[]>> = {};
filterKeys?.forEach((key) => {
const _key = key as keyof TViewFilters;
clearedFilters[_key] = [];
});
viewDetailStore?.setFilters(clearedFilters);
};
const clearAllFilters = () => viewDetailStore?.setFilters(undefined, "clear_all");
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 (
<div className="relative flex items-center gap-2 flex-wrap">
{filterKeys.map((key) => {
@ -50,12 +48,14 @@ export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((pro
viewId={viewId}
viewType={viewType}
filterKey={filterKey}
viewOperations={viewOperations}
/>
</Fragment>
);
})}
<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}
>
<div className="text-xs">Clear All</div>

View File

@ -7,6 +7,8 @@ import { MonitorDot } from "lucide-react";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components
import { ViewDisplayPropertiesRoot } from "../";
// ui
import { Tooltip } from "@plane/ui";
// types
import { TViewOperations } from "../types";
import { TViewTypes } from "@plane/types";
@ -17,22 +19,12 @@ type TViewDisplayFiltersDropdown = {
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
children?: ReactNode;
displayDropdownText?: boolean;
};
export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = observer((props) => {
const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
baseRoute,
children,
displayDropdownText = true,
} = props;
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props;
// state
const [dropdownToggle, setDropdownToggle] = useState(false);
// refs
@ -74,22 +66,24 @@ export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = obser
onClick={handleDropdownToggle}
>
{children ? (
<span className="relative inline-block">{children}</span>
<div className="relative inline-block">{children}</div>
) : (
<div
className={`relative flex items-center gap-1 h-8 rounded px-2 transition-all
<Tooltip tooltipContent={"Display"} position="bottom">
<div
className={`relative flex items-center gap-1 h-7 rounded px-2 transition-all
${
displayDropdownText
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
: `hover:bg-custom-background-80`
}
`}
>
<div className="w-4 h-4 relative flex justify-center items-center overflow-hidden">
<MonitorDot size={14} />
>
<div className="w-4 h-4 relative flex justify-center items-center overflow-hidden">
<MonitorDot size={14} />
</div>
{displayDropdownText && <div className="text-sm whitespace-nowrap">Display</div>}
</div>
{displayDropdownText && <div className="text-sm whitespace-nowrap">Display</div>}
</div>
</Tooltip>
)}
</button>
</Combobox.Button>

View File

@ -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>
);
});

View File

@ -1,4 +1,7 @@
import { FC } from "react";
import { FC, Fragment } from "react";
import { observer } from "mobx-react-lite";
// components
import { ViewDisplayPropertySelection } from "../";
// types
import { TViewDisplayProperties, TViewTypes } from "@plane/types";
import { TViewOperations } from "../types";
@ -11,7 +14,7 @@ type TViewDisplayPropertiesRoot = {
viewOperations: TViewOperations;
};
export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = (props) => {
export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
const displayProperties: Partial<keyof TViewDisplayProperties>[] = [
@ -31,20 +34,17 @@ export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = (props)
return (
<div className="relative flex items-center flex-wrap gap-2">
{displayProperties.map((property) => (
<div
key={property}
className={`relative flex items-center gap-1 text-xs rounded p-0.5 px-2 border transition-all capitalize cursor-pointer
${
false
? `border-custom-primary-100 bg-custom-primary-100`
: `border-custom-border-300 hover:bg-custom-background-80`
}
`}
onClick={() => {}}
>
{["key"].includes(property) ? "ID" : property.replaceAll("_", " ")}
</div>
<Fragment key={property}>
<ViewDisplayPropertySelection
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
property={property}
/>
</Fragment>
))}
</div>
);
};
});

View File

@ -7,6 +7,8 @@ import { ListFilter, Search } from "lucide-react";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components
import { ViewFiltersRoot } from "../";
// ui
import { Tooltip } from "@plane/ui";
// types
import { TViewOperations } from "../types";
import { TViewTypes } from "@plane/types";
@ -17,22 +19,12 @@ type TViewFiltersDropdown = {
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
children?: ReactNode;
displayDropdownText?: boolean;
};
export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) => {
const {
workspaceSlug,
projectId,
viewId,
viewType,
viewOperations,
baseRoute,
children,
displayDropdownText = true,
} = props;
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props;
// state
const [dropdownToggle, setDropdownToggle] = useState(false);
const [query, setQuery] = useState("");
@ -77,20 +69,22 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
{children ? (
<span className="relative inline-block">{children}</span>
) : (
<div
className={`relative flex items-center gap-1 h-8 rounded px-2 transition-all
<Tooltip tooltipContent={"Filters"} position="bottom">
<div
className={`relative flex items-center gap-1 h-7 rounded px-2 transition-all
${
displayDropdownText
? `border border-custom-border-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80`
: `hover:bg-custom-background-80`
}
`}
>
<div className="w-4 h-4 relative flex justify-center items-center overflow-hidden">
<ListFilter size={14} />
>
<div className="w-4 h-4 relative flex justify-center items-center overflow-hidden">
<ListFilter size={14} />
</div>
{displayDropdownText && <div className="text-sm whitespace-nowrap">Filters</div>}
</div>
{displayDropdownText && <div className="text-sm whitespace-nowrap">Filters</div>}
</div>
</Tooltip>
)}
</button>
</Combobox.Button>
@ -122,7 +116,6 @@ export const ViewFiltersDropdown: FC<TViewFiltersDropdown> = observer((props) =>
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
/>
</div>
</div>

View File

@ -1,10 +1,7 @@
import { FC, useState } from "react";
import { observer } from "mobx-react-lite";
import concat from "lodash/concat";
import pull from "lodash/pull";
import uniq from "lodash/uniq";
// hooks
import { useViewFilter, useViewDetail } from "hooks/store";
import { useViewFilter } from "hooks/store";
// components
import { ViewFiltersItem, ViewFilterSelection } from "../";
// types
@ -17,15 +14,13 @@ type TViewFiltersItemRoot = {
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
filterKey: keyof TViewFilters;
};
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
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
// state
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 handlePropertySelection = (_propertyId: string) => {
const _propertyIds = viewDetailStore?.appliedFilters?.filters?.[filterKey] || [];
const isSelected = _propertyIds?.includes(_propertyId) || false;
viewOperations?.setFilters({
[filterKey]: isSelected ? pull(_propertyIds, _propertyId) : uniq(concat(_propertyIds, [_propertyId])),
});
};
const handlePropertySelection = (_propertyId: string) => viewOperations?.setFilters(filterKey, _propertyId);
if (propertyIds.length <= 0)
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}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
filterKey={filterKey}
propertyId={propertyId}
/>
<ViewFiltersItem
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
filterKey={filterKey}
propertyId={propertyId}
/>

View File

@ -1,26 +1,20 @@
import { FC, Fragment } from "react";
import { CheckSquare } from "lucide-react";
import { ImagePlus } from "lucide-react";
// hooks
import { useViewFilter } from "hooks/store";
// types
import { TViewFilters, TViewTypes } from "@plane/types";
import { TViewOperations } from "../types";
// helpers
// import { filterPropertyItemByFilterKeyAndId } from "../helpers/filters";
import { TViewFilters } from "@plane/types";
type TViewFiltersItem = {
workspaceSlug: string;
projectId: string | undefined;
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
filterKey: keyof TViewFilters;
propertyId: string;
};
export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute, filterKey, propertyId } = props;
const { workspaceSlug, projectId, filterKey, propertyId } = props;
// hooks
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
@ -30,7 +24,7 @@ export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
return (
<Fragment>
<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 className="text-xs block truncate line-clamp-1 text-custom-text-200 group-hover:text-custom-text-100">
{propertyDetail?.label || propertyId}

View File

@ -5,21 +5,18 @@ import { observer } from "mobx-react-lite";
import { useViewDetail } from "hooks/store";
// types
import { TViewFilters, TViewTypes } from "@plane/types";
import { TViewOperations } from "../types";
type TViewFilterSelection = {
workspaceSlug: string;
projectId: string | undefined;
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
filterKey: keyof TViewFilters;
propertyId: string;
};
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);

View File

@ -1,6 +1,6 @@
import { FC, useState } from "react";
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 uniq from "lodash/uniq";
import filter from "lodash/filter";
@ -19,11 +19,10 @@ type TViewFiltersRoot = {
viewId: string;
viewType: TViewTypes;
viewOperations: TViewOperations;
baseRoute: string;
};
export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
const { workspaceSlug, projectId, viewId, viewType, viewOperations, baseRoute } = props;
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
// hooks
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
// state
@ -62,7 +61,6 @@ export const ViewFiltersRoot: FC<TViewFiltersRoot> = observer((props) => {
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
filterKey={filterKey}
/>
)}

View File

@ -25,6 +25,7 @@ export * from "./display-filters/root";
// view display properties
export * from "./display-properties/root";
export * from "./display-properties/property-selection";
// view applied filters
export * from "./applied-filters/root";

View File

@ -31,7 +31,7 @@ export const ViewLayoutRoot: FC<TViewLayoutRoot> = observer((props) => {
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
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) => (
<Fragment key={layout.key}>
<Tooltip tooltipContent={layout.title} position="bottom">

View File

@ -1,11 +1,11 @@
import { TView } from "@plane/types";
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties } from "@plane/types";
export type TViewOperations = {
setName: (name: 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;
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void;
localViewCreateEdit: (viewId: string | undefined) => void;
localViewCreateEditClear: (viewId: string | undefined) => Promise<void>;

View File

@ -15,8 +15,8 @@ const GlobalViewIssuesPage: 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">
<AllIssuesViewRoot
workspaceSlug={workspaceSlug.toString()}
projectId={undefined}
@ -25,6 +25,7 @@ const GlobalViewIssuesPage: NextPageWithLayout = () => {
baseRoute={`/${workspaceSlug?.toString()}/views/public`}
/>
</div>
<div className="w-full h-full overflow-hidden">Issues render</div>
</div>
);
};

View File

@ -1,4 +1,5 @@
import isEmpty from "lodash/isEmpty";
import get from "lodash/get";
// types
import {
TViewFilters,
@ -11,19 +12,19 @@ import {
export class FiltersHelper {
// computed filters
computedFilters = (filters: TViewFilters, defaultValues?: Partial<TViewFilters>): TViewFilters => ({
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 || [],
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", [])),
});
// computed display filters
@ -49,19 +50,19 @@ export class FiltersHelper {
displayProperties: TViewDisplayProperties,
defaultValues?: Partial<TViewDisplayProperties>
): TViewDisplayProperties => ({
assignee: defaultValues?.assignee || displayProperties?.assignee || true,
start_date: defaultValues?.start_date || displayProperties?.start_date || true,
due_date: defaultValues?.due_date || displayProperties?.due_date || true,
labels: defaultValues?.labels || displayProperties?.labels || true,
priority: defaultValues?.priority || displayProperties?.priority || true,
state: defaultValues?.state || displayProperties?.state || true,
sub_issue_count: defaultValues?.sub_issue_count || displayProperties?.sub_issue_count || true,
attachment_count: defaultValues?.attachment_count || displayProperties?.attachment_count || true,
link: defaultValues?.link || displayProperties?.link || true,
estimate: defaultValues?.estimate || displayProperties?.estimate || true,
key: defaultValues?.key || displayProperties?.key || true,
created_on: defaultValues?.created_on || displayProperties?.created_on || true,
updated_on: defaultValues?.updated_on || displayProperties?.updated_on || true,
assignee: get(defaultValues, "assignee", get(displayProperties, "assignee", true)),
start_date: get(defaultValues, "start_date", get(displayProperties, "start_date", true)),
due_date: get(defaultValues, "due_date", get(displayProperties, "due_date", true)),
labels: get(defaultValues, "labels", get(displayProperties, "labels", true)),
priority: get(defaultValues, "priority", get(displayProperties, "priority", true)),
state: get(defaultValues, "state", get(displayProperties, "state", true)),
sub_issue_count: get(defaultValues, "sub_issue_count", get(displayProperties, "sub_issue_count", true)),
attachment_count: get(defaultValues, "attachment_count", get(displayProperties, "attachment_count", true)),
link: get(defaultValues, "link", get(displayProperties, "link", true)),
estimate: get(defaultValues, "estimate", get(displayProperties, "estimate", true)),
key: get(defaultValues, "key", get(displayProperties, "key", true)),
created_on: get(defaultValues, "created_on", get(displayProperties, "created_on", true)),
updated_on: get(defaultValues, "updated_on", get(displayProperties, "updated_on", true)),
});
// compute filters and display_filters issue query parameters

View File

@ -1,5 +1,4 @@
// stores
import { autorun, makeObservable, observable } from "mobx";
import { ViewRootStore } from "./view-root.store";
// services
import {

View File

@ -1,5 +1,8 @@
import { action, computed, makeObservable, observable, runInAction } from "mobx";
import set from "lodash/set";
import update from "lodash/update";
import concat from "lodash/concat";
import pull from "lodash/pull";
// store
import { RootStore } from "store/root.store";
// types
@ -24,12 +27,13 @@ export type TViewStore = TView & {
// computed
appliedFilters: TViewFilterProps | undefined;
appliedFiltersQueryParams: string | undefined;
isFiltersApplied: boolean;
// helper actions
setName: (name: 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;
setDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
setDisplayProperties: (displayPropertyKey: keyof TViewDisplayProperties) => void;
resetChanges: () => void;
saveChanges: () => Promise<void>;
// actions
@ -132,6 +136,7 @@ export class ViewStore extends FiltersHelper implements TViewStore {
// computed
appliedFilters: computed,
appliedFiltersQueryParams: computed,
isFiltersApplied: computed,
// helper actions
setName: action,
setFilters: action,
@ -164,6 +169,16 @@ export class ViewStore extends FiltersHelper implements TViewStore {
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
setName = (name: string) => {
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(() => {
this.loader = "submit";
Object.keys(filters).forEach((key) => {
const _key = key as keyof TViewFilters;
set(this.filtersToUpdate, ["filters", _key], filters[_key]);
});
if (filterKey === undefined) {
if (filterValue === "clear_all") set(this.filtersToUpdate, ["filters"], {});
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(() => {
Object.keys(display_properties).forEach((key) => {
const _key = key as keyof TViewDisplayProperties;
set(this.filtersToUpdate, ["display_properties", _key], display_properties[_key]);
});
update(this.filtersToUpdate, ["display_properties", displayPropertyKey], (_value: boolean = true) => !_value);
});
};