Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a972c98
feat(component-library, insights): added a simple datetime picker and…
benduran Jan 8, 2026
21937de
fix(insights): fixed app state provider not being wrapped in a suspense
benduran Jan 8, 2026
8fa26a6
chore(insights): added console.info to db config but obfuscating the pwd
benduran Jan 8, 2026
431b189
chore(insights): missed global flag
benduran Jan 8, 2026
452cdeb
chore(insights): one more log
benduran Jan 8, 2026
6e3dc06
chore(insights): added a script to fix env variables with dollar sign…
benduran Jan 8, 2026
7c33093
chore(insights): removed console logging DB config because the issue …
benduran Jan 8, 2026
4e6407e
fix: lint
benduran Jan 8, 2026
acf6f28
chore(insights): worked on making the core PriceCard mostly reusable …
benduran Jan 8, 2026
a390b68
feat(insights): completed design and layout changes for demo page for…
benduran Jan 8, 2026
a44b409
chore(insights): added horizontal scrolling to cards on mobile
benduran Jan 8, 2026
5b24556
feat(component-library, insights): created a Popover abstraction comp…
benduran Jan 9, 2026
5aaa5b5
fix(insights): fixed Value is null and Object is disposed errors that…
benduran Jan 9, 2026
282b905
fix(insights): count a metric as fresh if there wasn't a previous point
benduran Jan 9, 2026
be5c229
fix(insights): ensure that date changes properly clear out any previo…
benduran Jan 9, 2026
d1d480c
chore: linting
benduran Jan 9, 2026
52deb0e
chore: linting
benduran Jan 9, 2026
87d81b1
Merge pull request #3354 from pyth-network/bduran/t-minus-7-design-tw…
benduran Jan 9, 2026
f129f2d
chore(insights): responded to PR feedback
benduran Jan 12, 2026
c967425
feat(insights): swapped to using nuqs for better query param management
benduran Jan 12, 2026
71c5cd2
fix(insights): removed typecasting by leveraging zod and fixed some bugs
benduran Jan 12, 2026
e6644db
Merge remote-tracking branch 'origin/main' into bduran/T-minus-7-hist…
benduran Jan 12, 2026
301270e
chore: linting
benduran Jan 12, 2026
87dbf3c
chore: linting
benduran Jan 12, 2026
0dc2e8f
Merge remote-tracking branch 'origin/main' into bduran/T-minus-7-hist…
benduran Jan 12, 2026
9a41631
chore: omg, please stop testing json files
benduran Jan 12, 2026
7532c14
fix: force all jest test runs to pass without tests
benduran Jan 12, 2026
fdbd725
chore: stop tsc from picking up files it shouldn't care about
benduran Jan 12, 2026
66a51cb
chore disable linting of examples fiels
benduran Jan 12, 2026
afe7dae
fix(insights): fixed desktop display having mobile buttons
benduran Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ client.subscribe({
```

<Callout type="info">
For a detailed explanation of every property passed to `client.subscribe`, see the [Payload Reference](/price-feeds/pro/payload-reference#stream-response-structure).
For a detailed explanation of every property passed to `client.subscribe`, see
the [Payload
Reference](/price-feeds/pro/payload-reference#stream-response-structure).
</Callout>


</Step>
</Steps>

Expand Down
2 changes: 0 additions & 2 deletions apps/insights/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const config = {
},
},

serverExternalPackages: ["@duckdb/node-api"],

turbopack: {
rules: {
"*.svg": {
Expand Down
5 changes: 2 additions & 3 deletions apps/insights/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"fix:format": "prettier --write .",
"fix:lint:eslint": "eslint --fix .",
"fix:lint:stylelint": "stylelint --fix 'src/**/*.scss'",
"pull:env": "[ $CI ] || VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_TBkf9EyQjQF37gs4Vk0sQKJj97kE vercel env pull",
"pull:env": "[ $CI ] || (VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_TBkf9EyQjQF37gs4Vk0sQKJj97kE vercel env pull && ../../scripts/nextjs/fix-env-vars.mjs)",
"start:dev": "next dev --port 3003",
"start:prod": "next start --port 3003",
"test:format": "prettier --check .",
Expand All @@ -22,7 +22,6 @@
},
"dependencies": {
"@clickhouse/client": "catalog:",
"@duckdb/node-api": "catalog:",
"@phosphor-icons/react": "catalog:",
"@pythnetwork/client": "catalog:",
"@pythnetwork/component-library": "workspace:*",
Expand Down Expand Up @@ -87,4 +86,4 @@
"stylelint-config-standard-scss": "catalog:",
"vercel": "catalog:"
}
}
}
Binary file removed apps/insights/public/db/historical-demo-data.db.br
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
import { NextRequest, NextResponse } from "next/server";

import { fetchHistoricalDataForPythFeedsDemo } from "../../../../../pyth-feed-demo-data/fetch-historical-data-from-db";
import { GetPythFeedsDemoDataRequestSchema } from "../../../../../schemas/pyth/pyth-pro-demo-schema";
import { getNbboAndPythProHistoricalPricesForSymbol } from "../../../../../services/clickhouse";

export const GET = async (
export async function GET(
req: NextRequest,
ctx: { params: Promise<Record<string, string>> },
) => {
) {
const params = await ctx.params;

const {
nextUrl: { searchParams },
} = req;
const dataSourcesToUse = searchParams.getAll("datasources[]");

const searchParamsToUse = {
const query = {
...Object.fromEntries(searchParams),
datasources: searchParams.getAll("datasources[]"),
datasources: dataSourcesToUse,
};

const paramsAndQueryValidation = GetPythFeedsDemoDataRequestSchema.safeParse({
const validatedParams = GetPythFeedsDemoDataRequestSchema.safeParse({
params,
searchParams: searchParamsToUse,
searchParams: query,
});

if (paramsAndQueryValidation.error) {
if (validatedParams.error) {
return NextResponse.json(
{
error: paramsAndQueryValidation.error.format(),
},
{ error: validatedParams.error.format() },
{ status: 400 },
);
}

const {
params: { symbol: symbolToUse },
searchParams: { datasources, startAt },
} = paramsAndQueryValidation.data;
data: {
params: { symbol },
searchParams: { datasources, startAt },
},
} = validatedParams;

const end = new Date(startAt);
// enforce the end time for this API,
// when called by a public user,
// only allows for 1 minute beyond the startAt.
end.setTime(end.getTime() + 1000 * 60);

try {
const { data, hasNext } = await fetchHistoricalDataForPythFeedsDemo({
datasources,
startAt: startAt.toISOString(),
symbol: symbolToUse,
const response = await getNbboAndPythProHistoricalPricesForSymbol({
end,
sources: datasources,
start: startAt,
symbol,
});

return NextResponse.json({
data,
hasNext,
});
return NextResponse.json(response);
} catch (error) {
return NextResponse.json(
{
error: error instanceof Error ? error.message : String(error),
},
{ status: 500 },
);
if (error instanceof Error) {
return NextResponse.json(
{ error: error.message || error },
{ status: 500 },
);
}
return NextResponse.json({ error: String(error) }, { status: 500 });
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
padding-top: theme.spacing(4);
}

.suspenseLoader {
@include theme.max-width;
align-items: center;
display: flex;
flex-flow: column;
gap: theme.spacing(2);
margin-top: theme.spacing(6);

& svg {
height: theme.spacing(10);
width: theme.spacing(10);
}
}

.subheader {
border-bottom: 1px solid theme.color("border");
display: flex;
Expand Down
58 changes: 36 additions & 22 deletions apps/insights/src/components/PythFeedsDemoPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"use client";

import { Spinner } from "@pythnetwork/component-library/Spinner";
import { Suspense } from "react";

import classes from "./index.module.scss";
import {
PythProApiTokensProvider,
Expand All @@ -12,32 +15,43 @@ import { PythProDemoPriceChart } from "../PythProDemoPriceChart";
import { PythProDemoSourceSelector } from "../PythProDemoSourceSelector";

export function PythFeedsDemoPage() {
const suspenseLoaderLabel = "Initializing Pyth Pro demo...";

return (
<PythProApiTokensProvider>
<PythProAppStateProvider>
<WebSocketsProvider>
<article className={classes.pythFeedsDemoPageRoot}>
<section>
<div className={classes.subheader}>
<div>
<h3>Pyth Pro</h3>
<h4>Real-time feed comparison tool</h4>
<Suspense
fallback={
<div className={classes.suspenseLoader}>
<div>{suspenseLoaderLabel}</div>
<Spinner isIndeterminate label={suspenseLoaderLabel} />
</div>
}
>
<PythProAppStateProvider>
<WebSocketsProvider>
<article className={classes.pythFeedsDemoPageRoot}>
<section>
<div className={classes.subheader}>
<div>
<h3>Pyth Pro</h3>
<h4>Real-time feed comparison tool</h4>
</div>
<div>
<PythProApiTokensMenu />
<PythProDemoSourceSelector />
</div>
</div>
<div>
<PythProApiTokensMenu />
<PythProDemoSourceSelector />
<div className={classes.body}>
<PythProDemoCards />
</div>
</div>
<div className={classes.body}>
<PythProDemoCards />
</div>
</section>
<aside>
<PythProDemoPriceChart />
</aside>
</article>
</WebSocketsProvider>
</PythProAppStateProvider>
</section>
<aside>
<PythProDemoPriceChart />
</aside>
</article>
</WebSocketsProvider>
</PythProAppStateProvider>
</Suspense>
</PythProApiTokensProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ export function PythProDemoCard({
{/* the token is either missing or it's a bad token */}
{!isReplaySymbol(selectedSource) &&
requiresToken &&
(!apiToken || socketStatus === "closed") ? (
!apiToken &&
socketStatus === "closed" ? (
<>
Please enter a good API token
<br />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
@use "@pythnetwork/component-library/theme";

.buttons {
align-items: center;
display: flex;
flex-flow: column;
flex-grow: 0;
flex-shrink: 0;
gap: theme.spacing(2);
justify-content: flex-end;

@include theme.breakpoint("sm") {
align-items: center;
flex-flow: initial;
justify-content: flex-end;
}
}

.chartContainer {
flex-grow: 1;
flex-shrink: 0;
}

.selectDateAndTimeMsg {
background-color: theme.color("background", "tooltip");
border-radius: theme.border-radius("md");
color: theme.color("tooltip");
margin-top: theme.spacing(4);
padding: theme.spacing(2);

&::before {
content: "";
border-bottom: theme.spacing(2) solid theme.color("background", "tooltip");
border-left: theme.spacing(2) solid transparent;
border-right: theme.spacing(2) solid transparent;
display: block;
height: 0;
left: 50%;
position: absolute;
top: calc((theme.spacing(2) * -1) + 1px);
width: 0;
}
}

.spinner {
align-items: center;
color: theme.color("muted");
display: flex;
flex-flow: column;
gap: theme.spacing(4);
margin-top: theme.spacing(6);

& svg {
height: theme.spacing(10);
width: theme.spacing(10);
}
}

.verticalDivider {
background-color: theme.color("border");
height: theme.spacing(6);
width: 1px;
display: none;

@include theme.breakpoint("sm") {
background-color: theme.color("border");
display: initial;
height: theme.spacing(6);
width: 1px;
}
}

.root {
Expand Down
Loading
Loading