chore: update favorite projects endpoint (#1522)

* chore: upate favorite projects list endpoint

* chore: add project button on the sidebar

* refactor: sidebar favorite projects workflow
This commit is contained in:
Aaryan Khandelwal 2023-07-13 19:44:53 +05:30 committed by GitHub
parent 7361657660
commit c9a5893c3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 251 additions and 308 deletions

View File

@ -7,7 +7,6 @@ import useSWR, { mutate } from "swr";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// services
import projectService from "services/project.service";
import modulesService from "services/modules.service";
import issuesService from "services/issues.service";
import inboxServices from "services/inbox.service";
@ -18,6 +17,7 @@ import useCalendarIssuesView from "hooks/use-calendar-issues-view";
import useToast from "hooks/use-toast";
import useInboxView from "hooks/use-inbox-view";
import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view";
import useProjects from "hooks/use-projects";
// components
import { IssueForm } from "components/issues";
// types
@ -27,7 +27,6 @@ import {
PROJECT_ISSUES_DETAILS,
PROJECT_ISSUES_LIST,
USER_ISSUE,
PROJECTS_LIST,
SUB_ISSUES,
PROJECT_ISSUES_LIST_WITH_PARAMS,
CYCLE_ISSUES_WITH_PARAMS,
@ -83,6 +82,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const { params: spreadsheetParams } = useSpreadsheetIssuesView();
const { user } = useUser();
const { projects } = useProjects();
const { setToastAlert } = useToast();
if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string };
@ -102,11 +103,6 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
: null
);
const { data: projects } = useSWR(
workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null,
workspaceSlug ? () => projectService.getProjects(workspaceSlug as string) : null
);
useEffect(() => {
if (projects && projects.length > 0)
setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null);

View File

@ -1,15 +1,9 @@
import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import useProjects from "hooks/use-projects";
// ui
import { CustomSelect } from "components/ui";
// icons
import { ClipboardDocumentListIcon } from "@heroicons/react/24/outline";
// services
import projectService from "services/project.service";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
export interface IssueProjectSelectProps {
value: string;
@ -22,14 +16,7 @@ export const IssueProjectSelect: React.FC<IssueProjectSelectProps> = ({
onChange,
setActiveProject,
}) => {
const router = useRouter();
const { workspaceSlug } = router.query;
// Fetching Projects List
const { data: projects } = useSWR(
workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null,
() => (workspaceSlug ? projectService.getProjects(workspaceSlug as string) : null)
);
const { projects } = useProjects();
return (
<CustomSelect

View File

@ -123,7 +123,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
.createProject(workspaceSlug as string, payload, user)
.then((res) => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) => [res, ...(prevData ?? [])],
false
);

View File

@ -1,4 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { mutate } from "swr";
@ -13,7 +15,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// ui
import { DangerButton, Input, SecondaryButton } from "components/ui";
// types
import type { ICurrentUserResponse, IProject, IWorkspace } from "types";
import type { ICurrentUserResponse, IProject } from "types";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
@ -37,7 +39,8 @@ export const DeleteProjectModal: React.FC<TConfirmProjectDeletionProps> = ({
const [confirmDeleteMyProject, setConfirmDeleteMyProject] = useState(false);
const [selectedProject, setSelectedProject] = useState<IProject | null>(null);
const workspaceSlug = (data?.workspace as IWorkspace)?.slug;
const router = useRouter();
const { workspaceSlug } = router.query;
const { setToastAlert } = useToast();
@ -64,26 +67,31 @@ export const DeleteProjectModal: React.FC<TConfirmProjectDeletionProps> = ({
};
const handleDeletion = async () => {
setIsDeleteLoading(true);
if (!data || !workspaceSlug || !canDelete) return;
setIsDeleteLoading(true);
if (data.is_favorite)
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: true }),
(prevData) => prevData?.filter((project: IProject) => project.id !== data.id),
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) => prevData?.filter((project: IProject) => project.id !== data.id),
false
);
await projectService
.deleteProject(workspaceSlug, data.id, user)
.deleteProject(workspaceSlug as string, data.id, user)
.then(() => {
handleClose();
mutate<IProject[]>(PROJECTS_LIST(workspaceSlug), (prevData) =>
prevData?.filter((project: IProject) => project.id !== data.id)
);
if (onSuccess) onSuccess();
setToastAlert({
title: "Success",
type: "success",
message: "Project deleted successfully",
});
})
.catch((error) => {
console.log(error);
setIsDeleteLoading(false);
});
.catch(() => setIsDeleteLoading(false));
};
return (

View File

@ -2,26 +2,19 @@ import React, { useState, FC } from "react";
import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// hooks
import useToast from "hooks/use-toast";
import useTheme from "hooks/use-theme";
import useUserAuth from "hooks/use-user-auth";
// services
import projectService from "services/project.service";
import useProjects from "hooks/use-projects";
// components
import { CreateProjectModal, DeleteProjectModal, SingleSidebarProject } from "components/project";
// ui
import { Loader } from "components/ui";
import { DeleteProjectModal, SingleSidebarProject } from "components/project";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import { IFavoriteProject, IProject } from "types";
// fetch-keys
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST } from "constants/fetch-keys";
import { IProject } from "types";
export const ProjectSidebarList: FC = () => {
const [deleteProjectModal, setDeleteProjectModal] = useState(false);
@ -33,93 +26,11 @@ export const ProjectSidebarList: FC = () => {
const { user } = useUserAuth();
// states
const [isCreateProjectModal, setCreateProjectModal] = useState(false);
// theme
const { collapsed: sidebarCollapse } = useTheme();
// toast handler
const { setToastAlert } = useToast();
const { data: favoriteProjects } = useSWR(
workspaceSlug ? FAVORITE_PROJECTS_LIST(workspaceSlug.toString()) : null,
() => (workspaceSlug ? projectService.getFavoriteProjects(workspaceSlug.toString()) : null)
);
const { data: projects } = useSWR(
workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null,
() => (workspaceSlug ? projectService.getProjects(workspaceSlug as string) : null)
);
const normalProjects = projects?.filter((p) => !p.is_favorite) ?? [];
const handleAddToFavorites = (project: IProject) => {
if (!workspaceSlug) return;
projectService
.addProjectToFavorites(workspaceSlug as string, {
project: project.id,
})
.then(() => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
(prevData) =>
(prevData ?? []).map((p) => ({
...p,
is_favorite: p.id === project.id ? true : p.is_favorite,
})),
false
);
mutate(FAVORITE_PROJECTS_LIST(workspaceSlug as string));
setToastAlert({
type: "success",
title: "Success!",
message: "Successfully added the project to favorites.",
});
})
.catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the project from favorites. Please try again.",
});
});
};
const handleRemoveFromFavorites = (project: IProject) => {
if (!workspaceSlug) return;
projectService
.removeProjectFromFavorites(workspaceSlug as string, project.id)
.then(() => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
(prevData) =>
(prevData ?? []).map((p) => ({
...p,
is_favorite: p.id === project.id ? false : p.is_favorite,
})),
false
);
mutate<IFavoriteProject[]>(
FAVORITE_PROJECTS_LIST(workspaceSlug as string),
(prevData) => (prevData ?? []).filter((p) => p.project !== project.id),
false
);
setToastAlert({
type: "success",
title: "Success!",
message: "Successfully removed the project from favorites.",
});
})
.catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the project from favorites. Please try again.",
});
});
};
const { projects: favoriteProjects } = useProjects(true);
const { projects: allProjects } = useProjects();
const handleDeleteProject = (project: IProject) => {
setProjectToDelete(project);
@ -140,93 +51,61 @@ export const ProjectSidebarList: FC = () => {
return (
<>
<CreateProjectModal
isOpen={isCreateProjectModal}
setIsOpen={setCreateProjectModal}
user={user}
/>
<DeleteProjectModal
isOpen={deleteProjectModal}
onClose={() => setDeleteProjectModal(false)}
data={projectToDelete}
user={user}
/>
<div className="h-full overflow-y-auto mt-5 px-4">
<div className="h-full overflow-y-auto px-4">
{favoriteProjects && favoriteProjects.length > 0 && (
<div className="flex flex-col space-y-2">
<div className="flex flex-col space-y-2 mt-5">
{!sidebarCollapse && (
<h5 className="text-sm font-medium text-custom-sidebar-text-200">Favorites</h5>
)}
{favoriteProjects.map((favoriteProject) => {
const project = favoriteProject.project_detail;
return (
<SingleSidebarProject
key={project.id}
project={project}
sidebarCollapse={sidebarCollapse}
handleDeleteProject={() => handleDeleteProject(project)}
handleCopyText={() => handleCopyText(project.id)}
handleRemoveFromFavorites={() => handleRemoveFromFavorites(project)}
/>
);
})}
{favoriteProjects.map((project) => (
<SingleSidebarProject
key={project.id}
project={project}
sidebarCollapse={sidebarCollapse}
handleDeleteProject={() => handleDeleteProject(project)}
handleCopyText={() => handleCopyText(project.id)}
shortContextMenu
/>
))}
</div>
)}
<div className="flex flex-col space-y-2 mt-5">
{!sidebarCollapse && (
<h5 className="text-sm font-medium text-custom-sidebar-text-200">Projects</h5>
)}
{projects ? (
<>
{normalProjects.length > 0 ? (
normalProjects.map((project) => (
<SingleSidebarProject
key={project.id}
project={project}
sidebarCollapse={sidebarCollapse}
handleDeleteProject={() => handleDeleteProject(project)}
handleCopyText={() => handleCopyText(project.id)}
handleAddToFavorites={() => handleAddToFavorites(project)}
/>
))
) : (
<div className="space-y-3 text-center">
{!sidebarCollapse && (
<h4 className="text-sm text-custom-text-200">
You don{"'"}t have any project yet
</h4>
)}
<button
type="button"
className="group flex w-full items-center justify-center gap-2 rounded-md bg-custom-background-80 p-2 text-xs text-custom-text-100"
onClick={() => setCreateProjectModal(true)}
>
<PlusIcon className="h-4 w-4" />
{!sidebarCollapse && "Create Project"}
</button>
</div>
)}
</>
) : (
<div className="w-full">
<Loader className="space-y-5">
<div className="space-y-2">
<Loader.Item height="30px" />
<Loader.Item height="15px" width="80%" />
<Loader.Item height="15px" width="80%" />
<Loader.Item height="15px" width="80%" />
</div>
<div className="space-y-2">
<Loader.Item height="30px" />
<Loader.Item height="15px" width="80%" />
<Loader.Item height="15px" width="80%" />
<Loader.Item height="15px" width="80%" />
</div>
</Loader>
</div>
)}
</div>
{allProjects && allProjects.length > 0 && (
<div className="flex flex-col space-y-2 mt-5">
{!sidebarCollapse && (
<h5 className="text-sm font-medium text-custom-sidebar-text-200">Projects</h5>
)}
{allProjects.map((project) => (
<SingleSidebarProject
key={project.id}
project={project}
sidebarCollapse={sidebarCollapse}
handleDeleteProject={() => handleDeleteProject(project)}
handleCopyText={() => handleCopyText(project.id)}
/>
))}
</div>
)}
{allProjects && allProjects.length === 0 && (
<button
type="button"
className="flex w-full items-center gap-2 px-3 py-2 text-sm text-custom-sidebar-text-200 mt-5"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
}}
>
<PlusIcon className="h-5 w-5" />
{!sidebarCollapse && "Add Project"}
</button>
)}
</div>
</>
);

View File

@ -26,9 +26,9 @@ import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { renderEmoji } from "helpers/emoji.helper";
// types
import type { IFavoriteProject, IProject } from "types";
import type { IProject } from "types";
// fetch-keys
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST } from "constants/fetch-keys";
import { PROJECTS_LIST } from "constants/fetch-keys";
export type ProjectCardProps = {
project: IProject;
@ -55,22 +55,23 @@ export const SingleProjectCard: React.FC<ProjectCardProps> = ({
const handleAddToFavorites = () => {
if (!workspaceSlug) return;
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: true }),
(prevData) => [...(prevData ?? []), { ...project, is_favorite: true }],
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) =>
(prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: true } : p)),
false
);
projectService
.addProjectToFavorites(workspaceSlug as string, {
project: project.id,
})
.then(() => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
(prevData) =>
(prevData ?? []).map((p) => ({
...p,
is_favorite: p.id === project.id ? true : p.is_favorite,
})),
false
);
mutate(FAVORITE_PROJECTS_LIST(workspaceSlug as string));
setToastAlert({
type: "success",
title: "Success!",
@ -89,24 +90,21 @@ export const SingleProjectCard: React.FC<ProjectCardProps> = ({
const handleRemoveFromFavorites = () => {
if (!workspaceSlug || !project) return;
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: true }),
(prevData) => (prevData ?? []).filter((p) => p.id !== project.id),
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) =>
(prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: false } : p)),
false
);
projectService
.removeProjectFromFavorites(workspaceSlug as string, project.id)
.then(() => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
(prevData) =>
(prevData ?? []).map((p) => ({
...p,
is_favorite: p.id === project.id ? false : p.is_favorite,
})),
false
);
mutate<IFavoriteProject[]>(
FAVORITE_PROJECTS_LIST(workspaceSlug as string),
(prevData) => (prevData ?? []).filter((p) => p.project !== project.id),
false
);
setToastAlert({
type: "success",
title: "Success!",

View File

@ -1,8 +1,14 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { mutate } from "swr";
// headless ui
import { Disclosure, Transition } from "@headlessui/react";
// services
import projectService from "services/project.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { CustomMenu, Icon, Tooltip } from "components/ui";
// icons
@ -12,14 +18,15 @@ import { truncateText } from "helpers/string.helper";
import { renderEmoji } from "helpers/emoji.helper";
// types
import { IProject } from "types";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
type Props = {
project: IProject;
sidebarCollapse: boolean;
handleDeleteProject: () => void;
handleCopyText: () => void;
handleAddToFavorites?: () => void;
handleRemoveFromFavorites?: () => void;
shortContextMenu?: boolean;
};
const navigation = (workspaceSlug: string, projectId: string) => [
@ -60,12 +67,65 @@ export const SingleSidebarProject: React.FC<Props> = ({
sidebarCollapse,
handleDeleteProject,
handleCopyText,
handleAddToFavorites,
handleRemoveFromFavorites,
shortContextMenu = false,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast();
const handleAddToFavorites = () => {
if (!workspaceSlug) return;
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: true }),
(prevData) => [...(prevData ?? []), { ...project, is_favorite: true }],
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) =>
(prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: true } : p)),
false
);
projectService
.addProjectToFavorites(workspaceSlug as string, {
project: project.id,
})
.catch(() =>
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the project from favorites. Please try again.",
})
);
};
const handleRemoveFromFavorites = () => {
if (!workspaceSlug) return;
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: true }),
(prevData) => (prevData ?? []).filter((p) => p.id !== project.id),
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
(prevData) =>
(prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: false } : p)),
false
);
projectService.removeProjectFromFavorites(workspaceSlug as string, project.id).catch(() =>
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the project from favorites. Please try again.",
})
);
};
return (
<Disclosure key={project?.id} defaultOpen={projectId === project?.id}>
{({ open }) => (
@ -124,13 +184,15 @@ export const SingleSidebarProject: React.FC<Props> = ({
{!sidebarCollapse && (
<CustomMenu ellipsis>
<CustomMenu.MenuItem onClick={handleDeleteProject}>
<span className="flex items-center justify-start gap-2 ">
<TrashIcon className="h-4 w-4" />
<span>Delete project</span>
</span>
</CustomMenu.MenuItem>
{handleAddToFavorites && (
{!shortContextMenu && (
<CustomMenu.MenuItem onClick={handleDeleteProject}>
<span className="flex items-center justify-start gap-2 ">
<TrashIcon className="h-4 w-4" />
<span>Delete project</span>
</span>
</CustomMenu.MenuItem>
)}
{!project.is_favorite && (
<CustomMenu.MenuItem onClick={handleAddToFavorites}>
<span className="flex items-center justify-start gap-2">
<StarIcon className="h-4 w-4" />
@ -138,7 +200,7 @@ export const SingleSidebarProject: React.FC<Props> = ({
</span>
</CustomMenu.MenuItem>
)}
{handleRemoveFromFavorites && (
{project.is_favorite && (
<CustomMenu.MenuItem onClick={handleRemoveFromFavorites}>
<span className="flex items-center justify-start gap-2">
<StarIcon className="h-4 w-4" />

View File

@ -52,10 +52,18 @@ export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS";
export const WORKSPACE_INVITATION = "WORKSPACE_INVITATION";
export const LAST_ACTIVE_WORKSPACE_AND_PROJECTS = "LAST_ACTIVE_WORKSPACE_AND_PROJECTS";
export const PROJECTS_LIST = (workspaceSlug: string) =>
`PROJECTS_LIST_${workspaceSlug.toUpperCase()}`;
export const FAVORITE_PROJECTS_LIST = (workspaceSlug: string) =>
`FAVORITE_PROJECTS_LIST_${workspaceSlug.toUpperCase()}`;
export const PROJECTS_LIST = (
workspaceSlug: string,
params: {
is_favorite: "all" | boolean;
}
) => {
if (!params) return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}`;
return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite
.toString()
.toUpperCase()}`;
};
export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`;
export const PROJECT_MEMBERS = (projectId: string) => `PROJECT_MEMBERS_${projectId.toUpperCase()}`;

View File

@ -11,14 +11,15 @@ import { IProject } from "types";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
const useProjects = () => {
// router
const useProjects = (type?: "all" | boolean) => {
const router = useRouter();
const { workspaceSlug } = router.query;
// api fetching
const { data: projects, mutate: mutateProjects } = useSWR(
workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null,
workspaceSlug ? () => projectService.getProjects(workspaceSlug as string) : null
workspaceSlug ? PROJECTS_LIST(workspaceSlug as string, { is_favorite: type ?? "all" }) : null,
workspaceSlug
? () => projectService.getProjects(workspaceSlug as string, { is_favorite: type ?? "all" })
: null
);
const recentProjects = [...(projects ?? [])]

View File

@ -59,7 +59,7 @@ const ControlSettings: NextPage = () => {
} = useForm<IProject>({ defaultValues });
const onSubmit = async (formData: IProject) => {
if (!workspaceSlug || !projectId) return;
if (!workspaceSlug || !projectId || !projectDetails) return;
const payload: Partial<IProject> = {
default_assignee: formData.default_assignee,
@ -70,7 +70,19 @@ const ControlSettings: NextPage = () => {
.updateProject(workspaceSlug as string, projectId as string, payload, user)
.then((res) => {
mutate(PROJECT_DETAILS(projectId as string));
mutate(PROJECTS_LIST(workspaceSlug as string));
if (projectDetails.is_favorite)
mutate(
PROJECTS_LIST(workspaceSlug as string, {
is_favorite: true,
})
);
mutate(
PROJECTS_LIST(workspaceSlug as string, {
is_favorite: "all",
})
);
setToastAlert({
title: "Success",

View File

@ -21,10 +21,10 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
import { ContrastIcon, PeopleGroupIcon, ViewListIcon, InboxIcon } from "components/icons";
import { DocumentTextIcon } from "@heroicons/react/24/outline";
// types
import { IFavoriteProject, IProject } from "types";
import { IProject } from "types";
import type { NextPage } from "next";
// fetch-keys
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
const featuresList = [
{
@ -100,17 +100,16 @@ const FeaturesSettings: NextPage = () => {
if (!workspaceSlug || !projectId || !projectDetails) return;
if (projectDetails.is_favorite)
mutate<IFavoriteProject[]>(
FAVORITE_PROJECTS_LIST(workspaceSlug.toString()),
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug.toString(), {
is_favorite: true,
}),
(prevData) =>
prevData?.map((p) => {
if (p.project === projectId)
if (p.id === projectId)
return {
...p,
project_detail: {
...p.project_detail,
...formData,
},
...formData,
};
return p;
@ -119,7 +118,9 @@ const FeaturesSettings: NextPage = () => {
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug.toString()),
PROJECTS_LIST(workspaceSlug.toString(), {
is_favorite: "all",
}),
(prevData) =>
prevData?.map((p) => {
if (p.id === projectId)
@ -147,21 +148,13 @@ const FeaturesSettings: NextPage = () => {
await projectService
.updateProject(workspaceSlug as string, projectId as string, formData, user)
.then(() => {
mutate(
projectDetails.is_favorite
? FAVORITE_PROJECTS_LIST(workspaceSlug.toString())
: PROJECTS_LIST(workspaceSlug.toString())
);
mutate(PROJECT_DETAILS(projectId as string));
})
.catch((err) => {
.catch(() =>
setToastAlert({
type: "error",
title: "Error!",
message: "Project feature could not be updated. Please try again.",
});
});
})
);
};
return (

View File

@ -94,7 +94,20 @@ const GeneralSettings: NextPage = () => {
(prevData) => ({ ...prevData, ...res }),
false
);
mutate(PROJECTS_LIST(workspaceSlug as string));
if (projectDetails.is_favorite)
mutate(
PROJECTS_LIST(workspaceSlug as string, {
is_favorite: true,
})
);
mutate(
PROJECTS_LIST(workspaceSlug as string, {
is_favorite: "all",
})
);
setToastAlert({
type: "success",
title: "Success!",

View File

@ -6,7 +6,6 @@ import trackEventServices from "services/track-event.service";
import type {
GithubRepositoriesResponse,
ICurrentUserResponse,
IFavoriteProject,
IProject,
IProjectBulkInviteFormData,
IProjectMember,
@ -53,8 +52,15 @@ class ProjectServices extends APIService {
});
}
async getProjects(workspaceSlug: string): Promise<IProject[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/`)
async getProjects(
workspaceSlug: string,
params: {
is_favorite: "all" | boolean;
}
): Promise<IProject[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/`, {
params,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
@ -298,14 +304,6 @@ class ProjectServices extends APIService {
});
}
async getFavoriteProjects(workspaceSlug: string): Promise<IFavoriteProject[]> {
return this.get(`/api/workspaces/${workspaceSlug}/user-favorite-projects/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async addProjectToFavorites(
workspaceSlug: string,
data: {

View File

@ -60,18 +60,6 @@ export interface IProjectLite {
identifier: string;
}
export interface IFavoriteProject {
created_at: Date;
created_by: string;
id: string;
project: string;
project_detail: IProject;
updated_at: Date;
updated_by: string;
user: string;
workspace: string;
}
type ProjectViewTheme = {
issueView: TIssueViewOptions;
groupByProperty: TIssueGroupByOptions;