@@ -9,9 +9,14 @@ import {
99import { MilestoneDetailHeader } from '@/components/crowdfunding/milestone-detail-header' ;
1010import { MilestoneDetailInfo } from '@/components/crowdfunding/milestone-detail-info' ;
1111import { MilestoneDetailDescription } from '@/components/crowdfunding/milestone-detail-description' ;
12- import { MilestoneDetailLinks } from '@/components/crowdfunding/milestone-detail-links' ;
1312import { MilestoneSubmitForm } from '@/components/crowdfunding/MilestoneSubmitForm' ;
14- import { AlertTriangle , CheckCircle2 } from 'lucide-react' ;
13+ import {
14+ AlertTriangle ,
15+ CheckCircle2 ,
16+ Clock ,
17+ ExternalLink ,
18+ FileText ,
19+ } from 'lucide-react' ;
1520import { toast } from 'sonner' ;
1621
1722interface PageProps {
@@ -23,8 +28,6 @@ export default function MilestoneDetailPage({ params }: PageProps) {
2328
2429 const { data : campaign , isLoading, error } = useCampaign ( slug ) ;
2530
26- // Milestone cards link by milestone id; fall back to array index for any
27- // legacy numeric links.
2831 const milestones = campaign ?. milestones ?? [ ] ;
2932 let milestone = milestones . find ( m => m . id === milestoneParam ) ?? null ;
3033 let milestoneIndex = milestones . findIndex ( m => m . id === milestoneParam ) ;
@@ -85,63 +88,154 @@ export default function MilestoneDetailPage({ params }: PageProps) {
8588 } ;
8689
8790 if ( isLoading ) {
88- return (
89- < div className = 'text-muted-foreground py-12 text-center' > Loading...</ div >
90- ) ;
91+ return < div className = 'py-12 text-center text-zinc-500' > Loading...</ div > ;
9192 }
9293
9394 if ( error || ! campaign || ! milestone ) {
9495 return (
95- < div className = 'text-muted-foreground py-12 text-center' >
96- Milestone not found
96+ < div className = 'py-12 text-center text-zinc-500 ' >
97+ Milestone not found.
9798 </ div >
9899 ) ;
99100 }
100101
101102 const reviewStatus = milestone . reviewStatus ?. toLowerCase ( ) ;
102103 const canSubmit =
103104 reviewStatus === 'pending' || reviewStatus === 'resubmission_required' ;
105+ const isInReview = reviewStatus === 'submitted' ;
104106 const isRejected =
105107 reviewStatus === 'rejected' || reviewStatus === 'resubmission_required' ;
106108 const isApproved = reviewStatus === 'approved' ;
107109
110+ const milestoneNumber = milestoneIndex + 1 ;
111+ const totalMilestones = milestones . length ;
112+
113+ const submittedLinks = milestone . proofOfWorkLinks ?? [ ] ;
114+ const submittedFiles = milestone . proofOfWorkFiles ?? [ ] ;
115+ const submittedNotes = milestone . submissionNotes ?? '' ;
116+
108117 return (
109- < div className = 'mx-auto max-w-4xl space-y-8 ' >
118+ < div className = 'mx-auto max-w-4xl space-y-6 ' >
110119 < MilestoneDetailHeader
111- title = { campaign . project . title }
112120 milestone = { milestone }
113- campaignSlug = { campaign . slug }
121+ milestoneNumber = { milestoneNumber }
122+ totalMilestones = { totalMilestones }
123+ campaignTitle = { campaign . project . title }
114124 backLink = { `/me/crowdfunding/${ campaign . slug } /milestones` }
115125 />
116126
117127 < MilestoneDetailInfo milestone = { milestone } campaign = { campaign } />
118128
119129 < MilestoneDetailDescription
120130 content = { milestone . description || '' }
121- title = 'Description '
131+ title = 'What needs to be delivered '
122132 />
123133
124- < MilestoneDetailLinks campaign = { campaign } />
125-
126- { /* Review feedback */ }
134+ { /* Rejection feedback */ }
127135 { isRejected && (
128- < div className = 'flex items-start gap -3 rounded-xl border border-amber-800/50 bg-amber-950/20 p-4 ' >
129- < AlertTriangle className = 'mt-0.5 h-5 w-5 flex-shrink-0 text-amber-400' / >
130- < div >
131- < p className = 'text-sm font-medium text-amber-300' >
136+ < div className = 'space-y -3 rounded-xl border border-amber-800/50 bg-amber-950/20 p-5 ' >
137+ < div className = 'flex items-center gap-2' >
138+ < AlertTriangle className = 'h-4 w-4 flex-shrink-0 text-amber-400' / >
139+ < p className = 'text-sm font-semibold text-amber-300' >
132140 { reviewStatus === 'resubmission_required'
133141 ? 'Resubmission required'
134142 : 'Submission not accepted' }
135143 </ p >
136- < p className = 'mt-1 text-sm text-amber-200/70' >
137- The review team has requested changes to your submission. Update
138- your evidence below and resubmit.
144+ </ div >
145+ { milestone . rejectionFeedback ? (
146+ < p className = 'text-sm leading-relaxed text-amber-200/80' >
147+ { milestone . rejectionFeedback }
148+ </ p >
149+ ) : (
150+ < p className = 'text-sm text-amber-200/60' >
151+ The review team has requested changes. Update your evidence below
152+ and resubmit.
153+ </ p >
154+ ) }
155+ { milestone . resubmissionDeadline && (
156+ < p className = 'text-xs text-amber-400/70' >
157+ Resubmit by:{ ' ' }
158+ { new Date ( milestone . resubmissionDeadline ) . toLocaleDateString (
159+ 'en-US' ,
160+ { month : 'long' , day : 'numeric' , year : 'numeric' }
161+ ) }
162+ </ p >
163+ ) }
164+ </ div >
165+ ) }
166+
167+ { /* In-review: show what was submitted */ }
168+ { isInReview && (
169+ < div className = 'space-y-4 rounded-xl border border-blue-800/40 bg-blue-950/15 p-5' >
170+ < div className = 'flex items-center gap-2' >
171+ < Clock className = 'h-4 w-4 text-blue-400' />
172+ < p className = 'text-sm font-semibold text-blue-300' >
173+ Under review — submission received
139174 </ p >
140175 </ div >
176+ < p className = 'text-xs text-blue-200/60' >
177+ The Boundless team is reviewing your evidence. You will be notified
178+ of the outcome.
179+ </ p >
180+
181+ { submittedNotes && (
182+ < div className = 'space-y-1' >
183+ < p className = 'text-xs font-medium text-zinc-400' >
184+ Your progress notes
185+ </ p >
186+ < p className = 'rounded-lg bg-zinc-900/60 p-3 text-sm text-zinc-300' >
187+ { submittedNotes }
188+ </ p >
189+ </ div >
190+ ) }
191+
192+ { submittedLinks . length > 0 && (
193+ < div className = 'space-y-1' >
194+ < p className = 'text-xs font-medium text-zinc-400' >
195+ Proof of work links
196+ </ p >
197+ < div className = 'space-y-1.5' >
198+ { submittedLinks . map ( ( link , i ) => (
199+ < a
200+ key = { i }
201+ href = { link }
202+ target = '_blank'
203+ rel = 'noreferrer'
204+ className = 'flex items-center gap-2 text-sm text-blue-400 hover:text-blue-300'
205+ >
206+ < ExternalLink className = 'h-3.5 w-3.5 flex-shrink-0' />
207+ < span className = 'truncate' > { link } </ span >
208+ </ a >
209+ ) ) }
210+ </ div >
211+ </ div >
212+ ) }
213+
214+ { submittedFiles . length > 0 && (
215+ < div className = 'space-y-1' >
216+ < p className = 'text-xs font-medium text-zinc-400' >
217+ Supporting files
218+ </ p >
219+ < div className = 'space-y-1.5' >
220+ { submittedFiles . map ( ( url , i ) => (
221+ < a
222+ key = { i }
223+ href = { url }
224+ target = '_blank'
225+ rel = 'noreferrer'
226+ className = 'flex items-center gap-2 text-sm text-blue-400 hover:text-blue-300'
227+ >
228+ < FileText className = 'h-3.5 w-3.5 flex-shrink-0' />
229+ < span className = 'truncate' > File { i + 1 } </ span >
230+ </ a >
231+ ) ) }
232+ </ div >
233+ </ div >
234+ ) }
141235 </ div >
142236 ) }
143237
144- { /* Inline evidence submission */ }
238+ { /* Evidence submission form */ }
145239 { canSubmit && (
146240 < MilestoneSubmitForm
147241 milestoneName = { milestone . title || milestone . name }
@@ -150,7 +244,7 @@ export default function MilestoneDetailPage({ params }: PageProps) {
150244 />
151245 ) }
152246
153- { /* Approved: payout is released by the Boundless team (no builder action) */ }
247+ { /* Approved: payout released by Boundless team */ }
154248 { isApproved && (
155249 < div className = 'rounded-xl border border-emerald-800/50 bg-emerald-950/20 p-6' >
156250 < div className = 'flex items-start gap-3' >
@@ -160,11 +254,11 @@ export default function MilestoneDetailPage({ params }: PageProps) {
160254 Milestone approved
161255 </ h3 >
162256 < p className = 'mt-1 text-sm text-emerald-200/70' >
163- This milestone is approved . The Boundless team releases the
164- payout to your wallet. No action is needed on your part.
257+ Approved . The Boundless team will release the payout to your
258+ wallet. No action needed on your part.
165259 </ p >
166260 { milestone . amount != null && (
167- < p className = 'mt-2 text-lg font-bold text-white' >
261+ < p className = 'mt-3 text-xl font-bold text-white' >
168262 ${ milestone . amount . toLocaleString ( ) } USDC
169263 </ p >
170264 ) }
0 commit comments