Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/api/plane/utils/filters/filterset.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class Meta:
"start_date": ["exact", "range"],
"target_date": ["exact", "range"],
"created_at": ["exact", "range"],
"updated_at": ["exact", "range"],
"is_draft": ["exact"],
"priority": ["exact", "in"],
}
Expand Down
15 changes: 15 additions & 0 deletions apps/web/ce/components/rich-filters/filter-value-input/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import { observer } from "mobx-react";
// plane imports
import { TFilterValue, TFilterProperty } from "@plane/types";
// local imports
import { TFilterValueInputProps } from "@/components/rich-filters/shared";

export const AdditionalFilterValueInput = observer(
<P extends TFilterProperty, V extends TFilterValue>(_props: TFilterValueInputProps<P, V>) => (
// Fallback
<div className="h-full flex items-center px-4 text-xs text-custom-text-400 transition-opacity duration-200 cursor-not-allowed">
Filter type not supported
</div>
)
);
4 changes: 4 additions & 0 deletions apps/web/ce/helpers/work-item-filters/project-level.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// plane imports
import { EIssuesStoreType } from "@plane/types";
// plane web imports
import { TWorkItemFiltersEntityProps } from "@/plane-web/hooks/work-item-filters/use-work-item-filters-config";

export type TGetAdditionalPropsForProjectLevelFiltersHOCParams = {
entityType: EIssuesStoreType;
workspaceSlug: string;
projectId: string;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useMemo } from "react";
import {
AtSign,
Briefcase,
Calendar,
CalendarCheck2,
CalendarClock,
CircleUserRound,
Expand Down Expand Up @@ -32,6 +33,7 @@ import {
import { Avatar, Logo } from "@plane/ui";
import {
getAssigneeFilterConfig,
getCreatedAtFilterConfig,
getCreatedByFilterConfig,
getCycleFilterConfig,
getFileURL,
Expand All @@ -45,6 +47,8 @@ import {
getStateGroupFilterConfig,
getSubscriberFilterConfig,
getTargetDateFilterConfig,
getUpdatedAtFilterConfig,
isLoaderReady,
} from "@plane/utils";
// store hooks
import { useCycle } from "@/hooks/store/use-cycle";
Expand Down Expand Up @@ -72,18 +76,20 @@ export type TUseWorkItemFiltersConfigProps = {
} & TWorkItemFiltersEntityProps;

export type TWorkItemFiltersConfig = {
areAllConfigsInitialized: boolean;
configs: TFilterConfig<TWorkItemFilterProperty, TFilterValue>[];
configMap: {
[key in TWorkItemFilterProperty]?: TFilterConfig<TWorkItemFilterProperty, TFilterValue>;
};
isFilterEnabled: (key: TWorkItemFilterProperty) => boolean;
members: IUserLite[];
};

export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps): TWorkItemFiltersConfig => {
const { allowedFilters, cycleIds, labelIds, memberIds, moduleIds, projectId, projectIds, stateIds, workspaceSlug } =
props;
// store hooks
const { getProjectById } = useProject();
const { loader: projectLoader, getProjectById } = useProject();
const { getCycleById } = useCycle();
const { getLabelById } = useLabel();
const { getModuleById } = useModule();
Expand Down Expand Up @@ -128,6 +134,7 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
: [],
[projectIds, getProjectById]
);
const areAllConfigsInitialized = useMemo(() => isLoaderReady(projectLoader), [projectLoader]);

/**
* Checks if a filter is enabled based on the filters to show.
Expand Down Expand Up @@ -317,6 +324,28 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
[operatorConfigs]
);

// created at filter config
const createdAtFilterConfig = useMemo(
() =>
getCreatedAtFilterConfig<TWorkItemFilterProperty>("created_at")({
isEnabled: true,
filterIcon: Calendar,
...operatorConfigs,
}),
[operatorConfigs]
);

// updated at filter config
const updatedAtFilterConfig = useMemo(
() =>
getUpdatedAtFilterConfig<TWorkItemFilterProperty>("updated_at")({
isEnabled: true,
filterIcon: Calendar,
...operatorConfigs,
}),
[operatorConfigs]
);

// project filter config
const projectFilterConfig = useMemo(
() =>
Expand All @@ -331,6 +360,7 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
);

return {
areAllConfigsInitialized,
configs: [
stateFilterConfig,
stateGroupFilterConfig,
Expand All @@ -343,6 +373,8 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
moduleFilterConfig,
startDateFilterConfig,
targetDateFilterConfig,
createdAtFilterConfig,
updatedAtFilterConfig,
createdByFilterConfig,
subscriberFilterConfig,
],
Expand All @@ -360,7 +392,10 @@ export const useWorkItemFiltersConfig = (props: TUseWorkItemFiltersConfigProps):
priority: priorityFilterConfig,
start_date: startDateFilterConfig,
target_date: targetDateFilterConfig,
created_at: createdAtFilterConfig,
updated_at: updatedAtFilterConfig,
},
isFilterEnabled,
members: members ?? [],
};
};
114 changes: 0 additions & 114 deletions apps/web/core/components/rich-filters/add-filters-button.tsx

This file was deleted.

71 changes: 71 additions & 0 deletions apps/web/core/components/rich-filters/add-filters/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from "react";
import { observer } from "mobx-react";
import { ListFilter } from "lucide-react";
// plane imports
import { IFilterInstance } from "@plane/shared-state";
import { LOGICAL_OPERATOR, TExternalFilter, TFilterProperty, TSupportedOperators } from "@plane/types";
import { cn, getButtonStyling, TButtonVariant } from "@plane/ui";
// local imports
import { AddFilterDropdown } from "./dropdown";

export type TAddFilterButtonProps<P extends TFilterProperty, E extends TExternalFilter> = {
buttonConfig?: {
label: string | null;
variant?: TButtonVariant;
className?: string;
defaultOpen?: boolean;
iconConfig?: {
shouldShowIcon: boolean;
iconComponent?: React.ElementType;
};
isDisabled?: boolean;
};
filter: IFilterInstance<P, E>;
onFilterSelect?: (id: string) => void;
};

export const AddFilterButton = observer(
<P extends TFilterProperty, E extends TExternalFilter>(props: TAddFilterButtonProps<P, E>) => {
const { filter, buttonConfig, onFilterSelect } = props;
const {
variant = "link-neutral",
className,
label,
iconConfig = { shouldShowIcon: true },
isDisabled = false,
} = buttonConfig || {};
// derived values
const FilterIcon = iconConfig.iconComponent || ListFilter;

const handleFilterSelect = (property: P, operator: TSupportedOperators, isNegation: boolean) => {
filter.addCondition(
LOGICAL_OPERATOR.AND,
{
property,
operator,
value: undefined,
},
isNegation
);
onFilterSelect?.(property);
};

if (isDisabled) return null;
return (
<AddFilterDropdown
{...props}
buttonConfig={{
...buttonConfig,
className: cn(getButtonStyling(variant, "sm"), "py-[5px]", className),
}}
handleFilterSelect={handleFilterSelect}
customButton={
<div className="flex items-center gap-1">
{iconConfig.shouldShowIcon && <FilterIcon className="size-4 text-custom-text-200" />}
{label}
</div>
}
/>
);
}
);
Loading
Loading