diff --git a/components/proposal/MilestoneBreakdown.tsx b/components/proposal/MilestoneBreakdown.tsx new file mode 100644 index 0000000..cb4e095 --- /dev/null +++ b/components/proposal/MilestoneBreakdown.tsx @@ -0,0 +1,174 @@ +import React, { useState } from 'react'; +import { Plus, Trash2, ChevronDown, ChevronUp } from 'lucide-react'; + +interface Milestone { + id: string; + title: string; + description: string; + amount: number; + deadline: string; +} + +interface MilestoneBreakdownProps { + milestones: Milestone[]; + onChange: (milestones: Milestone[]) => void; + error?: string; +} + +const initialMilestone = (): Milestone => ({ + id: crypto.randomUUID(), + title: '', + description: '', + amount: 0, + deadline: '', +}); + +export const MilestoneBreakdown: React.FC = ({ + milestones, + onChange, + error, +}) => { + const [isExpanded, setIsExpanded] = useState(true); + + const addMilestone = () => { + onChange([...milestones, initialMilestone()]); + }; + + const removeMilestone = (id: string) => { + onChange(milestones.filter((m) => m.id !== id)); + }; + + const updateMilestone = (id: string, field: keyof Milestone, value: any) => { + onChange( + milestones.map((m) => (m.id === id ? { ...m, [field]: value } : m)) + ); + }; + + const totalAmount = milestones.reduce((sum, m) => sum + (m.amount || 0), 0); + + if (!isExpanded) { + return ( +
+ +
+ ); + } + + return ( +
+
+

+ Milestone Breakdown (Optional) +

+ +
+ + {error && ( +

{error}

+ )} + +
+ {milestones.map((milestone, index) => ( +
+ + +
+
+ + updateMilestone(milestone.id, 'title', e.target.value)} + placeholder="e.g., Initial Design" + className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm" + /> +
+ +
+ + updateMilestone(milestone.id, 'amount', parseFloat(e.target.value) || 0)} + placeholder="0.00" + className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm" + /> +
+ +
+ +