From 47388ba5b71ce95b6e34c06cbf826aeddddf10e2 Mon Sep 17 00:00:00 2001 From: Collins Ikechukwu Date: Sat, 11 Oct 2025 09:34:08 +0100 Subject: [PATCH 1/2] refactor: update project components and API for voting functionality - Adjust layout and styling in ProjectsPage and ProjectCard components. - Enhance ProjectSidebar to include crowdfund data and voting logic. - Implement new API methods for voting, retrieving votes, and removing votes. - Update types for vote handling in the API. - Clean up unused code and improve responsiveness across components. --- app/(landing)/projects/[id]/page.tsx | 3 +- app/(landing)/projects/page.tsx | 2 +- components/Project-Page-Hero.tsx | 2 +- .../landing-page/project/ProjectCard.tsx | 18 +- components/project-details/project-layout.tsx | 20 +- .../project-details/project-sidebar.tsx | 303 +++++++++++++++--- components/projects/ExploreHeader.tsx | 2 +- lib/api/project.ts | 47 ++- lib/api/types.ts | 63 ++++ 9 files changed, 389 insertions(+), 71 deletions(-) diff --git a/app/(landing)/projects/[id]/page.tsx b/app/(landing)/projects/[id]/page.tsx index e1aee744d..26e7d5fc6 100644 --- a/app/(landing)/projects/[id]/page.tsx +++ b/app/(landing)/projects/[id]/page.tsx @@ -73,6 +73,7 @@ async function ProjectContent({ id }: { id: string }) { response.data.project, response.data.crowdfund ); + console.log(projectData); } else { throw new Error(response.message || 'Failed to fetch project'); } @@ -86,7 +87,7 @@ async function ProjectContent({ id }: { id: string }) { } return ( -
+
+
diff --git a/components/Project-Page-Hero.tsx b/components/Project-Page-Hero.tsx index 80e9bdb18..4db41aac6 100644 --- a/components/Project-Page-Hero.tsx +++ b/components/Project-Page-Hero.tsx @@ -7,7 +7,7 @@ import Link from 'next/link'; export default function ProjectPageHero() { return ( -
+
-
+
-
-
+
+
- -
+
+
{status === 'Validation' && votes && ( @@ -177,8 +177,8 @@ function ProjectCard({ className='h-2 w-full rounded-full' />
- - +
+
); } diff --git a/components/project-details/project-layout.tsx b/components/project-details/project-layout.tsx index c24747968..35d193d0c 100644 --- a/components/project-details/project-layout.tsx +++ b/components/project-details/project-layout.tsx @@ -45,7 +45,7 @@ interface ProjectLayoutProps { * Desktop: Two columns with proper spacing - sidebar left (400px), tabs+content right * Mobile: Single column - project info, tabs (including About), content */ -export function ProjectLayout({ project }: ProjectLayoutProps) { +export function ProjectLayout({ project, crowdfund }: ProjectLayoutProps) { const isMobile = useIsMobile(); const [activeTab, setActiveTab] = useState('details'); // Start with about tab on mobile const [isLeftScrollable, setIsLeftScrollable] = useState(true); @@ -117,7 +117,11 @@ export function ProjectLayout({ project }: ProjectLayoutProps) {
- +
@@ -268,12 +272,16 @@ export function ProjectLayout({ project }: ProjectLayoutProps) { return (
-
-
- +
+
+
-
+
(null); + + // Calculate vote percentage based on crowdfund data + const votePercentage = crowdfund + ? (crowdfund.totalVotes / crowdfund.thresholdVotes) * 100 + : project.voting + ? (currentVotes / project.voting.totalVotes) * 100 + : 0; + + // Calculate funding percentage + const fundingPercentage = project.funding + ? (project.funding.raised / project.funding.goal) * 100 : 0; + // Calculate milestone percentage + const milestonePercentage = project.milestones + ? (project.milestones.filter(m => m.status === 'completed').length / + project.milestones.length) * + 100 + : 0; + + // Determine project status based on data + const getProjectStatus = () => { + if (project.status === 'idea' && crowdfund?.isVotingActive) { + return 'Validation'; + } + if (project.funding?.raised >= project.funding?.goal) { + return 'Funded'; + } + if (project.status === 'idea' && !crowdfund?.isVotingActive) { + return 'Funding'; + } + return project.status; + }; + + const projectStatus = getProjectStatus(); + + const handleVote = async (value: 1 | -1) => { + if (isVoting) return; + + setIsVoting(true); + try { + const response = await voteProject(project._id, value); + setCurrentVotes(response.data.projectVotes.netVotes); + setUserVote(value); + } catch { + // Handle error silently or show user-friendly message + // You can add toast notifications here if needed + } finally { + setIsVoting(false); + } + }; + const getIcon = (iconType: string) => { switch (iconType) { case 'github': @@ -66,25 +133,111 @@ export function ProjectSidebar({ return ; } }; + const getStatusStyles = () => { - switch (project.status) { + switch (projectStatus) { case 'Funding': - return 'bg-blue-ish border-blue-ish-darker text-blue-ish-darker'; + return 'bg-blue-600 border-blue-600 text-white'; case 'Funded': return 'bg-transparent border-primary text-primary'; case 'Completed': - return 'bg-success-green border-success-green-darker text-success-green-darker'; + return 'bg-green-600 border-green-600 text-white'; case 'Validation': + return 'bg-orange-500 border-orange-500 text-white'; case 'idea': - return 'bg-warning-orange border-warning-orange-darker text-warning-orange-darker'; + return 'bg-orange-500 border-orange-500 text-white'; default: return ''; } }; + const renderProgressSection = () => { + switch (projectStatus) { + case 'Funding': + return ( +
+
+ + ${project.funding?.raised || 0}/${project.funding?.goal || 0}{' '} + USD Raised + + + {project.daysToDeadline || 0} days to deadline + +
+ +
+ ); + + case 'Validation': + return ( +
+
+ + {crowdfund?.totalVotes || 0}/{crowdfund?.thresholdVotes || 0}{' '} + Votes + + + {project.daysToDeadline || 0} days to deadline + +
+ +
+ ); + + case 'Completed': + return ( +
+
+ + {project.milestones?.filter(m => m.status === 'completed') + .length || 0} + /{project.milestones?.length || 0}{' '} + + Milestones Submitted + + + {project.milestones?.filter(m => m.status === 'rejected').length > + 0 && ( + + { + project.milestones.filter(m => m.status === 'rejected') + .length + }{' '} + milestone rejected + + )} +
+ +
+ ); + + default: + return ( +
+
+ + {crowdfund?.totalVotes || 0}/{crowdfund?.thresholdVotes || 0}{' '} + votes + + + {project.daysToDeadline || 0} days to deadline + +
+ +
+ ); + } + }; + return (
- {/* Project Header Section */}
- {/* Category and Validation badges - side by side */}
{project.category} @@ -109,11 +261,10 @@ export function ProjectSidebar({
- {project.status} + {projectStatus}
- {/* Date */}
@@ -127,61 +278,65 @@ export function ProjectSidebar({
- {/* Description */}

{project.vision}

- {/* Voting Progress */} -
-
- - {project.votes}/{project.voting?.totalVotes || 0}{' '} - votes - - - {project.daysToDeadline || 0} days to deadline - -
- -
+ {renderProgressSection()} - {/* Action Buttons - Responsive widths */}
- + {projectStatus === 'Validation' && ( + + )} - {/* Funding Button */} - {/* - + )} + + {projectStatus === 'Completed' && ( + - */} - {project.status === 'Funding' && ( - )} +