Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL Logs #207

Merged
merged 13 commits into from
Mar 6, 2024
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ export function DataTable<TData, TValue>({

return (
<div>
<div className="flex items-center py-4">
<h2 className="text-xl font-medium">Table Data</h2>
<div className="flex items-center">
{!!data.length && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
Expand Down Expand Up @@ -87,7 +86,7 @@ export function DataTable<TData, TValue>({
</DropdownMenu>
)}
</div>
<div className="rounded-md border">
<div className="mt-4 rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
Expand Down
2 changes: 1 addition & 1 deletion packages/web/app/_components/latest-projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Link from "next/link";
import TimeAgo from "javascript-time-ago";
import { useState } from "react";
import { Paginator } from "./paginator";
import { Paginator } from "@/components/paginator";
import { TypographyH3 } from "@/components/typography-h3";
import { type store } from "@/lib/store";
import TeamAvatar from "@/components/team-avatar";
Expand Down
3 changes: 2 additions & 1 deletion packages/web/app/_components/latest-tables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from "next/link";
import TimeAgo from "javascript-time-ago";
import { useEffect, useState } from "react";
import { chainsMap } from "../../lib/chains-map";
import { Paginator } from "./paginator";
import { Paginator } from "@/components/paginator";
import { TypographyH3 } from "@/components/typography-h3";
import ChainSelector from "@/components/chain-selector";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -85,6 +85,7 @@ export function LatestTables({ initialData }: { initialData: Table[] }) {
pageSize={pageSize}
page={page}
setPage={setPage}
disabled={loading}
/>
</>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/web/app/_components/popular-tables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Link from "next/link";
import { useEffect, useState } from "react";
import { chainsMap } from "../../lib/chains-map";
import { Paginator } from "./paginator";
import { Paginator } from "@/components/paginator";
import { TypographyH3 } from "@/components/typography-h3";
import ChainSelector from "@/components/chain-selector";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -101,6 +101,7 @@ export function PopularTables({
pageSize={pageSize}
page={page}
setPage={setPage}
disabled={loading}
/>
</>
);
Expand Down
24 changes: 24 additions & 0 deletions packages/web/app/sql-log/_components/explorer-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import { ExternalLink } from "lucide-react";
import { Button } from "@/components/ui/button";

export default function ExplorerButton({
explorerName,
txnUrl,
}: {
explorerName: string;
txnUrl: string;
}) {
return (
<Button
variant="ghost"
size="default"
className="ml-auto gap-x-1"
onClick={() => window.open(txnUrl)}
>
<ExternalLink className="shrink-0" />
View on {explorerName}
</Button>
);
}
172 changes: 172 additions & 0 deletions packages/web/app/sql-log/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import TimeAgo from "javascript-time-ago";
import { AlertCircle } from "lucide-react";
import { notFound } from "next/navigation";
import Link from "next/link";
import ExplorerButton from "./_components/explorer-button";
import {
MetricCard,
MetricCardContent,
MetricCardFooter,
MetricCardHeader,
MetricCardTitle,
} from "@/components/metric-card";
import AddressDisplay from "@/components/address-display";
import { chainsMap } from "@/lib/chains-map";
import { cn } from "@/lib/utils";
import { getSqlLog } from "@/lib/validator-queries";
import { blockExplorers } from "@/lib/block-explorers";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";

const timeAgo = new TimeAgo("en-US");

export default async function TxnPage({
searchParams,
}: {
searchParams: { chainId: string; txnHash: string; index: number };
}) {
const chainNumber = parseInt(searchParams.chainId, 10);
const log = await getSqlLog(
chainNumber,
searchParams.txnHash,
searchParams.index,
);

const chain = chainsMap.get(chainNumber);

if (!chain) {
notFound();
}

const explorer = blockExplorers.get(chainNumber);

return (
<div className="container flex flex-col gap-10 py-10">
<div className="flex items-center gap-2">
{log.error && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<AlertCircle />
</TooltipTrigger>
<TooltipContent>Txn Status: Error</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<div className="flex flex-col">
<AddressDisplay
address={log.txHash}
name="txn hash"
numCharacters={8}
copy
className={cn(
"text-3xl font-bold text-foreground",
log.error && "text-red-500",
)}
/>
<div className="text-base text-muted-foreground">
Log index:{" "}
<span className="font-bold text-foreground">{log.eventIndex}</span>
</div>
</div>
{explorer && (
<ExplorerButton
explorerName={explorer.explorer}
txnUrl={explorer.txUrl(searchParams.txnHash)}
/>
)}
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Sent by</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>
<AddressDisplay
address={log.caller}
copy
className="text-3xl text-foreground"
/>
</MetricCardContent>
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Timestamp</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>
{timeAgo.format(log.timestamp * 1000)}
</MetricCardContent>
<MetricCardFooter>
{new Date(log.timestamp * 1000).toLocaleString()}
</MetricCardFooter>
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Status</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>
{log.error ? "Error" : "Success"}
</MetricCardContent>
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Chain Id</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>{chainNumber}</MetricCardContent>
<MetricCardFooter>{chain.name}</MetricCardFooter>
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Block Number</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>{log.blockNumber}</MetricCardContent>
{explorer && (
<MetricCardFooter>
<Link href={explorer.blockUrl(log.blockNumber)}>
View on {explorer.explorer}
</Link>
</MetricCardFooter>
)}
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Txn Index</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>{log.txIndex}</MetricCardContent>
</MetricCard>
<MetricCard>
<MetricCardHeader>
<MetricCardTitle>Event Type</MetricCardTitle>
</MetricCardHeader>
<MetricCardContent>
{log.eventType === "ContractCreateTable"
? "Create Table"
: "Run SQL"}
</MetricCardContent>
</MetricCard>
</div>
<div>
<label className="text-sm uppercase text-muted-foreground">
Statement:
</label>
<pre className="whitespace-break-spaces rounded-sm border border-gray-300 bg-gray-100 p-4">
{log.statement}
</pre>
</div>
{log.error && (
<div>
<label className="text-sm uppercase text-muted-foreground">
Error:
</label>
<pre className="whitespace-break-spaces rounded-sm border border-gray-300 bg-gray-100 p-4">
{log.error}
</pre>
</div>
)}
</div>
);
}
40 changes: 30 additions & 10 deletions packages/web/components/address-display.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { Copy } from "lucide-react";
import { type HTMLProps } from "react";
import { Button } from "./ui/button";
import {
Tooltip,
Expand All @@ -9,12 +10,16 @@ import {
TooltipTrigger,
} from "./ui/tooltip";
import { useToast } from "@/components/ui/use-toast";
import { cn } from "@/lib/utils";

export default function AddressDisplay({
address,
numCharacters = 5,
copy = false,
}: {
name,
className,
...rest
}: HTMLProps<HTMLSpanElement> & {
address: string;
numCharacters?: number;
copy?: boolean;
Expand All @@ -30,13 +35,15 @@ export default function AddressDisplay({
.then(function () {
toast({
title: "Done!",
description: "The address has been copied to your clipboard.",
description: `The ${
name ?? "address"
} has been copied to your clipboard.`,
duration: 2000,
});
})
.catch(function (err) {
const errMessage = [
"Could not copy the address to your clipboard.",
`Could not copy the ${name ?? "address"} to your clipboard.`,
typeof err.message === "string" ? err.message : undefined,
]
.filter((s) => s)
Expand All @@ -51,25 +58,38 @@ export default function AddressDisplay({
};

return (
<div className="flex items-center gap-1">
<p className="text-sm text-muted-foreground">
{address.slice(0, numCharacters)}...{address.slice(-numCharacters)}
</p>
<div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span
className={cn("text-sm text-muted-foreground", className)}
{...rest}
>
{address.slice(0, numCharacters)}...
{address.slice(-numCharacters)}
</span>
</TooltipTrigger>
<TooltipContent>
<p>{address}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{copy && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={"ghost"}
className="h-auto p-1"
className="ml-1 h-auto p-1"
onClick={handleCopy}
>
<Copy className="h-4 w-4 stroke-slate-300" />
<span className="sr-only">Copy address</span>
<span className="sr-only">Copy {name ?? "address"}</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Click to copy address</p>
<p>Click to copy {name ?? "address"}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
Expand Down
Loading
Loading