Skip to content

Commit f7b0df7

Browse files
committed
wip: add nextjs loading state
1 parent 42a7dd2 commit f7b0df7

File tree

11 files changed

+313
-207
lines changed

11 files changed

+313
-207
lines changed

apps/insights/src/app/price-feeds/layout.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { Metadata } from "next";
2+
import type { ReactNode } from "react";
23

3-
export { ZoomLayoutTransition as default } from "../../components/ZoomLayoutTransition";
4+
const Layout = ({ children }: { children: ReactNode }) => children;
5+
export default Layout;
6+
// export { ZoomLayoutTransition as default } from "../../components/ZoomLayoutTransition";
47

58
export const metadata: Metadata = {
69
title: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { PerformanceLoading as default } from "../../../../components/Publisher/performance";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { PriceFeedsLoading as default } from "../../../../../components/Publisher/price-feeds-loading";

apps/insights/src/app/publishers/layout.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { Metadata } from "next";
2+
import type { ReactNode } from "react";
23

3-
export { ZoomLayoutTransition as default } from "../../components/ZoomLayoutTransition";
4+
const Layout = ({ children }: { children: ReactNode }) => children;
5+
export default Layout;
6+
// export { ZoomLayoutTransition as default } from "../../components/ZoomLayoutTransition";
47

58
export const metadata: Metadata = {
69
title: {

apps/insights/src/components/PriceComponentsCard/index.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,9 @@ export const PriceComponentsCardContents = <
347347
title={
348348
<>
349349
<span>{label}</span>
350-
{!props.isLoading && (
351-
<Badge style="filled" variant="neutral" size="md">
352-
{props.numResults}
353-
</Badge>
354-
)}
350+
<Badge style="filled" variant="neutral" size="md">
351+
{!props.isLoading && props.numResults}
352+
</Badge>
355353
</>
356354
}
357355
toolbar={

apps/insights/src/components/PriceFeedTag/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const PriceFeedTagImpl = ({ className, compact, ...props }: ImplProps) => {
6565
className={clsx(styles.priceFeedTag, className)}
6666
data-compact={compact ? "" : undefined}
6767
data-loading={props.isLoading ? "" : undefined}
68-
{...omitKeys(props, ["feedName", "icon", "description"])}
68+
{...omitKeys(props, ["feedName", "icon", "description", "isLoading"])}
6969
>
7070
{props.isLoading ? (
7171
<Skeleton fill className={styles.icon} />

apps/insights/src/components/Publisher/performance.tsx

+158-103
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Link } from "@pythnetwork/component-library/Link";
88
import { Table } from "@pythnetwork/component-library/Table";
99
import { lookup } from "@pythnetwork/known-publishers";
1010
import { notFound } from "next/navigation";
11-
import type { ReactNode } from "react";
11+
import type { ReactNode, ComponentProps } from "react";
1212

1313
import { getPriceFeeds } from "./get-price-feeds";
1414
import 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+
217270
const 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

277332
const 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
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"use client";
2+
3+
import { PriceComponentsCardContents } from "../PriceComponentsCard";
4+
import { PriceFeedTag } from "../PriceFeedTag";
5+
6+
export const PriceFeedsLoading = () => (
7+
<PriceComponentsCardContents
8+
label="Price Feeds"
9+
searchPlaceholder="Feed symbol"
10+
nameLoadingSkeleton={<PriceFeedTag compact isLoading />}
11+
isLoading
12+
extraColumns={[
13+
{
14+
id: "assetClass",
15+
name: "ASSET CLASS",
16+
alignment: "left",
17+
allowsSorting: true,
18+
},
19+
]}
20+
nameWidth={90}
21+
/>
22+
);

0 commit comments

Comments
 (0)