mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
refactor: filter components (#2359)
* fix: calendar layout dividers * refactor: filter selection components * fix: dropdown closing after selection * refactor: filters components
This commit is contained in:
parent
41fd9ce6e8
commit
7be038ac5a
@ -6,7 +6,7 @@ import useSWR from "swr";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { AppliedFiltersList, CalendarLayout, GanttLayout, KanBanLayout, SpreadsheetLayout } from "components/issues";
|
import { AppliedFiltersRoot, CalendarLayout, GanttLayout, KanBanLayout, SpreadsheetLayout } from "components/issues";
|
||||||
|
|
||||||
export const AllViews: React.FC = observer(() => {
|
export const AllViews: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -35,7 +35,7 @@ export const AllViews: React.FC = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full flex flex-col overflow-auto">
|
<div className="relative w-full h-full flex flex-col overflow-auto">
|
||||||
<AppliedFiltersList />
|
<AppliedFiltersRoot />
|
||||||
{activeLayout === "kanban" ? (
|
{activeLayout === "kanban" ? (
|
||||||
<KanBanLayout />
|
<KanBanLayout />
|
||||||
) : activeLayout === "calendar" ? (
|
) : activeLayout === "calendar" ? (
|
||||||
|
@ -38,7 +38,7 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
|||||||
<CalendarWeekHeader />
|
<CalendarWeekHeader />
|
||||||
<div className="h-full w-full overflow-y-auto">
|
<div className="h-full w-full overflow-y-auto">
|
||||||
{calendarLayout === "month" ? (
|
{calendarLayout === "month" ? (
|
||||||
<div className="h-full w-full grid grid-cols-1">
|
<div className="h-full w-full grid grid-cols-1 divide-y-[0.5px] divide-custom-border-200">
|
||||||
{allWeeksOfActiveMonth &&
|
{allWeeksOfActiveMonth &&
|
||||||
Object.values(allWeeksOfActiveMonth).map((week: ICalendarWeek, weekIndex) => (
|
Object.values(allWeeksOfActiveMonth).map((week: ICalendarWeek, weekIndex) => (
|
||||||
<CalendarWeekDays key={weekIndex} week={week} issues={issues} />
|
<CalendarWeekDays key={weekIndex} week={week} issues={issues} />
|
||||||
|
@ -4,9 +4,10 @@ import { observer } from "mobx-react-lite";
|
|||||||
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// icons
|
||||||
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
// constants
|
// constants
|
||||||
import { MONTHS_LIST } from "constants/calendar";
|
import { MONTHS_LIST } from "constants/calendar";
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
||||||
|
|
||||||
export const CalendarMonthsDropdown: React.FC = observer(() => {
|
export const CalendarMonthsDropdown: React.FC = observer(() => {
|
||||||
const { calendar: calendarStore, issueFilter: issueFilterStore } = useMobxStore();
|
const { calendar: calendarStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||||
@ -46,73 +47,63 @@ export const CalendarMonthsDropdown: React.FC = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover className="relative">
|
<Popover className="relative">
|
||||||
{({ close }) => (
|
<Popover.Button className="outline-none text-xl font-semibold" disabled={calendarLayout === "week"}>
|
||||||
<>
|
{calendarLayout === "month"
|
||||||
<Popover.Button className="outline-none text-xl font-semibold" disabled={calendarLayout === "week"}>
|
? `${MONTHS_LIST[activeMonthDate.getMonth() + 1].title} ${activeMonthDate.getFullYear()}`
|
||||||
{calendarLayout === "month"
|
: getWeekLayoutHeader()}
|
||||||
? `${MONTHS_LIST[activeMonthDate.getMonth() + 1].title} ${activeMonthDate.getFullYear()}`
|
</Popover.Button>
|
||||||
: getWeekLayoutHeader()}
|
<Transition
|
||||||
</Popover.Button>
|
as={React.Fragment}
|
||||||
<Transition
|
enter="transition ease-out duration-200"
|
||||||
as={React.Fragment}
|
enterFrom="opacity-0 translate-y-1"
|
||||||
enter="transition ease-out duration-200"
|
enterTo="opacity-100 translate-y-0"
|
||||||
enterFrom="opacity-0 translate-y-1"
|
leave="transition ease-in duration-150"
|
||||||
enterTo="opacity-100 translate-y-0"
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
leave="transition ease-in duration-150"
|
leaveTo="opacity-0 translate-y-1"
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
>
|
||||||
leaveTo="opacity-0 translate-y-1"
|
<Popover.Panel>
|
||||||
>
|
<div className="absolute left-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded w-56 p-3 divide-y divide-custom-border-200">
|
||||||
<Popover.Panel>
|
<div className="flex items-center justify-between gap-2 pb-3">
|
||||||
<div className="absolute left-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded w-56 p-3 divide-y divide-custom-border-200">
|
<button
|
||||||
<div className="flex items-center justify-between gap-2 pb-3">
|
type="button"
|
||||||
<button
|
className="grid place-items-center"
|
||||||
type="button"
|
onClick={() => {
|
||||||
className="grid place-items-center"
|
const previousYear = new Date(activeMonthDate.getFullYear() - 1, activeMonthDate.getMonth(), 1);
|
||||||
onClick={() => {
|
handleDateChange(previousYear);
|
||||||
const previousYear = new Date(activeMonthDate.getFullYear() - 1, activeMonthDate.getMonth(), 1);
|
}}
|
||||||
handleDateChange(previousYear);
|
>
|
||||||
|
<ChevronLeft size={14} />
|
||||||
close();
|
</button>
|
||||||
}}
|
<span className="text-xs">{activeMonthDate.getFullYear()}</span>
|
||||||
>
|
<button
|
||||||
<ChevronLeft size={14} />
|
type="button"
|
||||||
</button>
|
className="grid place-items-center"
|
||||||
<span className="text-xs">{activeMonthDate.getFullYear()}</span>
|
onClick={() => {
|
||||||
<button
|
const nextYear = new Date(activeMonthDate.getFullYear() + 1, activeMonthDate.getMonth(), 1);
|
||||||
type="button"
|
handleDateChange(nextYear);
|
||||||
className="grid place-items-center"
|
}}
|
||||||
onClick={() => {
|
>
|
||||||
const nextYear = new Date(activeMonthDate.getFullYear() + 1, activeMonthDate.getMonth(), 1);
|
<ChevronRight size={14} />
|
||||||
handleDateChange(nextYear);
|
</button>
|
||||||
|
</div>
|
||||||
close();
|
<div className="grid grid-cols-4 gap-4 items-stretch justify-items-stretch pt-3">
|
||||||
}}
|
{Object.values(MONTHS_LIST).map((month, index) => (
|
||||||
>
|
<button
|
||||||
<ChevronRight size={14} />
|
key={month.shortTitle}
|
||||||
</button>
|
type="button"
|
||||||
</div>
|
className="text-xs hover:bg-custom-background-80 rounded py-0.5"
|
||||||
<div className="grid grid-cols-4 gap-4 items-stretch justify-items-stretch pt-3">
|
onClick={() => {
|
||||||
{Object.values(MONTHS_LIST).map((month, index) => (
|
const newDate = new Date(activeMonthDate.getFullYear(), index, 1);
|
||||||
<button
|
handleDateChange(newDate);
|
||||||
key={month.shortTitle}
|
}}
|
||||||
type="button"
|
>
|
||||||
className="text-xs hover:bg-custom-background-80 rounded py-0.5"
|
{month.shortTitle}
|
||||||
onClick={() => {
|
</button>
|
||||||
const newDate = new Date(activeMonthDate.getFullYear(), index, 1);
|
))}
|
||||||
handleDateChange(newDate);
|
</div>
|
||||||
|
</div>
|
||||||
close();
|
</Popover.Panel>
|
||||||
}}
|
</Transition>
|
||||||
>
|
|
||||||
{month.shortTitle}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Popover.Panel>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -86,7 +86,7 @@ export const CalendarHeader: React.FC = observer(() => {
|
|||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-2.5 py-1 text-xs bg-custom-background-80 rounded font-medium"
|
className="px-2.5 py-1 text-xs bg-custom-background-80 rounded font-medium text-custom-text-200 hover:text-custom-text-100"
|
||||||
onClick={handleToday}
|
onClick={handleToday}
|
||||||
>
|
>
|
||||||
Today
|
Today
|
||||||
|
@ -27,9 +27,9 @@ export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`grid divide-x-[0.5px] divide-y-[0.5px] divide-custom-border-200 ${
|
className={`grid divide-x-[0.5px] divide-custom-border-200 ${showWeekends ? "grid-cols-7" : "grid-cols-5"} ${
|
||||||
showWeekends ? "grid-cols-7" : "grid-cols-5"
|
calendarLayout === "month" ? "" : "h-full"
|
||||||
} ${calendarLayout === "month" ? "" : "h-full"}`}
|
}`}
|
||||||
>
|
>
|
||||||
{Object.values(week).map((date: ICalendarDate) => {
|
{Object.values(week).map((date: ICalendarDate) => {
|
||||||
if (!showWeekends && (date.date.getDay() === 0 || date.date.getDay() === 6)) return null;
|
if (!showWeekends && (date.date.getDay() === 0 || date.date.getDay() === 6)) return null;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
// mobx store
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
AppliedDateFilters,
|
AppliedDateFilters,
|
||||||
@ -17,65 +14,19 @@ import { X } from "lucide-react";
|
|||||||
// helpers
|
// helpers
|
||||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueFilterOptions } from "types";
|
import { IIssueFilterOptions, IIssueLabels, IStateResponse, IUserLite } from "types";
|
||||||
|
|
||||||
export const AppliedFiltersList: React.FC = observer(() => {
|
type Props = {
|
||||||
const router = useRouter();
|
appliedFilters: IIssueFilterOptions;
|
||||||
const { workspaceSlug, projectId } = router.query;
|
handleClearAllFilters: () => void;
|
||||||
|
handleRemoveFilter: (key: keyof IIssueFilterOptions, value: string | null) => void;
|
||||||
|
labels: IIssueLabels[] | undefined;
|
||||||
|
members: IUserLite[] | undefined;
|
||||||
|
states: IStateResponse | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const { issueFilter: issueFilterStore, project: projectStore } = useMobxStore();
|
export const AppliedFiltersList: React.FC<Props> = observer((props) => {
|
||||||
|
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, labels, members, states } = props;
|
||||||
const userFilters = issueFilterStore.userFilters;
|
|
||||||
|
|
||||||
// filters whose value not null or empty array
|
|
||||||
const appliedFilters: IIssueFilterOptions = {};
|
|
||||||
Object.entries(userFilters).forEach(([key, value]) => {
|
|
||||||
if (!value) return;
|
|
||||||
|
|
||||||
if (Array.isArray(value) && value.length === 0) return;
|
|
||||||
|
|
||||||
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
|
|
||||||
// remove all values of the key if value is null
|
|
||||||
if (!value) {
|
|
||||||
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
|
||||||
filters: {
|
|
||||||
[key]: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the passed value from the key
|
|
||||||
let newValues = issueFilterStore.userFilters?.[key] ?? [];
|
|
||||||
newValues = newValues.filter((val) => val !== value);
|
|
||||||
|
|
||||||
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
|
||||||
filters: {
|
|
||||||
[key]: newValues,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearAllFilters = () => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
|
|
||||||
const newFilters: IIssueFilterOptions = {};
|
|
||||||
Object.keys(userFilters).forEach((key) => {
|
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
|
||||||
filters: { ...newFilters },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// return if no filters are applied
|
|
||||||
if (Object.keys(appliedFilters).length === 0) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-stretch gap-2 flex-wrap bg-custom-background-100 p-4">
|
<div className="flex items-stretch gap-2 flex-wrap bg-custom-background-100 p-4">
|
||||||
@ -91,7 +42,7 @@ export const AppliedFiltersList: React.FC = observer(() => {
|
|||||||
{(filterKey === "assignees" || filterKey === "created_by" || filterKey === "subscriber") && (
|
{(filterKey === "assignees" || filterKey === "created_by" || filterKey === "subscriber") && (
|
||||||
<AppliedMembersFilters
|
<AppliedMembersFilters
|
||||||
handleRemove={(val) => handleRemoveFilter(filterKey, val)}
|
handleRemove={(val) => handleRemoveFilter(filterKey, val)}
|
||||||
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
members={members}
|
||||||
values={value}
|
values={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -101,7 +52,7 @@ export const AppliedFiltersList: React.FC = observer(() => {
|
|||||||
{filterKey === "labels" && (
|
{filterKey === "labels" && (
|
||||||
<AppliedLabelsFilters
|
<AppliedLabelsFilters
|
||||||
handleRemove={(val) => handleRemoveFilter("labels", val)}
|
handleRemove={(val) => handleRemoveFilter("labels", val)}
|
||||||
labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? []}
|
labels={labels}
|
||||||
values={value}
|
values={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -111,7 +62,7 @@ export const AppliedFiltersList: React.FC = observer(() => {
|
|||||||
{filterKey === "state" && (
|
{filterKey === "state" && (
|
||||||
<AppliedStateFilters
|
<AppliedStateFilters
|
||||||
handleRemove={(val) => handleRemoveFilter("state", val)}
|
handleRemove={(val) => handleRemoveFilter("state", val)}
|
||||||
states={projectStore.states?.[projectId?.toString() ?? ""]}
|
states={states}
|
||||||
values={value}
|
values={value}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -3,5 +3,6 @@ export * from "./filters-list";
|
|||||||
export * from "./label";
|
export * from "./label";
|
||||||
export * from "./members";
|
export * from "./members";
|
||||||
export * from "./priority";
|
export * from "./priority";
|
||||||
|
export * from "./root";
|
||||||
export * from "./state";
|
export * from "./state";
|
||||||
export * from "./state-group";
|
export * from "./state-group";
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { AppliedFiltersList } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "types";
|
||||||
|
|
||||||
|
export const AppliedFiltersRoot: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
|
const { issueFilter: issueFilterStore, project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
|
const userFilters = issueFilterStore.userFilters;
|
||||||
|
|
||||||
|
// filters whose value not null or empty array
|
||||||
|
const appliedFilters: IIssueFilterOptions = {};
|
||||||
|
Object.entries(userFilters).forEach(([key, value]) => {
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
if (Array.isArray(value) && value.length === 0) return;
|
||||||
|
|
||||||
|
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
// remove all values of the key if value is null
|
||||||
|
if (!value) {
|
||||||
|
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||||
|
filters: {
|
||||||
|
[key]: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the passed value from the key
|
||||||
|
let newValues = issueFilterStore.userFilters?.[key] ?? [];
|
||||||
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
|
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||||
|
filters: {
|
||||||
|
[key]: newValues,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAllFilters = () => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
const newFilters: IIssueFilterOptions = {};
|
||||||
|
Object.keys(userFilters).forEach((key) => {
|
||||||
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||||
|
filters: { ...newFilters },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// return if no filters are applied
|
||||||
|
if (Object.keys(appliedFilters).length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppliedFiltersList
|
||||||
|
appliedFilters={appliedFilters}
|
||||||
|
handleClearAllFilters={handleClearAllFilters}
|
||||||
|
handleRemoveFilter={handleRemoveFilter}
|
||||||
|
labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? []}
|
||||||
|
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
||||||
|
states={projectStore.states?.[projectId?.toString() ?? ""]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
@ -1,34 +1,30 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
|
|
||||||
// mobx store
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar, Loader } from "components/ui";
|
import { Avatar, Loader } from "components/ui";
|
||||||
|
// types
|
||||||
|
import { IUserLite } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
itemsToRender: number;
|
||||||
projectId: string;
|
members: IUserLite[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
viewButtons: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterAssignees: React.FC<Props> = observer((props) => {
|
export const FilterAssignees: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, projectId, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, itemsToRender, members, searchQuery, viewButtons } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const store = useMobxStore();
|
|
||||||
const { project: projectStore } = store;
|
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) =>
|
const filteredOptions = members?.filter((member) =>
|
||||||
member.member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -45,11 +41,11 @@ export const FilterAssignees: React.FC<Props> = observer((props) => {
|
|||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((member) => (
|
{filteredOptions.slice(0, itemsToRender).map((member) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={`assignees-${member?.member?.id}`}
|
key={`assignees-${member.id}`}
|
||||||
isChecked={appliedFilters?.includes(member.member?.id) ? true : false}
|
isChecked={appliedFilters?.includes(member.id) ? true : false}
|
||||||
onClick={() => handleUpdate(member.member?.id)}
|
onClick={() => handleUpdate(member.id)}
|
||||||
icon={<Avatar user={member.member} height="18px" width="18px" />}
|
icon={<Avatar user={member} height="18px" width="18px" />}
|
||||||
title={member.member?.display_name}
|
title={member.display_name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{viewButtons}
|
||||||
@ -68,4 +64,4 @@ export const FilterAssignees: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -1,34 +1,30 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
|
|
||||||
// mobx store
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar, Loader } from "components/ui";
|
import { Avatar, Loader } from "components/ui";
|
||||||
|
// types
|
||||||
|
import { IUserLite } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
itemsToRender: number;
|
||||||
projectId: string;
|
members: IUserLite[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
viewButtons: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterCreatedBy: React.FC<Props> = observer((props) => {
|
export const FilterCreatedBy: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, projectId, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, itemsToRender, members, searchQuery, viewButtons } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const store = useMobxStore();
|
|
||||||
const { project: projectStore } = store;
|
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) =>
|
const filteredOptions = members?.filter((member) =>
|
||||||
member.member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -45,11 +41,11 @@ export const FilterCreatedBy: React.FC<Props> = observer((props) => {
|
|||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((member) => (
|
{filteredOptions.slice(0, itemsToRender).map((member) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={`created-by-${member.member?.id}`}
|
key={`created-by-${member.id}`}
|
||||||
isChecked={appliedFilters?.includes(member.member?.id) ? true : false}
|
isChecked={appliedFilters?.includes(member.id) ? true : false}
|
||||||
onClick={() => handleUpdate(member.member?.id)}
|
onClick={() => handleUpdate(member.id)}
|
||||||
icon={<Avatar user={member.member} height="18px" width="18px" />}
|
icon={<Avatar user={member} height="18px" width="18px" />}
|
||||||
title={member.member?.display_name}
|
title={member.display_name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{viewButtons}
|
||||||
@ -68,4 +64,4 @@ export const FilterCreatedBy: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -31,6 +31,29 @@ type Props = {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ViewButtonProps = {
|
||||||
|
handleLess: () => void;
|
||||||
|
handleMore: () => void;
|
||||||
|
isViewLessVisible: boolean;
|
||||||
|
isViewMoreVisible: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ViewButtons = ({ handleLess, handleMore, isViewLessVisible, isViewMoreVisible }: ViewButtonProps) => (
|
||||||
|
<div className="flex items-center gap-2 ml-7 mt-1">
|
||||||
|
{/* TODO: handle view more and less in a better way */}
|
||||||
|
{isViewMoreVisible && (
|
||||||
|
<button className="text-custom-primary-100 text-xs font-medium" onClick={handleMore}>
|
||||||
|
View more
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{isViewLessVisible && (
|
||||||
|
<button className="text-custom-primary-100 text-xs font-medium" onClick={handleLess}>
|
||||||
|
View less
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export const FilterSelection: React.FC<Props> = observer((props) => {
|
export const FilterSelection: React.FC<Props> = observer((props) => {
|
||||||
const { filters, handleFiltersUpdate, layoutDisplayFiltersOptions, projectId } = props;
|
const { filters, handleFiltersUpdate, layoutDisplayFiltersOptions, projectId } = props;
|
||||||
|
|
||||||
@ -157,25 +180,12 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
itemsToRender={filtersToRender.priority?.currentLength ?? 0}
|
itemsToRender={filtersToRender.priority?.currentLength ?? 0}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
viewButtons={
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
<ViewButtons
|
||||||
{/* TODO: handle view more and less in a better way */}
|
isViewLessVisible={isViewLessVisible("priority")}
|
||||||
{isViewMoreVisible("priority") && (
|
isViewMoreVisible={isViewMoreVisible("priority")}
|
||||||
<button
|
handleLess={() => handleViewLess("priority")}
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
handleMore={() => handleViewMore("priority")}
|
||||||
onClick={() => handleViewMore("priority")}
|
/>
|
||||||
>
|
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("priority") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
|
||||||
onClick={() => handleViewLess("priority")}
|
|
||||||
>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -189,25 +199,15 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
handleUpdate={(val) => handleFiltersUpdate("state_group", val)}
|
handleUpdate={(val) => handleFiltersUpdate("state_group", val)}
|
||||||
itemsToRender={filtersToRender.state_group?.currentLength ?? 0}
|
itemsToRender={filtersToRender.state_group?.currentLength ?? 0}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
|
viewButtons={
|
||||||
|
<ViewButtons
|
||||||
|
isViewLessVisible={isViewLessVisible("state_group")}
|
||||||
|
isViewMoreVisible={isViewMoreVisible("state_group")}
|
||||||
|
handleLess={() => handleViewLess("state_group")}
|
||||||
|
handleMore={() => handleViewMore("state_group")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
|
||||||
{isViewMoreVisible("state_group") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium ml-7"
|
|
||||||
onClick={() => handleViewMore("state_group")}
|
|
||||||
>
|
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("state_group") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
|
||||||
onClick={() => handleViewLess("state_group")}
|
|
||||||
>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -219,23 +219,16 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
handleUpdate={(val) => handleFiltersUpdate("state", val)}
|
handleUpdate={(val) => handleFiltersUpdate("state", val)}
|
||||||
itemsToRender={filtersToRender.state?.currentLength ?? 0}
|
itemsToRender={filtersToRender.state?.currentLength ?? 0}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
projectId={projectId}
|
states={projectStore.states?.[projectId]}
|
||||||
|
viewButtons={
|
||||||
|
<ViewButtons
|
||||||
|
isViewLessVisible={isViewLessVisible("state")}
|
||||||
|
isViewMoreVisible={isViewMoreVisible("state")}
|
||||||
|
handleLess={() => handleViewLess("state")}
|
||||||
|
handleMore={() => handleViewMore("state")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
|
||||||
{isViewMoreVisible("state") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium ml-7"
|
|
||||||
onClick={() => handleViewMore("state")}
|
|
||||||
>
|
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("state") && (
|
|
||||||
<button className="text-custom-primary-100 text-xs font-medium" onClick={() => handleViewLess("state")}>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -246,27 +239,15 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
appliedFilters={filters.assignees ?? null}
|
appliedFilters={filters.assignees ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("assignees", val)}
|
handleUpdate={(val) => handleFiltersUpdate("assignees", val)}
|
||||||
itemsToRender={filtersToRender.assignees?.currentLength ?? 0}
|
itemsToRender={filtersToRender.assignees?.currentLength ?? 0}
|
||||||
projectId={projectId}
|
members={projectStore.members?.[projectId]?.map((m) => m.member) ?? undefined}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
viewButtons={
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
<ViewButtons
|
||||||
{isViewMoreVisible("assignees") && (
|
isViewLessVisible={isViewLessVisible("assignees")}
|
||||||
<button
|
isViewMoreVisible={isViewMoreVisible("assignees")}
|
||||||
className="text-custom-primary-100 text-xs font-medium ml-7"
|
handleLess={() => handleViewLess("assignees")}
|
||||||
onClick={() => handleViewMore("assignees")}
|
handleMore={() => handleViewMore("assignees")}
|
||||||
>
|
/>
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("assignees") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
|
||||||
onClick={() => handleViewLess("assignees")}
|
|
||||||
>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -279,27 +260,15 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
appliedFilters={filters.created_by ?? null}
|
appliedFilters={filters.created_by ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("created_by", val)}
|
handleUpdate={(val) => handleFiltersUpdate("created_by", val)}
|
||||||
itemsToRender={filtersToRender.created_by?.currentLength ?? 0}
|
itemsToRender={filtersToRender.created_by?.currentLength ?? 0}
|
||||||
projectId={projectId}
|
members={projectStore.members?.[projectId]?.map((m) => m.member) ?? undefined}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
viewButtons={
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
<ViewButtons
|
||||||
{isViewMoreVisible("created_by") && (
|
isViewLessVisible={isViewLessVisible("created_by")}
|
||||||
<button
|
isViewMoreVisible={isViewMoreVisible("created_by")}
|
||||||
className="text-custom-primary-100 text-xs font-medium ml-7"
|
handleLess={() => handleViewLess("created_by")}
|
||||||
onClick={() => handleViewMore("created_by")}
|
handleMore={() => handleViewMore("created_by")}
|
||||||
>
|
/>
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("created_by") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
|
||||||
onClick={() => handleViewLess("created_by")}
|
|
||||||
>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -312,27 +281,15 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
appliedFilters={filters.labels ?? null}
|
appliedFilters={filters.labels ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("labels", val)}
|
handleUpdate={(val) => handleFiltersUpdate("labels", val)}
|
||||||
itemsToRender={filtersToRender.labels?.currentLength ?? 0}
|
itemsToRender={filtersToRender.labels?.currentLength ?? 0}
|
||||||
projectId={projectId}
|
labels={projectStore.labels?.[projectId] ?? undefined}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
viewButtons={
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
<ViewButtons
|
||||||
{isViewMoreVisible("labels") && (
|
isViewLessVisible={isViewLessVisible("labels")}
|
||||||
<button
|
isViewMoreVisible={isViewMoreVisible("labels")}
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
handleLess={() => handleViewLess("labels")}
|
||||||
onClick={() => handleViewMore("labels")}
|
handleMore={() => handleViewMore("labels")}
|
||||||
>
|
/>
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible("labels") && (
|
|
||||||
<button
|
|
||||||
className="text-custom-primary-100 text-xs font-medium"
|
|
||||||
onClick={() => handleViewLess("labels")}
|
|
||||||
>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
|
|
||||||
// mobx store
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "components/ui";
|
import { Loader } from "components/ui";
|
||||||
|
// types
|
||||||
|
import { IIssueLabels } from "types";
|
||||||
|
|
||||||
const LabelIcons = ({ color }: { color: string }) => (
|
const LabelIcons = ({ color }: { color: string }) => (
|
||||||
<span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: color }} />
|
<span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: color }} />
|
||||||
@ -16,24 +15,19 @@ type Props = {
|
|||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
itemsToRender: number;
|
||||||
projectId: string;
|
labels: IIssueLabels[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
viewButtons: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterLabels: React.FC<Props> = observer((props) => {
|
export const FilterLabels: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, projectId, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, itemsToRender, labels, searchQuery, viewButtons } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const store = useMobxStore();
|
|
||||||
const { project: projectStore } = store;
|
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = projectStore.labels?.[projectId?.toString() ?? ""]?.filter((label) =>
|
const filteredOptions = labels?.filter((label) => label.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||||
label.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -72,4 +66,4 @@ export const FilterLabels: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -49,15 +49,10 @@ export const FilterStartDate: React.FC<Props> = observer((props) => {
|
|||||||
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
||||||
onClick={() => handleUpdate(option.value)}
|
onClick={() => handleUpdate(option.value)}
|
||||||
title={option.name}
|
title={option.name}
|
||||||
multiple={false}
|
multiple
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<FilterOption
|
<FilterOption isChecked={false} onClick={() => setIsDateFilterModalOpen(true)} title="Custom" multiple />
|
||||||
isChecked={false}
|
|
||||||
onClick={() => setIsDateFilterModalOpen(true)}
|
|
||||||
title="Custom"
|
|
||||||
multiple={false}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -13,10 +13,11 @@ type Props = {
|
|||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
itemsToRender: number;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
|
viewButtons: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery } = props;
|
const { appliedFilters, handleUpdate, itemsToRender, searchQuery, viewButtons } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
@ -34,9 +35,8 @@ export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
|||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions.length > 0 ? (
|
{filteredOptions.length > 0 ? (
|
||||||
filteredOptions
|
<>
|
||||||
.slice(0, itemsToRender)
|
{filteredOptions.slice(0, itemsToRender).map((stateGroup) => (
|
||||||
.map((stateGroup) => (
|
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={stateGroup.key}
|
key={stateGroup.key}
|
||||||
isChecked={appliedFilters?.includes(stateGroup.key) ? true : false}
|
isChecked={appliedFilters?.includes(stateGroup.key) ? true : false}
|
||||||
@ -44,7 +44,9 @@ export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
|||||||
icon={<StateGroupIcon stateGroup={stateGroup.key} />}
|
icon={<StateGroupIcon stateGroup={stateGroup.key} />}
|
||||||
title={stateGroup.title}
|
title={stateGroup.title}
|
||||||
/>
|
/>
|
||||||
))
|
))}
|
||||||
|
{viewButtons}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
)}
|
)}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
|
|
||||||
// mobx store
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
@ -11,25 +8,24 @@ import { Loader } from "components/ui";
|
|||||||
import { StateGroupIcon } from "components/icons";
|
import { StateGroupIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { getStatesList } from "helpers/state.helper";
|
import { getStatesList } from "helpers/state.helper";
|
||||||
|
// types
|
||||||
|
import { IStateResponse } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
itemsToRender: number;
|
||||||
projectId: string;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
|
states: IStateResponse | undefined;
|
||||||
|
viewButtons: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterState: React.FC<Props> = observer((props) => {
|
export const FilterState: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, projectId, searchQuery } = props;
|
const { appliedFilters, handleUpdate, itemsToRender, searchQuery, states, viewButtons } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const store = useMobxStore();
|
const statesList = getStatesList(states);
|
||||||
const { project: projectStore } = store;
|
|
||||||
|
|
||||||
const statesByGroups = projectStore.states?.[projectId?.toString() ?? ""];
|
|
||||||
const statesList = getStatesList(statesByGroups);
|
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
@ -56,6 +52,7 @@ export const FilterState: React.FC<Props> = observer((props) => {
|
|||||||
title={state.name}
|
title={state.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{viewButtons}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
@ -71,4 +68,4 @@ export const FilterState: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -49,15 +49,10 @@ export const FilterTargetDate: React.FC<Props> = observer((props) => {
|
|||||||
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
||||||
onClick={() => handleUpdate(option.value)}
|
onClick={() => handleUpdate(option.value)}
|
||||||
title={option.name}
|
title={option.name}
|
||||||
multiple={false}
|
multiple
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<FilterOption
|
<FilterOption isChecked={false} onClick={() => setIsDateFilterModalOpen(true)} title="Custom" multiple />
|
||||||
isChecked={false}
|
|
||||||
onClick={() => setIsDateFilterModalOpen(true)}
|
|
||||||
title="Custom"
|
|
||||||
multiple={false}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
Loading…
Reference in New Issue
Block a user