mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: issue filters dropdown created (#441)
This commit is contained in:
parent
0117ccfca2
commit
bcd2ac1317
@ -8,5 +8,4 @@ export * from "./issues-view-filter";
|
|||||||
export * from "./issues-view";
|
export * from "./issues-view";
|
||||||
export * from "./link-modal";
|
export * from "./link-modal";
|
||||||
export * from "./not-authorized-view";
|
export * from "./not-authorized-view";
|
||||||
export * from "./multi-level-select";
|
|
||||||
export * from "./image-picker-popover";
|
export * from "./image-picker-popover";
|
||||||
|
@ -2,6 +2,12 @@ import React from "react";
|
|||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
// services
|
||||||
|
import projectService from "services/project.service";
|
||||||
|
import issuesService from "services/issues.service";
|
||||||
|
import stateService from "services/state.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useIssuesProperties from "hooks/use-issue-properties";
|
import useIssuesProperties from "hooks/use-issue-properties";
|
||||||
import useIssueView from "hooks/use-issue-view";
|
import useIssueView from "hooks/use-issue-view";
|
||||||
@ -14,10 +20,14 @@ import { ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline";
|
|||||||
import { Squares2X2Icon } from "@heroicons/react/20/solid";
|
import { Squares2X2Icon } from "@heroicons/react/20/solid";
|
||||||
// helpers
|
// helpers
|
||||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||||
|
import { getStatesList } from "helpers/state.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, Properties } from "types";
|
import { IIssue, IIssueLabels, Properties } from "types";
|
||||||
// common
|
// fetch-keys
|
||||||
|
import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys";
|
||||||
|
// constants
|
||||||
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
|
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
|
||||||
|
import { PRIORITIES } from "constants/project";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issues?: IIssue[];
|
issues?: IIssue[];
|
||||||
@ -46,6 +56,28 @@ export const IssuesFilterView: React.FC<Props> = ({ issues }) => {
|
|||||||
projectId as string
|
projectId as string
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: states } = useSWR(
|
||||||
|
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
const statesList = getStatesList(states ?? {});
|
||||||
|
|
||||||
|
const { data: members } = useSWR(
|
||||||
|
projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
||||||
|
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{issues && issues.length > 0 && (
|
{issues && issues.length > 0 && (
|
||||||
@ -70,6 +102,42 @@ export const IssuesFilterView: React.FC<Props> = ({ issues }) => {
|
|||||||
<Squares2X2Icon className="h-4 w-4" />
|
<Squares2X2Icon className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<CustomMenu
|
||||||
|
label={
|
||||||
|
<span className="flex items-center gap-2 rounded-md py-1 text-xs font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900 focus:outline-none">
|
||||||
|
Filters
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<h4 className="px-1 py-2 font-medium">Status</h4>
|
||||||
|
{statesList?.map((state) => (
|
||||||
|
<CustomMenu.MenuItem onClick={() => {}}>
|
||||||
|
<>{state.name}</>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
))}
|
||||||
|
<h4 className="px-1 py-2 font-medium">Members</h4>
|
||||||
|
{members?.map((member) => (
|
||||||
|
<CustomMenu.MenuItem onClick={() => {}}>
|
||||||
|
<>
|
||||||
|
{member.member.first_name && member.member.first_name !== ""
|
||||||
|
? member.member.first_name + " " + member.member.last_name
|
||||||
|
: member.member.email}
|
||||||
|
</>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
))}
|
||||||
|
<h4 className="px-1 py-2 font-medium">Labels</h4>
|
||||||
|
{issueLabels?.map((label) => (
|
||||||
|
<CustomMenu.MenuItem onClick={() => {}}>
|
||||||
|
<>{label.name}</>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
))}
|
||||||
|
<h4 className="px-1 py-2 font-medium">Priority</h4>
|
||||||
|
{PRIORITIES?.map((priority) => (
|
||||||
|
<CustomMenu.MenuItem onClick={() => {}}>
|
||||||
|
<span className="capitalize">{priority ?? "None"}</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
))}
|
||||||
|
</CustomMenu>
|
||||||
<Popover className="relative">
|
<Popover className="relative">
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
|
@ -11,6 +11,7 @@ export * from "./empty-space";
|
|||||||
export * from "./header-button";
|
export * from "./header-button";
|
||||||
export * from "./loader";
|
export * from "./loader";
|
||||||
export * from "./multi-input";
|
export * from "./multi-input";
|
||||||
|
export * from "./multi-level-select";
|
||||||
export * from "./outline-button";
|
export * from "./outline-button";
|
||||||
export * from "./progress-bar";
|
export * from "./progress-bar";
|
||||||
export * from "./spinner";
|
export * from "./spinner";
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
// headless ui
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
|
// icons
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
|
||||||
|
|
||||||
type TSelectOption = {
|
type TSelectOption = {
|
||||||
@ -23,9 +24,13 @@ type TMultipleSelectProps = {
|
|||||||
direction?: "left" | "right";
|
direction?: "left" | "right";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MultiLevelSelect: React.FC<TMultipleSelectProps> = (props) => {
|
export const MultiLevelSelect: React.FC<TMultipleSelectProps> = ({
|
||||||
const { options, selected, setSelected, label, direction = "right" } = props;
|
options,
|
||||||
|
selected,
|
||||||
|
setSelected,
|
||||||
|
label,
|
||||||
|
direction = "right",
|
||||||
|
}) => {
|
||||||
const [openChildFor, setOpenChildFor] = useState<TSelectOption | null>(null);
|
const [openChildFor, setOpenChildFor] = useState<TSelectOption | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
Loading…
Reference in New Issue
Block a user