mirror of
https://github.com/makeplane/plane.git
synced 2026-04-25 17:49:35 -05:00
[WEB-5296]chore: grouping filter refactor (#8034)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import type { TIssue } from "@plane/types";
|
||||
|
||||
export type TDateAlertProps = {
|
||||
date: string;
|
||||
workItem: TIssue;
|
||||
projectId: string;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const DateAlert = (props: TDateAlertProps) => <></>;
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { FC } from "react";
|
||||
import { CalendarDays, LayersIcon, Link2, Paperclip } from "lucide-react";
|
||||
// types
|
||||
import { ISSUE_GROUP_BY_OPTIONS } from "@plane/constants";
|
||||
import type { ISvgIcons } from "@plane/propel/icons";
|
||||
import {
|
||||
CycleIcon,
|
||||
@@ -13,7 +14,13 @@ import {
|
||||
PriorityPropertyIcon,
|
||||
StartDatePropertyIcon,
|
||||
} from "@plane/propel/icons";
|
||||
import type { IGroupByColumn, IIssueDisplayProperties, TGetColumns, TSpreadsheetColumn } from "@plane/types";
|
||||
import type {
|
||||
IGroupByColumn,
|
||||
IIssueDisplayProperties,
|
||||
TGetColumns,
|
||||
TIssueGroupByOptions,
|
||||
TSpreadsheetColumn,
|
||||
} from "@plane/types";
|
||||
// components
|
||||
import {
|
||||
SpreadsheetAssigneeColumn,
|
||||
@@ -96,3 +103,13 @@ export const SPREADSHEET_COLUMNS: { [key in keyof IIssueDisplayProperties]: TSpr
|
||||
updated_on: SpreadsheetUpdatedOnColumn,
|
||||
attachment_count: SpreadsheetAttachmentColumn,
|
||||
};
|
||||
|
||||
export const useGroupByOptions = (
|
||||
options: TIssueGroupByOptions[]
|
||||
): {
|
||||
key: TIssueGroupByOptions;
|
||||
titleTranslationKey: string;
|
||||
}[] => {
|
||||
const groupByOptions = ISSUE_GROUP_BY_OPTIONS.filter((option) => options.includes(option.key));
|
||||
return groupByOptions;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { IIssueDisplayFilterOptions } from "@plane/types";
|
||||
|
||||
export const getEnabledDisplayFilters = (displayFilters: IIssueDisplayFilterOptions) => displayFilters;
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Rocket, Search } from "lucide-react";
|
||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// i18n
|
||||
@@ -66,12 +66,14 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
|
||||
const { isMobile } = usePlatformOS();
|
||||
const debouncedSearchTerm: string = useDebounce(searchTerm, 500);
|
||||
const { baseTabIndex } = getTabIndex(undefined, isMobile);
|
||||
const hasInitializedSelection = useRef(false);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setSearchTerm("");
|
||||
setSelectedIssues([]);
|
||||
setIsWorkspaceLevel(false);
|
||||
hasInitializedSelection.current = false;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
@@ -118,10 +120,11 @@ export const ExistingIssuesListModal: React.FC<Props> = (props) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedWorkItemIds) {
|
||||
if (isOpen && !hasInitializedSelection.current && selectedWorkItemIds && issues.length > 0) {
|
||||
setSelectedIssues(issues.filter((issue) => selectedWorkItemIds.includes(issue.id)));
|
||||
hasInitializedSelection.current = true;
|
||||
}
|
||||
}, [isOpen, selectedWorkItemIds]);
|
||||
}, [isOpen, issues, selectedWorkItemIds]);
|
||||
|
||||
useEffect(() => {
|
||||
handleSearch();
|
||||
|
||||
@@ -36,6 +36,7 @@ import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
// components
|
||||
import { WorkItemAdditionalSidebarProperties } from "@/plane-web/components/issues/issue-details/additional-properties";
|
||||
import { IssueParentSelectRoot } from "@/plane-web/components/issues/issue-details/parent-select-root";
|
||||
import { DateAlert } from "@/plane-web/components/issues/issue-details/sidebar.tsx/date-alert";
|
||||
import { IssueWorklogProperty } from "@/plane-web/components/issues/worklog/property";
|
||||
import { IssueCycleSelect } from "./cycle-select";
|
||||
import { IssueLabel } from "./label";
|
||||
@@ -186,28 +187,31 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<DueDatePropertyIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>{t("common.order_by.due_date")}</span>
|
||||
</div>
|
||||
<DateDropdown
|
||||
placeholder={t("issue.add.due_date")}
|
||||
value={issue.target_date}
|
||||
onChange={(val) =>
|
||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
})
|
||||
}
|
||||
minDate={minDate ?? undefined}
|
||||
disabled={!isEditable}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="group w-3/5 flex-grow"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={cn("text-sm", {
|
||||
"text-custom-text-400": !issue.target_date,
|
||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
})}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline !text-custom-text-100"
|
||||
// TODO: add this logic
|
||||
// showPlaceholderIcon
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<DateDropdown
|
||||
placeholder={t("issue.add.due_date")}
|
||||
value={issue.target_date}
|
||||
onChange={(val) =>
|
||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
})
|
||||
}
|
||||
minDate={minDate ?? undefined}
|
||||
disabled={!isEditable}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="group w-3/5 flex-grow"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={cn("text-sm", {
|
||||
"text-custom-text-400": !issue.target_date,
|
||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
})}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline !text-custom-text-100"
|
||||
// TODO: add this logic
|
||||
// showPlaceholderIcon
|
||||
/>
|
||||
{issue.target_date && <DateAlert date={issue.target_date} workItem={issue} projectId={projectId} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{projectId && areEstimateEnabledByProjectId(projectId) && (
|
||||
|
||||
+4
-2
@@ -1,10 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ISSUE_GROUP_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { IIssueDisplayFilterOptions, TIssueGroupByOptions } from "@plane/types";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
import { useGroupByOptions } from "@/plane-web/components/issues/issue-layouts/utils";
|
||||
|
||||
type Props = {
|
||||
displayFilters: IIssueDisplayFilterOptions | undefined;
|
||||
@@ -22,6 +22,8 @@ export const FilterGroupBy: React.FC<Props> = observer((props) => {
|
||||
const selectedGroupBy = displayFilters?.group_by ?? null;
|
||||
const selectedSubGroupBy = displayFilters?.sub_group_by ?? null;
|
||||
|
||||
const options = useGroupByOptions(groupByOptions);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterHeader
|
||||
@@ -31,7 +33,7 @@ export const FilterGroupBy: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
{previewEnabled && (
|
||||
<div>
|
||||
{ISSUE_GROUP_BY_OPTIONS.filter((option) => groupByOptions.includes(option.key)).map((groupBy) => {
|
||||
{options.map((groupBy) => {
|
||||
if (
|
||||
displayFilters?.layout === "kanban" &&
|
||||
selectedSubGroupBy !== null &&
|
||||
|
||||
@@ -34,6 +34,7 @@ import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
// plane web components
|
||||
import { WorkItemAdditionalSidebarProperties } from "@/plane-web/components/issues/issue-details/additional-properties";
|
||||
import { IssueParentSelectRoot } from "@/plane-web/components/issues/issue-details/parent-select-root";
|
||||
import { DateAlert } from "@/plane-web/components/issues/issue-details/sidebar.tsx/date-alert";
|
||||
import { IssueWorklogProperty } from "@/plane-web/components/issues/worklog/property";
|
||||
import type { TIssueOperations } from "../issue-detail";
|
||||
import { IssueCycleSelect } from "../issue-detail/cycle-select";
|
||||
@@ -189,28 +190,31 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
<DueDatePropertyIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>{t("common.order_by.due_date")}</span>
|
||||
</div>
|
||||
<DateDropdown
|
||||
value={issue.target_date}
|
||||
onChange={(val) =>
|
||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
})
|
||||
}
|
||||
placeholder={t("issue.add.due_date")}
|
||||
buttonVariant="transparent-with-text"
|
||||
minDate={minDate ?? undefined}
|
||||
disabled={disabled}
|
||||
className="w-3/4 flex-grow group"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={cn("text-sm", {
|
||||
"text-custom-text-400": !issue.target_date,
|
||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
})}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline !text-custom-text-100"
|
||||
// TODO: add this logic
|
||||
// showPlaceholderIcon
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<DateDropdown
|
||||
value={issue.target_date}
|
||||
onChange={(val) =>
|
||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
})
|
||||
}
|
||||
placeholder={t("issue.add.due_date")}
|
||||
buttonVariant="transparent-with-text"
|
||||
minDate={minDate ?? undefined}
|
||||
disabled={disabled}
|
||||
className="w-3/4 flex-grow group"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={cn("text-sm", {
|
||||
"text-custom-text-400": !issue.target_date,
|
||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
})}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline !text-custom-text-100"
|
||||
// TODO: add this logic
|
||||
// showPlaceholderIcon
|
||||
/>
|
||||
{issue.target_date && <DateAlert date={issue.target_date} workItem={issue} projectId={projectId} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* estimate */}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { EIssueLayoutTypes } from "@plane/types";
|
||||
import { getComputedDisplayFilters, getComputedDisplayProperties } from "@plane/utils";
|
||||
// lib
|
||||
import { storage } from "@/lib/local-storage";
|
||||
import { getEnabledDisplayFilters } from "@/plane-web/store/issue/helpers/filter-utils";
|
||||
|
||||
interface ILocalStoreIssueFilters {
|
||||
key: EIssuesStoreType;
|
||||
@@ -176,7 +177,10 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
|
||||
computedDisplayFilters = (
|
||||
displayFilters: IIssueDisplayFilterOptions,
|
||||
defaultValues?: IIssueDisplayFilterOptions
|
||||
): IIssueDisplayFilterOptions => getComputedDisplayFilters(displayFilters, defaultValues);
|
||||
): IIssueDisplayFilterOptions => {
|
||||
const computedFilters = getComputedDisplayFilters(displayFilters, defaultValues);
|
||||
return getEnabledDisplayFilters(computedFilters);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to apply the display properties on the issues
|
||||
|
||||
@@ -76,7 +76,7 @@ export class ProjectStore implements IProjectStore {
|
||||
fetchStatus: TFetchStatus = undefined;
|
||||
projectMap: Record<string, TProject> = {};
|
||||
projectAnalyticsCountMap: Record<string, TProjectAnalyticsCount> = {};
|
||||
openCollapsibleSection: ProjectOverviewCollapsible[] = [];
|
||||
openCollapsibleSection: ProjectOverviewCollapsible[] = ["milestones"];
|
||||
lastCollapsibleAction: ProjectOverviewCollapsible | null = null;
|
||||
|
||||
// root store
|
||||
|
||||
Reference in New Issue
Block a user