From 6facc4637b1d795d3e0cbfd448fece791712044a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 15:06:27 +0000
Subject: [PATCH 1/3] Initial plan
From e895b8bd4bb0cf35f9864564e94ad1aa9c30645d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 15:14:05 +0000
Subject: [PATCH 2/3] Add task structure to milestones with UI components
Co-authored-by: 0xdevcollins <90073781+0xdevcollins@users.noreply.github.com>
---
.../[id]/milestone/[milestoneId]/page.tsx | 8 +-
.../project-milestone/index.tsx | 17 ++
.../project-milestone/milestone-card.tsx | 23 ++
.../milestone-details/MilestoneDetails.tsx | 19 +-
.../milestone-details/TaskList.tsx | 210 ++++++++++++++++++
components/ui/timeline/TimelineItem.tsx | 1 +
components/ui/timeline/types.ts | 4 +
types/milestone.ts | 19 +-
types/project.ts | 12 +
9 files changed, 300 insertions(+), 13 deletions(-)
create mode 100644 components/project-details/project-milestone/milestone-details/TaskList.tsx
diff --git a/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx b/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
index 01590836..fc87fae4 100644
--- a/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
+++ b/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
@@ -31,14 +31,12 @@ const MilestonePage = async ({ params }: MilestonePageProps) => {
// Transform milestone to match component expectations
milestone = foundMilestone
- ? {
+ ? ({
+ ...foundMilestone,
_id: foundMilestone.id || foundMilestone.name,
title: foundMilestone.name,
- description: foundMilestone.description,
- status: foundMilestone.status,
dueDate: foundMilestone.endDate,
- amount: foundMilestone.amount,
- }
+ } as any)
: 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/types/milestone.ts b/types/milestone.ts
index cf3eec5a..2d8a2fee 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..d79eada0 100644
--- a/types/project.ts
+++ b/types/project.ts
@@ -35,6 +35,18 @@ export interface Milestone {
endDate: string;
startDate: string;
description: string;
+ deliverables?: string[]; // Legacy field for backward compatibility
+ tasks?: Array<{
+ 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 User {
From 0a36c8eb24b87ab6a558bba8bbbe42841da42312 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 15:17:40 +0000
Subject: [PATCH 3/3] Address code review feedback: remove type duplication and
fix type assertions
Co-authored-by: 0xdevcollins <90073781+0xdevcollins@users.noreply.github.com>
---
.../[id]/milestone/[milestoneId]/page.tsx | 23 +--
.../milestone-details/MilestoneLinks.tsx | 11 +-
.../milestone-details/MilstoneOverview.tsx | 11 +-
docs/TASK_STRUCTURE.md | 153 ++++++++++++++++++
types/milestone.ts | 2 +-
types/project.ts | 14 +-
6 files changed, 179 insertions(+), 35 deletions(-)
create mode 100644 docs/TASK_STRUCTURE.md
diff --git a/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx b/app/(landing)/projects/[id]/milestone/[milestoneId]/page.tsx
index fc87fae4..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,14 +42,7 @@ const MilestonePage = async ({ params }: MilestonePageProps) => {
);
// Transform milestone to match component expectations
- milestone = foundMilestone
- ? ({
- ...foundMilestone,
- _id: foundMilestone.id || foundMilestone.name,
- title: foundMilestone.name,
- dueDate: foundMilestone.endDate,
- } as any)
- : null;
+ milestone = foundMilestone ? transformMilestoneForDisplay(foundMilestone) : null;
} catch {
// Handle error silently
}
diff --git a/components/project-details/project-milestone/milestone-details/MilestoneLinks.tsx b/components/project-details/project-milestone/milestone-details/MilestoneLinks.tsx
index 3410f860..7437e812 100644
--- a/components/project-details/project-milestone/milestone-details/MilestoneLinks.tsx
+++ b/components/project-details/project-milestone/milestone-details/MilestoneLinks.tsx
@@ -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;
};
}
diff --git a/components/project-details/project-milestone/milestone-details/MilstoneOverview.tsx b/components/project-details/project-milestone/milestone-details/MilstoneOverview.tsx
index b792f4a5..01c33bd2 100644
--- a/components/project-details/project-milestone/milestone-details/MilstoneOverview.tsx
+++ b/components/project-details/project-milestone/milestone-details/MilstoneOverview.tsx
@@ -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;
};
}
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 2d8a2fee..8ccf3694 100644
--- a/types/milestone.ts
+++ b/types/milestone.ts
@@ -19,7 +19,7 @@ export interface Milestone {
endDate: string;
budget: number;
currency: string;
- deliverables: string[]; // Legacy field, kept for backward compatibility
+ deliverables?: string[]; // Legacy field, kept for backward compatibility
tasks?: Task[]; // New structured task list
demoVideo?: string;
attachments?: Array<{
diff --git a/types/project.ts b/types/project.ts
index d79eada0..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;
@@ -36,17 +38,7 @@ export interface Milestone {
startDate: string;
description: string;
deliverables?: string[]; // Legacy field for backward compatibility
- tasks?: Array<{
- id: string;
- title: string;
- description?: string;
- status: 'todo' | 'in_progress' | 'completed';
- priority?: 'low' | 'medium' | 'high';
- assignee?: string;
- dueDate?: string;
- completedAt?: string;
- order: number;
- }>;
+ tasks?: Task[];
}
export interface User {