diff --git a/packages/types/src/inbox.d.ts b/packages/types/src/inbox.d.ts index 68e62f7bf..01a1dfce3 100644 --- a/packages/types/src/inbox.d.ts +++ b/packages/types/src/inbox.d.ts @@ -29,6 +29,7 @@ export type TInboxIssueFilter = { } & { [key in TInboxIssueFilterDateKeys]: string[] | undefined; } & { + state: string[] | undefined; status: TInboxIssueStatus[] | undefined; priority: TIssuePriorities[] | undefined; labels: string[] | undefined; diff --git a/web/components/inbox/inbox-filter/applied-filters/index.ts b/web/components/inbox/inbox-filter/applied-filters/index.ts index 35f0bc261..6113e51dd 100644 --- a/web/components/inbox/inbox-filter/applied-filters/index.ts +++ b/web/components/inbox/inbox-filter/applied-filters/index.ts @@ -1,5 +1,6 @@ export * from "./root"; export * from "./status"; +export * from "./state"; export * from "./priority"; export * from "./member"; export * from "./label"; diff --git a/web/components/inbox/inbox-filter/applied-filters/root.tsx b/web/components/inbox/inbox-filter/applied-filters/root.tsx index 7aa50b0eb..77b29deca 100644 --- a/web/components/inbox/inbox-filter/applied-filters/root.tsx +++ b/web/components/inbox/inbox-filter/applied-filters/root.tsx @@ -7,6 +7,7 @@ import { InboxIssueAppliedFiltersMember, InboxIssueAppliedFiltersLabel, InboxIssueAppliedFiltersDate, + InboxIssueAppliedFiltersState, } from "@/components/inbox"; // hooks import { useProjectInbox } from "@/hooks/store"; @@ -19,6 +20,8 @@ export const InboxIssueAppliedFilters: FC = observer(() => {
{/* status */} + {/* state */} + {/* priority */} {/* assignees */} diff --git a/web/components/inbox/inbox-filter/applied-filters/state.tsx b/web/components/inbox/inbox-filter/applied-filters/state.tsx new file mode 100644 index 000000000..e4f7f5fbd --- /dev/null +++ b/web/components/inbox/inbox-filter/applied-filters/state.tsx @@ -0,0 +1,52 @@ +import { FC } from "react"; +import { observer } from "mobx-react"; +import { X } from "lucide-react"; +import { StateGroupIcon } from "@plane/ui"; +// hooks +import { useProjectInbox, useProjectState } from "@/hooks/store"; + +export const InboxIssueAppliedFiltersState: FC = observer(() => { + // hooks + const { inboxFilters, handleInboxIssueFilters } = useProjectInbox(); + const { getStateById } = useProjectState(); + // derived values + const filteredValues = inboxFilters?.state || []; + const currentOptionDetail = (stateId: string) => getStateById(stateId) || undefined; + + const handleFilterValue = (value: string): string[] => + filteredValues?.includes(value) ? filteredValues.filter((v) => v !== value) : [...filteredValues, value]; + + const clearFilter = () => handleInboxIssueFilters("state", undefined); + + if (filteredValues.length === 0) return <>; + return ( +
+
Status
+ {filteredValues.map((value) => { + const optionDetail = currentOptionDetail(value); + if (!optionDetail) return <>; + return ( +
+
+ +
+
{optionDetail?.name}
+
handleInboxIssueFilters("state", handleFilterValue(optionDetail?.id))} + > + +
+
+ ); + })} + +
+ +
+
+ ); +}); diff --git a/web/components/inbox/inbox-filter/filters/filter-selection.tsx b/web/components/inbox/inbox-filter/filters/filter-selection.tsx index d7bebdc11..fd1dc951c 100644 --- a/web/components/inbox/inbox-filter/filters/filter-selection.tsx +++ b/web/components/inbox/inbox-filter/filters/filter-selection.tsx @@ -8,9 +8,10 @@ import { FilterMember, FilterDate, FilterLabels, + FilterState, } from "@/components/inbox/inbox-filter/filters"; // hooks -import { useMember, useLabel } from "@/hooks/store"; +import { useMember, useLabel, useProjectState } from "@/hooks/store"; export const InboxIssueFilterSelection: FC = observer(() => { // hooks @@ -18,6 +19,7 @@ export const InboxIssueFilterSelection: FC = observer(() => { project: { projectMemberIds }, } = useMember(); const { projectLabels } = useLabel(); + const { projectStates } = useProjectState(); // states const [filtersSearchQuery, setFiltersSearchQuery] = useState(""); @@ -47,6 +49,10 @@ export const InboxIssueFilterSelection: FC = observer(() => {
+ {/* state */} +
+ +
{/* Priority */}
diff --git a/web/components/inbox/inbox-filter/filters/index.ts b/web/components/inbox/inbox-filter/filters/index.ts index a389dda9d..4cd0d31f1 100644 --- a/web/components/inbox/inbox-filter/filters/index.ts +++ b/web/components/inbox/inbox-filter/filters/index.ts @@ -1,5 +1,6 @@ export * from "./filter-selection"; export * from "./status"; +export * from "./state"; export * from "./priority"; export * from "./labels"; export * from "./members"; diff --git a/web/components/inbox/inbox-filter/filters/state.tsx b/web/components/inbox/inbox-filter/filters/state.tsx new file mode 100644 index 000000000..b3cfad435 --- /dev/null +++ b/web/components/inbox/inbox-filter/filters/state.tsx @@ -0,0 +1,84 @@ +import { FC, useState } from "react"; +import { observer } from "mobx-react"; +import { IState } from "@plane/types"; +import { Loader, StateGroupIcon } from "@plane/ui"; +// components +import { FilterHeader, FilterOption } from "@/components/issues"; +// hooks +import { useProjectInbox } from "@/hooks/store"; + +type Props = { + states: IState[] | undefined; + searchQuery: string; +}; + +export const FilterState: FC = observer((props) => { + const { states, searchQuery } = props; + + const [itemsToRender, setItemsToRender] = useState(5); + const [previewEnabled, setPreviewEnabled] = useState(true); + + const { inboxFilters, handleInboxIssueFilters } = useProjectInbox(); + + const filterValue = inboxFilters?.state || []; + + const appliedFiltersCount = filterValue?.length ?? 0; + + const filteredOptions = states?.filter((state) => state.name.toLowerCase().includes(searchQuery.toLowerCase())); + + const handleViewToggle = () => { + if (!filteredOptions) return; + + if (itemsToRender === filteredOptions.length) setItemsToRender(5); + else setItemsToRender(filteredOptions.length); + }; + + const handleFilterValue = (value: string): string[] => + filterValue?.includes(value) ? filterValue.filter((v) => v !== value) : [...filterValue, value]; + + return ( + <> + 0 ? ` (${appliedFiltersCount})` : ""}`} + isPreviewEnabled={previewEnabled} + handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} + /> + {previewEnabled && ( +
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + <> + {filteredOptions.slice(0, itemsToRender).map((state) => ( + handleInboxIssueFilters("state", handleFilterValue(state.id))} + icon={} + title={state.name} + /> + ))} + {filteredOptions.length > 5 && ( + + )} + + ) : ( +

No matches found

+ ) + ) : ( + + + + + + )} +
+ )} + + ); +}); diff --git a/web/store/inbox/project-inbox.store.ts b/web/store/inbox/project-inbox.store.ts index 07013e83d..b253b76fc 100644 --- a/web/store/inbox/project-inbox.store.ts +++ b/web/store/inbox/project-inbox.store.ts @@ -216,6 +216,8 @@ export class ProjectInboxStore implements IProjectInboxStore { set(this, "inboxFilters", undefined); set(this, ["inboxSorting", "order_by"], "issue__created_at"); set(this, ["inboxSorting", "sort_by"], "desc"); + set(this, ["inboxIssues"], {}); + set(this, ["inboxIssuePaginationInfo"], undefined); if (tab === "closed") set(this, ["inboxFilters", "status"], [-1, 1, 2]); else set(this, ["inboxFilters", "status"], [-2]); const { workspaceSlug, projectId } = this.store.app.router; @@ -224,12 +226,16 @@ export class ProjectInboxStore implements IProjectInboxStore { handleInboxIssueFilters = (key: T, value: TInboxIssueFilter[T]) => { set(this.inboxFilters, key, value); + set(this, ["inboxIssues"], {}); + set(this, ["inboxIssuePaginationInfo"], undefined); const { workspaceSlug, projectId } = this.store.app.router; if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading"); }; handleInboxIssueSorting = (key: T, value: TInboxIssueSorting[T]) => { set(this.inboxSorting, key, value); + set(this, ["inboxIssues"], {}); + set(this, ["inboxIssuePaginationInfo"], undefined); const { workspaceSlug, projectId } = this.store.app.router; if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading"); };