diff --git a/apps/app/components/project/card.tsx b/apps/app/components/project/card.tsx deleted file mode 100644 index 8100379d4..000000000 --- a/apps/app/components/project/card.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -// ui -// icons -import { - CalendarDaysIcon, - CheckIcon, - PencilIcon, - PlusIcon, - TrashIcon, - ClipboardDocumentListIcon, -} from "@heroicons/react/24/outline"; -// types -// ui -import { Button } from "components/ui"; -// hooks -import useProjectMembers from "hooks/use-project-members"; -// helpers -import { renderShortNumericDateFormat } from "helpers/date-time.helper"; -// types -import type { IProject } from "types"; - -export type ProjectCardProps = { - workspaceSlug: string; - project: IProject; - setToJoinProject: (id: string | null) => void; - setDeleteProject: (id: string | null) => void; -}; - -export const ProjectCard: React.FC = (props) => { - const { workspaceSlug, project, setToJoinProject, setDeleteProject } = props; - // router - const router = useRouter(); - // fetching project members information - const { members, isMember, canDelete, canEdit } = useProjectMembers(workspaceSlug, project.id); - - if (!members) { - return ( -
-
-
- ); - } - - return ( - <> -
-
-
- - - {project.icon && ( - {String.fromCodePoint(parseInt(project.icon))} - )} - - {project.name} - - - -
- {isMember ? ( -
- {canEdit && ( - - - - - - )} - {canDelete && ( - - )} -
- ) : null} -
-
-

{project.description}

-
-
-
- - {!isMember ? ( - - ) : ( -
- - Member -
- )} -
-
- - {renderShortNumericDateFormat(project.created_at)} -
-
-
- - ); -}; diff --git a/apps/app/components/project/index.ts b/apps/app/components/project/index.ts index c36630d8f..bf18fe07d 100644 --- a/apps/app/components/project/index.ts +++ b/apps/app/components/project/index.ts @@ -1,4 +1,4 @@ -export * from "./card"; +export * from "./single-project-card"; export * from "./create-project-modal"; export * from "./join-project"; export * from "./sidebar-list"; diff --git a/apps/app/components/project/join-project-modal.tsx b/apps/app/components/project/join-project-modal.tsx index df05f1309..60530e8a7 100644 --- a/apps/app/components/project/join-project-modal.tsx +++ b/apps/app/components/project/join-project-modal.tsx @@ -13,9 +13,7 @@ type TJoinProjectModalProps = { onJoin: () => Promise; }; -export const JoinProjectModal: React.FC = (props) => { - const { onClose, onJoin, data } = props; - +export const JoinProjectModal: React.FC = ({ onClose, onJoin, data }) => { const [isJoiningLoading, setIsJoiningLoading] = useState(false); const handleJoin = () => { @@ -36,7 +34,7 @@ export const JoinProjectModal: React.FC = (props) => { return ( - + = (props) => {
-
+
= (props) => {

-
- + ) : ( + Member + )} + {project.is_favourite && ( + + + + )} +
+
+ + +
+ + +
+

{project.name}

+ {project.icon && ( + + {String.fromCodePoint(parseInt(project.icon))} + + )} +
+

{truncateText(project.description ?? "", 100)}

+
+ +
+ +
+ + {renderShortNumericDateFormat(project.created_at)} +
+
+ {hasJoined ? ( +
+ {(isOwner || isMember) && ( + + + + + + )} + + {isOwner && ( + setDeleteProject(project.id)}> + Delete project + + )} + {project.is_favourite ? ( + + Remove from favourites + + ) : ( + + Add to favourites + + )} + + Copy project link + + +
+ ) : null} +
+
+
+ ) : ( + + + + )} + + ); +}; diff --git a/apps/app/components/ui/custom-menu.tsx b/apps/app/components/ui/custom-menu.tsx index d82a1a347..64fa11338 100644 --- a/apps/app/components/ui/custom-menu.tsx +++ b/apps/app/components/ui/custom-menu.tsx @@ -11,6 +11,7 @@ type Props = { label?: string | JSX.Element; className?: string; ellipsis?: boolean; + verticalEllipsis?: boolean; width?: "sm" | "md" | "lg" | "xl" | "auto"; textAlignment?: "left" | "center" | "right"; noBorder?: boolean; @@ -31,6 +32,7 @@ const CustomMenu = ({ label, className = "", ellipsis = false, + verticalEllipsis = false, width = "auto", textAlignment, noBorder = false, @@ -38,44 +40,40 @@ const CustomMenu = ({ customButton, }: Props) => ( - {customButton ? ( - {customButton} - ) : ( -
- {ellipsis ? ( - - - - ) : ( - - {label} - {!noBorder && - )} -
- )} +
+ {ellipsis || verticalEllipsis ? ( + + + + ) : ( + + {label} + {!noBorder && + )} +
= ({ @@ -33,21 +34,18 @@ export const Tooltip: React.FC = ({ children, disabled = false, className = "", + theme = "light", }) => ( - {tooltipHeading ? ( - <> -
{tooltipHeading}
-

{tooltipContent}

- - ) : ( -

{tooltipContent}

- )} + {tooltipHeading &&
{tooltipHeading}
} +

{tooltipContent}

} position={position} diff --git a/apps/app/hooks/use-project-members.tsx b/apps/app/hooks/use-project-members.tsx index 5f3519610..9764ade75 100644 --- a/apps/app/hooks/use-project-members.tsx +++ b/apps/app/hooks/use-project-members.tsx @@ -13,20 +13,24 @@ const useProjectMembers = (workspaceSlug: string, projectId: string) => { projectService.projectMembers(workspaceSlug, projectId) ); - const isMember = members?.some((item: any) => item.member.id === (user as any)?.id); + const hasJoined = members?.some((item: any) => item.member.id === (user as any)?.id); - const canEdit = members?.some( - (item) => (item.member.id === (user as any)?.id && item.role === 20) || item.role === 15 + const isOwner = members?.some((item) => item.member.id === (user as any)?.id && item.role === 20); + const isMember = members?.some( + (item) => item.member.id === (user as any)?.id && item.role === 15 ); - const canDelete = members?.some( - (item) => item.member.id === (user as any)?.id && item.role === 20 + const isViewer = members?.some( + (item) => item.member.id === (user as any)?.id && item.role === 10 ); + const isGuest = members?.some((item) => item.member.id === (user as any)?.id && item.role === 5); return { members, + hasJoined, + isOwner, isMember, - canEdit, - canDelete, + isViewer, + isGuest, }; }; diff --git a/apps/app/hooks/use-projects.tsx b/apps/app/hooks/use-projects.tsx index f9583eea8..6b4a8b413 100644 --- a/apps/app/hooks/use-projects.tsx +++ b/apps/app/hooks/use-projects.tsx @@ -1,10 +1,8 @@ import useSWR from "swr"; import { useRouter } from "next/router"; -// types -import { IProject } from "types"; // services import projectService from "services/project.service"; -// constants +// fetch-keys import { PROJECTS_LIST } from "constants/fetch-keys"; const useProjects = () => { @@ -12,7 +10,7 @@ const useProjects = () => { const router = useRouter(); const { workspaceSlug } = router.query; // api fetching - const { data: projects, mutate: mutateProjects } = useSWR( + const { data: projects, mutate: mutateProjects } = useSWR( workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null, workspaceSlug ? () => projectService.getProjects(workspaceSlug as string) : null ); diff --git a/apps/app/pages/[workspaceSlug]/projects/index.tsx b/apps/app/pages/[workspaceSlug]/projects/index.tsx index 8b8d0408a..4569552ac 100644 --- a/apps/app/pages/[workspaceSlug]/projects/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/index.tsx @@ -10,7 +10,7 @@ import useWorkspaces from "hooks/use-workspaces"; import AppLayout from "layouts/app-layout"; // components import { JoinProjectModal } from "components/project/join-project-modal"; -import { ProjectCard } from "components/project"; +import { SingleProjectCard } from "components/project"; import ConfirmProjectDeletion from "components/project/confirm-project-deletion"; // ui import { HeaderButton, EmptySpace, EmptySpaceItem, Loader } from "components/ui"; @@ -57,6 +57,7 @@ const ProjectsPage: NextPage = () => { onJoin={async () => { const project = projects?.find((item) => item.id === selectedProjectToJoin); if (!project) return; + await projectService .joinProject(workspaceSlug as string, { project_ids: [project.id], @@ -101,18 +102,15 @@ const ProjectsPage: NextPage = () => {
) : ( -
-
- {projects.map((item) => ( - - ))} -
+
+ {projects.map((project) => ( + + ))}
)} diff --git a/apps/app/services/project.service.ts b/apps/app/services/project.service.ts index 1172f8823..702298880 100644 --- a/apps/app/services/project.service.ts +++ b/apps/app/services/project.service.ts @@ -222,7 +222,7 @@ class ProjectServices extends APIService { } async syncGiuthubRepository( - slug: string, + workspaceSlug: string, projectId: string, workspaceIntegrationId: string, data: { @@ -233,7 +233,7 @@ class ProjectServices extends APIService { } ): Promise { return this.post( - `/api/workspaces/${slug}/projects/${projectId}/workspace-integrations/${workspaceIntegrationId}/github-repository-sync/`, + `/api/workspaces/${workspaceSlug}/projects/${projectId}/workspace-integrations/${workspaceIntegrationId}/github-repository-sync/`, data ) .then((response) => response?.data) @@ -255,6 +255,27 @@ class ProjectServices extends APIService { throw error?.response?.data; }); } + + async addProjectToFavourites( + workspaceSlug: string, + data: { + project: string; + } + ): Promise { + return this.post(`/api/workspaces/${workspaceSlug}/user-favourite-projects/`, data) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async removeProjectFromFavourites(workspaceSlug: string, projectId: string): Promise { + return this.delete(`/api/workspaces/${workspaceSlug}/user-favourite-projects/${projectId}/`) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } } export default new ProjectServices(); diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index ccb9ea2f1..abc50d9dc 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -4,6 +4,13 @@ @tailwind components; @tailwind utilities; +@layer components { + .text-1\.5xl { + font-size: 1.375rem; + line-height: 1.875rem; + } +} + @layer base { html { font-family: "Inter", sans-serif; @@ -24,7 +31,7 @@ } .scrollbar-enable::-webkit-scrollbar { - display: block ; + display: block; } /* Scrollbar style */ diff --git a/apps/app/types/projects.d.ts b/apps/app/types/projects.d.ts index 7364682a8..af5012f2c 100644 --- a/apps/app/types/projects.d.ts +++ b/apps/app/types/projects.d.ts @@ -10,6 +10,7 @@ export interface IProject { icon: string; id: string; identifier: string; + is_favourite: boolean; module_view: boolean; name: string; network: number;