From be5c4140ff098e5d1f8a897ee1a8b1ff5fdf8752 Mon Sep 17 00:00:00 2001 From: Saheb Giri <47132373+iamsahebgiri@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:59:07 +0530 Subject: [PATCH] refactor: issue label and make design consistent (#610) --- apps/app/components/core/filter-list.tsx | 337 +++++++++++++++++++++++ apps/app/components/core/index.ts | 1 + apps/app/components/core/issues-view.tsx | 284 +------------------ apps/app/components/views/form.tsx | 130 +-------- 4 files changed, 359 insertions(+), 393 deletions(-) create mode 100644 apps/app/components/core/filter-list.tsx diff --git a/apps/app/components/core/filter-list.tsx b/apps/app/components/core/filter-list.tsx new file mode 100644 index 000000000..afaefffdf --- /dev/null +++ b/apps/app/components/core/filter-list.tsx @@ -0,0 +1,337 @@ +import React from "react"; +import { useRouter } from "next/router"; +import useSWR from "swr"; + +// icons +import { XMarkIcon } from "@heroicons/react/24/outline"; +import { getPriorityIcon, getStateGroupIcon } from "components/icons"; +// ui +import { Avatar } from "components/ui"; +// helpers +import { getStatesList } from "helpers/state.helper"; +import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; +// services +import issuesService from "services/issues.service"; +import projectService from "services/project.service"; +import stateService from "services/state.service"; +// types +import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys"; +import { IIssueFilterOptions } from "types"; + +export const FilterList: React.FC = ({ filters, setFilters }) => { + const router = useRouter(); + const { workspaceSlug, projectId, viewId } = router.query; + + 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( + projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, + workspaceSlug && projectId + ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId.toString()) + : null + ); + + const { data: stateGroups } = useSWR( + workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug + ? () => stateService.getStates(workspaceSlug as string, projectId as string) + : null + ); + const states = getStatesList(stateGroups ?? {}); + + if (!filters) return <>; + + const nullFilters = Object.keys(filters).filter( + (key) => filters[key as keyof IIssueFilterOptions] === null + ); + + return ( +
+ {Object.keys(filters).map((key) => { + if (filters[key as keyof typeof filters] !== null) + return ( +
+ + {replaceUnderscoreIfSnakeCase(key)}: + + {filters[key as keyof IIssueFilterOptions] === null || + (filters[key as keyof IIssueFilterOptions]?.length ?? 0) <= 0 ? ( + None + ) : Array.isArray(filters[key as keyof IIssueFilterOptions]) ? ( +
+ {key === "state" ? ( +
+ {filters.state?.map((stateId: any) => { + const state = states?.find((s) => s.id === stateId); + + return ( +

+ + {getStateGroupIcon( + state?.group ?? "backlog", + "12", + "12", + state?.color + )} + + {state?.name ?? ""} + + setFilters( + { + state: filters.state?.filter((s: any) => s !== stateId), + }, + !Boolean(viewId) + ) + } + > + + +

+ ); + })} + +
+ ) : key === "priority" ? ( +
+ {filters.priority?.map((priority: any) => ( +

+ {getPriorityIcon(priority)} + {priority} + + setFilters( + { + priority: filters.priority?.filter((p: any) => p !== priority), + }, + !Boolean(viewId) + ) + } + > + + +

+ ))} + +
+ ) : key === "assignees" ? ( +
+ {filters.assignees?.map((memberId: string) => { + const member = members?.find((m) => m.member.id === memberId)?.member; + + return ( +
+ + {member?.first_name} + + setFilters( + { + assignees: filters.assignees?.filter( + (p: any) => p !== memberId + ), + }, + !Boolean(viewId) + ) + } + > + + +
+ ); + })} + +
+ ) : (key as keyof IIssueFilterOptions) === "created_by" ? ( +
+ {filters.created_by?.map((memberId: string) => { + const member = members?.find((m) => m.member.id === memberId)?.member; + + return ( +
+ + {member?.first_name} + + setFilters( + { + created_by: filters.created_by?.filter( + (p: any) => p !== memberId + ), + }, + !Boolean(viewId) + ) + } + > + + +
+ ); + })} + +
+ ) : key === "labels" ? ( +
+ {filters.labels?.map((labelId: string) => { + const label = issueLabels?.find((l) => l.id === labelId); + + if (!label) return null; + const color = label.color !== "" ? label.color : "#0f172a"; + return ( +
+
+ + {label.name} + + + setFilters( + { + labels: filters.labels?.filter((l: any) => l !== labelId), + }, + !Boolean(viewId) + ) + } + > + + +
+ ); + })} + +
+ ) : ( + (filters[key as keyof IIssueFilterOptions] as any)?.join(", ") + )} +
+ ) : ( + {filters[key as keyof typeof filters]} + )} +
+ ); + })} + {Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && ( + + )} +
+ ); +}; diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index d25df79d2..6ce5074f5 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -10,3 +10,4 @@ export * from "./issues-view"; export * from "./link-modal"; export * from "./not-authorized-view"; export * from "./image-picker-popover"; +export * from "./filter-list"; diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 28378ddbc..6eb76440e 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -9,18 +9,17 @@ import { DragDropContext, DropResult } from "react-beautiful-dnd"; // services import issuesService from "services/issues.service"; import stateService from "services/state.service"; -import projectService from "services/project.service"; import modulesService from "services/modules.service"; // hooks import useToast from "hooks/use-toast"; import useIssuesView from "hooks/use-issues-view"; // components -import { AllLists, AllBoards } from "components/core"; +import { AllLists, AllBoards, FilterList } from "components/core"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { CreateUpdateViewModal } from "components/views"; // ui -import { Avatar, EmptySpace, EmptySpaceItem, PrimaryButton, Spinner } from "components/ui"; +import { EmptySpace, EmptySpaceItem, PrimaryButton, Spinner } from "components/ui"; import { CalendarView } from "./calendar-view"; // icons import { @@ -28,12 +27,10 @@ import { PlusIcon, RectangleStackIcon, TrashIcon, - XMarkIcon, } from "@heroicons/react/24/outline"; -import { ExclamationIcon, getStateGroupIcon } from "components/icons"; +import { ExclamationIcon } from "components/icons"; // helpers import { getStatesList } from "helpers/state.helper"; -import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; // types import { CycleIssueResponse, @@ -49,11 +46,8 @@ import { MODULE_ISSUES, MODULE_ISSUES_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS, - PROJECT_ISSUE_LABELS, - PROJECT_MEMBERS, STATE_LIST, } from "constants/fetch-keys"; -import { getPriorityIcon } from "components/icons/priority-icon"; type Props = { type?: "issue" | "cycle" | "module"; @@ -112,20 +106,6 @@ export const IssuesView: React.FC = ({ ); const states = getStatesList(stateGroups ?? {}); - 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( - projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, - workspaceSlug && projectId - ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId.toString()) - : null - ); - const handleDeleteIssue = useCallback( (issue: IIssue) => { setDeleteIssueModal(true); @@ -428,243 +408,7 @@ export const IssuesView: React.FC = ({ />
-
- {Object.keys(filters).map((key) => { - if (filters[key as keyof typeof filters] !== null) - return ( -
- - {replaceUnderscoreIfSnakeCase(key)}: - - {filters[key as keyof IIssueFilterOptions] === null || - (filters[key as keyof IIssueFilterOptions]?.length ?? 0) <= 0 ? ( - None - ) : Array.isArray(filters[key as keyof IIssueFilterOptions]) ? ( -
- {key === "state" ? ( -
- {filters.state?.map((stateId: any) => { - const state = states?.find((s) => s.id === stateId); - - return ( -

- - {getStateGroupIcon( - state?.group ?? "backlog", - "12", - "12", - state?.color - )} - - {state?.name ?? ""} - - setFilters( - { - state: filters.state?.filter((s: any) => s !== stateId), - }, - !Boolean(viewId) - ) - } - > - - -

- ); - })} - -
- ) : key === "priority" ? ( -
- {filters.priority?.map((priority: any) => ( -

- {getPriorityIcon(priority)} - {priority} - - setFilters( - { - priority: filters.priority?.filter( - (p: any) => p !== priority - ), - }, - !Boolean(viewId) - ) - } - > - - -

- ))} - -
- ) : key === "assignees" ? ( -
- {filters.assignees?.map((memberId: string) => { - const member = members?.find((m) => m.member.id === memberId)?.member; - - return ( -

- - {member?.first_name} - - setFilters( - { - assignees: filters.assignees?.filter( - (p: any) => p !== memberId - ), - }, - !Boolean(viewId) - ) - } - > - - -

- ); - })} - -
- ) : (key as keyof IIssueFilterOptions) === "created_by" ? ( -
- {filters.created_by?.map((memberId: string) => { - const member = members?.find((m) => m.member.id === memberId)?.member; - - return ( -

- - {member?.first_name} - - setFilters( - { - assignees: filters.created_by?.filter( - (p: any) => p !== memberId - ), - }, - !Boolean(viewId) - ) - } - > - - -

- ); - })} - -
- ) : key === "labels" ? ( -
- {filters.labels?.map((labelId: string) => { - const label = issueLabels?.find((l) => l.id === labelId); - - if (!label) return null; - - return ( -
-
- {label.name} -
- ); - })} - -
- ) : ( - (filters[key as keyof IIssueFilterOptions] as any)?.join(", ") - )} -
- ) : ( - {filters[key as keyof typeof filters]} - )} -
- ); - })} -
+ {Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && ( = ({ )}
- {Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && ( - - )}
+ +
+ {(provided, snapshot) => ( diff --git a/apps/app/components/views/form.tsx b/apps/app/components/views/form.tsx index e30e34145..fffa7bfcc 100644 --- a/apps/app/components/views/form.tsx +++ b/apps/app/components/views/form.tsx @@ -6,23 +6,18 @@ import useSWR from "swr"; import { useForm } from "react-hook-form"; // ui -import { Avatar, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; +import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; +// components +import { FilterList } from "components/core"; // types import { IView } from "types"; // constant -import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys"; -// helpers -import { getStatesList } from "helpers/state.helper"; +import { STATE_LIST } from "constants/fetch-keys"; + // services import stateService from "services/state.service"; -import projectService from "services/project.service"; -import issuesService from "services/issues.service"; // components import { SelectFilters } from "components/views"; -// icons -import { getStateGroupIcon } from "components/icons"; -import { getPriorityIcon } from "components/icons/priority-icon"; -// components type Props = { handleFormSubmit: (values: IView) => Promise; @@ -44,31 +39,6 @@ export const ViewForm: React.FC = ({ data, preLoadedData, }) => { - 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 - ); - - const { data: issueLabels } = useSWR( - projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, - workspaceSlug && projectId - ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId.toString()) - : null - ); - const { register, formState: { errors, isSubmitting }, @@ -161,87 +131,15 @@ export const ViewForm: React.FC = ({ />
-
- {Object.keys(filters ?? {}).map((key) => { - const queryKey = key as keyof typeof filters; - if (queryKey === "state") - return ( -
- {filters.state?.map((stateID) => { - const state = statesList.find((state) => state.id === stateID); - if (!state) return null; - return ( -
- {getStateGroupIcon(state?.group, "16", "16", state?.color)} - {state?.name} -
- ); - })} -
- ); - else if (queryKey === "priority") - return ( -
- {filters.priority?.map((priority) => ( -
- {getPriorityIcon(priority)} - {priority} -
- ))} -
- ); - else if (queryKey === "assignees") - return ( -
- {filters.assignees?.map((assigneeId) => { - const member = members?.find((member) => member.member.id === assigneeId); - - if (!member) return null; - return ( -
- - {member.member.first_name && member.member.first_name !== "" - ? member.member.first_name - : member.member.email} -
- ); - })} -
- ); - else if (queryKey === "labels") - return ( -
-
- {filters.labels?.map((labelId: string) => { - const label = issueLabels?.find((l) => l.id === labelId); - if (!label) return null; - return ( -
-
- {label.name} -
- ); - })} -
-
- ); - })} -
+ { + setValue("query", { + ...filters, + ...query, + }); + }} + />