fix: mutation of project detail, removed two identifier validation and added default value for deep checking

This commit is contained in:
Dakshesh Jain 2022-12-08 20:29:12 +05:30
parent 1368fb9164
commit a1598e7310
19 changed files with 170 additions and 78 deletions

View File

@ -8,6 +8,8 @@ import { Dialog, Transition } from "@headlessui/react";
// services // services
import projectServices from "lib/services/project.service"; import projectServices from "lib/services/project.service";
import workspaceService from "lib/services/workspace.service"; import workspaceService from "lib/services/workspace.service";
// constants
import { NETWORK_CHOICES } from "constants/";
// fetch keys // fetch keys
import { PROJECTS_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys"; import { PROJECTS_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
// hooks // hooks
@ -15,8 +17,6 @@ import useUser from "lib/hooks/useUser";
import useToast from "lib/hooks/useToast"; import useToast from "lib/hooks/useToast";
// ui // ui
import { Button, Input, TextArea, Select } from "ui"; import { Button, Input, TextArea, Select } from "ui";
// common
import { debounce } from "constants/common";
// types // types
import { IProject, WorkspaceMember } from "types"; import { IProject, WorkspaceMember } from "types";
@ -25,11 +25,11 @@ type Props = {
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}; };
const NETWORK_CHOICES = { "0": "Secret", "2": "Public" };
const defaultValues: Partial<IProject> = { const defaultValues: Partial<IProject> = {
name: "", name: "",
identifier: "",
description: "", description: "",
network: 0,
}; };
const IsGuestCondition: React.FC<{ const IsGuestCondition: React.FC<{
@ -62,7 +62,10 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
const { data: workspaceMembers } = useSWR<WorkspaceMember[]>( const { data: workspaceMembers } = useSWR<WorkspaceMember[]>(
activeWorkspace ? WORKSPACE_MEMBERS(activeWorkspace.slug) : null, activeWorkspace ? WORKSPACE_MEMBERS(activeWorkspace.slug) : null,
activeWorkspace ? () => workspaceService.workspaceMembers(activeWorkspace.slug) : null activeWorkspace ? () => workspaceService.workspaceMembers(activeWorkspace.slug) : null,
{
shouldRetryOnError: false,
}
); );
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -79,8 +82,6 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
setValue, setValue,
} = useForm<IProject>({ } = useForm<IProject>({
defaultValues, defaultValues,
reValidateMode: "onChange",
mode: "all",
}); });
const onSubmit = async (formData: IProject) => { const onSubmit = async (formData: IProject) => {
@ -111,6 +112,7 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
handleClose(); handleClose();
return; return;
} }
err = err.data;
Object.keys(err).map((key) => { Object.keys(err).map((key) => {
const errorMessages = err[key]; const errorMessages = err[key];
setError(key as keyof IProject, { setError(key as keyof IProject, {
@ -123,16 +125,6 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
const projectName = watch("name") ?? ""; const projectName = watch("name") ?? "";
const projectIdentifier = watch("identifier") ?? ""; const projectIdentifier = watch("identifier") ?? "";
const checkIdentifier = (slug: string, value: string) => {
projectServices.checkProjectIdentifierAvailability(slug, value).then((response) => {
console.log(response);
if (response.exists) setError("identifier", { message: "Identifier already exists" });
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const checkIdentifierAvailability = useCallback(debounce(checkIdentifier, 1500), []);
useEffect(() => { useEffect(() => {
if (projectName && isChangeIdentifierRequired) { if (projectName && isChangeIdentifierRequired) {
setValue("identifier", projectName.replace(/ /g, "-").toUpperCase().substring(0, 3)); setValue("identifier", projectName.replace(/ /g, "-").toUpperCase().substring(0, 3));
@ -234,11 +226,7 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
placeholder="Enter Project Identifier" placeholder="Enter Project Identifier"
error={errors.identifier} error={errors.identifier}
register={register} register={register}
onChange={(e: any) => { onChange={() => setIsChangeIdentifierRequired(false)}
setIsChangeIdentifierRequired(false);
if (!activeWorkspace || !e.target.value) return;
checkIdentifierAvailability(activeWorkspace.slug, e.target.value);
}}
validations={{ validations={{
required: "Identifier is required", required: "Identifier is required",
minLength: { minLength: {

View File

@ -65,6 +65,8 @@ export const PROJECT_MEMBERS = (workspaceSlug: string, projectId: string) =>
`/api/workspaces/${workspaceSlug}/projects/${projectId}/members/`; `/api/workspaces/${workspaceSlug}/projects/${projectId}/members/`;
export const PROJECT_MEMBER_DETAIL = (workspaceSlug: string, projectId: string, memberId: string) => export const PROJECT_MEMBER_DETAIL = (workspaceSlug: string, projectId: string, memberId: string) =>
`/api/workspaces/${workspaceSlug}/projects/${projectId}/members/${memberId}/`; `/api/workspaces/${workspaceSlug}/projects/${projectId}/members/${memberId}/`;
export const PROJECT_VIEW_ENDPOINT = (workspaceSlug: string, projectId: string) =>
`/api/workspaces/${workspaceSlug}/projects/${projectId}/project-views/`;
export const PROJECT_INVITATIONS = (workspaceSlug: string, projectId: string) => export const PROJECT_INVITATIONS = (workspaceSlug: string, projectId: string) =>
`/api/workspaces/${workspaceSlug}/projects/${projectId}/invitations/`; `/api/workspaces/${workspaceSlug}/projects/${projectId}/invitations/`;

View File

@ -7,7 +7,7 @@ export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS";
export const WORKSPACE_INVITATION = "WORKSPACE_INVITATION"; export const WORKSPACE_INVITATION = "WORKSPACE_INVITATION";
export const PROJECTS_LIST = (workspaceSlug: string) => `PROJECTS_LIST_${workspaceSlug}`; export const PROJECTS_LIST = (workspaceSlug: string) => `PROJECTS_LIST_${workspaceSlug}`;
export const PROJECT_DETAILS = "PROJECT_DETAILS"; export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId}`;
export const PROJECT_MEMBERS = (projectId: string) => `PROJECT_MEMBERS_${projectId}`; export const PROJECT_MEMBERS = (projectId: string) => `PROJECT_MEMBERS_${projectId}`;
export const PROJECT_INVITATIONS = "PROJECT_INVITATIONS"; export const PROJECT_INVITATIONS = "PROJECT_INVITATIONS";

View File

@ -6,3 +6,5 @@ export const ROLE = {
15: "Member", 15: "Member",
20: "Admin", 20: "Admin",
}; };
export const NETWORK_CHOICES = { "0": "Secret", "2": "Public" };

View File

@ -2,3 +2,5 @@ export const TOGGLE_SIDEBAR = "TOGGLE_SIDEBAR";
export const REHYDRATE_THEME = "REHYDRATE_THEME"; export const REHYDRATE_THEME = "REHYDRATE_THEME";
export const SET_ISSUE_VIEW = "SET_ISSUE_VIEW"; 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_FILTER_ISSUES = "SET_FILTER_ISSUES";

View File

@ -5,6 +5,8 @@ import {
REHYDRATE_THEME, REHYDRATE_THEME,
SET_ISSUE_VIEW, SET_ISSUE_VIEW,
SET_GROUP_BY_PROPERTY, SET_GROUP_BY_PROPERTY,
SET_ORDER_BY_PROPERTY,
SET_FILTER_ISSUES,
} from "constants/theme.context.constants"; } from "constants/theme.context.constants";
// components // components
import ToastAlert from "components/toast-alert"; import ToastAlert from "components/toast-alert";
@ -12,30 +14,30 @@ import ToastAlert from "components/toast-alert";
export const themeContext = createContext<ContextType>({} as ContextType); export const themeContext = createContext<ContextType>({} as ContextType);
// types // types
import type { IIssue, NestedKeyOf } from "types"; import type { IIssue, NestedKeyOf, ProjectViewTheme as Theme } from "types";
type Theme = {
collapsed: boolean;
issueView: "list" | "kanban" | null;
groupByProperty: NestedKeyOf<IIssue> | null;
};
type ReducerActionType = { type ReducerActionType = {
type: type:
| typeof TOGGLE_SIDEBAR | typeof TOGGLE_SIDEBAR
| typeof REHYDRATE_THEME | typeof REHYDRATE_THEME
| typeof SET_ISSUE_VIEW | typeof SET_ISSUE_VIEW
| typeof SET_ORDER_BY_PROPERTY
| typeof SET_FILTER_ISSUES
| typeof SET_GROUP_BY_PROPERTY; | typeof SET_GROUP_BY_PROPERTY;
payload?: Partial<Theme>; payload?: Partial<Theme>;
}; };
type ContextType = { type ContextType = {
collapsed: boolean; collapsed: boolean;
orderBy: NestedKeyOf<IIssue> | null;
issueView: "list" | "kanban" | null; issueView: "list" | "kanban" | null;
groupByProperty: NestedKeyOf<IIssue> | null; groupByProperty: NestedKeyOf<IIssue> | null;
filterIssue: "activeIssue" | "backlogIssue" | null;
toggleCollapsed: () => void; toggleCollapsed: () => void;
setIssueView: (display: "list" | "kanban") => void; setIssueView: (display: "list" | "kanban") => void;
setGroupByProperty: (property: NestedKeyOf<IIssue> | null) => void; setGroupByProperty: (property: NestedKeyOf<IIssue> | null) => void;
setOrderBy: (property: NestedKeyOf<IIssue> | null) => void;
setFilterIssue: (property: "activeIssue" | "backlogIssue" | null) => void;
}; };
type StateType = Theme; type StateType = Theme;
@ -45,6 +47,8 @@ export const initialState: StateType = {
collapsed: false, collapsed: false,
issueView: "list", issueView: "list",
groupByProperty: null, groupByProperty: null,
orderBy: null,
filterIssue: null,
}; };
export const reducer: ReducerFunctionType = (state, action) => { export const reducer: ReducerFunctionType = (state, action) => {
@ -87,6 +91,28 @@ export const reducer: ReducerFunctionType = (state, action) => {
...newState, ...newState,
}; };
} }
case SET_ORDER_BY_PROPERTY: {
const newState = {
...state,
orderBy: payload?.orderBy || null,
};
localStorage.setItem("theme", JSON.stringify(newState));
return {
...state,
...newState,
};
}
case SET_FILTER_ISSUES: {
const newState = {
...state,
filterIssue: payload?.filterIssue || null,
};
localStorage.setItem("theme", JSON.stringify(newState));
return {
...state,
...newState,
};
}
default: { default: {
return state; return state;
} }
@ -120,6 +146,24 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
}); });
}, []); }, []);
const setOrderBy = useCallback((property: NestedKeyOf<IIssue> | null) => {
dispatch({
type: SET_ORDER_BY_PROPERTY,
payload: {
orderBy: property,
},
});
}, []);
const setFilterIssue = useCallback((property: "activeIssue" | "backlogIssue" | null) => {
dispatch({
type: SET_FILTER_ISSUES,
payload: {
filterIssue: property,
},
});
}, []);
useEffect(() => { useEffect(() => {
dispatch({ dispatch({
type: REHYDRATE_THEME, type: REHYDRATE_THEME,
@ -135,6 +179,10 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
setIssueView, setIssueView,
groupByProperty: state.groupByProperty, groupByProperty: state.groupByProperty,
setGroupByProperty, setGroupByProperty,
orderBy: state.orderBy,
setOrderBy,
filterIssue: state.filterIssue,
setFilterIssue,
}} }}
> >
<ToastAlert /> <ToastAlert />

View File

@ -1,4 +1,3 @@
import { useState } from "react";
// hooks // hooks
import useTheme from "./useTheme"; import useTheme from "./useTheme";
import useUser from "./useUser"; import useUser from "./useUser";
@ -7,14 +6,19 @@ import { groupBy, orderArrayBy } from "constants/common";
// constants // constants
import { PRIORITIES } from "constants/"; import { PRIORITIES } from "constants/";
// types // types
import type { IssueResponse, IIssue, NestedKeyOf } from "types"; import type { IssueResponse, IIssue } from "types";
const useIssuesFilter = (projectIssues?: IssueResponse) => { const useIssuesFilter = (projectIssues?: IssueResponse) => {
const { issueView, setIssueView, groupByProperty, setGroupByProperty } = useTheme(); const {
issueView,
const [orderBy, setOrderBy] = useState<NestedKeyOf<IIssue> | null>(null); setIssueView,
groupByProperty,
const [filterIssue, setFilterIssue] = useState<"activeIssue" | "backlogIssue" | null>(null); setGroupByProperty,
orderBy,
setOrderBy,
filterIssue,
setFilterIssue,
} = useTheme();
const { states } = useUser(); const { states } = useUser();
@ -52,29 +56,29 @@ const useIssuesFilter = (projectIssues?: IssueResponse) => {
if (filterIssue !== null) { if (filterIssue !== null) {
if (filterIssue === "activeIssue") { if (filterIssue === "activeIssue") {
groupedByIssues = Object.keys(groupedByIssues).reduce((acc, key) => { const filteredStates = states?.filter(
const value = groupedByIssues[key]; (state) => state.group === "started" || state.group === "unstarted"
const filteredValue = value.filter( );
(issue) => groupedByIssues = Object.fromEntries(
issue.state_detail.group === "started" || issue.state_detail.group === "unstarted" filteredStates
); ?.sort((a, b) => a.sequence - b.sequence)
if (filteredValue.length > 0) { ?.map((state) => [
acc[key] = filteredValue; state.name,
} projectIssues?.results.filter((issue) => issue.state === state.id) ?? [],
return acc; ]) ?? []
}, {} as typeof groupedByIssues); );
} else if (filterIssue === "backlogIssue") { } else if (filterIssue === "backlogIssue") {
groupedByIssues = Object.keys(groupedByIssues).reduce((acc, key) => { const filteredStates = states?.filter(
const value = groupedByIssues[key]; (state) => state.group === "backlog" || state.group === "cancelled"
const filteredValue = value.filter( );
(issue) => groupedByIssues = Object.fromEntries(
issue.state_detail.group === "backlog" || issue.state_detail.group === "cancelled" filteredStates
); ?.sort((a, b) => a.sequence - b.sequence)
if (filteredValue.length > 0) { ?.map((state) => [
acc[key] = filteredValue; state.name,
} projectIssues?.results.filter((issue) => issue.state === state.id) ?? [],
return acc; ]) ?? []
}, {} as typeof groupedByIssues); );
} }
} }

View File

@ -10,9 +10,12 @@ import {
PROJECT_MEMBERS, PROJECT_MEMBERS,
PROJECT_MEMBER_DETAIL, PROJECT_MEMBER_DETAIL,
USER_PROJECT_INVITATIONS, USER_PROJECT_INVITATIONS,
PROJECT_VIEW_ENDPOINT,
} from "constants/api-routes"; } from "constants/api-routes";
// services // services
import APIService from "lib/services/api.service"; import APIService from "lib/services/api.service";
// types
import type { ProjectViewTheme } from "types";
const { NEXT_PUBLIC_API_BASE_URL } = process.env; const { NEXT_PUBLIC_API_BASE_URL } = process.env;
@ -177,6 +180,7 @@ class ProjectServices extends APIService {
throw error?.response?.data; throw error?.response?.data;
}); });
} }
async deleteProjectInvitation( async deleteProjectInvitation(
workspace_slug: string, workspace_slug: string,
project_id: string, project_id: string,
@ -190,6 +194,20 @@ class ProjectServices extends APIService {
throw error?.response?.data; throw error?.response?.data;
}); });
} }
async setProjectView(
workspace_slug: string,
project_id: string,
data: ProjectViewTheme
): Promise<any> {
await this.patch(PROJECT_VIEW_ENDPOINT(workspace_slug, project_id), data)
.then((response) => {
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
} }
export default new ProjectServices(); export default new ProjectServices();

View File

@ -8,6 +8,8 @@ import { useForm } from "react-hook-form";
import workspaceService from "lib/services/workspace.service"; import workspaceService from "lib/services/workspace.service";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import DefaultLayout from "layouts/DefaultLayout"; import DefaultLayout from "layouts/DefaultLayout";
// ui // ui
@ -144,4 +146,4 @@ const CreateWorkspace: NextPage = () => {
); );
}; };
export default CreateWorkspace; export default withAuth(CreateWorkspace);

View File

@ -11,6 +11,8 @@ import userService from "lib/services/user.service";
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// constants // constants
import { USER_WORKSPACE_INVITATIONS } from "constants/api-routes"; import { USER_WORKSPACE_INVITATIONS } from "constants/api-routes";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import DefaultLayout from "layouts/DefaultLayout"; import DefaultLayout from "layouts/DefaultLayout";
// components // components
@ -204,4 +206,4 @@ const OnBoard: NextPage = () => {
); );
}; };
export default OnBoard; export default withAuth(OnBoard);

View File

@ -19,6 +19,8 @@ import { classNames } from "constants/common";
// services // services
import userService from "lib/services/user.service"; import userService from "lib/services/user.service";
import issuesServices from "lib/services/issues.services"; import issuesServices from "lib/services/issues.services";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// components // components
import ChangeStateDropdown from "components/project/issues/my-issues/ChangeStateDropdown"; import ChangeStateDropdown from "components/project/issues/my-issues/ChangeStateDropdown";
// icons // icons
@ -278,4 +280,4 @@ const MyIssues: NextPage = () => {
); );
}; };
export default MyIssues; export default withAuth(MyIssues);

View File

@ -8,6 +8,8 @@ import { useForm } from "react-hook-form";
import Dropzone, { useDropzone } from "react-dropzone"; import Dropzone, { useDropzone } from "react-dropzone";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// services // services
@ -307,4 +309,4 @@ const Profile: NextPage = () => {
); );
}; };
export default Profile; export default withAuth(Profile);

View File

@ -11,6 +11,8 @@ import sprintService from "lib/services/cycles.services";
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// fetching keys // fetching keys
import { CYCLE_ISSUES, CYCLE_LIST } from "constants/fetch-keys"; import { CYCLE_ISSUES, CYCLE_LIST } from "constants/fetch-keys";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// components // components
@ -258,4 +260,4 @@ const ProjectSprints: NextPage = () => {
); );
}; };
export default ProjectSprints; export default withAuth(ProjectSprints);

View File

@ -22,6 +22,8 @@ import {
} from "constants/fetch-keys"; } from "constants/fetch-keys";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// components // components
@ -606,4 +608,4 @@ const IssueDetail: NextPage = () => {
); );
}; };
export default IssueDetail; export default withAuth(IssueDetail);

View File

@ -14,6 +14,8 @@ import useUser from "lib/hooks/useUser";
import useToast from "lib/hooks/useToast"; import useToast from "lib/hooks/useToast";
// fetching keys // fetching keys
import { PROJECT_MEMBERS, PROJECT_INVITATIONS } from "constants/fetch-keys"; import { PROJECT_MEMBERS, PROJECT_INVITATIONS } from "constants/fetch-keys";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// components // components
@ -313,4 +315,4 @@ const ProjectMembers: NextPage = () => {
); );
}; };
export default ProjectMembers; export default withAuth(ProjectMembers);

View File

@ -10,6 +10,8 @@ import useSWR from "swr";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
// headless ui // headless ui
import { Listbox, Tab, Transition } from "@headlessui/react"; import { Listbox, Tab, Transition } from "@headlessui/react";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// service // service
@ -20,6 +22,8 @@ import useUser from "lib/hooks/useUser";
import useToast from "lib/hooks/useToast"; import useToast from "lib/hooks/useToast";
// fetch keys // fetch keys
import { PROJECT_DETAILS, PROJECTS_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys"; import { PROJECT_DETAILS, PROJECTS_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
// constants
import { NETWORK_CHOICES } from "constants/";
// commons // commons
import { addSpaceIfCamelCase, debounce } from "constants/common"; import { addSpaceIfCamelCase, debounce } from "constants/common";
// components // components
@ -44,8 +48,6 @@ const defaultValues: Partial<IProject> = {
description: "", description: "",
}; };
const NETWORK_CHOICES = { "0": "Secret", "2": "Public" };
const ProjectSettings: NextPage = () => { const ProjectSettings: NextPage = () => {
const { const {
register, register,
@ -71,7 +73,7 @@ const ProjectSettings: NextPage = () => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { data: projectDetails } = useSWR<IProject>( const { data: projectDetails } = useSWR<IProject>(
activeWorkspace && projectId ? PROJECT_DETAILS : null, activeWorkspace && projectId ? PROJECT_DETAILS(projectId as string) : null,
activeWorkspace activeWorkspace
? () => projectServices.getProject(activeWorkspace.slug, projectId as string) ? () => projectServices.getProject(activeWorkspace.slug, projectId as string)
: null : null
@ -99,7 +101,7 @@ const ProjectSettings: NextPage = () => {
}, [projectDetails, reset]); }, [projectDetails, reset]);
const onSubmit = async (formData: IProject) => { const onSubmit = async (formData: IProject) => {
if (!activeWorkspace) return; if (!activeWorkspace || !projectId) return;
const payload: Partial<IProject> = { const payload: Partial<IProject> = {
name: formData.name, name: formData.name,
network: formData.network, network: formData.network,
@ -111,7 +113,11 @@ const ProjectSettings: NextPage = () => {
await projectServices await projectServices
.updateProject(activeWorkspace.slug, projectId as string, payload) .updateProject(activeWorkspace.slug, projectId as string, payload)
.then((res) => { .then((res) => {
mutate<IProject>(PROJECT_DETAILS, (prevData) => ({ ...prevData, ...res }), false); mutate<IProject>(
PROJECT_DETAILS(projectId as string),
(prevData) => ({ ...prevData, ...res }),
false
);
mutate<IProject[]>( mutate<IProject[]>(
PROJECTS_LIST(activeWorkspace.slug), PROJECTS_LIST(activeWorkspace.slug),
(prevData) => { (prevData) => {
@ -253,9 +259,6 @@ const ProjectSettings: NextPage = () => {
register={register} register={register}
label="Description" label="Description"
placeholder="Enter project description" placeholder="Enter project description"
validations={{
required: "Description is required",
}}
/> />
</div> </div>
<div className="flex justify-end"> <div className="flex justify-end">
@ -547,4 +550,4 @@ const ProjectSettings: NextPage = () => {
); );
}; };
export default ProjectSettings; export default withAuth(ProjectSettings);

View File

@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
import type { NextPage } from "next"; import type { NextPage } from "next";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// components // components
@ -129,4 +131,4 @@ const Projects: NextPage = () => {
); );
}; };
export default Projects; export default withAuth(Projects);

View File

@ -8,9 +8,10 @@ import Dropzone from "react-dropzone";
// services // services
import workspaceService from "lib/services/workspace.service"; import workspaceService from "lib/services/workspace.service";
import fileServices from "lib/services/file.services"; import fileServices from "lib/services/file.services";
// hoc
import withAuth from "lib/hoc/withAuthWrapper";
// layouts // layouts
import AdminLayout from "layouts/AdminLayout"; import AdminLayout from "layouts/AdminLayout";
// hooks // hooks
import useUser from "lib/hooks/useUser"; import useUser from "lib/hooks/useUser";
import useToast from "lib/hooks/useToast"; import useToast from "lib/hooks/useToast";
@ -232,4 +233,4 @@ const WorkspaceSettings = () => {
); );
}; };
export default WorkspaceSettings; export default withAuth(WorkspaceSettings);

View File

@ -15,3 +15,11 @@ export interface IProject {
created_by: string; created_by: string;
updated_by: string; updated_by: string;
} }
type ProjectViewTheme = {
collapsed: boolean;
issueView: "list" | "kanban" | null;
groupByProperty: NestedKeyOf<IIssue> | null;
filterIssue: "activeIssue" | "backlogIssue" | null;
orderBy: NestedKeyOf<IIssue> | null;
};