forked from github/plane
[WEB-469] chore: selected filter sorting added in filter dropdown (#3869)
* chore: selected filter sorting added in filter dropdown * chore: handleClearAllFilters function updated * chore: filter dropdown sorting updated * chore: filter dropdown sorting updated * chore: filter dropdown sorting updated --------- Co-authored-by: gurusainath <gurusainath007@gmail.com>
This commit is contained in:
parent
47a7f60611
commit
7b88a2a88c
@ -32,7 +32,7 @@ export const ProjectArchivedEmptyState: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
||||||
...newFilters,
|
...newFilters,
|
||||||
|
@ -31,7 +31,7 @@ export const ProjectDraftEmptyState: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
||||||
...newFilters,
|
...newFilters,
|
||||||
|
@ -34,7 +34,7 @@ export const ProjectEmptyState: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
issuesFilter.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
||||||
...newFilters,
|
...newFilters,
|
||||||
|
@ -56,7 +56,7 @@ export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
|
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
||||||
|
@ -62,7 +62,7 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(
|
updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
@ -53,7 +53,7 @@ export const DraftIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
|
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, { ...newFilters });
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, { ...newFilters });
|
||||||
|
@ -76,7 +76,7 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||||||
if (!workspaceSlug || !globalViewId) return;
|
if (!workspaceSlug || !globalViewId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(
|
updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
@ -61,7 +61,7 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId || !moduleId) return;
|
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(
|
updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
@ -57,7 +57,7 @@ export const ProfileIssuesAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !userId) return;
|
if (!workspaceSlug || !userId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(workspaceSlug.toString(), undefined, EIssueFilterType.FILTERS, { ...newFilters }, userId.toString());
|
updateFilters(workspaceSlug.toString(), undefined, EIssueFilterType.FILTERS, { ...newFilters }, userId.toString());
|
||||||
};
|
};
|
||||||
|
@ -59,7 +59,7 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, { ...newFilters });
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, { ...newFilters });
|
||||||
};
|
};
|
||||||
|
@ -67,7 +67,7 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId || !viewId) return;
|
if (!workspaceSlug || !projectId || !viewId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
updateFilters(
|
updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// hooks
|
// hooks
|
||||||
import { Avatar, Loader } from "@plane/ui";
|
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
|
||||||
import { useMember } from "hooks/store";
|
import { useMember } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
|
import { Avatar, Loader } from "@plane/ui";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
@ -24,15 +25,23 @@ export const FilterAssignees: React.FC<Props> = observer((props: Props) => {
|
|||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = memberIds?.filter(
|
const sortedOptions = useMemo(() => {
|
||||||
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
const filteredOptions = (memberIds || []).filter((memberId) =>
|
||||||
);
|
getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(memberId) => !(appliedFilters ?? []).includes(memberId),
|
||||||
|
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,10 +53,10 @@ export const FilterAssignees: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((memberId) => {
|
{sortedOptions.slice(0, itemsToRender).map((memberId) => {
|
||||||
const member = getUserDetails(memberId);
|
const member = getUserDetails(memberId);
|
||||||
|
|
||||||
if (!member) return null;
|
if (!member) return null;
|
||||||
@ -61,13 +70,13 @@ export const FilterAssignees: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// hooks
|
// hooks
|
||||||
import { Avatar, Loader } from "@plane/ui";
|
import { Avatar, Loader } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
@ -22,16 +23,25 @@ export const FilterCreatedBy: React.FC<Props> = observer((props: Props) => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const { getUserDetails } = useMember();
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
const filteredOptions = memberIds?.filter(
|
const sortedOptions = useMemo(() => {
|
||||||
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
const filteredOptions = (memberIds || []).filter((memberId) =>
|
||||||
);
|
getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(memberId) => !(appliedFilters ?? []).includes(memberId),
|
||||||
|
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -43,10 +53,10 @@ export const FilterCreatedBy: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((memberId) => {
|
{sortedOptions.slice(0, itemsToRender).map((memberId) => {
|
||||||
const member = getUserDetails(memberId);
|
const member = getUserDetails(memberId);
|
||||||
|
|
||||||
if (!member) return null;
|
if (!member) return null;
|
||||||
@ -60,13 +70,13 @@ export const FilterCreatedBy: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
@ -31,16 +31,24 @@ export const FilterCycle: React.FC<Props> = observer((props) => {
|
|||||||
const cycleIds = projectId ? getProjectCycleIds(projectId) : undefined;
|
const cycleIds = projectId ? getProjectCycleIds(projectId) : undefined;
|
||||||
const cycles = cycleIds?.map((projectId) => getCycleById(projectId)!) ?? null;
|
const cycles = cycleIds?.map((projectId) => getCycleById(projectId)!) ?? null;
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
const filteredOptions = sortBy(
|
|
||||||
cycles?.filter((cycle) => cycle.name.toLowerCase().includes(searchQuery.toLowerCase())),
|
const sortedOptions = useMemo(() => {
|
||||||
(cycle) => cycle.name.toLowerCase()
|
const filteredOptions = (cycles || []).filter((cycle) =>
|
||||||
);
|
cycle.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(cycle) => !appliedFilters?.includes(cycle.id),
|
||||||
|
(cycle) => cycle.name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cycleStatus = (status: TCycleGroups) => (status ? status.toLocaleLowerCase() : "draft") as TCycleGroups;
|
const cycleStatus = (status: TCycleGroups) => (status ? status.toLocaleLowerCase() : "draft") as TCycleGroups;
|
||||||
@ -54,10 +62,10 @@ export const FilterCycle: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((cycle) => (
|
{sortedOptions.slice(0, itemsToRender).map((cycle) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={cycle.id}
|
key={cycle.id}
|
||||||
isChecked={appliedFilters?.includes(cycle.id) ? true : false}
|
isChecked={appliedFilters?.includes(cycle.id) ? true : false}
|
||||||
@ -69,13 +77,13 @@ export const FilterCycle: React.FC<Props> = observer((props) => {
|
|||||||
activePulse={cycleStatus(cycle?.status) === "current" ? true : false}
|
activePulse={cycleStatus(cycle?.status) === "current" ? true : false}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// components
|
// components
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
@ -26,13 +27,23 @@ export const FilterLabels: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = labels?.filter((label) => label.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
const sortedOptions = useMemo(() => {
|
||||||
|
const filteredOptions = (labels || []).filter((label) =>
|
||||||
|
label.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(label) => !(appliedFilters ?? []).includes(label.id),
|
||||||
|
(label) => label.name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,10 +55,10 @@ export const FilterLabels: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((label) => (
|
{sortedOptions.slice(0, itemsToRender).map((label) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={label?.id}
|
key={label?.id}
|
||||||
isChecked={appliedFilters?.includes(label?.id) ? true : false}
|
isChecked={appliedFilters?.includes(label?.id) ? true : false}
|
||||||
@ -56,13 +67,13 @@ export const FilterLabels: React.FC<Props> = observer((props) => {
|
|||||||
title={label.name}
|
title={label.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// hooks
|
// hooks
|
||||||
import { Loader, Avatar } from "@plane/ui";
|
import { Loader, Avatar } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
@ -24,15 +25,23 @@ export const FilterMentions: React.FC<Props> = observer((props: Props) => {
|
|||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = memberIds?.filter(
|
const sortedOptions = useMemo(() => {
|
||||||
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
const filteredOptions = (memberIds || []).filter((memberId) =>
|
||||||
);
|
getUserDetails(memberId)?.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(memberId) => !(appliedFilters ?? []).includes(memberId),
|
||||||
|
(memberId) => getUserDetails(memberId)?.display_name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,10 +53,10 @@ export const FilterMentions: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((memberId) => {
|
{sortedOptions.slice(0, itemsToRender).map((memberId) => {
|
||||||
const member = getUserDetails(memberId);
|
const member = getUserDetails(memberId);
|
||||||
|
|
||||||
if (!member) return null;
|
if (!member) return null;
|
||||||
@ -61,13 +70,13 @@ export const FilterMentions: React.FC<Props> = observer((props: Props) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
@ -29,16 +29,24 @@ export const FilterModule: React.FC<Props> = observer((props) => {
|
|||||||
const moduleIds = projectId ? getProjectModuleIds(projectId) : undefined;
|
const moduleIds = projectId ? getProjectModuleIds(projectId) : undefined;
|
||||||
const modules = moduleIds?.map((projectId) => getModuleById(projectId)!) ?? null;
|
const modules = moduleIds?.map((projectId) => getModuleById(projectId)!) ?? null;
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
const filteredOptions = sortBy(
|
|
||||||
modules?.filter((module) => module.name.toLowerCase().includes(searchQuery.toLowerCase())),
|
const sortedOptions = useMemo(() => {
|
||||||
(module) => module.name.toLowerCase()
|
const filteredOptions = (modules || []).filter((module) =>
|
||||||
);
|
module.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(module) => !appliedFilters?.includes(module.id),
|
||||||
|
(module) => module.name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -50,10 +58,10 @@ export const FilterModule: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((cycle) => (
|
{sortedOptions.slice(0, itemsToRender).map((cycle) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={cycle.id}
|
key={cycle.id}
|
||||||
isChecked={appliedFilters?.includes(cycle.id) ? true : false}
|
isChecked={appliedFilters?.includes(cycle.id) ? true : false}
|
||||||
@ -62,13 +70,13 @@ export const FilterModule: React.FC<Props> = observer((props) => {
|
|||||||
title={cycle.name}
|
title={cycle.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// components
|
// components
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
@ -26,13 +27,23 @@ export const FilterProjects: React.FC<Props> = observer((props) => {
|
|||||||
// derived values
|
// derived values
|
||||||
const projects = workspaceProjectIds?.map((projectId) => getProjectById(projectId)!) ?? null;
|
const projects = workspaceProjectIds?.map((projectId) => getProjectById(projectId)!) ?? null;
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
const filteredOptions = projects?.filter((project) => project.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
||||||
|
const sortedOptions = useMemo(() => {
|
||||||
|
const filteredOptions = (projects || []).filter((project) =>
|
||||||
|
project.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
return sortBy(filteredOptions, [
|
||||||
|
(project) => !(appliedFilters ?? []).includes(project.id),
|
||||||
|
(project) => project.name.toLowerCase(),
|
||||||
|
]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,10 +55,10 @@ export const FilterProjects: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((project) => (
|
{sortedOptions.slice(0, itemsToRender).map((project) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={`project-${project.id}`}
|
key={`project-${project.id}`}
|
||||||
isChecked={appliedFilters?.includes(project.id) ? true : false}
|
isChecked={appliedFilters?.includes(project.id) ? true : false}
|
||||||
@ -60,13 +71,13 @@ export const FilterProjects: React.FC<Props> = observer((props) => {
|
|||||||
title={project.name}
|
title={project.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// components
|
// components
|
||||||
import { StateGroupIcon } from "@plane/ui";
|
import { StateGroupIcon } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// components
|
// components
|
||||||
import { Loader, StateGroupIcon } from "@plane/ui";
|
import { Loader, StateGroupIcon } from "@plane/ui";
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
@ -22,13 +23,18 @@ export const FilterState: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = states?.filter((s) => s.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
const sortedOptions = useMemo(() => {
|
||||||
|
const filteredOptions = (states ?? []).filter((s) => s.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||||
|
|
||||||
|
return sortBy(filteredOptions, [(s) => !(appliedFilters ?? []).includes(s.id), (s) => s.name.toLowerCase()]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handleViewToggle = () => {
|
const handleViewToggle = () => {
|
||||||
if (!filteredOptions) return;
|
if (!sortedOptions) return;
|
||||||
|
|
||||||
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
if (itemsToRender === sortedOptions.length) setItemsToRender(5);
|
||||||
else setItemsToRender(filteredOptions.length);
|
else setItemsToRender(sortedOptions.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -40,10 +46,10 @@ export const FilterState: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions ? (
|
{sortedOptions ? (
|
||||||
filteredOptions.length > 0 ? (
|
sortedOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((state) => (
|
{sortedOptions.slice(0, itemsToRender).map((state) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={state.id}
|
key={state.id}
|
||||||
isChecked={appliedFilters?.includes(state.id) ? true : false}
|
isChecked={appliedFilters?.includes(state.id) ? true : false}
|
||||||
@ -52,13 +58,13 @@ export const FilterState: React.FC<Props> = observer((props) => {
|
|||||||
title={state.name}
|
title={state.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredOptions.length > 5 && (
|
{sortedOptions.length > 5 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ml-8 text-xs font-medium text-custom-primary-100"
|
className="ml-8 text-xs font-medium text-custom-primary-100"
|
||||||
onClick={handleViewToggle}
|
onClick={handleViewToggle}
|
||||||
>
|
>
|
||||||
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
{itemsToRender === sortedOptions.length ? "View less" : "View all"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -68,7 +68,7 @@ export const CycleLayoutRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
issuesFilter.updateFilters(
|
issuesFilter.updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
@ -59,7 +59,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId || !moduleId) return;
|
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = [];
|
||||||
});
|
});
|
||||||
issuesFilter.updateFilters(
|
issuesFilter.updateFilters(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
|
Loading…
Reference in New Issue
Block a user