mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: storing my issue view display properties (#1124)
This commit is contained in:
parent
f095594be9
commit
def391cb76
@ -1,26 +1,18 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { useRouter } from "next/router";
|
import useSWR, { mutate } from "swr";
|
||||||
import useSWR from "swr";
|
|
||||||
// services
|
// services
|
||||||
import stateService from "services/state.service";
|
import workspaceService from "services/workspace.service";
|
||||||
import userService from "services/user.service";
|
|
||||||
// hooks
|
// hooks
|
||||||
import useUser from "hooks/use-user";
|
import useUser from "hooks/use-user";
|
||||||
// helpers
|
|
||||||
import { groupBy } from "helpers/array.helper";
|
|
||||||
import { getStatesList } from "helpers/state.helper";
|
|
||||||
// types
|
// types
|
||||||
import { Properties, NestedKeyOf, IIssue } from "types";
|
import { IWorkspaceMember, Properties } from "types";
|
||||||
// fetch-keys
|
import { WORKSPACE_MEMBERS_ME } from "constants/fetch-keys";
|
||||||
import { STATES_LIST } from "constants/fetch-keys";
|
|
||||||
// constants
|
|
||||||
import { PRIORITIES } from "constants/project";
|
|
||||||
|
|
||||||
const initialValues: Properties = {
|
const initialValues: Properties = {
|
||||||
assignee: true,
|
assignee: true,
|
||||||
due_date: false,
|
due_date: false,
|
||||||
key: true,
|
key: true,
|
||||||
labels: true,
|
labels: false,
|
||||||
priority: false,
|
priority: false,
|
||||||
state: true,
|
state: true,
|
||||||
sub_issue_count: false,
|
sub_issue_count: false,
|
||||||
@ -29,99 +21,80 @@ const initialValues: Properties = {
|
|||||||
estimate: false,
|
estimate: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Refactor this logic
|
const useMyIssuesProperties = (workspaceSlug?: string) => {
|
||||||
const useMyIssuesProperties = (issues?: IIssue[]) => {
|
|
||||||
const [properties, setProperties] = useState<Properties>(initialValues);
|
const [properties, setProperties] = useState<Properties>(initialValues);
|
||||||
const [groupByProperty, setGroupByProperty] = useState<NestedKeyOf<IIssue> | null>(null);
|
|
||||||
|
|
||||||
// FIXME: where this hook is used we may not have project id in the url
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, projectId } = router.query;
|
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const { data: stateGroups } = useSWR(
|
const { data: myWorkspace } = useSWR(
|
||||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug as string) : null,
|
||||||
workspaceSlug && projectId
|
workspaceSlug ? () => workspaceService.workspaceMemberMe(workspaceSlug as string) : null,
|
||||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
{
|
||||||
: null
|
shouldRetryOnError: false,
|
||||||
);
|
|
||||||
const states = getStatesList(stateGroups ?? {});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!user) return;
|
|
||||||
setProperties({ ...initialValues, ...user.my_issues_prop?.properties });
|
|
||||||
setGroupByProperty(user.my_issues_prop?.groupBy ?? null);
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
const groupedByIssues: {
|
|
||||||
[key: string]: IIssue[];
|
|
||||||
} = {
|
|
||||||
...(groupByProperty === "state_detail.name"
|
|
||||||
? Object.fromEntries(
|
|
||||||
states
|
|
||||||
?.sort((a, b) => a.sequence - b.sequence)
|
|
||||||
?.map((state) => [
|
|
||||||
state.name,
|
|
||||||
issues?.filter((issue) => issue.state === state.name) ?? [],
|
|
||||||
]) ?? []
|
|
||||||
)
|
|
||||||
: groupByProperty === "priority"
|
|
||||||
? Object.fromEntries(
|
|
||||||
PRIORITIES.map((priority) => [
|
|
||||||
priority,
|
|
||||||
issues?.filter((issue) => issue.priority === priority) ?? [],
|
|
||||||
])
|
|
||||||
)
|
|
||||||
: {}),
|
|
||||||
...groupBy(issues ?? [], groupByProperty ?? ""),
|
|
||||||
};
|
|
||||||
|
|
||||||
const setMyIssueProperty = (key: keyof Properties) => {
|
|
||||||
if (!user) return;
|
|
||||||
userService.updateUser({ my_issues_prop: { properties, groupBy: groupByProperty } });
|
|
||||||
setProperties((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
[key]: !prevData[key],
|
|
||||||
}));
|
|
||||||
localStorage.setItem(
|
|
||||||
"my_issues_prop",
|
|
||||||
JSON.stringify({
|
|
||||||
properties: {
|
|
||||||
...properties,
|
|
||||||
[key]: !properties[key],
|
|
||||||
},
|
|
||||||
groupBy: groupByProperty,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setMyIssueGroupByProperty = (groupByProperty: NestedKeyOf<IIssue> | null) => {
|
|
||||||
if (!user) return;
|
|
||||||
userService.updateUser({ my_issues_prop: { properties, groupBy: groupByProperty } });
|
|
||||||
setGroupByProperty(groupByProperty);
|
|
||||||
localStorage.setItem(
|
|
||||||
"my_issues_prop",
|
|
||||||
JSON.stringify({ properties, groupBy: groupByProperty })
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const viewProps = localStorage.getItem("my_issues_prop");
|
|
||||||
if (viewProps) {
|
|
||||||
const { properties, groupBy } = JSON.parse(viewProps);
|
|
||||||
setProperties(properties);
|
|
||||||
setGroupByProperty(groupBy);
|
|
||||||
}
|
}
|
||||||
}, []);
|
);
|
||||||
|
|
||||||
return {
|
useEffect(() => {
|
||||||
filteredIssues: groupedByIssues,
|
if (!myWorkspace || !workspaceSlug || !user) return;
|
||||||
groupByProperty,
|
|
||||||
properties,
|
setProperties({ ...initialValues, ...myWorkspace.view_props });
|
||||||
setMyIssueProperty,
|
|
||||||
setMyIssueGroupByProperty,
|
if (!myWorkspace.view_props) {
|
||||||
} as const;
|
workspaceService.updateWorkspaceView(workspaceSlug, {
|
||||||
|
view_props: { ...initialValues },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [myWorkspace, workspaceSlug, user]);
|
||||||
|
|
||||||
|
const updateIssueProperties = useCallback(
|
||||||
|
(key: keyof Properties) => {
|
||||||
|
if (!workspaceSlug || !user) return;
|
||||||
|
|
||||||
|
setProperties((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||||
|
|
||||||
|
if (myWorkspace) {
|
||||||
|
mutate<IWorkspaceMember>(
|
||||||
|
WORKSPACE_MEMBERS_ME(workspaceSlug.toString()),
|
||||||
|
(prevData) => {
|
||||||
|
if (!prevData) return;
|
||||||
|
return {
|
||||||
|
...prevData,
|
||||||
|
view_props: { ...prevData?.view_props, [key]: !prevData.view_props?.[key] },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
if (myWorkspace.view_props) {
|
||||||
|
workspaceService.updateWorkspaceView(workspaceSlug, {
|
||||||
|
view_props: {
|
||||||
|
...myWorkspace.view_props,
|
||||||
|
[key]: !myWorkspace.view_props[key],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
workspaceService.updateWorkspaceView(workspaceSlug, {
|
||||||
|
view_props: { ...initialValues },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[workspaceSlug, myWorkspace, user]
|
||||||
|
);
|
||||||
|
|
||||||
|
const newProperties: Properties = {
|
||||||
|
assignee: properties.assignee,
|
||||||
|
due_date: properties.due_date,
|
||||||
|
key: properties.key,
|
||||||
|
labels: properties.labels,
|
||||||
|
priority: properties.priority,
|
||||||
|
state: properties.state,
|
||||||
|
sub_issue_count: properties.sub_issue_count,
|
||||||
|
attachment_count: properties.attachment_count,
|
||||||
|
link: properties.link,
|
||||||
|
estimate: properties.estimate,
|
||||||
|
};
|
||||||
|
|
||||||
|
return [newProperties, updateIssueProperties] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useMyIssuesProperties;
|
export default useMyIssuesProperties;
|
||||||
|
@ -14,7 +14,7 @@ import useIssues from "hooks/use-issues";
|
|||||||
import { Spinner, EmptySpace, EmptySpaceItem, PrimaryButton } from "components/ui";
|
import { Spinner, EmptySpace, EmptySpaceItem, PrimaryButton } from "components/ui";
|
||||||
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
|
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
|
||||||
// hooks
|
// hooks
|
||||||
import useIssuesProperties from "hooks/use-issue-properties";
|
import useMyIssuesProperties from "hooks/use-my-issues-filter";
|
||||||
// types
|
// types
|
||||||
import { IIssue, Properties } from "types";
|
import { IIssue, Properties } from "types";
|
||||||
// components
|
// components
|
||||||
@ -31,10 +31,7 @@ const MyIssuesPage: NextPage = () => {
|
|||||||
// fetching user issues
|
// fetching user issues
|
||||||
const { myIssues } = useIssues(workspaceSlug as string);
|
const { myIssues } = useIssues(workspaceSlug as string);
|
||||||
|
|
||||||
const [properties, setProperties] = useIssuesProperties(
|
const [properties, setProperties] = useMyIssuesProperties(workspaceSlug as string);
|
||||||
workspaceSlug ? (workspaceSlug as string) : undefined,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkspaceAuthorizationLayout
|
<WorkspaceAuthorizationLayout
|
||||||
|
@ -142,6 +142,14 @@ class WorkspaceService extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateWorkspaceView(workspaceSlug: string, data: any): Promise<any> {
|
||||||
|
return this.post(`/api/workspaces/${workspaceSlug}/workspace-views/`, data)
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async updateWorkspaceMember(
|
async updateWorkspaceMember(
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
memberId: string,
|
memberId: string,
|
||||||
|
14
apps/app/types/workspace.d.ts
vendored
14
apps/app/types/workspace.d.ts
vendored
@ -33,6 +33,19 @@ export interface IWorkspaceMemberInvitation {
|
|||||||
workspace: IWorkspace;
|
workspace: IWorkspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Properties = {
|
||||||
|
assignee: boolean;
|
||||||
|
due_date: boolean;
|
||||||
|
labels: boolean;
|
||||||
|
key: boolean;
|
||||||
|
priority: boolean;
|
||||||
|
state: boolean;
|
||||||
|
sub_issue_count: boolean;
|
||||||
|
link: boolean;
|
||||||
|
attachment_count: boolean;
|
||||||
|
estimate: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export interface IWorkspaceMember {
|
export interface IWorkspaceMember {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
user: IUserLite;
|
user: IUserLite;
|
||||||
@ -40,6 +53,7 @@ export interface IWorkspaceMember {
|
|||||||
member: IUserLite;
|
member: IUserLite;
|
||||||
role: 5 | 10 | 15 | 20;
|
role: 5 | 10 | 15 | 20;
|
||||||
company_role: string | null;
|
company_role: string | null;
|
||||||
|
view_props: Properties;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
updated_at: Date;
|
updated_at: Date;
|
||||||
created_by: string;
|
created_by: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user