@@ -8,7 +8,7 @@ import { Link } from "@pythnetwork/component-library/Link";
8
8
import { Table } from "@pythnetwork/component-library/Table" ;
9
9
import { lookup } from "@pythnetwork/known-publishers" ;
10
10
import { notFound } from "next/navigation" ;
11
- import type { ReactNode } from "react" ;
11
+ import type { ReactNode , ComponentProps } from "react" ;
12
12
13
13
import { getPriceFeeds } from "./get-price-feeds" ;
14
14
import styles from "./performance.module.scss" ;
@@ -119,101 +119,154 @@ export const Performance = async ({ params }: Props) => {
119
119
return rows === undefined ? (
120
120
notFound ( )
121
121
) : (
122
- < div className = { styles . performance } >
123
- < Card
124
- icon = { < Broadcast /> }
125
- title = "Publishers Ranking"
126
- className = { styles . publishersRankingCard ?? "" }
127
- >
128
- < EntityList
129
- label = "Publishers Ranking"
130
- className = { styles . publishersRankingList ?? "" }
131
- fields = { [
132
- { id : "ranking" , name : "Ranking" } ,
133
- { id : "averageScore" , name : "Average Score" } ,
134
- { id : "activeFeeds" , name : "Active Feeds" } ,
135
- { id : "inactiveFeeds" , name : "Inactive Feeds" } ,
136
- ] }
137
- rows = { rows . map ( ( row ) => ( {
138
- ...row ,
139
- textValue : row . nameAsString ,
140
- header : row . data . name ,
141
- } ) ) }
142
- />
143
- < Table
144
- rounded
145
- fill
146
- className = { styles . publishersRankingTable ?? "" }
147
- label = "Publishers Ranking"
148
- columns = { [
149
- {
150
- id : "ranking" ,
151
- name : "RANKING" ,
152
- width : 25 ,
153
- } ,
154
- {
155
- id : "name" ,
156
- name : "NAME / ID" ,
157
- isRowHeader : true ,
158
- alignment : "left" ,
159
- } ,
160
- {
161
- id : "activeFeeds" ,
162
- name : (
163
- < >
164
- ACTIVE FEEDS
165
- < ExplainActive />
166
- </ >
167
- ) ,
168
- alignment : "center" ,
169
- width : 30 ,
170
- } ,
171
- {
172
- id : "inactiveFeeds" ,
173
- name : (
174
- < >
175
- INACTIVE FEEDS
176
- < ExplainInactive />
177
- </ >
178
- ) ,
179
- alignment : "center" ,
180
- width : 30 ,
181
- } ,
182
- {
183
- id : "averageScore" ,
184
- name : (
185
- < >
186
- AVERAGE SCORE
187
- < ExplainAverage scoreTime = { publishers [ 0 ] ?. scoreTime } />
188
- </ >
189
- ) ,
190
- alignment : "right" ,
191
- width : PUBLISHER_SCORE_WIDTH ,
192
- } ,
193
- ] }
194
- rows = { rows }
195
- />
196
- </ Card >
197
- < TopFeedsCard
198
- title = "High-Performing"
199
- emptyIcon = { < SmileySad /> }
200
- emptyHeader = "Oh no!"
201
- emptyBody = "This publisher has no high performing feeds"
202
- emptyVariant = "error"
203
- feeds = { highPerformingFeeds }
204
- />
205
- < TopFeedsCard
206
- title = "Low-Performing"
207
- emptyIcon = { < Confetti /> }
208
- emptyHeader = "Looking good!"
209
- emptyBody = "This publisher has no low performing feeds"
210
- emptyVariant = "success"
211
- feeds = { lowPerformingFeeds }
212
- />
213
- </ div >
122
+ < PerformanceImpl
123
+ publishers = { rows }
124
+ highPerformingFeeds = { highPerformingFeeds }
125
+ lowPerformingFeeds = { lowPerformingFeeds }
126
+ averageScoreTime = { publishers [ 0 ] ?. scoreTime }
127
+ />
214
128
) ;
215
129
} ;
216
130
131
+ export const PerformanceLoading = ( ) => < PerformanceImpl isLoading /> ;
132
+
133
+ type PerformanceImplProps =
134
+ | { isLoading : true }
135
+ | {
136
+ isLoading ?: false ;
137
+ publishers : ( NonNullable <
138
+ ComponentProps <
139
+ typeof Table <
140
+ | "ranking"
141
+ | "averageScore"
142
+ | "activeFeeds"
143
+ | "inactiveFeeds"
144
+ | "name"
145
+ >
146
+ > [ "rows" ]
147
+ > [ number ] & {
148
+ nameAsString : string ;
149
+ } ) [ ] ;
150
+ highPerformingFeeds : ReturnType < typeof getFeedRows > ;
151
+ lowPerformingFeeds : ReturnType < typeof getFeedRows > ;
152
+ averageScoreTime ?: Date | undefined ;
153
+ } ;
154
+
155
+ const PerformanceImpl = ( props : PerformanceImplProps ) => (
156
+ < div className = { styles . performance } >
157
+ < Card
158
+ icon = { < Broadcast /> }
159
+ title = "Publishers Ranking"
160
+ className = { styles . publishersRankingCard ?? "" }
161
+ >
162
+ < EntityList
163
+ label = "Publishers Ranking"
164
+ className = { styles . publishersRankingList ?? "" }
165
+ fields = { [
166
+ { id : "ranking" , name : "Ranking" } ,
167
+ { id : "averageScore" , name : "Average Score" } ,
168
+ { id : "activeFeeds" , name : "Active Feeds" } ,
169
+ { id : "inactiveFeeds" , name : "Inactive Feeds" } ,
170
+ ] }
171
+ { ...( props . isLoading
172
+ ? { isLoading : true }
173
+ : {
174
+ rows : props . publishers . map ( ( publisher ) => ( {
175
+ ...publisher ,
176
+ textValue : publisher . nameAsString ,
177
+ header : publisher . data . name ,
178
+ } ) ) ,
179
+ } ) }
180
+ />
181
+ < Table
182
+ rounded
183
+ fill
184
+ className = { styles . publishersRankingTable ?? "" }
185
+ label = "Publishers Ranking"
186
+ columns = { [
187
+ {
188
+ id : "ranking" ,
189
+ name : "RANKING" ,
190
+ width : 25 ,
191
+ } ,
192
+ {
193
+ id : "name" ,
194
+ name : "NAME / ID" ,
195
+ isRowHeader : true ,
196
+ alignment : "left" ,
197
+ } ,
198
+ {
199
+ id : "activeFeeds" ,
200
+ name : (
201
+ < >
202
+ ACTIVE FEEDS
203
+ < ExplainActive />
204
+ </ >
205
+ ) ,
206
+ alignment : "center" ,
207
+ width : 30 ,
208
+ } ,
209
+ {
210
+ id : "inactiveFeeds" ,
211
+ name : (
212
+ < >
213
+ INACTIVE FEEDS
214
+ < ExplainInactive />
215
+ </ >
216
+ ) ,
217
+ alignment : "center" ,
218
+ width : 30 ,
219
+ } ,
220
+ {
221
+ id : "averageScore" ,
222
+ name : (
223
+ < >
224
+ AVERAGE SCORE
225
+ < ExplainAverage
226
+ { ...( ! props . isLoading && {
227
+ scoreTime : props . averageScoreTime ,
228
+ } ) }
229
+ />
230
+ </ >
231
+ ) ,
232
+ alignment : "right" ,
233
+ width : PUBLISHER_SCORE_WIDTH ,
234
+ } ,
235
+ ] }
236
+ { ...( props . isLoading
237
+ ? { isLoading : true }
238
+ : {
239
+ rows : props . publishers ,
240
+ } ) }
241
+ />
242
+ </ Card >
243
+ < TopFeedsCard
244
+ title = "High-Performing"
245
+ emptyIcon = { < SmileySad /> }
246
+ emptyHeader = "Oh no!"
247
+ emptyBody = "This publisher has no high performing feeds"
248
+ emptyVariant = "error"
249
+ { ...( props . isLoading
250
+ ? { isLoading : true }
251
+ : {
252
+ feeds : props . highPerformingFeeds ,
253
+ } ) }
254
+ />
255
+ < TopFeedsCard
256
+ title = "Low-Performing"
257
+ emptyIcon = { < Confetti /> }
258
+ emptyHeader = "Looking good!"
259
+ emptyBody = "This publisher has no low performing feeds"
260
+ emptyVariant = "success"
261
+ { ...( props . isLoading
262
+ ? { isLoading : true }
263
+ : {
264
+ feeds : props . lowPerformingFeeds ,
265
+ } ) }
266
+ />
267
+ </ div >
268
+ ) ;
269
+
217
270
const getFeedRows = (
218
271
priceFeeds : ( Omit <
219
272
Awaited < ReturnType < typeof getPriceFeeds > > ,
@@ -271,31 +324,33 @@ type TopFeedsCardProps = {
271
324
emptyHeader : string ;
272
325
emptyBody : string ;
273
326
emptyVariant : NoResultsVariant ;
274
- feeds : ReturnType < typeof getFeedRows > ;
275
- } ;
327
+ } & (
328
+ | { isLoading : true }
329
+ | { isLoading ?: false | undefined ; feeds : ReturnType < typeof getFeedRows > }
330
+ ) ;
276
331
277
332
const TopFeedsCard = ( {
278
333
title,
279
334
emptyIcon,
280
335
emptyHeader,
281
336
emptyBody,
282
337
emptyVariant,
283
- feeds ,
338
+ ... props
284
339
} : TopFeedsCardProps ) => (
285
340
< Card icon = { < Network /> } title = { `${ title } Feeds` } >
286
- { feeds . length === 0 ? (
341
+ { props . isLoading || props . feeds . length > 0 ? (
342
+ < TopFeedsTable
343
+ label = { `${ title } Feeds` }
344
+ publisherScoreWidth = { PUBLISHER_SCORE_WIDTH }
345
+ { ...( props . isLoading ? { isLoading : true } : { rows : props . feeds } ) }
346
+ />
347
+ ) : (
287
348
< NoResults
288
349
icon = { emptyIcon }
289
350
header = { emptyHeader }
290
351
body = { emptyBody }
291
352
variant = { emptyVariant }
292
353
/>
293
- ) : (
294
- < TopFeedsTable
295
- label = { `${ title } Feeds` }
296
- publisherScoreWidth = { PUBLISHER_SCORE_WIDTH }
297
- rows = { feeds }
298
- />
299
354
) }
300
355
</ Card >
301
356
) ;
0 commit comments