Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import MilestoneDetails from '@/components/project-details/project-milestone/mil
import MilestoneLinks from '@/components/project-details/project-milestone/milestone-details/MilestoneLinks';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { getCrowdfundingProject } from '@/lib/api/project';
import { Crowdfunding } from '@/types/project';
import { Crowdfunding, Milestone } from '@/types/project';

interface MilestonePageProps {
params: Promise<{
Expand All @@ -13,6 +13,18 @@ interface MilestonePageProps {
}>;
}

// Helper function to transform milestone data for the component
function transformMilestoneForDisplay(
foundMilestone: Milestone
): Milestone & { _id: string; title?: string; dueDate?: string } {
return {
...foundMilestone,
_id: foundMilestone.id || foundMilestone.name,
title: foundMilestone.name,
dueDate: foundMilestone.endDate,
};
}

const MilestonePage = async ({ params }: MilestonePageProps) => {
const { id: projectId, milestoneId } = await params;

Expand All @@ -30,16 +42,7 @@ const MilestonePage = async ({ params }: MilestonePageProps) => {
);

// Transform milestone to match component expectations
milestone = foundMilestone
? {
_id: foundMilestone.id || foundMilestone.name,
title: foundMilestone.name,
description: foundMilestone.description,
status: foundMilestone.status,
dueDate: foundMilestone.endDate,
amount: foundMilestone.amount,
}
: null;
milestone = foundMilestone ? transformMilestoneForDisplay(foundMilestone) : null;
} catch {
// Handle error silently
}
Expand Down
17 changes: 17 additions & 0 deletions components/project-details/project-milestone/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ const ProjectMilestone = ({ crowdfund }: ProjectMilestoneProps) => {

const mappedStatus = mapStatus(milestone.status);

// Calculate task progress
const taskProgress =
milestone.tasks && milestone.tasks.length > 0
? {
completed: milestone.tasks.filter(
task => task.status === 'completed'
).length,
total: milestone.tasks.length,
}
: milestone.deliverables && milestone.deliverables.length > 0
? {
completed: 0, // Legacy deliverables don't have status
total: milestone.deliverables.length,
}
: undefined;

return {
id: milestone.name,
title: milestone.name,
Expand All @@ -99,6 +115,7 @@ const ProjectMilestone = ({ crowdfund }: ProjectMilestoneProps) => {
amount: milestone.amount,
percentage,
status: mappedStatus,
taskProgress,
...(milestone.status === 'in-review' && { feedbackDays: 3 }),
...(milestone.status === 'rejected' && { deadline: dueDate }),
...(milestone.status === 'approved' && { isUnlocked: true }),
Expand Down
23 changes: 23 additions & 0 deletions components/project-details/project-milestone/milestone-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface MilestoneCardProps {
isUnlocked?: boolean;
onClick?: () => void;
isClickable?: boolean;
taskProgress?: {
completed: number;
total: number;
};
}

//status badge colors
Expand Down Expand Up @@ -60,6 +64,7 @@ const MilestoneCard = ({
feedbackDays,
onClick,
isClickable = false,
taskProgress,
}: MilestoneCardProps) => {
// Generate dynamic header text based on status
const getHeaderText = () => {
Expand Down Expand Up @@ -126,6 +131,24 @@ const MilestoneCard = ({
</Badge>
</div>
<p className='text-sm leading-relaxed text-gray-400'>{description}</p>

{/* Task Progress */}
{taskProgress && taskProgress.total > 0 && (
<div className='flex w-full items-center gap-2'>
<div className='h-1.5 flex-1 overflow-hidden rounded-full bg-gray-800'>
<div
className='h-full bg-green-500 transition-all duration-300'
style={{
width: `${(taskProgress.completed / taskProgress.total) * 100}%`,
}}
/>
</div>
<span className='text-xs text-gray-500'>
{taskProgress.completed}/{taskProgress.total} tasks
</span>
</div>
)}

<div className='flex items-center justify-start gap-2 text-gray-400'>
<span>
<svg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
import React from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { useMarkdown } from '@/hooks/use-markdown';
import { CrowdfundingProject } from '@/types/project';
import { CrowdfundingProject, Milestone } from '@/types/project';
import TaskList from './TaskList';

interface MilestoneDetailsProps {
milestoneId: string;
project?: CrowdfundingProject | null;
milestone?: {
milestone?: Milestone & {
_id: string;
title: string;
description: string;
status: string;
dueDate: string;
amount: number;
title?: string;
dueDate?: string;
};
}

Expand Down Expand Up @@ -66,6 +64,13 @@ const MilestoneDetails = ({
)}
</div>

{/* Tasks & Deliverables */}
{milestone && (milestone.tasks || milestone.deliverables) && (
<section>
<TaskList tasks={milestone.tasks} deliverables={milestone.deliverables} />
</section>
)}

{/* Video Media Showcase */}
{project?.demoVideo && (
<section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ import React from 'react';
import Link from 'next/link';
import { Github } from 'lucide-react';
import Image from 'next/image';
import { CrowdfundingProject } from '@/types/project';
import { CrowdfundingProject, Milestone } from '@/types/project';

interface MilestoneLinksProps {
project?: CrowdfundingProject | null;
milestone?: {
milestone?: Milestone & {
_id: string;
title: string;
description: string;
status: string;
dueDate: string;
amount: number;
title?: string;
dueDate?: string;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@ import { Separator } from '@/components/ui/separator';
import Link from 'next/link';
import { Github } from 'lucide-react';
import Image from 'next/image';
import { CrowdfundingProject } from '@/types/project';
import { CrowdfundingProject, Milestone } from '@/types/project';

interface MilstoneOverviewProps {
project?: CrowdfundingProject | null;
milestone?: {
milestone?: Milestone & {
_id: string;
title: string;
description: string;
status: string;
dueDate: string;
amount: number;
title?: string;
dueDate?: string;
};
}

Expand Down
Loading