diff --git a/src/api/errors.tsx b/src/api/errors.tsx
new file mode 100644
index 0000000..6d43fb6
--- /dev/null
+++ b/src/api/errors.tsx
@@ -0,0 +1,164 @@
+import type { QueryHookOptions } from '@apollo/client';
+import { gql, useQuery } from '@apollo/client';
+import { endOfToday } from 'date-fns/endOfToday';
+import { startOfToday } from 'date-fns/startOfToday';
+
+import type { ErrorEventWhereInput } from '@/__generated__/graphql';
+import { QUERY_DEFAULT_LIMIT } from '@/constants/query-default-limit';
+import { QUERY_RECENT_LIMIT } from '@/constants/query-recent-limit';
+import type { ErrorEventSorts } from '@/constants/query-sorts';
+import { ERROR_EVENT_SORTS } from '@/constants/query-sorts';
+import type {
+ ErrorEventListResponse,
+ ErrorEventResponse,
+ ErrorEventsStatsResponse,
+ RecentErrorEventsResponse
+} from '@/schemas';
+import type { PaginatedQueryVariables } from '@/types/query';
+
+export const errors = {
+ useGetAll: (
+ config?: QueryHookOptions<
+ ErrorEventListResponse,
+ PaginatedQueryVariables
+ >
+ ) => {
+ const GET_ERROR_EVENTS = gql`
+ query GetErrorEvents(
+ $limit: Int
+ $offset: Int
+ $orderBy: [ErrorEventOrderByInput!]
+ $where: ErrorEventWhereInput
+ ) {
+ errorEvents(
+ limit: $limit
+ offset: $offset
+ orderBy: $orderBy
+ where: $where
+ ) {
+ errorDocs
+ errorModule
+ errorName
+ errorType
+ extrinsicHash
+ id
+ timestamp
+ block {
+ height
+ }
+ }
+ meta: errorEventsConnection(orderBy: id_ASC, where: $where) {
+ totalCount
+ }
+ }
+ `;
+
+ return useQuery<
+ ErrorEventListResponse,
+ PaginatedQueryVariables
+ >(GET_ERROR_EVENTS, {
+ ...config,
+ variables: {
+ orderBy: config?.variables?.orderBy ?? ERROR_EVENT_SORTS.timestamp.DESC,
+ limit: config?.variables?.limit ?? QUERY_DEFAULT_LIMIT,
+ offset: config?.variables?.offset ?? 0,
+ where: config?.variables?.where
+ }
+ });
+ },
+ useGetRecent: (
+ config?: Omit, 'variables'>
+ ) => {
+ const GET_RECENT_ERROR_EVENTS = gql`
+ query GetRecentErrorEvents(
+ $limit: Int
+ $offset: Int
+ $orderBy: [ErrorEventOrderByInput!]
+ $where: ErrorEventWhereInput
+ ) {
+ errorEvents(
+ limit: $limit
+ offset: $offset
+ orderBy: $orderBy
+ where: $where
+ ) {
+ errorDocs
+ errorModule
+ errorName
+ errorType
+ extrinsicHash
+ id
+ timestamp
+ block {
+ height
+ }
+ }
+ }
+ `;
+
+ return useQuery<
+ RecentErrorEventsResponse,
+ PaginatedQueryVariables
+ >(GET_RECENT_ERROR_EVENTS, {
+ ...config,
+ variables: {
+ orderBy: ERROR_EVENT_SORTS.timestamp.DESC,
+ limit: QUERY_RECENT_LIMIT
+ }
+ });
+ },
+ useGetStats: (
+ config?: Omit, 'variables'>
+ ) => {
+ const startDate = startOfToday().toISOString();
+ const endDate = endOfToday().toISOString();
+
+ const GET_ERROR_EVENTS_STATS = gql`
+ query GetErrorEventsStats($startDate: DateTime!, $endDate: DateTime!) {
+ last24Hour: errorEventsConnection(
+ orderBy: id_ASC
+ where: { timestamp_gte: $startDate, timestamp_lte: $endDate }
+ ) {
+ totalCount
+ }
+ allTime: errorEventsConnection(orderBy: id_ASC) {
+ totalCount
+ }
+ }
+ `;
+
+ return useQuery(GET_ERROR_EVENTS_STATS, {
+ ...config,
+ variables: {
+ startDate,
+ endDate
+ }
+ });
+ },
+ getByHash: () => {
+ const GET_ERROR_EVENT_BY_HASH = gql`
+ query GetErrorEventByHash($hash: String!) {
+ errorEvents: errorEvents(where: { extrinsicHash_eq: $hash }) {
+ errorDocs
+ errorModule
+ errorName
+ errorType
+ extrinsicHash
+ id
+ timestamp
+ block {
+ height
+ }
+ }
+ }
+ `;
+
+ return {
+ useQuery: (hash: string, config?: QueryHookOptions) =>
+ useQuery(GET_ERROR_EVENT_BY_HASH, {
+ ...config,
+ variables: { hash }
+ })
+ };
+ }
+};
diff --git a/src/api/index.tsx b/src/api/index.tsx
index c5d78a8..059ed5a 100644
--- a/src/api/index.tsx
+++ b/src/api/index.tsx
@@ -3,6 +3,7 @@ import { useFetchClient } from '@/hooks/useFetchClient';
import { accounts } from './accounts';
import { blocks } from './blocks';
import { chainStatus } from './chain-status';
+import { errors } from './errors';
import { highSecuritySets } from './high-security-sets';
import { minerLeaderboard } from './miner-leaderboard';
import { minerRewards } from './miner-rewards';
@@ -16,6 +17,7 @@ const useApiClient = () => {
const api = {
accounts,
chainStatus,
+ errors,
transactions,
reversibleTransactions,
search: search(fetcher),
diff --git a/src/components/common/table-columns/ERROR_EVENT_COLUMNS.tsx b/src/components/common/table-columns/ERROR_EVENT_COLUMNS.tsx
new file mode 100644
index 0000000..a27c5eb
--- /dev/null
+++ b/src/components/common/table-columns/ERROR_EVENT_COLUMNS.tsx
@@ -0,0 +1,56 @@
+import { createColumnHelper } from '@tanstack/react-table';
+
+import { LinkWithCopy } from '@/components/ui/composites/link-with-copy/LinkWithCopy';
+import { TimestampDisplay } from '@/components/ui/timestamp-display';
+import { RESOURCES } from '@/constants/resources';
+import type { ErrorEvent } from '@/schemas';
+import { formatTxAddress } from '@/utils/formatter';
+
+const columnHelper = createColumnHelper();
+
+export const ERROR_EVENT_COLUMNS = [
+ columnHelper.accessor('extrinsicHash', {
+ id: 'extrinsicHash',
+ header: 'Extrinsic Hash',
+ cell: (props) =>
+ props.getValue() ? (
+
+ ) : (
+ 'Is not available'
+ ),
+ enableSorting: false
+ }),
+ columnHelper.accessor('block.height', {
+ id: 'block_height',
+ header: 'Block',
+ cell: (props) => (
+
+ ),
+ enableSorting: true
+ }),
+ columnHelper.accessor('timestamp', {
+ id: 'timestamp',
+ header: 'Timestamp',
+ cell: (props) => ,
+ enableSorting: true
+ }),
+ columnHelper.accessor('errorType', {
+ id: 'errorType',
+ header: 'Type',
+ cell: (props) => props.getValue(),
+ enableSorting: true
+ }),
+ columnHelper.accessor('errorName', {
+ id: 'errorName',
+ header: 'Name',
+ cell: (props) => props.getValue() ?? '-',
+ enableSorting: true
+ })
+];
diff --git a/src/components/features/error-event-details/error-event-information/ErrorEventInformation.tsx b/src/components/features/error-event-details/error-event-information/ErrorEventInformation.tsx
new file mode 100644
index 0000000..fa6ffe7
--- /dev/null
+++ b/src/components/features/error-event-details/error-event-information/ErrorEventInformation.tsx
@@ -0,0 +1,93 @@
+import { notFound } from '@tanstack/react-router';
+import * as React from 'react';
+
+import useApiClient from '@/api';
+import { DataList } from '@/components/ui/composites/data-list/DataList';
+import { LinkWithCopy } from '@/components/ui/composites/link-with-copy/LinkWithCopy';
+import { TextWithCopy } from '@/components/ui/composites/text-with-copy/TextWithCopy';
+import { RESOURCES } from '@/constants/resources';
+import type { ErrorEvent } from '@/schemas';
+import { formatTimestamp } from '@/utils/formatter';
+
+export interface ErrorEventInformationProps {
+ id: string;
+}
+
+export const ErrorEventInformation: React.FC = ({
+ id
+}) => {
+ const api = useApiClient();
+ const { data, loading } = api.errors.getByHash().useQuery(id);
+
+ if (!loading && (!data || data.errorEvents.length <= 0)) throw notFound();
+
+ const event = data?.errorEvents[0];
+
+ const information: Partial[] = [
+ {
+ timestamp: event?.timestamp,
+ block: event?.block,
+ extrinsicHash: event?.extrinsicHash,
+ errorType: event?.errorType,
+ errorModule: event?.errorModule,
+ errorName: event?.errorName,
+ errorDocs: event?.errorDocs
+ }
+ ];
+
+ return (
+
+ }}
+ withControls
+ />
+ );
+};
diff --git a/src/components/features/error-events-listing/error-events-table/hook.tsx b/src/components/features/error-events-listing/error-events-table/hook.tsx
new file mode 100644
index 0000000..f28f75e
--- /dev/null
+++ b/src/components/features/error-events-listing/error-events-table/hook.tsx
@@ -0,0 +1,141 @@
+import { useSearch } from '@tanstack/react-router';
+import type {
+ OnChangeFn,
+ PaginationState,
+ SortingState
+} from '@tanstack/react-table';
+import { getCoreRowModel, useReactTable } from '@tanstack/react-table';
+import { parseAsInteger, parseAsStringLiteral, useQueryState } from 'nuqs';
+import { useEffect, useMemo, useState } from 'react';
+
+import useApiClient from '@/api';
+import { ERROR_EVENT_COLUMNS } from '@/components/common/table-columns/ERROR_EVENT_COLUMNS';
+import { DATA_POOL_INTERVAL } from '@/constants/data-pool-interval';
+import { QUERY_DEFAULT_LIMIT } from '@/constants/query-default-limit';
+import type { ErrorEventSorts } from '@/constants/query-sorts';
+import { ERROR_EVENT_SORTS_LITERALS } from '@/constants/query-sorts';
+import type { ErrorEvent } from '@/schemas';
+import { transformSortLiteral } from '@/utils/transform-sort';
+
+export const useErrorEventsTable = () => {
+ const api = useApiClient();
+ const { block } = useSearch({
+ strict: false
+ }) as any;
+
+ const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
+ const [limit, setLimit] = useQueryState(
+ 'limit',
+ parseAsInteger.withDefault(QUERY_DEFAULT_LIMIT)
+ );
+ const [sortBy, setSortBy] = useQueryState(
+ 'sortBy',
+ parseAsStringLiteral(ERROR_EVENT_SORTS_LITERALS)
+ );
+
+ const currentPageIndex = page - 1;
+
+ const sortingValue: SortingState = transformSortLiteral(sortBy);
+ const paginationValue: PaginationState = {
+ pageSize: limit,
+ pageIndex: currentPageIndex
+ };
+
+ const handleChangeSorting: OnChangeFn = (sorting) => {
+ if (typeof sorting === 'function') {
+ const newValue = sorting(sortingValue);
+
+ if (newValue[0]?.id) {
+ const key = newValue[0].id;
+ const order = newValue[0].desc ? 'DESC' : 'ASC';
+
+ const newSortBy = `${key}_${order}` as ErrorEventSorts;
+
+ setSortBy(newSortBy);
+ } else {
+ setSortBy(null);
+ }
+ } else if (sorting[0]?.id) {
+ const key = sorting[0].id;
+ const order = sorting[0].desc ? 'DESC' : 'ASC';
+
+ const newSortBy = `${key}_${order}` as ErrorEventSorts;
+
+ setSortBy(newSortBy);
+ }
+ };
+
+ const handleChangePagination: OnChangeFn = (pagination) => {
+ if (typeof pagination === 'function') {
+ const newPagination = pagination(paginationValue);
+
+ setPage(newPagination.pageIndex + 1);
+ setLimit(newPagination.pageSize);
+ } else {
+ setPage(pagination.pageIndex + 1);
+ setLimit(pagination.pageSize);
+ }
+ };
+
+ const {
+ loading,
+ data,
+ error: fetchError
+ } = api.errors.useGetAll({
+ pollInterval: DATA_POOL_INTERVAL,
+ variables: {
+ orderBy: sortBy,
+ limit,
+ offset: currentPageIndex * limit,
+ ...(block && {
+ where: {
+ block: { height_eq: Number(block) }
+ }
+ })
+ }
+ });
+
+ const errorEventColumns = useMemo(() => ERROR_EVENT_COLUMNS, []);
+ const [rowCount, setRowCount] = useState(data?.meta.totalCount ?? 0);
+
+ const table = useReactTable({
+ data: data?.errorEvents ?? [],
+ columns: errorEventColumns,
+ getCoreRowModel: getCoreRowModel(),
+ state: {
+ sorting: sortingValue,
+ pagination: paginationValue
+ },
+ rowCount,
+ onSortingChange: handleChangeSorting,
+ onPaginationChange: handleChangePagination,
+ manualSorting: true,
+ manualPagination: true
+ });
+
+ const success = !loading && !fetchError;
+ const error = !loading && fetchError;
+
+ const getStatus = () => {
+ switch (true) {
+ case success:
+ return 'success';
+ case !!error:
+ return 'error';
+ case !!loading:
+ return 'loading';
+ default:
+ return 'idle';
+ }
+ };
+
+ useEffect(() => {
+ if (!loading && data?.meta.totalCount) setRowCount(data.meta.totalCount);
+ }, [loading, data?.meta.totalCount]);
+
+ return {
+ table,
+ getStatus,
+ error
+ };
+};
diff --git a/src/components/features/landing/data-tabs/DataTabs.tsx b/src/components/features/landing/data-tabs/DataTabs.tsx
index f2016c0..a7b9307 100644
--- a/src/components/features/landing/data-tabs/DataTabs.tsx
+++ b/src/components/features/landing/data-tabs/DataTabs.tsx
@@ -14,6 +14,7 @@ import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { formatOption } from '@/utils/formatter';
import { RecentBlocks } from '../recent-blocks/RecentBlocks';
+import { RecentErrorEvents } from '../recent-error-events/RecentErrorEvents';
import { RecentHighSecuritySets } from '../recent-high-security-sets/RecentHighSecuritySets';
import { RecentMinerRewards } from '../recent-miner-rewards/RecentMinerRewards';
import { RecentReversibleTransactions } from '../recent-reversible-transactions/RecentReversibleTransactions';
@@ -26,7 +27,8 @@ const TAB_OPTIONS = {
reversible: 'reversible-transactions',
blocks: 'blocks',
miners: 'miner-rewards',
- highSecuritySets: 'high-security-sets'
+ highSecuritySets: 'high-security-sets',
+ errors: 'errors'
} as const;
const TAB_LIST = Object.values(TAB_OPTIONS);
@@ -38,7 +40,7 @@ export const DataTabs: React.FC = () => {
-
+
{TAB_LIST.map((val) => (
= () => {
+
+
+
diff --git a/src/components/features/landing/error-events-table/ErrorEventsTable.tsx b/src/components/features/landing/error-events-table/ErrorEventsTable.tsx
new file mode 100644
index 0000000..316e406
--- /dev/null
+++ b/src/components/features/landing/error-events-table/ErrorEventsTable.tsx
@@ -0,0 +1,17 @@
+import { DataTable } from '@/components/ui/composites/data-table/DataTable';
+
+import { useErrorEventsTable } from './hook';
+
+export const ErrorEventsTable = () => {
+ const { getStatus, table, error } = useErrorEventsTable();
+
+ return (
+ Error : {error && error.message}
+ }}
+ />
+ );
+};
diff --git a/src/components/features/landing/error-events-table/hook.tsx b/src/components/features/landing/error-events-table/hook.tsx
new file mode 100644
index 0000000..76e6cf3
--- /dev/null
+++ b/src/components/features/landing/error-events-table/hook.tsx
@@ -0,0 +1,53 @@
+import { getCoreRowModel, useReactTable } from '@tanstack/react-table';
+import { useMemo } from 'react';
+
+import useApiClient from '@/api';
+import { ERROR_EVENT_COLUMNS } from '@/components/common/table-columns/ERROR_EVENT_COLUMNS';
+import { DATA_POOL_INTERVAL } from '@/constants/data-pool-interval';
+import type { ErrorEvent } from '@/schemas';
+
+export const useErrorEventsTable = () => {
+ const api = useApiClient();
+
+ const {
+ loading,
+ data,
+ error: fetchError
+ } = api.errors.useGetRecent({
+ pollInterval: DATA_POOL_INTERVAL
+ });
+
+ const errorEventColumns = useMemo(() => ERROR_EVENT_COLUMNS, []);
+
+ const table = useReactTable({
+ data: data?.errorEvents ?? [],
+ columns: errorEventColumns,
+ getCoreRowModel: getCoreRowModel(),
+ enableSorting: false,
+ rowCount: data?.errorEvents?.length ?? 0,
+ manualPagination: true,
+ manualSorting: true
+ });
+
+ const success = !loading && !fetchError;
+ const error = !loading && fetchError;
+
+ const getStatus = () => {
+ switch (true) {
+ case success:
+ return 'success';
+ case !!error:
+ return 'error';
+ case !!loading:
+ return 'loading';
+ default:
+ return 'idle';
+ }
+ };
+
+ return {
+ table,
+ getStatus,
+ error
+ };
+};
diff --git a/src/components/features/landing/hero/Hero.tsx b/src/components/features/landing/hero/Hero.tsx
index d484e0c..fc8874a 100644
--- a/src/components/features/landing/hero/Hero.tsx
+++ b/src/components/features/landing/hero/Hero.tsx
@@ -40,7 +40,7 @@ export const Hero = (props: HeroProps) => {
ref={inputRef}
onFocus={handleInputFocus}
onKeyDown={handleKeyDown}
- placeholder="Search transaction hash, account id, block number/hash, or miner rewards hash"
+ placeholder="Search by hash, id, or height"
onKeywordChange={handleKeywordChange}
/>
diff --git a/src/components/features/landing/recent-error-events/RecentErrorEvents.tsx b/src/components/features/landing/recent-error-events/RecentErrorEvents.tsx
new file mode 100644
index 0000000..703618f
--- /dev/null
+++ b/src/components/features/landing/recent-error-events/RecentErrorEvents.tsx
@@ -0,0 +1,21 @@
+import { Link } from '@tanstack/react-router';
+
+import { Button } from '@/components/ui/button';
+import { ContentContainer } from '@/components/ui/content-container';
+import { RESOURCES } from '@/constants/resources';
+
+import { ErrorEventsTable } from '../error-events-table/ErrorEventsTable';
+
+export const RecentErrorEvents = () => {
+ return (
+
+ Recent Error Events
+
+
+
+
+
+ );
+};
diff --git a/src/components/features/landing/recent-error-events/RecentTransactions.tsx b/src/components/features/landing/recent-error-events/RecentTransactions.tsx
new file mode 100644
index 0000000..4958237
--- /dev/null
+++ b/src/components/features/landing/recent-error-events/RecentTransactions.tsx
@@ -0,0 +1,21 @@
+import { Link } from '@tanstack/react-router';
+
+import { Button } from '@/components/ui/button';
+import { ContentContainer } from '@/components/ui/content-container';
+import { RESOURCES } from '@/constants/resources';
+
+import { TransactionsTable } from '../transactions-table/TransactionsTable';
+
+export const RecentTransactions = () => {
+ return (
+
+ Recent Immediate Transactions
+
+
+
+
+
+ );
+};
diff --git a/src/components/layout/header/Topbar.tsx b/src/components/layout/header/Topbar.tsx
index be9f6dc..0c969f7 100644
--- a/src/components/layout/header/Topbar.tsx
+++ b/src/components/layout/header/Topbar.tsx
@@ -56,7 +56,7 @@ export const Topbar: React.FC = ({
ref={inputRef}
onFocus={handleInputFocus}
onKeyDown={handleKeyDown}
- placeholder="Search transaction hash, account id, or block number/hash"
+ placeholder="Search by hash, id, or height"
onKeywordChange={handleKeywordChange}
inputClassName="h-8"
buttonClassName="size-7"
diff --git a/src/config/site-navigations.ts b/src/config/site-navigations.ts
index c43d95c..aac8b88 100644
--- a/src/config/site-navigations.ts
+++ b/src/config/site-navigations.ts
@@ -58,7 +58,17 @@ export const SITE_NAVIGATIONS: (SiteNavigation | ParentNavigation)[] = [
},
{
label: 'Blocks',
- path: '/blocks',
- longLabel: 'Blocks'
+ children: [
+ {
+ label: 'Blocks',
+ path: '/blocks',
+ longLabel: 'Blocks'
+ },
+ {
+ label: 'Errors',
+ path: '/errors',
+ longLabel: 'Error Events'
+ }
+ ]
}
] as const;
diff --git a/src/constants/query-sorts/errors.ts b/src/constants/query-sorts/errors.ts
new file mode 100644
index 0000000..76f5cdf
--- /dev/null
+++ b/src/constants/query-sorts/errors.ts
@@ -0,0 +1,41 @@
+export const ERROR_EVENT_SORTS = {
+ id: {
+ ASC: 'id_ASC',
+ DESC: 'id_DESC'
+ },
+ timestamp: {
+ ASC: 'timestamp_ASC',
+ DESC: 'timestamp_DESC'
+ },
+ errorType: {
+ ASC: 'errorType_ASC',
+ DESC: 'errorType_DESC'
+ },
+ errorModule: {
+ ASC: 'errorModule_ASC',
+ DESC: 'errorModule_DESC'
+ },
+ errorName: {
+ ASC: 'errorName_ASC',
+ DESC: 'errorName_DESC'
+ },
+ extrinsicHash: {
+ ASC: 'extrinsicHash_ASC',
+ DESC: 'extrinsicHash_DESC'
+ },
+ blockHeight: {
+ ASC: 'block_height_ASC',
+ DESC: 'block_height_DESC'
+ }
+} as const;
+
+export const ERROR_EVENT_SORTS_LITERALS = Object.values(
+ ERROR_EVENT_SORTS
+).flatMap((sort) => Object.values(sort));
+
+export const ERROR_EVENT_SORTS_KEY = Object.keys(ERROR_EVENT_SORTS);
+
+// Adding null, because we can sort by null to indicate no sorting
+export type ErrorEventSorts =
+ | (typeof ERROR_EVENT_SORTS_LITERALS)[number]
+ | null;
diff --git a/src/constants/query-sorts/index.ts b/src/constants/query-sorts/index.ts
index 20ff909..c356fe1 100644
--- a/src/constants/query-sorts/index.ts
+++ b/src/constants/query-sorts/index.ts
@@ -1,5 +1,6 @@
export * from './accounts';
export * from './blocks';
+export * from './errors';
export * from './high-security-sets';
export * from './miner-rewards';
export * from './reversible-transactions';
diff --git a/src/constants/resources.ts b/src/constants/resources.ts
index b34e986..b209911 100644
--- a/src/constants/resources.ts
+++ b/src/constants/resources.ts
@@ -4,5 +4,6 @@ export const RESOURCES = {
accounts: '/accounts',
blocks: '/blocks',
minerRewards: '/miner-rewards',
- highSecuritySets: '/high-security-sets'
+ highSecuritySets: '/high-security-sets',
+ errors: '/errors'
} as const;
diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts
index 5ce4779..a98d3f7 100644
--- a/src/routeTree.gen.ts
+++ b/src/routeTree.gen.ts
@@ -15,12 +15,14 @@ import { Route as MinerRewardsIndexRouteImport } from './routes/miner-rewards/in
import { Route as MinerLeaderboardIndexRouteImport } from './routes/miner-leaderboard/index'
import { Route as ImmediateTransactionsIndexRouteImport } from './routes/immediate-transactions/index'
import { Route as HighSecuritySetsIndexRouteImport } from './routes/high-security-sets/index'
+import { Route as ErrorsIndexRouteImport } from './routes/errors/index'
import { Route as BlocksIndexRouteImport } from './routes/blocks/index'
import { Route as AccountsIndexRouteImport } from './routes/accounts/index'
import { Route as ReversibleTransactionsHashRouteImport } from './routes/reversible-transactions/$hash'
import { Route as MinerRewardsHashRouteImport } from './routes/miner-rewards/$hash'
import { Route as ImmediateTransactionsHashRouteImport } from './routes/immediate-transactions/$hash'
import { Route as HighSecuritySetsHashRouteImport } from './routes/high-security-sets/$hash'
+import { Route as ErrorsIdRouteImport } from './routes/errors/$id'
import { Route as BlocksIdRouteImport } from './routes/blocks/$id'
import { Route as AccountsIdRouteImport } from './routes/accounts/$id'
@@ -56,6 +58,11 @@ const HighSecuritySetsIndexRoute = HighSecuritySetsIndexRouteImport.update({
path: '/high-security-sets/',
getParentRoute: () => rootRouteImport,
} as any)
+const ErrorsIndexRoute = ErrorsIndexRouteImport.update({
+ id: '/errors/',
+ path: '/errors/',
+ getParentRoute: () => rootRouteImport,
+} as any)
const BlocksIndexRoute = BlocksIndexRouteImport.update({
id: '/blocks/',
path: '/blocks/',
@@ -88,6 +95,11 @@ const HighSecuritySetsHashRoute = HighSecuritySetsHashRouteImport.update({
path: '/high-security-sets/$hash',
getParentRoute: () => rootRouteImport,
} as any)
+const ErrorsIdRoute = ErrorsIdRouteImport.update({
+ id: '/errors/$id',
+ path: '/errors/$id',
+ getParentRoute: () => rootRouteImport,
+} as any)
const BlocksIdRoute = BlocksIdRouteImport.update({
id: '/blocks/$id',
path: '/blocks/$id',
@@ -103,12 +115,14 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/accounts/$id': typeof AccountsIdRoute
'/blocks/$id': typeof BlocksIdRoute
+ '/errors/$id': typeof ErrorsIdRoute
'/high-security-sets/$hash': typeof HighSecuritySetsHashRoute
'/immediate-transactions/$hash': typeof ImmediateTransactionsHashRoute
'/miner-rewards/$hash': typeof MinerRewardsHashRoute
'/reversible-transactions/$hash': typeof ReversibleTransactionsHashRoute
'/accounts': typeof AccountsIndexRoute
'/blocks': typeof BlocksIndexRoute
+ '/errors': typeof ErrorsIndexRoute
'/high-security-sets': typeof HighSecuritySetsIndexRoute
'/immediate-transactions': typeof ImmediateTransactionsIndexRoute
'/miner-leaderboard': typeof MinerLeaderboardIndexRoute
@@ -119,12 +133,14 @@ export interface FileRoutesByTo {
'/': typeof IndexRoute
'/accounts/$id': typeof AccountsIdRoute
'/blocks/$id': typeof BlocksIdRoute
+ '/errors/$id': typeof ErrorsIdRoute
'/high-security-sets/$hash': typeof HighSecuritySetsHashRoute
'/immediate-transactions/$hash': typeof ImmediateTransactionsHashRoute
'/miner-rewards/$hash': typeof MinerRewardsHashRoute
'/reversible-transactions/$hash': typeof ReversibleTransactionsHashRoute
'/accounts': typeof AccountsIndexRoute
'/blocks': typeof BlocksIndexRoute
+ '/errors': typeof ErrorsIndexRoute
'/high-security-sets': typeof HighSecuritySetsIndexRoute
'/immediate-transactions': typeof ImmediateTransactionsIndexRoute
'/miner-leaderboard': typeof MinerLeaderboardIndexRoute
@@ -136,12 +152,14 @@ export interface FileRoutesById {
'/': typeof IndexRoute
'/accounts/$id': typeof AccountsIdRoute
'/blocks/$id': typeof BlocksIdRoute
+ '/errors/$id': typeof ErrorsIdRoute
'/high-security-sets/$hash': typeof HighSecuritySetsHashRoute
'/immediate-transactions/$hash': typeof ImmediateTransactionsHashRoute
'/miner-rewards/$hash': typeof MinerRewardsHashRoute
'/reversible-transactions/$hash': typeof ReversibleTransactionsHashRoute
'/accounts/': typeof AccountsIndexRoute
'/blocks/': typeof BlocksIndexRoute
+ '/errors/': typeof ErrorsIndexRoute
'/high-security-sets/': typeof HighSecuritySetsIndexRoute
'/immediate-transactions/': typeof ImmediateTransactionsIndexRoute
'/miner-leaderboard/': typeof MinerLeaderboardIndexRoute
@@ -154,12 +172,14 @@ export interface FileRouteTypes {
| '/'
| '/accounts/$id'
| '/blocks/$id'
+ | '/errors/$id'
| '/high-security-sets/$hash'
| '/immediate-transactions/$hash'
| '/miner-rewards/$hash'
| '/reversible-transactions/$hash'
| '/accounts'
| '/blocks'
+ | '/errors'
| '/high-security-sets'
| '/immediate-transactions'
| '/miner-leaderboard'
@@ -170,12 +190,14 @@ export interface FileRouteTypes {
| '/'
| '/accounts/$id'
| '/blocks/$id'
+ | '/errors/$id'
| '/high-security-sets/$hash'
| '/immediate-transactions/$hash'
| '/miner-rewards/$hash'
| '/reversible-transactions/$hash'
| '/accounts'
| '/blocks'
+ | '/errors'
| '/high-security-sets'
| '/immediate-transactions'
| '/miner-leaderboard'
@@ -186,12 +208,14 @@ export interface FileRouteTypes {
| '/'
| '/accounts/$id'
| '/blocks/$id'
+ | '/errors/$id'
| '/high-security-sets/$hash'
| '/immediate-transactions/$hash'
| '/miner-rewards/$hash'
| '/reversible-transactions/$hash'
| '/accounts/'
| '/blocks/'
+ | '/errors/'
| '/high-security-sets/'
| '/immediate-transactions/'
| '/miner-leaderboard/'
@@ -203,12 +227,14 @@ export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AccountsIdRoute: typeof AccountsIdRoute
BlocksIdRoute: typeof BlocksIdRoute
+ ErrorsIdRoute: typeof ErrorsIdRoute
HighSecuritySetsHashRoute: typeof HighSecuritySetsHashRoute
ImmediateTransactionsHashRoute: typeof ImmediateTransactionsHashRoute
MinerRewardsHashRoute: typeof MinerRewardsHashRoute
ReversibleTransactionsHashRoute: typeof ReversibleTransactionsHashRoute
AccountsIndexRoute: typeof AccountsIndexRoute
BlocksIndexRoute: typeof BlocksIndexRoute
+ ErrorsIndexRoute: typeof ErrorsIndexRoute
HighSecuritySetsIndexRoute: typeof HighSecuritySetsIndexRoute
ImmediateTransactionsIndexRoute: typeof ImmediateTransactionsIndexRoute
MinerLeaderboardIndexRoute: typeof MinerLeaderboardIndexRoute
@@ -260,6 +286,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof HighSecuritySetsIndexRouteImport
parentRoute: typeof rootRouteImport
}
+ '/errors/': {
+ id: '/errors/'
+ path: '/errors'
+ fullPath: '/errors'
+ preLoaderRoute: typeof ErrorsIndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/blocks/': {
id: '/blocks/'
path: '/blocks'
@@ -302,6 +335,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof HighSecuritySetsHashRouteImport
parentRoute: typeof rootRouteImport
}
+ '/errors/$id': {
+ id: '/errors/$id'
+ path: '/errors/$id'
+ fullPath: '/errors/$id'
+ preLoaderRoute: typeof ErrorsIdRouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/blocks/$id': {
id: '/blocks/$id'
path: '/blocks/$id'
@@ -323,12 +363,14 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AccountsIdRoute: AccountsIdRoute,
BlocksIdRoute: BlocksIdRoute,
+ ErrorsIdRoute: ErrorsIdRoute,
HighSecuritySetsHashRoute: HighSecuritySetsHashRoute,
ImmediateTransactionsHashRoute: ImmediateTransactionsHashRoute,
MinerRewardsHashRoute: MinerRewardsHashRoute,
ReversibleTransactionsHashRoute: ReversibleTransactionsHashRoute,
AccountsIndexRoute: AccountsIndexRoute,
BlocksIndexRoute: BlocksIndexRoute,
+ ErrorsIndexRoute: ErrorsIndexRoute,
HighSecuritySetsIndexRoute: HighSecuritySetsIndexRoute,
ImmediateTransactionsIndexRoute: ImmediateTransactionsIndexRoute,
MinerLeaderboardIndexRoute: MinerLeaderboardIndexRoute,
diff --git a/src/routes/errors/$id.tsx b/src/routes/errors/$id.tsx
new file mode 100644
index 0000000..c96a9fc
--- /dev/null
+++ b/src/routes/errors/$id.tsx
@@ -0,0 +1,26 @@
+import { createFileRoute } from '@tanstack/react-router';
+import * as React from 'react';
+
+import { ErrorEventInformation } from '@/components/features/error-event-details/error-event-information/ErrorEventInformation';
+import { ContentContainer } from '@/components/ui/content-container';
+import { SectionContainer } from '@/components/ui/section-container';
+
+export const Route = createFileRoute('/errors/$id')({
+ component: ErrorEventDetails
+});
+
+function ErrorEventDetails() {
+ const { id } = Route.useParams();
+
+ return (
+
+
+ Error Event Details
+
+
+
+
+ );
+}
+
+export default ErrorEventDetails;
diff --git a/src/routes/errors/index.tsx b/src/routes/errors/index.tsx
new file mode 100644
index 0000000..ee573f4
--- /dev/null
+++ b/src/routes/errors/index.tsx
@@ -0,0 +1,27 @@
+import { createFileRoute } from '@tanstack/react-router';
+
+import { ErrorEventsHeading } from '@/components/features/error-events-listing/error-events-heading/ErrorEventsHeading';
+import { ErrorEventsStats } from '@/components/features/error-events-listing/error-events-stats/ErrorEventsStats';
+import { ErrorEventsTable } from '@/components/features/error-events-listing/error-events-table/ErrorEventsTable';
+import { ContentContainer } from '@/components/ui/content-container';
+import { SectionContainer } from '@/components/ui/section-container';
+
+export const Route = createFileRoute('/errors/')({
+ component: ErrorEvents
+});
+
+function ErrorEvents() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default ErrorEvents;
diff --git a/src/schemas/errors.ts b/src/schemas/errors.ts
new file mode 100644
index 0000000..85da080
--- /dev/null
+++ b/src/schemas/errors.ts
@@ -0,0 +1,29 @@
+import type * as gql from '../__generated__/graphql';
+
+export interface ErrorEvent extends Omit {
+ block: Pick;
+}
+
+export interface ErrorEventResponse {
+ errorEvents: [ErrorEvent];
+}
+
+export interface ErrorEventListResponse {
+ errorEvents: ErrorEvent[];
+ meta: {
+ totalCount: number;
+ };
+}
+
+export interface RecentErrorEventsResponse {
+ errorEvents: ErrorEvent[];
+}
+
+export interface ErrorEventsStatsResponse {
+ allTime: {
+ totalCount: number;
+ };
+ last24Hour: {
+ totalCount: number;
+ };
+}
diff --git a/src/schemas/index.ts b/src/schemas/index.ts
index acb05d8..c305e67 100644
--- a/src/schemas/index.ts
+++ b/src/schemas/index.ts
@@ -1,6 +1,7 @@
export * from './account';
export * from './blocks';
export * from './chain-status';
+export * from './errors';
export * from './high-security-set';
export * from './miner-leaderboard';
export * from './miner-reward';