Merge branch 'stage-release' of https://github.com/makeplane/plane

This commit is contained in:
Aaryan Khandelwal 2022-12-20 20:19:02 +05:30
commit afcf1083ff
7 changed files with 139 additions and 50 deletions

View File

@ -4,3 +4,4 @@ export const SET_ISSUE_VIEW = "SET_ISSUE_VIEW";
export const SET_GROUP_BY_PROPERTY = "SET_GROUP_BY_PROPERTY"; export const SET_GROUP_BY_PROPERTY = "SET_GROUP_BY_PROPERTY";
export const SET_ORDER_BY_PROPERTY = "SET_ORDER_BY_PROPERTY"; export const SET_ORDER_BY_PROPERTY = "SET_ORDER_BY_PROPERTY";
export const SET_FILTER_ISSUES = "SET_FILTER_ISSUES"; export const SET_FILTER_ISSUES = "SET_FILTER_ISSUES";
export const RESET_TO_DEFAULT = "RESET_TO_DEFAULT";

View File

@ -9,13 +9,14 @@ import {
SET_GROUP_BY_PROPERTY, SET_GROUP_BY_PROPERTY,
SET_ORDER_BY_PROPERTY, SET_ORDER_BY_PROPERTY,
SET_FILTER_ISSUES, SET_FILTER_ISSUES,
RESET_TO_DEFAULT,
} from "constants/theme.context.constants"; } from "constants/theme.context.constants";
// components // components
import ToastAlert from "components/toast-alert"; import ToastAlert from "components/toast-alert";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// constants // constants
import { PROJECT_MEMBERS, USER_PROJECT_VIEW } from "constants/fetch-keys"; import { USER_PROJECT_VIEW } from "constants/fetch-keys";
// services // services
import projectService from "lib/services/project.service"; import projectService from "lib/services/project.service";
@ -31,7 +32,8 @@ type ReducerActionType = {
| typeof SET_ISSUE_VIEW | typeof SET_ISSUE_VIEW
| typeof SET_ORDER_BY_PROPERTY | typeof SET_ORDER_BY_PROPERTY
| typeof SET_FILTER_ISSUES | typeof SET_FILTER_ISSUES
| typeof SET_GROUP_BY_PROPERTY; | typeof SET_GROUP_BY_PROPERTY
| typeof RESET_TO_DEFAULT;
payload?: Partial<Theme>; payload?: Partial<Theme>;
}; };
@ -42,10 +44,13 @@ type ContextType = {
groupByProperty: NestedKeyOf<IIssue> | null; groupByProperty: NestedKeyOf<IIssue> | null;
filterIssue: "activeIssue" | "backlogIssue" | null; filterIssue: "activeIssue" | "backlogIssue" | null;
toggleCollapsed: () => void; toggleCollapsed: () => void;
setIssueView: (display: "list" | "kanban") => void;
setGroupByProperty: (property: NestedKeyOf<IIssue> | null) => void; setGroupByProperty: (property: NestedKeyOf<IIssue> | null) => void;
setOrderBy: (property: NestedKeyOf<IIssue> | null) => void; setOrderBy: (property: NestedKeyOf<IIssue> | null) => void;
setFilterIssue: (property: "activeIssue" | "backlogIssue" | null) => void; setFilterIssue: (property: "activeIssue" | "backlogIssue" | null) => void;
resetFilterToDefault: () => void;
setNewFilterDefaultView: () => void;
setIssueViewToKanban: () => void;
setIssueViewToList: () => void;
}; };
type StateType = Theme; type StateType = Theme;
@ -68,10 +73,12 @@ export const reducer: ReducerFunctionType = (state, action) => {
...state, ...state,
collapsed: !state.collapsed, collapsed: !state.collapsed,
}; };
localStorage.setItem("collapsed", JSON.stringify(newState.collapsed));
return newState; return newState;
case REHYDRATE_THEME: { case REHYDRATE_THEME: {
const newState = payload; let collapsed: any = localStorage.getItem("collapsed");
return { ...initialState, ...newState }; collapsed = collapsed ? JSON.parse(collapsed) : false;
return { ...initialState, ...payload, collapsed };
} }
case SET_ISSUE_VIEW: { case SET_ISSUE_VIEW: {
const newState = { const newState = {
@ -113,6 +120,12 @@ export const reducer: ReducerFunctionType = (state, action) => {
...newState, ...newState,
}; };
} }
case RESET_TO_DEFAULT: {
return {
...initialState,
...payload,
};
}
default: { default: {
return state; return state;
} }
@ -120,18 +133,27 @@ export const reducer: ReducerFunctionType = (state, action) => {
}; };
const saveDataToServer = async (workspaceSlug: string, projectID: string, state: any) => { const saveDataToServer = async (workspaceSlug: string, projectID: string, state: any) => {
await projectService.setProjectView(workspaceSlug, projectID, state); await projectService.setProjectView(workspaceSlug, projectID, {
view_props: state,
});
};
const setNewDefault = async (workspaceSlug: string, projectID: string, state: any) => {
await projectService.setProjectView(workspaceSlug, projectID, {
view_props: state,
default_props: state,
});
}; };
export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState); const [state, dispatch] = useReducer(reducer, initialState);
const { activeProject, activeWorkspace, user } = useUser(); const { activeProject, activeWorkspace } = useUser();
const { data: projectMember } = useSWR( const { data: myViewProps, mutate: mutateMyViewProps } = useSWR(
activeWorkspace && activeProject ? PROJECT_MEMBERS(activeProject.id) : null, activeWorkspace && activeProject ? USER_PROJECT_VIEW(activeProject.id) : null,
activeWorkspace && activeProject activeWorkspace && activeProject
? () => projectService.projectMembers(activeWorkspace.slug, activeProject.id) ? () => projectService.projectMemberMe(activeWorkspace.slug, activeProject.id)
: null : null
); );
@ -141,23 +163,47 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
}); });
}, []); }, []);
const setIssueView = useCallback( const setIssueViewToKanban = useCallback(() => {
(display: "list" | "kanban") => { dispatch({
dispatch({ type: SET_ISSUE_VIEW,
type: SET_ISSUE_VIEW, payload: {
payload: { issueView: "kanban",
issueView: display, },
}, });
}); dispatch({
type: SET_GROUP_BY_PROPERTY,
payload: {
groupByProperty: "state_detail.name",
},
});
if (!activeWorkspace || !activeProject) return;
saveDataToServer(activeWorkspace.slug, activeProject.id, {
...state,
issueView: "kanban",
groupByProperty: "state_detail.name",
});
}, [activeWorkspace, activeProject, state]);
if (!activeWorkspace || !activeProject) return; const setIssueViewToList = useCallback(() => {
saveDataToServer(activeWorkspace.slug, activeProject.id, { dispatch({
...state, type: SET_ISSUE_VIEW,
issueView: display, payload: {
}); issueView: "list",
}, },
[activeProject, activeWorkspace, state] });
); dispatch({
type: SET_GROUP_BY_PROPERTY,
payload: {
groupByProperty: null,
},
});
if (!activeWorkspace || !activeProject) return;
saveDataToServer(activeWorkspace.slug, activeProject.id, {
...state,
issueView: "list",
groupByProperty: null,
});
}, [activeWorkspace, activeProject, state]);
const setGroupByProperty = useCallback( const setGroupByProperty = useCallback(
(property: NestedKeyOf<IIssue> | null) => { (property: NestedKeyOf<IIssue> | null) => {
@ -191,6 +237,7 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
}, },
[activeProject, activeWorkspace, state] [activeProject, activeWorkspace, state]
); );
const setFilterIssue = useCallback( const setFilterIssue = useCallback(
(property: "activeIssue" | "backlogIssue" | null) => { (property: "activeIssue" | "backlogIssue" | null) => {
dispatch({ dispatch({
@ -209,12 +256,30 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
[activeProject, activeWorkspace, state] [activeProject, activeWorkspace, state]
); );
const setNewDefaultView = useCallback(() => {
if (!activeWorkspace || !activeProject) return;
setNewDefault(activeWorkspace.slug, activeProject.id, state);
}, [activeProject, activeWorkspace, state]);
const resetToDefault = useCallback(() => {
dispatch({
type: RESET_TO_DEFAULT,
payload: myViewProps?.default_props,
});
if (!activeWorkspace || !activeProject) return;
saveDataToServer(activeWorkspace.slug, activeProject.id, myViewProps?.default_props).then(
() => {
mutateMyViewProps();
}
);
}, [activeProject, activeWorkspace, myViewProps, mutateMyViewProps]);
useEffect(() => { useEffect(() => {
dispatch({ dispatch({
type: REHYDRATE_THEME, type: REHYDRATE_THEME,
payload: projectMember?.find((member) => member.member.id === user?.id)?.view_props, payload: myViewProps?.view_props,
}); });
}, [projectMember, user]); }, [myViewProps]);
return ( return (
<themeContext.Provider <themeContext.Provider
@ -222,13 +287,16 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
collapsed: state.collapsed, collapsed: state.collapsed,
toggleCollapsed, toggleCollapsed,
issueView: state.issueView, issueView: state.issueView,
setIssueView,
groupByProperty: state.groupByProperty, groupByProperty: state.groupByProperty,
setGroupByProperty, setGroupByProperty,
orderBy: state.orderBy, orderBy: state.orderBy,
setOrderBy, setOrderBy,
filterIssue: state.filterIssue, filterIssue: state.filterIssue,
setFilterIssue, setFilterIssue,
resetFilterToDefault: resetToDefault,
setNewFilterDefaultView: setNewDefaultView,
setIssueViewToKanban,
setIssueViewToList,
}} }}
> >
<ToastAlert /> <ToastAlert />

View File

@ -10,13 +10,16 @@ import type { IIssue } from "types";
const useIssuesFilter = (projectIssues: IIssue[]) => { const useIssuesFilter = (projectIssues: IIssue[]) => {
const { const {
issueView, issueView,
setIssueView,
groupByProperty, groupByProperty,
setGroupByProperty, setGroupByProperty,
orderBy, orderBy,
setOrderBy, setOrderBy,
filterIssue, filterIssue,
setFilterIssue, setFilterIssue,
resetFilterToDefault,
setNewFilterDefaultView,
setIssueViewToKanban,
setIssueViewToList,
} = useTheme(); } = useTheme();
const { states } = useUser(); const { states } = useUser();
@ -88,13 +91,16 @@ const useIssuesFilter = (projectIssues: IIssue[]) => {
return { return {
groupedByIssues, groupedByIssues,
issueView, issueView,
setIssueView,
groupByProperty, groupByProperty,
setGroupByProperty, setGroupByProperty,
orderBy, orderBy,
setOrderBy, setOrderBy,
filterIssue, filterIssue,
setFilterIssue, setFilterIssue,
resetFilterToDefault,
setNewFilterDefaultView,
setIssueViewToKanban,
setIssueViewToList,
} as const; } as const;
}; };

View File

@ -230,7 +230,10 @@ class ProjectServices extends APIService {
async setProjectView( async setProjectView(
workspacSlug: string, workspacSlug: string,
projectId: string, projectId: string,
data: ProjectViewTheme data: {
view_props?: ProjectViewTheme;
default_props?: ProjectViewTheme;
}
): Promise<any> { ): Promise<any> {
await this.post(PROJECT_VIEW_ENDPOINT(workspacSlug, projectId), data) await this.post(PROJECT_VIEW_ENDPOINT(workspacSlug, projectId), data)
.then((response) => { .then((response) => {

View File

@ -126,7 +126,6 @@ const SingleCycle: React.FC = () => {
const { const {
issueView, issueView,
setIssueView,
groupByProperty, groupByProperty,
setGroupByProperty, setGroupByProperty,
groupedByIssues, groupedByIssues,
@ -134,6 +133,8 @@ const SingleCycle: React.FC = () => {
setFilterIssue, setFilterIssue,
orderBy, orderBy,
filterIssue, filterIssue,
setIssueViewToKanban,
setIssueViewToList,
} = useIssuesFilter(cycleIssuesArray ?? []); } = useIssuesFilter(cycleIssuesArray ?? []);
const openCreateIssueModal = ( const openCreateIssueModal = (
@ -269,10 +270,7 @@ const SingleCycle: React.FC = () => {
className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${ className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${
issueView === "list" ? "bg-gray-200" : "" issueView === "list" ? "bg-gray-200" : ""
}`} }`}
onClick={() => { onClick={() => setIssueViewToList()}
setIssueView("list");
setGroupByProperty(null);
}}
> >
<ListBulletIcon className="h-4 w-4" /> <ListBulletIcon className="h-4 w-4" />
</button> </button>
@ -281,10 +279,7 @@ const SingleCycle: React.FC = () => {
className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${ className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${
issueView === "kanban" ? "bg-gray-200" : "" issueView === "kanban" ? "bg-gray-200" : ""
}`} }`}
onClick={() => { onClick={() => setIssueViewToKanban()}
setIssueView("kanban");
setGroupByProperty("state_detail.name");
}}
> >
<Squares2X2Icon className="h-4 w-4" /> <Squares2X2Icon className="h-4 w-4" />
</button> </button>

View File

@ -134,7 +134,6 @@ const ProjectIssues: NextPage = () => {
const { const {
issueView, issueView,
setIssueView,
groupByProperty, groupByProperty,
setGroupByProperty, setGroupByProperty,
groupedByIssues, groupedByIssues,
@ -142,6 +141,10 @@ const ProjectIssues: NextPage = () => {
setFilterIssue, setFilterIssue,
orderBy, orderBy,
filterIssue, filterIssue,
resetFilterToDefault,
setNewFilterDefaultView,
setIssueViewToKanban,
setIssueViewToList,
} = useIssuesFilter(projectIssues?.results.filter((p) => p.parent === null) ?? []); } = useIssuesFilter(projectIssues?.results.filter((p) => p.parent === null) ?? []);
useEffect(() => { useEffect(() => {
@ -169,10 +172,7 @@ const ProjectIssues: NextPage = () => {
className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${ className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${
issueView === "list" ? "bg-gray-200" : "" issueView === "list" ? "bg-gray-200" : ""
}`} }`}
onClick={() => { onClick={() => setIssueViewToList()}
setIssueView("list");
setGroupByProperty(null);
}}
> >
<ListBulletIcon className="h-4 w-4" /> <ListBulletIcon className="h-4 w-4" />
</button> </button>
@ -181,10 +181,7 @@ const ProjectIssues: NextPage = () => {
className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${ className={`h-7 w-7 p-1 grid place-items-center rounded hover:bg-gray-200 duration-300 outline-none ${
issueView === "kanban" ? "bg-gray-200" : "" issueView === "kanban" ? "bg-gray-200" : ""
}`} }`}
onClick={() => { onClick={() => setIssueViewToKanban()}
setIssueView("kanban");
setGroupByProperty("state_detail.name");
}}
> >
<Squares2X2Icon className="h-4 w-4" /> <Squares2X2Icon className="h-4 w-4" />
</button> </button>
@ -292,6 +289,23 @@ const ProjectIssues: NextPage = () => {
))} ))}
</div> </div>
</div> </div>
<div className="border-b-2"></div>
<div className="relative flex justify-end gap-x-3">
<button
type="button"
className="text-xs"
onClick={() => resetFilterToDefault()}
>
Reset to default
</button>
<button
type="button"
className="text-xs font-medium text-theme"
onClick={() => setNewFilterDefaultView()}
>
Set as default
</button>
</div>
</div> </div>
</Popover.Panel> </Popover.Panel>
</Transition> </Transition>

View File

@ -31,7 +31,9 @@ export interface IProjectMember {
workspace: IWorkspace; workspace: IWorkspace;
comment: string; comment: string;
role: 5 | 10 | 15 | 20; role: 5 | 10 | 15 | 20;
view_props: ProjectViewTheme; view_props: ProjectViewTheme;
default_props: ProjectViewTheme;
created_at: Date; created_at: Date;
updated_at: Date; updated_at: Date;