Skip to content

Commit a1e9e1a

Browse files
committed
feat: add aggregate parameter input with presets
1 parent 317211a commit a1e9e1a

File tree

2 files changed

+169
-1
lines changed

2 files changed

+169
-1
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"use client";
2+
3+
import { Input } from "@/components/ui/input";
4+
import {
5+
Select,
6+
SelectContent,
7+
SelectItem,
8+
SelectTrigger,
9+
SelectValue,
10+
} from "@/components/ui/select";
11+
import { cn } from "@/lib/utils";
12+
import type { ControllerRenderProps } from "react-hook-form";
13+
14+
interface Preset {
15+
label: string;
16+
value: string;
17+
}
18+
19+
const DEFAULT_AGGREGATE_PRESETS: Preset[] = [
20+
{ label: "Count All Items", value: "count() AS count_all" },
21+
{ label: "Sum (gas_used)", value: "sum(gas_used) AS total_gas_used" },
22+
{ label: "Average (gas_used)", value: "avg(gas_used) AS avg_gas_used" },
23+
{ label: "Min (gas_used)", value: "min(gas_used) AS min_gas_used" },
24+
{ label: "Max (gas_used)", value: "max(gas_used) AS max_gas_used" },
25+
// Presets for a user-defined field
26+
{ label: "Count (custom field)", value: "count(your_field_here) AS count_custom" },
27+
{ label: "Sum (custom field)", value: "sum(your_field_here) AS sum_custom" },
28+
{ label: "Average (custom field)", value: "avg(your_field_here) AS avg_custom" },
29+
{ label: "Min (custom field)", value: "min(your_field_here) AS min_custom" },
30+
{ label: "Max (custom field)", value: "max(your_field_here) AS max_custom" },
31+
];
32+
33+
const GENERAL_TRANSACTIONS_PRESETS: Preset[] = [
34+
{ label: "Transaction Count", value: "count() AS transaction_count" },
35+
{ label: "Total Value Transferred (Wei)", value: "sum(value) AS total_value_wei" },
36+
{ label: "Total Gas Used", value: "sum(gas_used) AS total_gas_used" },
37+
{ label: "Average Value Transferred (Wei)", value: "avg(value) AS average_value_wei" },
38+
{ label: "Average Gas Used", value: "avg(gas_used) AS average_gas_used" },
39+
{ label: "Max Value Transferred (Wei)", value: "max(value) AS max_value_wei" },
40+
{ label: "Max Gas Used", value: "max(gas_used) AS max_gas_used" },
41+
{ label: "Min Value Transferred (Wei)", value: "min(value) AS min_value_wei" },
42+
{ label: "Min Gas Used", value: "min(gas_used) AS min_gas_used" },
43+
];
44+
45+
const WALLET_TRANSACTIONS_PRESETS: Preset[] = [
46+
{ label: "Wallet Transaction Count", value: "count() AS wallet_tx_count" },
47+
{ label: "Wallet Total Value (Wei)", value: "sum(value) AS wallet_total_value_wei" },
48+
{ label: "Wallet Total Gas Spent", value: "sum(gas_used) AS wallet_total_gas_spent" },
49+
{ label: "Wallet Average Value (Wei)", value: "avg(value) AS wallet_avg_value" },
50+
{ label: "Wallet Average Gas Spent", value: "avg(gas_used) AS wallet_avg_gas_spent" },
51+
{ label: "Wallet Max Value Tx (Wei)", value: "max(value) AS wallet_max_value_tx" },
52+
{ label: "Wallet Max Gas Tx", value: "max(gas_used) AS wallet_max_gas_tx" },
53+
{ label: "Wallet Min Value Tx (Wei)", value: "min(value) AS wallet_min_value_tx" },
54+
{ label: "Wallet Min Gas Tx", value: "min(gas_used) AS wallet_min_gas_tx" },
55+
];
56+
57+
const EVENTS_PRESETS: Preset[] = [
58+
{ label: "Event Count", value: "count() AS event_count" },
59+
// If supported, use count_distinct, otherwise remove or replace
60+
{ label: "Unique Event Names", value: "count_distinct(event_name) AS unique_event_names" },
61+
{ label: "Unique Addresses", value: "count_distinct(address) AS unique_addresses" },
62+
{ label: "Min Block Number", value: "min(block_number) AS min_block" },
63+
{ label: "Max Block Number", value: "max(block_number) AS max_block" },
64+
{ label: "Total Value", value: "sum(value) AS total_value" },
65+
{ label: "Average Value", value: "avg(value) AS avg_value" },
66+
{ label: "Max Value", value: "max(value) AS max_value" },
67+
{ label: "Min Value", value: "min(value) AS min_value" },
68+
];
69+
70+
const BLOCKS_PRESETS: Preset[] = [
71+
{ label: "Block Count", value: "count() AS block_count" },
72+
{ label: "Min Block Number", value: "min(block_number) AS min_block_number" },
73+
{ label: "Max Block Number", value: "max(block_number) AS max_block_number" },
74+
{ label: "Total Gas Used", value: "sum(gas_used) AS total_gas_used" },
75+
{ label: "Average Gas Used", value: "avg(gas_used) AS avg_gas_used" },
76+
{ label: "Max Gas Used", value: "max(gas_used) AS max_gas_used" },
77+
{ label: "Min Gas Used", value: "min(gas_used) AS min_gas_used" },
78+
{ label: "Total Transactions", value: "sum(transaction_count) AS total_transactions" },
79+
{ label: "Average Transactions per Block", value: "avg(transaction_count) AS avg_txs_per_block" },
80+
{ label: "Max Transactions in a Block", value: "max(transaction_count) AS max_txs_in_block" },
81+
{ label: "Min Transactions in a Block", value: "min(transaction_count) AS min_txs_in_block" },
82+
];
83+
84+
const ENDPOINT_SPECIFIC_PRESETS: Record<string, Preset[]> = {
85+
"/v1/transactions": GENERAL_TRANSACTIONS_PRESETS,
86+
"/v1/wallets/{wallet_address}/transactions": WALLET_TRANSACTIONS_PRESETS,
87+
"/v1/events": EVENTS_PRESETS,
88+
"/v1/blocks": BLOCKS_PRESETS,
89+
// Add more endpoint paths and their specific presets here
90+
};
91+
92+
function getAggregatePresets(endpointPath: string): Preset[] {
93+
return ENDPOINT_SPECIFIC_PRESETS[endpointPath] || DEFAULT_AGGREGATE_PRESETS;
94+
}
95+
96+
interface AggregateParameterInputProps {
97+
field: ControllerRenderProps<
98+
{
99+
[x: string]: string | number;
100+
},
101+
string
102+
>;
103+
showTip: boolean;
104+
hasError: boolean;
105+
placeholder: string;
106+
endpointPath: string; // New prop
107+
}
108+
109+
export function AggregateParameterInput(props: AggregateParameterInputProps) {
110+
const { field, showTip, hasError, placeholder, endpointPath } = props;
111+
const { value, onChange } = field;
112+
113+
const presets = getAggregatePresets(endpointPath);
114+
115+
return (
116+
<div className="flex flex-col space-y-1">
117+
<Input
118+
{...field}
119+
className={cn(
120+
"h-auto truncate rounded-none border-0 bg-transparent py-5 font-mono text-sm focus-visible:ring-0 focus-visible:ring-offset-0",
121+
showTip && "lg:pr-10",
122+
hasError && "text-destructive-text",
123+
)}
124+
placeholder={placeholder}
125+
/>
126+
<Select
127+
value={presets.find((p) => p.value === value)?.value || ""}
128+
onValueChange={(selectedValue) => {
129+
if (selectedValue) {
130+
onChange({ target: { value: selectedValue } });
131+
}
132+
}}
133+
>
134+
<SelectTrigger
135+
className={cn(
136+
"h-8 border-dashed bg-transparent text-xs focus:ring-0 focus:ring-offset-0",
137+
!presets.find((p) => p.value === value) &&
138+
"text-muted-foreground",
139+
)}
140+
>
141+
<SelectValue placeholder="Select a preset (optional)" />
142+
</SelectTrigger>
143+
<SelectContent className="font-mono">
144+
{presets.map((preset) => (
145+
<SelectItem key={preset.value} value={preset.value}>
146+
{preset.label}
147+
</SelectItem>
148+
))}
149+
</SelectContent>
150+
</Select>
151+
</div>
152+
);
153+
}

apps/playground-web/src/app/insight/[blueprint_slug]/blueprint-playground.client.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
import { z } from "zod";
3636
import { MultiNetworkSelector } from "../../../components/blocks/NetworkSelectors";
3737
import type { BlueprintParameter, BlueprintPathMetadata } from "../utils";
38+
import { AggregateParameterInput } from "./aggregate-parameter-input.client";
3839

3940
export function BlueprintPlayground(props: {
4041
metadata: BlueprintPathMetadata;
@@ -521,7 +522,8 @@ function ParameterSection(props: {
521522
}}
522523
showTip={showTip}
523524
hasError={hasError}
524-
placeholder={placeholder}
525+
placeholder={param.description || param.name}
526+
endpointPath={props.path}
525527
/>
526528

527529
{showTip && (
@@ -586,6 +588,7 @@ function ParameterInput(props: {
586588
showTip: boolean;
587589
hasError: boolean;
588590
placeholder: string;
591+
endpointPath: string;
589592
}) {
590593
const { param, field, showTip, hasError, placeholder } = props;
591594

@@ -622,6 +625,18 @@ function ParameterInput(props: {
622625
);
623626
}
624627

628+
if (param.name === "aggregate") {
629+
return (
630+
<AggregateParameterInput
631+
field={field}
632+
showTip={showTip}
633+
hasError={hasError}
634+
placeholder={placeholder}
635+
endpointPath={props.endpointPath}
636+
/>
637+
);
638+
}
639+
625640
return (
626641
<Input
627642
{...field}

0 commit comments

Comments
 (0)