@@ -8,7 +8,7 @@ import { Link } from "@pythnetwork/component-library/Link";
88import { Table } from "@pythnetwork/component-library/Table" ;
99import { lookup } from "@pythnetwork/known-publishers" ;
1010import { notFound } from "next/navigation" ;
11- import type { ReactNode } from "react" ;
11+ import type { ReactNode , ComponentProps } from "react" ;
1212
1313import { getPriceFeeds } from "./get-price-feeds" ;
1414import styles from "./performance.module.scss" ;
@@ -119,101 +119,154 @@ export const Performance = async ({ params }: Props) => {
119119 return rows === undefined ? (
120120 notFound ( )
121121 ) : (
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+ />
214128 ) ;
215129} ;
216130
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+
217270const getFeedRows = (
218271 priceFeeds : ( Omit <
219272 Awaited < ReturnType < typeof getPriceFeeds > > ,
@@ -271,31 +324,33 @@ type TopFeedsCardProps = {
271324 emptyHeader : string ;
272325 emptyBody : string ;
273326 emptyVariant : NoResultsVariant ;
274- feeds : ReturnType < typeof getFeedRows > ;
275- } ;
327+ } & (
328+ | { isLoading : true }
329+ | { isLoading ?: false | undefined ; feeds : ReturnType < typeof getFeedRows > }
330+ ) ;
276331
277332const TopFeedsCard = ( {
278333 title,
279334 emptyIcon,
280335 emptyHeader,
281336 emptyBody,
282337 emptyVariant,
283- feeds ,
338+ ... props
284339} : TopFeedsCardProps ) => (
285340 < 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+ ) : (
287348 < NoResults
288349 icon = { emptyIcon }
289350 header = { emptyHeader }
290351 body = { emptyBody }
291352 variant = { emptyVariant }
292353 />
293- ) : (
294- < TopFeedsTable
295- label = { `${ title } Feeds` }
296- publisherScoreWidth = { PUBLISHER_SCORE_WIDTH }
297- rows = { feeds }
298- />
299354 ) }
300355 </ Card >
301356) ;
0 commit comments