소스 검색

fix: PR fixes

Alexandru Cambose 2 달 전
부모
커밋
f9b2665506

+ 3 - 1
apps/insights/src/components/ConformanceReport/conformance-report.module.scss

@@ -1,4 +1,6 @@
+@use "@pythnetwork/component-library/theme";
+
 .conformanceReport {
   display: flex;
-  gap: 0.5rem;
+  gap: theme.spacing(2);
 }

+ 7 - 40
apps/insights/src/components/ConformanceReport/conformance-report.tsx

@@ -3,59 +3,30 @@
 import { Download } from "@phosphor-icons/react/dist/ssr/Download";
 import { Button } from "@pythnetwork/component-library/Button";
 import { Select } from "@pythnetwork/component-library/Select";
-import { Skeleton } from "@pythnetwork/component-library/Skeleton";
 import { useAlert } from "@pythnetwork/component-library/useAlert";
 import { useLogger } from "@pythnetwork/component-library/useLogger";
-import { useState } from "react";
+import { useCallback, useState } from "react";
 
 import styles from "./conformance-report.module.scss";
 import type { Interval } from "./types";
 import { INTERVALS } from "./types";
-import { useDownloadReportForFeed } from "./use-download-report-for-feed";
-import { useDownloadReportForPublisher } from "./use-download-report-for-publisher";
-import { CLUSTER_NAMES } from "../../services/pyth";
 
-type ConformanceReportProps =
-  | { isLoading: true }
-  | {
-      isLoading?: false | undefined;
-      symbol?: string;
-      cluster: (typeof CLUSTER_NAMES)[number];
-      publisher?: string;
-    };
+type ConformanceReportProps = {
+  onClick: (timeframe: Interval) => Promise<void>;
+};
 
 const ConformanceReport = (props: ConformanceReportProps) => {
   const [timeframe, setTimeframe] = useState<Interval>(INTERVALS[0]);
   const [isGeneratingReport, setIsGeneratingReport] = useState(false);
   const { open } = useAlert();
-  const downloadReportForFeed = useDownloadReportForFeed();
-  const downloadReportForPublisher = useDownloadReportForPublisher();
   const logger = useLogger();
 
   /**
    * Download the conformance report for the given symbol or publisher
    */
-  const downloadReport = async () => {
-    if (props.isLoading) {
-      return;
-    }
-    if (props.symbol && props.publisher) {
-      return downloadReportForFeed({
-        symbol: props.symbol,
-        publisher: props.publisher,
-        timeframe,
-        cluster: props.cluster,
-      });
-    }
-
-    if (props.publisher) {
-      return downloadReportForPublisher({
-        publisher: props.publisher,
-        cluster: props.cluster,
-        interval: timeframe,
-      });
-    }
-  };
+  const downloadReport = useCallback(async () => {
+    await props.onClick(timeframe);
+  }, [props, timeframe]);
 
   const handleReport = () => {
     setIsGeneratingReport(true);
@@ -72,10 +43,6 @@ const ConformanceReport = (props: ConformanceReportProps) => {
       });
   };
 
-  if (props.isLoading) {
-    return <Skeleton width={100} />;
-  }
-
   return (
     <div className={styles.conformanceReport}>
       <Select

+ 10 - 13
apps/insights/src/components/ConformanceReport/use-download-report-for-publisher.tsx

@@ -15,7 +15,7 @@ import { z } from "zod";
 import { WEB_API_BASE_URL } from "./constants";
 import type { Interval } from "./types";
 import { useDownloadBlob } from "../../hooks/use-download-blob";
-import { priceFeedsSchema } from "../../schemas/pyth";
+import { priceFeedsSchema } from "../../schemas/pyth/price-feeds-schema";
 import { CLUSTER_NAMES } from "../../services/pyth";
 
 // If interval is 'daily', set interval_days=1
@@ -72,7 +72,7 @@ const getFeeds = async (cluster: (typeof CLUSTER_NAMES)[number]) => {
   return priceFeedsSchema.element.array().parse(parsedData);
 };
 
-const PublisherQualityScoreSchema = z.object({
+const publisherQualityScoreSchema = z.object({
   symbol: z.string(),
   uptime_score: z.string(),
   deviation_penalty: z.string(),
@@ -82,7 +82,7 @@ const PublisherQualityScoreSchema = z.object({
   final_score: z.string(),
 });
 
-const PublisherQuantityScoreSchema = z.object({
+const publisherQuantityScoreSchema = z.object({
   numSymbols: z.number(),
   rank: z.number(),
   symbols: z.array(z.string()),
@@ -98,10 +98,7 @@ const fetchRankingData = async (
     new Date(),
     interval,
   );
-  const quantityRankUrl = new URL(
-    `/publisher_ranking?publisher=${publisher}&cluster=${cluster}`,
-    WEB_API_BASE_URL,
-  );
+  const quantityRankUrl = new URL(`/publisher_ranking`, WEB_API_BASE_URL);
   quantityRankUrl.searchParams.set("cluster", cluster);
   quantityRankUrl.searchParams.set("publisher", publisher);
   const qualityRankUrl = new URL(
@@ -119,12 +116,12 @@ const fetchRankingData = async (
   ]);
 
   return {
-    quantityRankData: PublisherQuantityScoreSchema.array().parse(
-      await quantityRankRes.json(),
-    ),
-    qualityRankData: PublisherQualityScoreSchema.array().parse(
-      await qualityRankRes.json(),
-    ),
+    quantityRankData: publisherQuantityScoreSchema
+      .array()
+      .parse(await quantityRankRes.json()),
+    qualityRankData: publisherQualityScoreSchema
+      .array()
+      .parse(await qualityRankRes.json()),
   };
 };
 const csvHeaders = [

+ 17 - 5
apps/insights/src/components/PriceComponentDrawer/index.tsx

@@ -42,6 +42,8 @@ import { PriceName } from "../PriceName";
 import { Score } from "../Score";
 import { Status as StatusComponent } from "../Status";
 import styles from "./index.module.scss";
+import type { Interval } from "../ConformanceReport/types";
+import { useDownloadReportForFeed } from "../ConformanceReport/use-download-report-for-feed";
 
 const LineChart = dynamic(
   () => import("recharts").then((recharts) => recharts.LineChart),
@@ -273,13 +275,23 @@ type HeadingExtraProps = {
 };
 
 const HeadingExtra = ({ status, ...props }: HeadingExtraProps) => {
+  const downloadReportForFeed = useDownloadReportForFeed();
+
+  const handleDownloadReport = useCallback(
+    (timeframe: Interval) => {
+      return downloadReportForFeed({
+        symbol: props.symbol,
+        publisher: props.publisherKey,
+        timeframe,
+        cluster: ClusterToName[props.cluster],
+      });
+    },
+    [downloadReportForFeed, props.cluster, props.publisherKey, props.symbol],
+  );
+
   return (
     <>
-      <ConformanceReport
-        symbol={props.symbol}
-        publisher={props.publisherKey}
-        cluster={ClusterToName[props.cluster]}
-      />
+      <ConformanceReport onClick={handleDownloadReport} />
       <div className={styles.bigScreenBadges}>
         <StatusComponent status={status} />
       </div>

+ 71 - 46
apps/insights/src/components/Publisher/layout.tsx

@@ -13,7 +13,7 @@ import { StatCard } from "@pythnetwork/component-library/StatCard";
 import { lookup } from "@pythnetwork/known-publishers";
 import { notFound } from "next/navigation";
 import type { ReactNode } from "react";
-import { Suspense } from "react";
+import { Suspense, useCallback } from "react";
 
 import {
   getPublishers,
@@ -46,6 +46,8 @@ import { TabPanel, TabRoot, Tabs } from "../Tabs";
 import { TokenIcon } from "../TokenIcon";
 import { OisApyHistory } from "./ois-apy-history";
 import ConformanceReport from "../ConformanceReport/conformance-report";
+import type { Interval } from "../ConformanceReport/types";
+import { useDownloadReportForPublisher } from "../ConformanceReport/use-download-report-for-publisher";
 
 type Props = {
   children: ReactNode;
@@ -62,53 +64,9 @@ export const PublisherLayout = async ({ children, params }: Props) => {
   if (parsedCluster === undefined) {
     notFound();
   } else {
-    const knownPublisher = lookup(key);
     return (
       <div className={styles.publisherLayout}>
-        <section className={styles.header}>
-          <div className={styles.breadcrumbRow}>
-            <Breadcrumbs
-              className={styles.breadcrumbs ?? ""}
-              label="Breadcrumbs"
-              items={[
-                { href: "/", label: "Home" },
-                { href: "/publishers", label: "Publishers" },
-                { label: <PublisherKey publisherKey={key} /> },
-              ]}
-            />
-          </div>
-          <div className={styles.titleRow}>
-            <PublisherTag
-              cluster={parsedCluster}
-              publisherKey={key}
-              {...(knownPublisher && {
-                name: knownPublisher.name,
-                icon: <PublisherIcon knownPublisher={knownPublisher} />,
-              })}
-            />
-            <ConformanceReport
-              publisher={key}
-              cluster={ClusterToName[parsedCluster]}
-            />
-          </div>
-
-          <Cards className={styles.stats ?? ""}>
-            <Suspense fallback={<RankingCardImpl isLoading />}>
-              <RankingCard cluster={parsedCluster} publisherKey={key} />
-            </Suspense>
-            <Suspense fallback={<ScoreCardImpl isLoading />}>
-              <ScoreCard cluster={parsedCluster} publisherKey={key} />
-            </Suspense>
-            <Suspense fallback={<ActiveFeedsCardImpl isLoading />}>
-              <ActiveFeedsCard cluster={parsedCluster} publisherKey={key} />
-            </Suspense>
-            {parsedCluster === Cluster.Pythnet && (
-              <Suspense fallback={<OisPoolCardImpl isLoading />}>
-                <OisPoolCard publisherKey={key} />
-              </Suspense>
-            )}
-          </Cards>
-        </section>
+        <PublisherHeader cluster={parsedCluster} publisherKey={key} />
         <TabRoot>
           <Tabs
             label="Price Feed Navigation"
@@ -140,6 +98,73 @@ export const PublisherLayout = async ({ children, params }: Props) => {
   }
 };
 
+const PublisherHeader = ({
+  cluster,
+  publisherKey,
+}: {
+  cluster: Cluster;
+  publisherKey: string;
+}) => {
+  const knownPublisher = lookup(publisherKey);
+
+  const downloadReportForPublisher = useDownloadReportForPublisher();
+
+  const handleDownloadReport = useCallback(
+    (interval: Interval) => {
+      return downloadReportForPublisher({
+        publisher: publisherKey,
+        cluster: ClusterToName[cluster],
+        interval,
+      });
+    },
+    [publisherKey, cluster, downloadReportForPublisher],
+  );
+
+  return (
+    <section className={styles.header}>
+      <div className={styles.breadcrumbRow}>
+        <Breadcrumbs
+          className={styles.breadcrumbs ?? ""}
+          label="Breadcrumbs"
+          items={[
+            { href: "/", label: "Home" },
+            { href: "/publishers", label: "Publishers" },
+            { label: <PublisherKey publisherKey={publisherKey} /> },
+          ]}
+        />
+      </div>
+      <div className={styles.titleRow}>
+        <PublisherTag
+          cluster={cluster}
+          publisherKey={publisherKey}
+          {...(knownPublisher && {
+            name: knownPublisher.name,
+            icon: <PublisherIcon knownPublisher={knownPublisher} />,
+          })}
+        />
+        <ConformanceReport onClick={handleDownloadReport} />
+      </div>
+
+      <Cards className={styles.stats ?? ""}>
+        <Suspense fallback={<RankingCardImpl isLoading />}>
+          <RankingCard cluster={cluster} publisherKey={publisherKey} />
+        </Suspense>
+        <Suspense fallback={<ScoreCardImpl isLoading />}>
+          <ScoreCard cluster={cluster} publisherKey={publisherKey} />
+        </Suspense>
+        <Suspense fallback={<ActiveFeedsCardImpl isLoading />}>
+          <ActiveFeedsCard cluster={cluster} publisherKey={publisherKey} />
+        </Suspense>
+        {cluster === Cluster.Pythnet && (
+          <Suspense fallback={<OisPoolCardImpl isLoading />}>
+            <OisPoolCard publisherKey={publisherKey} />
+          </Suspense>
+        )}
+      </Cards>
+    </section>
+  );
+};
+
 const NumFeeds = async ({
   cluster,
   publisherKey,

+ 1 - 1
apps/insights/src/components/PublisherTag/index.module.scss

@@ -28,7 +28,7 @@
   .name {
     color: theme.color("heading");
 
-    @include theme.text("lg", "medium");
+    @include theme.text("base", "medium");
   }
 
   .publisherKey,

+ 0 - 0
apps/insights/src/schemas/pyth.ts → apps/insights/src/schemas/pyth/price-feeds-schema.ts


+ 1 - 1
apps/insights/src/server/pyth.ts

@@ -4,7 +4,7 @@ import { z } from "zod";
 import { DEFAULT_CACHE_TTL } from "../cache";
 import { VERCEL_REQUEST_HEADERS } from "../config/server";
 import { getHost } from "../get-host";
-import { priceFeedsSchema } from "../schemas/pyth";
+import { priceFeedsSchema } from "../schemas/pyth/price-feeds-schema";
 import { Cluster, ClusterToName } from "../services/pyth";
 
 export async function getPublishersForFeedRequest(

+ 1 - 1
apps/insights/src/services/pyth/get-feeds.ts

@@ -1,7 +1,7 @@
 import { Cluster } from ".";
 import { getPythMetadata } from "./get-metadata";
 import { redisCache } from "../../cache";
-import { priceFeedsSchema } from "../../schemas/pyth";
+import { priceFeedsSchema } from "../../schemas/pyth/price-feeds-schema";
 
 const _getFeeds = async (cluster: Cluster) => {
   const unfilteredData = await getPythMetadata(cluster);