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
164 changes: 164 additions & 0 deletions src/api/errors.tsx
Original file line number Diff line number Diff line change
@@ -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<ErrorEventSorts, ErrorEventWhereInput>
>
) => {
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<ErrorEventSorts, ErrorEventWhereInput>
>(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<QueryHookOptions<RecentErrorEventsResponse>, '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<ErrorEventSorts, ErrorEventWhereInput>
>(GET_RECENT_ERROR_EVENTS, {
...config,
variables: {
orderBy: ERROR_EVENT_SORTS.timestamp.DESC,
limit: QUERY_RECENT_LIMIT
}
});
},
useGetStats: (
config?: Omit<QueryHookOptions<ErrorEventsStatsResponse>, '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<ErrorEventsStatsResponse>(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<ErrorEventResponse>) =>
useQuery<ErrorEventResponse>(GET_ERROR_EVENT_BY_HASH, {
...config,
variables: { hash }
})
};
}
};
2 changes: 2 additions & 0 deletions src/api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -16,6 +17,7 @@ const useApiClient = () => {
const api = {
accounts,
chainStatus,
errors,
transactions,
reversibleTransactions,
search: search(fetcher),
Expand Down
56 changes: 56 additions & 0 deletions src/components/common/table-columns/ERROR_EVENT_COLUMNS.tsx
Original file line number Diff line number Diff line change
@@ -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<ErrorEvent>();

export const ERROR_EVENT_COLUMNS = [
columnHelper.accessor('extrinsicHash', {
id: 'extrinsicHash',
header: 'Extrinsic Hash',
cell: (props) =>
props.getValue() ? (
<LinkWithCopy
href={`${RESOURCES.errors}/${props.getValue()}`}
text={formatTxAddress(props.getValue() ?? '-')}
textCopy={props.getValue() ?? ''}
/>
) : (
'Is not available'
),
enableSorting: false
}),
columnHelper.accessor('block.height', {
id: 'block_height',
header: 'Block',
cell: (props) => (
<LinkWithCopy
href={`${RESOURCES.blocks}/${props.getValue()}`}
text={props.getValue().toString()}
/>
),
enableSorting: true
}),
columnHelper.accessor('timestamp', {
id: 'timestamp',
header: 'Timestamp',
cell: (props) => <TimestampDisplay timestamp={props.getValue()} />,
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
})
];
Original file line number Diff line number Diff line change
@@ -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<ErrorEventInformationProps> = ({
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<ErrorEvent>[] = [
{
timestamp: event?.timestamp,
block: event?.block,
extrinsicHash: event?.extrinsicHash,
errorType: event?.errorType,
errorModule: event?.errorModule,
errorName: event?.errorName,
errorDocs: event?.errorDocs
}
];

return (
<DataList<Partial<ErrorEvent>>
loading={loading}
data={information}
fields={[
{
label: 'Extrinsic Hash',
key: 'extrinsicHash',
render: (value) => (
<TextWithCopy text={value as string} className="break-all" />
)
},
{
label: 'Timestamp',
key: 'timestamp',
render: (value) => formatTimestamp(value, true)
},
{
label: 'Block',
key: 'block',
render: (value) => (
<LinkWithCopy
text={(value as ErrorEvent['block']).height.toString()}
href={`${RESOURCES.blocks}/${(value as ErrorEvent['block']).height}`}
className="break-all"
/>
)
},
{
label: 'Error Type',
key: 'errorType'
},
{
label: 'Error Module',
key: 'errorModule',
render: (value) => (value ? (value as string) : '-')
},
{
label: 'Error Name',
key: 'errorName',
render: (value) => (value ? (value as string) : '-')
},
{
label: 'Error Docs',
key: 'errorDocs',
render: (value) =>
value ? (
<TextWithCopy text={value as string} className="break-all" />
) : (
'-'
)
}
]}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Link, useSearch } from '@tanstack/react-router';
import * as React from 'react';

export interface ErrorEventsHeadingProps {}

export const ErrorEventsHeading: React.FC<ErrorEventsHeadingProps> = () => {
const { block } = useSearch({
strict: false
}) as any;

return (
<div>
<h1>Error Events</h1>

{block && (
<div className="flex gap-1">
<span>In block</span>
<Link to="/blocks/$id" params={{ id: block }}>
{block}
</Link>
</div>
)}
</div>
);
};
Loading