diff --git a/package-lock.json b/package-lock.json
index 5029598..19aa32e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.52.0",
+ "react-timeago": "^8.3.0",
"react-truncate-inside": "^1.0.3",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
@@ -6015,6 +6016,15 @@
}
}
},
+ "node_modules/react-timeago": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-8.3.0.tgz",
+ "integrity": "sha512-BeR0hj/5qqTc2+zxzBSQZMky6MmqwOtKseU3CSmcjKR5uXerej2QY34v2d+cdz11PoeVfAdWLX+qjM/UdZkUUg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-truncate-inside": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/react-truncate-inside/-/react-truncate-inside-1.0.3.tgz",
diff --git a/package.json b/package.json
index f8237df..8a2e932 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.52.0",
+ "react-timeago": "^8.3.0",
"react-truncate-inside": "^1.0.3",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
diff --git a/src/app/batches/[height]/page.tsx b/src/app/batches/[height]/page.tsx
index d777b2d..33a5ec6 100644
--- a/src/app/batches/[height]/page.tsx
+++ b/src/app/batches/[height]/page.tsx
@@ -4,6 +4,7 @@
import { useParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
import { DetailsLayout } from "@/components/details/layout";
import DataTable from "@/components/ui/DataTable";
@@ -19,6 +20,7 @@ export interface GetBatchQueryResponse {
blocks: {
height: string;
hash: string;
+ createdAt: string;
result: {
stateRoot: string;
};
@@ -28,6 +30,7 @@ export interface GetBatchQueryResponse {
}[];
settlementTransactionHash: string;
height: string;
+ createdAt: string;
}
| undefined;
};
@@ -44,9 +47,11 @@ export default function BatchDetail() {
batch(where: { height: $height }) {
height
settlementTransactionHash
+ createdAt
blocks {
height
hash
+ createdAt
result { stateRoot }
_count { transactions }
}
@@ -95,6 +100,12 @@ export default function BatchDetail() {
label: "Blocks",
value: `${data?.batch?.blocks?.length ?? "—"}`,
},
+ {
+ label: "Created",
+ value: data?.batch?.createdAt
+ ?
+ : "—",
+ },
];
const blocks: TableItem[] = (data?.batch?.blocks || []).map((item) => ({
@@ -102,6 +113,7 @@ export default function BatchDetail() {
hash: item.hash,
transactions: item._count?.transactions?.toString(),
stateRoot: item.result?.stateRoot,
+ createdAt: item.createdAt,
}));
return (
@@ -123,6 +135,7 @@ export default function BatchDetail() {
loading={loading}
navigationPath="/blocks/{hash}"
copyKeys={["hash", "stateRoot"]}
+ columnRenderers={{ createdAt: (item) => }}
/>
);
diff --git a/src/app/blocks/[hash]/page.tsx b/src/app/blocks/[hash]/page.tsx
index 43839b8..6fade7c 100644
--- a/src/app/blocks/[hash]/page.tsx
+++ b/src/app/blocks/[hash]/page.tsx
@@ -2,6 +2,8 @@
import { useParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
+import { CircleCheck, CircleX } from "lucide-react";
import { DetailsLayout } from "@/components/details/layout";
import DataTable from "@/components/ui/DataTable";
@@ -19,6 +21,7 @@ export interface GetBlockQueryResponse {
| {
hash: string;
height: string;
+ createdAt: string;
result: {
stateRoot: string;
};
@@ -49,6 +52,7 @@ export default function BlockDetail() {
block(where: { hash: $hash }) {
height
hash
+ createdAt
result { stateRoot }
transactions {
tx { hash, methodId, sender, nonce }
@@ -100,11 +104,20 @@ export default function BlockDetail() {
label: "StateRoot",
value: data?.block?.result.stateRoot ?? "—",
},
+ {
+ label: "Created",
+ value: data?.block?.createdAt ? (
+
+ ) : (
+ "—"
+ ),
+ },
];
const transactions: TableItem[] = (data?.block?.transactions || []).map(
(tx) => ({
...tx.tx,
+ createdAt: data?.block?.createdAt || "",
status: {
isSuccess: tx.status === true,
message: tx.statusMessage,
@@ -112,6 +125,27 @@ export default function BlockDetail() {
}),
);
+ const statusRenderer = (item: TableItem) => {
+ const { isSuccess, message } = item.status;
+
+ return (
+
+ {isSuccess === true ? (
+
+ ) : (
+ <>
+
+ {message !== undefined && (
+
+ {message}
+
+ )}
+ >
+ )}
+
+ );
+ };
+
return (
,
+ status: statusRenderer,
+ }}
/>
);
diff --git a/src/app/settlements/[transactionHash]/page.tsx b/src/app/settlements/[transactionHash]/page.tsx
index 8d4981d..527c89a 100644
--- a/src/app/settlements/[transactionHash]/page.tsx
+++ b/src/app/settlements/[transactionHash]/page.tsx
@@ -4,6 +4,7 @@
import { useParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
import Truncate from "react-truncate-inside/es";
import { DetailsLayout } from "@/components/details/layout";
@@ -20,12 +21,14 @@ export interface GetSettlementQueryResponse {
batches: {
height: string;
settlementTransactionHash: string;
+ createdAt: string;
_count: {
blocks: number;
};
}[];
transactionHash: string;
promisedMessagesHash: string;
+ createdAt: string;
}
| undefined;
};
@@ -43,10 +46,12 @@ export default function SettlementDetail() {
batches {
height
settlementTransactionHash
+ createdAt
_count { blocks }
}
transactionHash
promisedMessagesHash
+ createdAt
}
}`;
@@ -92,6 +97,14 @@ export default function SettlementDetail() {
label: "Batches",
value: `${data?.settlement?.batches?.length ?? "—"}`,
},
+ {
+ label: "Created",
+ value: data?.settlement?.createdAt ? (
+
+ ) : (
+ "—"
+ ),
+ },
];
const batches: TableItem[] = (data?.settlement?.batches || []).map(
@@ -99,6 +112,7 @@ export default function SettlementDetail() {
height: item.height,
settlementTransactionHash: item.settlementTransactionHash,
blocks: item._count?.blocks?.toString(),
+ createdAt: item.createdAt,
}),
);
@@ -128,6 +142,7 @@ export default function SettlementDetail() {
navigationPath="/batches/{height}"
copyKeys={["settlementTransactionHash"]}
columnRenderers={{
+ createdAt: (item) => ,
settlementTransactionHash: (item) => (
();
const [data, setData] = useState();
const [loading, setLoading] = useState(true);
@@ -47,7 +49,7 @@ export default function BlockDetail() {
executionResult {
status
statusMessage
- block { batch { proof settlementTransactionHash } }
+ block { createdAt batch { proof settlementTransactionHash } }
}
}
}`;
@@ -131,6 +133,12 @@ export default function BlockDetail() {
label: "Sender",
value: data?.transaction?.sender ?? "—",
},
+ {
+ label: "Created",
+ value: data?.transaction?.executionResult?.block?.createdAt
+ ?
+ : "—",
+ },
];
return (
diff --git a/src/components/batches/BatchesPageClient.tsx b/src/components/batches/BatchesPageClient.tsx
index 0ce5d48..96d208b 100644
--- a/src/components/batches/BatchesPageClient.tsx
+++ b/src/components/batches/BatchesPageClient.tsx
@@ -3,6 +3,7 @@
/* eslint-disable no-underscore-dangle */
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
import { z } from "zod";
import useQueryParams from "@/hooks/use-query-params";
@@ -17,6 +18,7 @@ export interface TableItem {
height: string;
blocks: string;
settlementTransactionHash: string;
+ createdAt: string;
}
export interface GetBatchesQueryResponse {
@@ -24,6 +26,7 @@ export interface GetBatchesQueryResponse {
batches: {
height: string;
settlementTransactionHash: string;
+ createdAt: string;
_count: {
blocks: number;
};
@@ -40,6 +43,7 @@ export const columns: Record = {
height: "Height",
blocks: "Blocks",
settlementTransactionHash: "Settlement Transaction Hash",
+ createdAt: "Created",
};
const formSchema = z.object({
@@ -71,6 +75,7 @@ const graphqlQuery = `query GetBatches($take: Int!, $skip: Int!, $where: BatchWh
batches(take: $take, skip: $skip, orderBy: {height: desc}, where: $where) {
settlementTransactionHash
height
+ createdAt
_count { blocks }
}
aggregateBatch(where: $where) { _count { _all } }
@@ -111,6 +116,7 @@ export default function BatchesPageClient() {
height: item.height,
settlementTransactionHash: item.settlementTransactionHash,
blocks: item._count?.blocks?.toString() || "0",
+ createdAt: item.createdAt,
}));
setData(mappedItems);
@@ -148,6 +154,7 @@ export default function BatchesPageClient() {
navigationPath="/batches/{height}"
copyKeys={["settlementTransactionHash"]}
columnRenderers={{
+ createdAt: (item) => ,
settlementTransactionHash: (item) => (
= {
hash: "Hash",
transactions: "Transactions",
stateRoot: "State Root",
+ createdAt: "Created",
};
const formSchema = z.object({
@@ -83,6 +87,7 @@ const graphqlQuery = `query GetBlocks($take: Int!, $skip: Int!, $where: BlockWhe
blocks(take: $take, skip: $skip, orderBy: {height: desc}, where: $where) {
height
hash
+ createdAt
result { stateRoot }
_count { transactions }
}
@@ -91,7 +96,7 @@ const graphqlQuery = `query GetBlocks($take: Int!, $skip: Int!, $where: BlockWhe
const queryTransformer = (
filters: Record,
- schema: Record
+ schema: Record,
) => {
const where: Record = {};
Object.entries(filters).forEach(([key, value]) => {
@@ -123,7 +128,7 @@ const queryTransformer = (
export default function BlocksPageClient() {
const [page, view, filters, setPage, setView, setFilters] = useQueryParams(
columns,
- querySchema
+ querySchema,
);
const [data, setData] = useState([]);
const [totalCount, setTotalCount] = useState("0");
@@ -156,11 +161,12 @@ export default function BlocksPageClient() {
hash: item.hash,
transactions: item._count?.transactions?.toString(),
stateRoot: item.result.stateRoot,
+ createdAt: item.createdAt,
}));
setData(mappedItems);
setTotalCount(
- result.data?.aggregateBlock?._count?._all?.toString() || "0"
+ result.data?.aggregateBlock?._count?._all?.toString() || "0",
);
setLoading(false);
} catch (error) {
@@ -191,6 +197,9 @@ export default function BlocksPageClient() {
onViewChange={setView}
navigationPath="/blocks/{hash}"
copyKeys={["hash", "stateRoot"]}
+ columnRenderers={{
+ createdAt: (item) => ,
+ }}
/>
);
}
diff --git a/src/components/dashboard/DashboardStats.tsx b/src/components/dashboard/DashboardStats.tsx
index 4be9887..caaf2bb 100644
--- a/src/components/dashboard/DashboardStats.tsx
+++ b/src/components/dashboard/DashboardStats.tsx
@@ -141,7 +141,7 @@ export default function DashboardStats() {
height
hash
}
- settlements(take: 1, orderBy: { transactionHash: desc }) {
+ settlements(take: 1, orderBy: { createdAt: desc }) {
transactionHash
promisedMessagesHash
}
diff --git a/src/components/settlements/settlementsPageClient.tsx b/src/components/settlements/settlementsPageClient.tsx
index 978669a..3f8c1da 100644
--- a/src/components/settlements/settlementsPageClient.tsx
+++ b/src/components/settlements/settlementsPageClient.tsx
@@ -3,6 +3,7 @@
/* eslint-disable no-underscore-dangle */
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
import { z } from "zod";
import DataTable from "@/components/ui/DataTable";
@@ -18,6 +19,7 @@ export interface TableItem {
transactionHash: string;
promisedMessagesHash: string;
batches: string;
+ createdAt: string;
}
export interface GetSettlementsQueryResponse {
@@ -25,6 +27,7 @@ export interface GetSettlementsQueryResponse {
settlements: {
transactionHash: string;
promisedMessagesHash: string;
+ createdAt: string;
_count: {
batches: number;
};
@@ -41,6 +44,7 @@ export const columns: Record = {
transactionHash: "Transaction Hash",
promisedMessagesHash: "Promised Messages Hash",
batches: "Batches",
+ createdAt: "Created",
};
export function TransactionHash(props: { transactionHash?: string | null }) {
@@ -80,9 +84,10 @@ const fields: FilterFieldDef[] = [
];
const graphqlQuery = `query GetSettlements($take: Int!, $skip: Int!, $where: SettlementWhereInput) {
- settlements(take: $take, skip: $skip, where: $where) {
+ settlements(take: $take, skip: $skip, orderBy: { createdAt: desc }, where: $where) {
transactionHash
promisedMessagesHash
+ createdAt
_count { batches }
}
aggregateSettlement(where: $where) { _count { _all } }
@@ -125,6 +130,7 @@ export default function SettlementsPageClient() {
transactionHash: item.transactionHash,
promisedMessagesHash: item.promisedMessagesHash,
batches: item._count?.batches?.toString() || "0",
+ createdAt: item.createdAt,
}));
setData(mappedItems);
@@ -167,6 +173,7 @@ export default function SettlementsPageClient() {
transactionHash={String(item.transactionHash ?? "")}
/>
),
+ createdAt: (item) => ,
}}
/>
);
diff --git a/src/components/transactions/TransactionsPageClient.tsx b/src/components/transactions/TransactionsPageClient.tsx
index 631d89f..aa7ed42 100644
--- a/src/components/transactions/TransactionsPageClient.tsx
+++ b/src/components/transactions/TransactionsPageClient.tsx
@@ -3,6 +3,7 @@
/* eslint-disable no-underscore-dangle */
import { useCallback, useEffect, useState } from "react";
+import TimeAgo from "react-timeago";
import { z } from "zod";
import { CircleCheck, CircleX } from "lucide-react";
@@ -23,6 +24,9 @@ export interface GetTransactionsQueryResponse {
executionResult: {
status: boolean;
statusMessage?: string;
+ block: {
+ createdAt: string;
+ };
};
}[];
aggregateTransaction: {
@@ -38,6 +42,7 @@ export interface TableItem {
methodId: string;
sender: string;
nonce: string;
+ createdAt: string;
status: { isSuccess: boolean; message?: string };
}
@@ -46,6 +51,7 @@ export const columns: Record = {
methodId: "Method ID",
sender: "Sender",
nonce: "Nonce",
+ createdAt: "Created",
status: "Status",
};
@@ -83,7 +89,7 @@ const fields: FilterFieldDef[] = [
];
const graphqlQuery = `query GetTransactions($take: Int!, $skip: Int!, $where: TransactionWhereInput) {
- transactions(take: $take, skip: $skip, where: $where) {
+ transactions(take: $take, skip: $skip, orderBy: { executionResult: { block: { createdAt: desc } } }, where: $where) {
methodId
hash
nonce
@@ -91,6 +97,9 @@ const graphqlQuery = `query GetTransactions($take: Int!, $skip: Int!, $where: Tr
executionResult {
status
statusMessage
+ block {
+ createdAt
+ }
}
}
aggregateTransaction(where: $where) {
@@ -116,6 +125,7 @@ export const statusRenderer = (item: TableItem) => {
);
};
+
export default function TransactionsPageClient() {
const [page, view, filters, setPage, setView, setFilters] = useQueryParams(
columns,
@@ -160,6 +170,7 @@ export default function TransactionsPageClient() {
methodId: item.methodId,
sender: item.sender,
nonce: item.nonce,
+ createdAt: item.executionResult?.block?.createdAt || "-",
status: statusDisplay,
};
});
@@ -196,7 +207,7 @@ export default function TransactionsPageClient() {
onViewChange={setView}
navigationPath="/transactions/{hash}"
copyKeys={["hash", "sender", "methodId"]}
- columnRenderers={{ status: statusRenderer }}
+ columnRenderers={{ status: statusRenderer, createdAt: (item) => }}
/>
);
}