1- import { LayoutGrid } from "lucide-react" ;
2- import { Campaign } from "../types/campaign" ;
3- import { EmptyState } from "./EmptyState" ;
1+
42
53interface CampaignsTableProps {
64 campaigns : Campaign [ ] ;
@@ -19,14 +17,47 @@ export function CampaignsTable({
1917 onSelect,
2018 isLoading,
2119} : CampaignsTableProps ) {
22- if ( isLoading ) {
20+
21+
22+ if ( campaigns . length === 0 ) {
2323 return (
24- < section className = "card" >
25- < div className = "section-heading" >
26- < h2 > Campaign board</ h2 >
27- < p className = "muted" > Loading campaigns...</ p >
28- </ div >
24+ < EmptyState
25+ variant = "card"
26+ icon = { LayoutGrid }
27+ title = "Campaign board"
28+ message = "No campaigns yet. Create the first vault to make this board active."
29+ />
30+ ) ;
31+ }
32+
33+ return (
34+ < section className = "card" >
35+ < div className = "section-heading" >
36+ < h2 > Campaign board</ h2 >
37+ { isEmpty ? (
38+ < p className = "muted" >
39+ No campaigns yet. Create the first vault to make this board active.
40+ </ p >
41+ ) : (
42+ < p className = "muted" >
43+ Monitor progress and open one campaign at a time in the action
44+ panel.
45+ </ p >
46+ ) }
47+ </ div >
48+
49+ < div className = "board-controls" >
50+ < AssetFilterDropdown
51+ options = { distinctAssetCodes }
52+ value = { selectedAssetCode }
53+ onChange = { setSelectedAssetCode }
54+ disabled = { isEmpty }
55+ />
56+ </ div >
2957
58+ { ! isEmpty && filteredCampaigns . length === 0 ? (
59+ < p className = "muted" > No campaigns match the current filters.</ p >
60+ ) : (
3061 < div className = "table-wrap" >
3162 < table >
3263 < thead >
@@ -40,116 +71,61 @@ export function CampaignsTable({
4071 </ tr >
4172 </ thead >
4273 < tbody >
43- { Array . from ( { length : 4 } ) . map ( ( _ , idx ) => (
44- < tr key = { idx } >
74+ { filteredCampaigns . map ( ( campaign ) => (
75+ < tr key = { campaign . id } >
4576 < td >
4677 < div className = "stacked" >
47- < div className = "skeleton skeleton-line" style = { { width : 160 } } / >
48- < div className = "skeleton skeleton-line" style = { { width : 80 , height : 12 } } / >
78+ < strong > { campaign . title } </ strong >
79+ < span className = "muted" > # { campaign . id } </ span >
4980 </ div >
5081 </ td >
82+ < td className = "mono" > { campaign . creator . slice ( 0 , 8 ) } ...</ td >
5183 < td >
52- < div className = "skeleton skeleton-line" style = { { width : 100 } } / >
53- </ td >
54- < td >
55- < div className = "skeleton skeleton-line" style = { { width : 140 } } / >
84+ < div className = "progress-copy" >
85+ { campaign . pledgedAmount } / { campaign . targetAmount } { " " }
86+ { campaign . assetCode }
87+ </ div >
5688 < div className = "progress-bar" aria-hidden >
57- < div style = { { width : `20%` } } />
89+ < div
90+ style = { {
91+ width : `${ Math . min ( campaign . progress . percentFunded , 100 ) } %` ,
92+ } }
93+ />
5894 </ div >
95+ < span className = "muted" >
96+ { campaign . progress . percentFunded } % funded
97+ </ span >
5998 </ td >
6099 < td >
61- < div className = "skeleton skeleton-line" style = { { width : 80 } } />
100+ < span className = { `badge badge-${ campaign . progress . status } ` } >
101+ { campaign . progress . status }
102+ </ span >
62103 </ td >
63- < td >
64- < div className = "skeleton skeleton-line" style = { { width : 120 } } />
65- < div className = "skeleton skeleton-line" style = { { width : 80 , height : 12 } } />
104+ < td className = "stacked" >
105+ < span > { formatTimestamp ( campaign . deadline ) } </ span >
106+ < span className = "muted" >
107+ { campaign . progress . hoursLeft } h left
108+ </ span >
66109 </ td >
67110 < td >
68- < div className = "skeleton skeleton-line" style = { { width : 64 , height : 36 } } />
111+ < button
112+ className = {
113+ selectedCampaignId === campaign . id
114+ ? "btn-secondary"
115+ : "btn-ghost"
116+ }
117+ type = "button"
118+ onClick = { ( ) => onSelect ( campaign . id ) }
119+ >
120+ { selectedCampaignId === campaign . id ? "Selected" : "View" }
121+ </ button >
69122 </ td >
70123 </ tr >
71124 ) ) }
72125 </ tbody >
73126 </ table >
74127 </ div >
75- </ section >
76- ) ;
77- }
78-
79- if ( campaigns . length === 0 ) {
80- return (
81- < EmptyState
82- variant = "card"
83- icon = { LayoutGrid }
84- title = "Campaign board"
85- message = "No campaigns yet. Create the first vault to make this board active."
86- />
87- ) ;
88- }
89-
90- return (
91- < section className = "card" >
92- < div className = "section-heading" >
93- < h2 > Campaign board</ h2 >
94- < p className = "muted" > Monitor progress and open one campaign at a time in the action panel.</ p >
95- </ div >
96-
97- < div className = "table-wrap" >
98- < table >
99- < thead >
100- < tr >
101- < th > Campaign</ th >
102- < th > Creator</ th >
103- < th > Funding</ th >
104- < th > Status</ th >
105- < th > Deadline</ th >
106- < th />
107- </ tr >
108- </ thead >
109- < tbody >
110- { campaigns . map ( ( campaign ) => (
111- < tr key = { campaign . id } >
112- < td >
113- < div className = "stacked" >
114- < strong > { campaign . title } </ strong >
115- < span className = "muted" > #{ campaign . id } </ span >
116- </ div >
117- </ td >
118- < td className = "mono" > { campaign . creator . slice ( 0 , 8 ) } ...</ td >
119- < td >
120- < div className = "progress-copy" >
121- { campaign . pledgedAmount } / { campaign . targetAmount } { campaign . assetCode }
122- </ div >
123- < div className = "progress-bar" aria-hidden >
124- < div style = { { width : `${ Math . min ( campaign . progress . percentFunded , 100 ) } %` } } />
125- </ div >
126- < span className = "muted" > { campaign . progress . percentFunded } % funded</ span >
127- </ td >
128- < td >
129- < span className = { `badge badge-${ campaign . progress . status } ` } >
130- { campaign . progress . status }
131- </ span >
132- </ td >
133- < td className = "stacked" >
134- < span > { formatTimestamp ( campaign . deadline ) } </ span >
135- < span className = "muted" > { campaign . progress . hoursLeft } h left</ span >
136- </ td >
137- < td >
138- < button
139- className = {
140- selectedCampaignId === campaign . id ? "btn-secondary" : "btn-ghost"
141- }
142- type = "button"
143- onClick = { ( ) => onSelect ( campaign . id ) }
144- >
145- { selectedCampaignId === campaign . id ? "Selected" : "View" }
146- </ button >
147- </ td >
148- </ tr >
149- ) ) }
150- </ tbody >
151- </ table >
152- </ div >
128+ ) }
153129 </ section >
154130 ) ;
155131}
0 commit comments