chore: new global select filters component (#501)

This commit is contained in:
Aaryan Khandelwal 2023-03-23 12:01:50 +05:30 committed by GitHub
parent 10e5ba7b3e
commit ad2fa91a2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 183 deletions

View File

@ -2,33 +2,22 @@ import React from "react";
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
import useIssuesProperties from "hooks/use-issue-properties";
import useIssuesView from "hooks/use-issues-view";
// headless ui
import { Popover, Transition } from "@headlessui/react";
// components
import { PRIORITIES } from "constants/project";
import { SelectFilters } from "components/views";
// ui
import { Avatar, CustomMenu, MultiLevelDropdown } from "components/ui";
import { CustomMenu } from "components/ui";
// icons
import { ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline";
import { Squares2X2Icon } from "@heroicons/react/20/solid";
import { getStateGroupIcon } from "components/icons";
import { getPriorityIcon } from "components/icons/priority-icon";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
import { getStatesList } from "helpers/state.helper";
// types
import { IIssueLabels, Properties } from "types";
// fetch-keys
import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys";
import { Properties } from "types";
// constants
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
@ -57,28 +46,6 @@ export const IssuesFilterView: React.FC = () => {
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 (
<div className="flex items-center gap-2">
<div className="flex items-center gap-x-1">
@ -101,8 +68,8 @@ export const IssuesFilterView: React.FC = () => {
<Squares2X2Icon className="h-4 w-4" />
</button>
</div>
<MultiLevelDropdown
label="Filters"
<SelectFilters
filters={filters}
onSelect={(option) => {
setFilters(
{
@ -116,71 +83,6 @@ export const IssuesFilterView: React.FC = () => {
);
}}
direction="left"
options={[
{
id: "priority",
label: "Priority",
value: PRIORITIES,
children: [
...PRIORITIES.map((priority) => ({
id: priority ?? "none",
label: (
<div className="flex items-center gap-2">
{getPriorityIcon(priority)} {priority ?? "None"}
</div>
),
value: {
key: "priority",
value: priority,
},
selected: filters?.priority?.includes(priority ?? "none"),
})),
],
},
{
id: "state",
label: "State",
value: statesList,
children: [
...statesList.map((state) => ({
id: state.id,
label: (
<div className="flex items-center gap-2">
{getStateGroupIcon(state.group, "16", "16", state.color)} {state.name}
</div>
),
value: {
key: "state",
value: state.id,
},
selected: filters?.state?.includes(state.id),
})),
],
},
{
id: "assignees",
label: "Assignees",
value: members,
children: [
...(members?.map((member) => ({
id: member.member.id,
label: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
</div>
),
value: {
key: "assignees",
value: member.member.id,
},
selected: filters?.assignees?.includes(member.member.id),
})) ?? []),
],
},
]}
/>
<Popover className="relative">
{({ open }) => (

View File

@ -24,6 +24,7 @@ export * from "./layer-diagonal-icon";
export * from "./lock-icon";
export * from "./menu-icon";
export * from "./plus-icon";
export * from "./priority-icon";
export * from "./question-mark-circle-icon";
export * from "./setting-icon";
export * from "./signal-cellular-icon";

View File

@ -6,14 +6,7 @@ import useSWR from "swr";
import { useForm } from "react-hook-form";
// ui
import {
Avatar,
Input,
MultiLevelDropdown,
PrimaryButton,
SecondaryButton,
TextArea,
} from "components/ui";
import { Avatar, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
// types
import { IView } from "types";
// constant
@ -23,11 +16,12 @@ import { getStatesList } from "helpers/state.helper";
// services
import stateService from "services/state.service";
import projectService from "services/project.service";
// components
import { SelectFilters } from "components/views";
// icons
import { getStateGroupIcon } from "components/icons";
import { getPriorityIcon } from "components/icons/priority-icon";
// components
import { PRIORITIES } from "constants/project";
type Props = {
handleFormSubmit: (values: IView) => Promise<void>;
@ -139,8 +133,8 @@ export const ViewForm: React.FC<Props> = ({
/>
</div>
<div>
<MultiLevelDropdown
label="Filters"
<SelectFilters
filters={filters}
onSelect={(option) => {
const key = option.key as keyof typeof filters;
@ -156,72 +150,6 @@ export const ViewForm: React.FC<Props> = ({
});
}
}}
direction="right"
options={[
{
id: "priority",
label: "Priority",
value: PRIORITIES,
children: [
...PRIORITIES.map((priority) => ({
id: priority ?? "none",
label: (
<div className="flex items-center gap-2">
{getPriorityIcon(priority)} {priority ?? "None"}
</div>
),
value: {
key: "priority",
value: priority,
},
selected: filters?.priority?.includes(priority ?? "none"),
})),
],
},
{
id: "state",
label: "State",
value: statesList,
children: [
...statesList.map((state) => ({
id: state.id,
label: (
<div className="flex items-center gap-2">
{getStateGroupIcon(state.group, "16", "16", state.color)} {state.name}
</div>
),
value: {
key: "state",
value: state.id,
},
selected: filters?.state?.includes(state.id),
})),
],
},
{
id: "assignee",
label: "Assignee",
value: members,
children: [
...(members?.map((member) => ({
id: member.member.id,
label: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
</div>
),
value: {
key: "assignees",
value: member.member.id,
},
selected: filters?.assignees?.includes(member.member.id),
})) ?? []),
],
},
]}
/>
</div>
<div>
@ -257,8 +185,9 @@ export const ViewForm: React.FC<Props> = ({
else if (queryKey === "assignees")
return (
<div className="flex gap-3" key={key}>
{filters.assignees?.map((assigneeID) => {
const member = members?.find((member) => member.member.id === assigneeID);
{filters.assignees?.map((assigneeId) => {
const member = members?.find((member) => member.member.id === assigneeId);
if (!member) return null;
return (
<div className="flex items-center gap-2 text-xs" key={member.member.id}>

View File

@ -1,3 +1,4 @@
export * from "./delete-view-modal";
export * from "./form";
export * from "./modal";
export * from "./select-filters";

View File

@ -0,0 +1,118 @@
import { useRouter } from "next/router";
import useSWR from "swr";
// services
import stateService from "services/state.service";
import projectService from "services/project.service";
// ui
import { Avatar, MultiLevelDropdown } from "components/ui";
// icons
import { getPriorityIcon, getStateGroupIcon } from "components/icons";
// helpers
import { getStatesList } from "helpers/state.helper";
// types
import { IIssueFilterOptions, IQuery } from "types";
// fetch-keys
import { PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys";
// constants
import { PRIORITIES } from "constants/project";
type Props = {
filters: IIssueFilterOptions | IQuery;
onSelect: (option: any) => void;
direction?: "left" | "right";
};
export const SelectFilters: React.FC<Props> = ({ filters, onSelect, direction = "right" }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
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
);
return (
<MultiLevelDropdown
label="Filters"
onSelect={onSelect}
direction={direction}
options={[
{
id: "priority",
label: "Priority",
value: PRIORITIES,
children: [
...PRIORITIES.map((priority) => ({
id: priority ?? "none",
label: (
<div className="flex items-center gap-2">
{getPriorityIcon(priority)} {priority ?? "None"}
</div>
),
value: {
key: "priority",
value: priority,
},
selected: filters?.priority?.includes(priority ?? "none"),
})),
],
},
{
id: "state",
label: "State",
value: statesList,
children: [
...statesList.map((state) => ({
id: state.id,
label: (
<div className="flex items-center gap-2">
{getStateGroupIcon(state.group, "16", "16", state.color)} {state.name}
</div>
),
value: {
key: "state",
value: state.id,
},
selected: filters?.state?.includes(state.id),
})),
],
},
{
id: "assignees",
label: "Assignees",
value: members,
children: [
...(members?.map((member) => ({
id: member.member.id,
label: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
</div>
),
value: {
key: "assignees",
value: member.member.id,
},
selected: filters?.assignees?.includes(member.member.id),
})) ?? []),
],
},
]}
/>
);
};

View File

@ -116,7 +116,7 @@ const ProjectViews: NextPage = () => {
/>
)
) : (
<Loader>
<Loader className="space-y-3">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />