From 52c1b20f56fd7b7b2ee0febc09fee8ed0ea1c668 Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Tue, 1 Oct 2024 19:27:39 +0600 Subject: [PATCH] feat: add project status (#7252) * feat: add project status * Update project card and fix warnings * Add loading indicator to project card --------- Co-authored-by: Kamran Ahmed --- src/components/Projects/ProjectCard.tsx | 50 ++++++++++++++++++++---- src/components/Projects/ProjectsList.tsx | 50 +++++++++++++++++++++++- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/components/Projects/ProjectCard.tsx b/src/components/Projects/ProjectCard.tsx index efa0ac210..73dc4c2bb 100644 --- a/src/components/Projects/ProjectCard.tsx +++ b/src/components/Projects/ProjectCard.tsx @@ -5,10 +5,12 @@ import type { } from '../../lib/project.ts'; import { Users } from 'lucide-react'; import { formatCommaNumber } from '../../lib/number.ts'; +import { cn } from '../../lib/classname.ts'; type ProjectCardProps = { project: ProjectFileType; userCount?: number; + status?: 'completed' | 'started' | 'none'; }; const badgeVariants: Record = { @@ -18,10 +20,13 @@ const badgeVariants: Record = { }; export function ProjectCard(props: ProjectCardProps) { - const { project, userCount = 0 } = props; - + const { project, userCount = 0, status } = props; const { frontmatter, id } = project; + const isLoadingStatus = status === undefined; + const userStartedCount = + status && status !== 'none' ? userCount + 1 : userCount; + return ( - + {frontmatter.title} {frontmatter.description} - - - {userCount > 0 ? ( - <>{formatCommaNumber(userCount)} Started + + {isLoadingStatus ? ( + <> + {' '} + {' '} + ) : ( - <>Be the first to solve! + <> + + + {userCount > 0 ? ( + <>{formatCommaNumber(userCount)} Started + ) : ( + <>Be the first to solve! + )} + + + {status !== 'none' && ( + + + {status} + + )} + )} diff --git a/src/components/Projects/ProjectsList.tsx b/src/components/Projects/ProjectsList.tsx index aa2bb8c3e..2b5a783bf 100644 --- a/src/components/Projects/ProjectsList.tsx +++ b/src/components/Projects/ProjectsList.tsx @@ -1,7 +1,7 @@ import { ProjectCard } from './ProjectCard.tsx'; import { HeartHandshake, Trash2 } from 'lucide-react'; import { cn } from '../../lib/classname.ts'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { projectDifficulties, type ProjectDifficultyType, @@ -12,6 +12,8 @@ import { getUrlParams, setUrlParams, } from '../../lib/browser.ts'; +import { httpPost } from '../../lib/http.ts'; +import { isLoggedIn } from '../../lib/jwt.ts'; type DifficultyButtonProps = { difficulty: ProjectDifficultyType; @@ -38,6 +40,11 @@ function DifficultyButton(props: DifficultyButtonProps) { ); } +export type ListProjectStatusesResponse = Record< + string, + 'completed' | 'started' +>; + type ProjectsListProps = { projects: ProjectFileType[]; userCounts: Record; @@ -50,6 +57,25 @@ export function ProjectsList(props: ProjectsListProps) { const [difficulty, setDifficulty] = useState< ProjectDifficultyType | undefined >(urlDifficulty); + const [projectStatuses, setProjectStatuses] = + useState(); + + const loadProjectStatuses = async () => { + const projectIds = projects.map((project) => project.id); + const { response, error } = await httpPost( + `${import.meta.env.PUBLIC_API_URL}/v1-list-project-statuses`, + { + projectIds, + }, + ); + + if (error || !response) { + console.error(error); + return; + } + + setProjectStatuses(response); + }; const projectsByDifficulty: Map = useMemo(() => { @@ -72,12 +98,21 @@ export function ProjectsList(props: ProjectsListProps) { ? projectsByDifficulty.get(difficulty) || [] : projects; + useEffect(() => { + if (!isLoggedIn()) { + return; + } + + loadProjectStatuses().finally(); + }, []); + return (
{projectDifficulties.map((projectDifficulty) => ( { setDifficulty(projectDifficulty); setUrlParams({ difficulty: projectDifficulty }); @@ -130,7 +165,18 @@ export function ProjectsList(props: ProjectsListProps) { }) .map((matchingProject) => { const count = userCounts[matchingProject?.id] || 0; - return ; + return ( + + ); })}