diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 8c93be74d..52567c06a 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -418,109 +418,95 @@ export const IssuesView: React.FC = ({ isOpen={deleteIssueModal} data={issueToDelete} /> -
-
- {Object.keys(filters).map((key) => { - if (filters[key as keyof typeof filters] !== null) - return ( -
- {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); +
+
+
+ {Object.keys(filters).map((key) => { + if (filters[key as keyof typeof filters] !== null) + return ( +
+ {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) - ) - } + return ( +

- - -

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

+ {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) => (

- - {member?.first_name} + {getPriorityIcon(priority)} + {priority} setFilters( { - assignees: filters.assignees?.filter( - (p: any) => p !== memberId + priority: filters.priority?.filter( + (p: any) => p !== priority ), }, !Boolean(viewId) @@ -530,38 +516,150 @@ export const IssuesView: React.FC = ({

- ); - }) - : (filters[key as keyof IIssueFilterOptions] as any)?.join(", ")} -
- ) : ( - {filters[key as keyof typeof filters]} - )} -
- ); - })} -
+ ))} + +
+ ) : 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) + ) + } + > + + +

+ ); + })} + +
+ ) : ( + (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 && ( + { + if (viewId) { + setFilters({}, true); + setToastAlert({ + title: "View updated", + message: "Your view has been updated", + type: "success", + }); + } else + setCreateViewModal({ + query: filters, + }); + }} + className="flex items-center gap-2 text-sm" + > + {!viewId && } + {viewId ? "Update" : "Save"} view + + )} +
{Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && ( - { - if (viewId) { - setFilters({}, true); - setToastAlert({ - title: "View updated", - message: "Your view has been updated", - type: "success", - }); - } else - setCreateViewModal({ - query: filters, - }); - }} - className="flex items-center gap-2 text-sm" + )}
diff --git a/apps/app/components/ui/multi-level-dropdown.tsx b/apps/app/components/ui/multi-level-dropdown.tsx index 1ce14a802..9be634ba6 100644 --- a/apps/app/components/ui/multi-level-dropdown.tsx +++ b/apps/app/components/ui/multi-level-dropdown.tsx @@ -130,7 +130,7 @@ export const MultiLevelDropdown: React.FC = ({ className={({ active }) => `${ active || child.selected ? "bg-gray-100" : "text-gray-900" - } flex w-full items-center break-all rounded px-1 py-1.5 capitalize` + } flex w-full items-center break-all rounded px-1 py-1.5 text-left capitalize` } > {child.label} diff --git a/apps/app/components/views/select-filters.tsx b/apps/app/components/views/select-filters.tsx index 297be737f..456fdfa80 100644 --- a/apps/app/components/views/select-filters.tsx +++ b/apps/app/components/views/select-filters.tsx @@ -119,6 +119,29 @@ export const SelectFilters: React.FC = ({ })) ?? []), ], }, + { + id: "created_by", + label: "Created By", + value: members, + children: [ + ...(members?.map((member) => ({ + id: member.member.id, + label: ( +
+ + {member.member.first_name && member.member.first_name !== "" + ? member.member.first_name + : member.member.email} +
+ ), + value: { + key: "created_by", + value: member.member.id, + }, + selected: filters?.created_by?.includes(member.member.id), + })) ?? []), + ], + }, ]} /> ); diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index 44b2c9e2c..b5e3851c4 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -1,9 +1,10 @@ const paramsToKey = (params: any) => { - const { state, priority, assignees } = params; + const { state, priority, assignees, created_by } = params; let stateKey = state ? state.split(",") : []; let priorityKey = priority ? priority.split(",") : []; let assigneesKey = assignees ? assignees.split(",") : []; + let createdByKey = created_by ? created_by.split(",") : []; const type = params.type ? params.type.toUpperCase() : "NULL"; const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL"; const orderBy = params.order_by ? params.order_by.toUpperCase() : "NULL"; @@ -12,8 +13,9 @@ const paramsToKey = (params: any) => { stateKey = stateKey.sort().join("_"); priorityKey = priorityKey.sort().join("_"); assigneesKey = assigneesKey.sort().join("_"); + createdByKey = createdByKey.sort().join("_"); - return `${stateKey}_${priorityKey}_${assigneesKey}_${type}_${groupBy}_${orderBy}`; + return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}`; }; export const CURRENT_USER = "CURRENT_USER"; diff --git a/apps/app/contexts/issue-view.context.tsx b/apps/app/contexts/issue-view.context.tsx index e081eebaf..1c0ec92c1 100644 --- a/apps/app/contexts/issue-view.context.tsx +++ b/apps/app/contexts/issue-view.context.tsx @@ -69,6 +69,7 @@ export const initialState: StateType = { state: null, issue__assignees__id: null, issue__labels__id: null, + created_by: null, }, }; diff --git a/apps/app/hooks/use-issues-view.tsx b/apps/app/hooks/use-issues-view.tsx index a1a4b3ebe..d6ada03bf 100644 --- a/apps/app/hooks/use-issues-view.tsx +++ b/apps/app/hooks/use-issues-view.tsx @@ -59,6 +59,7 @@ const useIssuesView = () => { issue__labels__id: filters?.issue__labels__id ? filters?.issue__labels__id.join(",") : undefined, + created_by: filters?.created_by ? filters?.created_by.join(",") : undefined, }; const { data: projectIssues } = useSWR( diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index 80bfc055e..d723ab053 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -224,6 +224,7 @@ export interface IIssueFilterOptions { issue__assignees__id: string[] | null; issue__labels__id: string[] | null; priority: string[] | null; + created_by: string[] | null; } export type TIssueGroupByOptions = "state" | "priority" | "labels" | "created_by" | null;