feat: reset to default view, and create new default view
This commit is contained in:
parent
f7538f17b1
commit
052079c72e
6 changed files with 80 additions and 12 deletions
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -46,6 +48,8 @@ type ContextType = {
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateType = Theme;
|
type StateType = Theme;
|
||||||
|
|
@ -70,8 +74,7 @@ export const reducer: ReducerFunctionType = (state, action) => {
|
||||||
};
|
};
|
||||||
return newState;
|
return newState;
|
||||||
case REHYDRATE_THEME: {
|
case REHYDRATE_THEME: {
|
||||||
const newState = payload;
|
return { ...initialState, ...payload };
|
||||||
return { ...initialState, ...newState };
|
|
||||||
}
|
}
|
||||||
case SET_ISSUE_VIEW: {
|
case SET_ISSUE_VIEW: {
|
||||||
const newState = {
|
const newState = {
|
||||||
|
|
@ -113,6 +116,12 @@ export const reducer: ReducerFunctionType = (state, action) => {
|
||||||
...newState,
|
...newState,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case RESET_TO_DEFAULT: {
|
||||||
|
return {
|
||||||
|
...initialState,
|
||||||
|
...payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
@ -120,18 +129,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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -191,6 +209,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 +228,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
|
||||||
|
|
@ -229,6 +266,8 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||||
setOrderBy,
|
setOrderBy,
|
||||||
filterIssue: state.filterIssue,
|
filterIssue: state.filterIssue,
|
||||||
setFilterIssue,
|
setFilterIssue,
|
||||||
|
resetFilterToDefault: resetToDefault,
|
||||||
|
setNewFilterDefaultView: setNewDefaultView,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ToastAlert />
|
<ToastAlert />
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ const useIssuesFilter = (projectIssues: IIssue[]) => {
|
||||||
setOrderBy,
|
setOrderBy,
|
||||||
filterIssue,
|
filterIssue,
|
||||||
setFilterIssue,
|
setFilterIssue,
|
||||||
|
resetFilterToDefault,
|
||||||
|
setNewFilterDefaultView,
|
||||||
} = useTheme();
|
} = useTheme();
|
||||||
|
|
||||||
const { states } = useUser();
|
const { states } = useUser();
|
||||||
|
|
@ -95,6 +97,8 @@ const useIssuesFilter = (projectIssues: IIssue[]) => {
|
||||||
setOrderBy,
|
setOrderBy,
|
||||||
filterIssue,
|
filterIssue,
|
||||||
setFilterIssue,
|
setFilterIssue,
|
||||||
|
resetFilterToDefault,
|
||||||
|
setNewFilterDefaultView,
|
||||||
} as const;
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,8 @@ const ProjectIssues: NextPage = () => {
|
||||||
setFilterIssue,
|
setFilterIssue,
|
||||||
orderBy,
|
orderBy,
|
||||||
filterIssue,
|
filterIssue,
|
||||||
|
resetFilterToDefault,
|
||||||
|
setNewFilterDefaultView,
|
||||||
} = useIssuesFilter(projectIssues?.results.filter((p) => p.parent === null) ?? []);
|
} = useIssuesFilter(projectIssues?.results.filter((p) => p.parent === null) ?? []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -292,6 +294,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>
|
||||||
|
|
|
||||||
2
apps/app/types/projects.d.ts
vendored
2
apps/app/types/projects.d.ts
vendored
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue