Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/blue-ears-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Export `toEventSelector` utility function from "thirdweb/utils"
118 changes: 65 additions & 53 deletions apps/dashboard/src/@/components/blocks/charts/area-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import {
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { formatDate } from "date-fns";
import { useMemo } from "react";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import {
EmptyChartState,
LoadingChartState,
} from "../../../../components/analytics/empty-chart-state";

type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
// chart config
Expand All @@ -19,71 +24,78 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {

// chart className
chartClassName?: string;
isPending: boolean;
};

export function ThirdwebAreaChart<TConfig extends ChartConfig>(
props: ThirdwebAreaChartProps<TConfig>,
) {
const configKeys = useMemo(() => Object.keys(props.config), [props.config]);
return (
<div className="rounded-lg border border-border bg-muted/50 px-4 pt-10 pb-4">
<div className="rounded-lg border border-border px-4 pt-10 pb-4">
<ChartContainer config={props.config} className={props.chartClassName}>
<AreaChart
accessibilityLayer
data={props.data}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={20}
tickFormatter={(value) => new Date(value).toLocaleDateString()}
/>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<defs>
{props.isPending ? (
<LoadingChartState />
) : props.data.length === 0 ? (
<EmptyChartState />
) : (
<AreaChart
accessibilityLayer
data={props.data}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={20}
tickFormatter={(value) => formatDate(new Date(value), "MMM dd")}
/>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<defs>
{configKeys.map((key) => (
<linearGradient
key={key}
id={`fill_${key}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor={`var(--color-${key})`}
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor={`var(--color-${key})`}
stopOpacity={0.1}
/>
</linearGradient>
))}
</defs>
{configKeys.map((key) => (
<linearGradient
<Area
key={key}
id={`fill_${key}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor={`var(--color-${key})`}
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor={`var(--color-${key})`}
stopOpacity={0.1}
/>
</linearGradient>
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId="a"
/>
))}
</defs>
{configKeys.map((key) => (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId="a"
/>
))}

{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} className="pt-8" />
)}
</AreaChart>
{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} className="pt-8" />
)}
</AreaChart>
)}
</ChartContainer>
</div>
);
Expand Down
82 changes: 47 additions & 35 deletions apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import {
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { formatDate } from "date-fns";
import { useMemo } from "react";
import {
EmptyChartState,
LoadingChartState,
} from "../../../../components/analytics/empty-chart-state";

type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
// metadata
Expand All @@ -30,6 +35,7 @@ type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
variant?: "stacked" | "grouped";
// chart className
chartClassName?: string;
isPending: boolean;
};

export function ThirdwebBarChart<TConfig extends ChartConfig>(
Expand All @@ -42,48 +48,54 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
return (
<Card>
<CardHeader>
<CardTitle>{props.title}</CardTitle>
<CardTitle className="mb-2">{props.title}</CardTitle>
{props.description && (
<CardDescription>{props.description}</CardDescription>
)}
</CardHeader>
<CardContent>
<ChartContainer config={props.config} className={props.chartClassName}>
<BarChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={10}
tickFormatter={(value) => new Date(value).toLocaleDateString()}
/>
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} />
)}
{configKeys.map((key, idx) => (
<Bar
key={key}
dataKey={key}
// if stacked then they should all be the same stackId
// if grouped then they should all be unique stackId (so the key works great)
stackId={variant === "stacked" ? "a" : key}
fill={`var(--color-${key})`}
// if stacked then we need to figure out the radius based on the index in the array
// if grouped then we can just use the same radius for all
radius={
variant === "stacked"
? idx === 0
? [0, 0, 4, 4]
: idx === configKeys.length - 1
? [4, 4, 0, 0]
: [0, 0, 0, 0]
: [4, 4, 4, 4]
}
{props.isPending ? (
<LoadingChartState />
) : props.data.length === 0 ? (
<EmptyChartState />
) : (
<BarChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={10}
tickFormatter={(value) => formatDate(new Date(value), "MMM d")}
/>
))}
</BarChart>
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} />
)}
{configKeys.map((key, idx) => (
<Bar
key={key}
dataKey={key}
// if stacked then they should all be the same stackId
// if grouped then they should all be unique stackId (so the key works great)
stackId={variant === "stacked" ? "a" : key}
fill={`var(--color-${key})`}
// if stacked then we need to figure out the radius based on the index in the array
// if grouped then we can just use the same radius for all
radius={
variant === "stacked"
? idx === 0
? [0, 0, 4, 4]
: idx === configKeys.length - 1
? [4, 4, 0, 0]
: [0, 0, 0, 0]
: [4, 4, 4, 4]
}
/>
))}
</BarChart>
)}
</ChartContainer>
</CardContent>
</Card>
Expand Down
3 changes: 3 additions & 0 deletions apps/dashboard/src/@/constants/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ export const BASE_URL = isProd
: "http://localhost:3000") || "https://thirdweb-dev.com";

export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL;

export const INSIGHT_SERVICE_API_KEY =
process.env.INSIGHT_SERVICE_API_KEY || "";
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import { isProd } from "@/constants/env";
import { INSIGHT_SERVICE_API_KEY } from "@/constants/env";
import { getVercelEnv } from "lib/vercel-utils";

const thirdwebDomain =
getVercelEnv() !== "production" ? "thirdweb-dev" : "thirdweb";

export async function isAnalyticsSupportedForChain(
chainId: number,
): Promise<boolean> {
try {
if (!process.env.CHAINSAW_API_KEY) {
throw new Error("Missing CHAINSAW_API_KEY env var");
}

const res = await fetch(
`https://chainsaw.${isProd ? "thirdweb" : "thirdweb-dev"}.com/service/chains/${chainId}`,
`https://insight.${thirdwebDomain}.com/service/chains/${chainId}`,
{
method: "GET",
headers: {
"content-type": "application/json",
// pass the shared secret
"x-service-api-key": process.env.CHAINSAW_API_KEY || "",
// service api key required - because this is endpoint is internal
"x-service-api-key": INSIGHT_SERVICE_API_KEY,
},
},
);

if (!res.ok) {
// assume not supported if we get a non-200 response
return false;
}

const { data } = await res.json();
return data;
const json = (await res.json()) as { data: boolean };

return json.data;
} catch (e) {
console.error("Error checking if analytics is supported for chain", e);
console.error(`Error checking analytics support for chain ${chainId}`);
console.error(e);
}

return false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ContractAnalyticsPage } from "./ContractAnalyticsPage";

export function ContractAnalyticsPageClient(props: {
contract: ThirdwebContract;
writeFnSelectorToNameRecord: Record<string, string>;
eventSelectorToNameRecord: Record<string, string>;
}) {
const metadataQuery = useContractPageMetadata(props.contract);

Expand All @@ -23,5 +25,11 @@ export function ContractAnalyticsPageClient(props: {
return <RedirectToContractOverview contract={props.contract} />;
}

return <ContractAnalyticsPage contract={props.contract} />;
return (
<ContractAnalyticsPage
contract={props.contract}
writeFnSelectorToNameRecord={props.writeFnSelectorToNameRecord}
eventSelectorToNameRecord={props.eventSelectorToNameRecord}
/>
);
}
Loading
Loading