diff --git a/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx b/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
index 01590836..aa4d9151 100644
--- a/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
+++ b/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
@@ -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<{
@@ -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;
@@ -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
}
diff --git a/components/project-details/project-milestone/index.tsx b/components/project-details/project-milestone/index.tsx
index e9ca5189..6631cae9 100644
--- a/components/project-details/project-milestone/index.tsx
+++ b/components/project-details/project-milestone/index.tsx
@@ -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,
@@ -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 }),
diff --git a/components/project-details/project-milestone/milestone-card.tsx b/components/project-details/project-milestone/milestone-card.tsx
index 6cbf2c07..00d92ec0 100644
--- a/components/project-details/project-milestone/milestone-card.tsx
+++ b/components/project-details/project-milestone/milestone-card.tsx
@@ -24,6 +24,10 @@ interface MilestoneCardProps {
isUnlocked?: boolean;
onClick?: () => void;
isClickable?: boolean;
+ taskProgress?: {
+ completed: number;
+ total: number;
+ };
}
//status badge colors
@@ -60,6 +64,7 @@ const MilestoneCard = ({
feedbackDays,
onClick,
isClickable = false,
+ taskProgress,
}: MilestoneCardProps) => {
// Generate dynamic header text based on status
const getHeaderText = () => {
@@ -126,6 +131,24 @@ const MilestoneCard = ({
{description}
+
+ {/* Task Progress */}
+ {taskProgress && taskProgress.total > 0 && (
+
+
+
+ {taskProgress.completed}/{taskProgress.total} tasks
+
+
+ )}
+
diff --git a/components/ui/timeline/types.ts b/components/ui/timeline/types.ts
index 39d2a6e4..023ee9ad 100644
--- a/components/ui/timeline/types.ts
+++ b/components/ui/timeline/types.ts
@@ -20,6 +20,10 @@ export interface TimelineItem {
deadline?: string;
feedbackDays?: number;
isUnlocked?: boolean;
+ taskProgress?: {
+ completed: number;
+ total: number;
+ };
}
export interface TimelineProps {
diff --git a/docs/TASK_STRUCTURE.md b/docs/TASK_STRUCTURE.md
new file mode 100644
index 00000000..518262cc
--- /dev/null
+++ b/docs/TASK_STRUCTURE.md
@@ -0,0 +1,153 @@
+# Task Structure Improvement
+
+This document describes the improvements made to the milestone task structure in the Boundless platform.
+
+## Overview
+
+Previously, milestones only had a simple `deliverables: string[]` field which was too simplistic for proper project management. This update adds a comprehensive task tracking system to milestones.
+
+## New Task Type
+
+```typescript
+export interface Task {
+ id: string;
+ title: string;
+ description?: string;
+ status: 'todo' | 'in_progress' | 'completed';
+ priority?: 'low' | 'medium' | 'high';
+ assignee?: string;
+ dueDate?: string;
+ completedAt?: string;
+ order: number;
+}
+```
+
+## Updated Milestone Interface
+
+The Milestone interface now includes:
+- `tasks?: Task[]` - New structured task list with full metadata
+- `deliverables: string[]` - Legacy field kept for backward compatibility
+
+## UI Components
+
+### TaskList Component
+
+A new `TaskList` component displays tasks with:
+- Progress bar showing completion percentage
+- Task status indicators (todo, in progress, completed)
+- Priority badges (low, medium, high)
+- Assignee information
+- Due dates and completion dates
+
+### Milestone Card Enhancements
+
+Milestone cards now show:
+- Task progress bar when tasks are present
+- "X/Y tasks" completion count
+- Visual progress indicator
+
+## Example Usage
+
+### Backend API Response
+
+```json
+{
+ "milestones": [
+ {
+ "id": "milestone-1",
+ "name": "Initial Development",
+ "description": "Set up the basic infrastructure",
+ "status": "in-progress",
+ "amount": 10000,
+ "startDate": "2024-01-01",
+ "endDate": "2024-02-01",
+ "tasks": [
+ {
+ "id": "task-1",
+ "title": "Set up repository",
+ "description": "Create GitHub repo and initial structure",
+ "status": "completed",
+ "priority": "high",
+ "assignee": "john@example.com",
+ "dueDate": "2024-01-05",
+ "completedAt": "2024-01-04",
+ "order": 1
+ },
+ {
+ "id": "task-2",
+ "title": "Configure CI/CD pipeline",
+ "status": "in_progress",
+ "priority": "high",
+ "assignee": "jane@example.com",
+ "dueDate": "2024-01-10",
+ "order": 2
+ },
+ {
+ "id": "task-3",
+ "title": "Write documentation",
+ "status": "todo",
+ "priority": "medium",
+ "dueDate": "2024-01-15",
+ "order": 3
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Backward Compatibility
+
+The system maintains backward compatibility with existing milestones that use the `deliverables` field:
+
+- If a milestone has `tasks`, they will be displayed with full functionality
+- If a milestone only has `deliverables`, they will be displayed as simple task items
+- Both fields can coexist during the migration period
+
+## Benefits
+
+1. **Better Project Tracking**: Track individual tasks with status, priority, and assignment
+2. **Visual Progress**: See at-a-glance task completion in milestone cards
+3. **Accountability**: Assign tasks to specific team members
+4. **Prioritization**: Mark tasks with priority levels for better planning
+5. **Timeline Management**: Set and track due dates for individual tasks
+6. **Historical Data**: Track when tasks were completed
+
+## Migration Path
+
+For existing milestones with deliverables:
+
+```typescript
+// Old format
+{
+ deliverables: [
+ "Complete user authentication",
+ "Build dashboard UI",
+ "Deploy to production"
+ ]
+}
+
+// Can be migrated to new format
+{
+ tasks: [
+ {
+ id: "1",
+ title: "Complete user authentication",
+ status: "completed",
+ order: 1
+ },
+ {
+ id: "2",
+ title: "Build dashboard UI",
+ status: "in_progress",
+ order: 2
+ },
+ {
+ id: "3",
+ title: "Deploy to production",
+ status: "todo",
+ order: 3
+ }
+ ]
+}
+```
diff --git a/types/milestone.ts b/types/milestone.ts
index cf3eec5a..8ccf3694 100644
--- a/types/milestone.ts
+++ b/types/milestone.ts
@@ -1,3 +1,15 @@
+export interface Task {
+ id: string;
+ title: string;
+ description?: string;
+ status: 'todo' | 'in_progress' | 'completed';
+ priority?: 'low' | 'medium' | 'high';
+ assignee?: string;
+ dueDate?: string;
+ completedAt?: string;
+ order: number;
+}
+
export interface Milestone {
id: string;
title: string;
@@ -7,7 +19,8 @@ export interface Milestone {
endDate: string;
budget: number;
currency: string;
- deliverables: string[];
+ deliverables?: string[]; // Legacy field, kept for backward compatibility
+ tasks?: Task[]; // New structured task list
demoVideo?: string;
attachments?: Array<{
name: string;
@@ -29,3 +42,7 @@ export type MilestoneStatus =
| 'in_progress'
| 'completed'
| 'cancelled';
+
+export type TaskStatus = 'todo' | 'in_progress' | 'completed';
+
+export type TaskPriority = 'low' | 'medium' | 'high';
diff --git a/types/project.ts b/types/project.ts
index aed298fb..71e24cf7 100644
--- a/types/project.ts
+++ b/types/project.ts
@@ -1,4 +1,6 @@
// Crowdfunding types
+import { Task } from './milestone';
+
export interface Contributor {
date: string;
amount: number;
@@ -35,6 +37,8 @@ export interface Milestone {
endDate: string;
startDate: string;
description: string;
+ deliverables?: string[]; // Legacy field for backward compatibility
+ tasks?: Task[];
}
export interface User {