diff --git a/package.json b/package.json index e0bceac5..06ae798b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firefly-ui", - "version": "0.4.4", + "version": "0.4.5", "private": true, "dependencies": { "@emotion/react": "^11.4.1", diff --git a/src/core/components/HashPopover.tsx b/src/core/components/HashPopover.tsx index 028f8ef3..9d65d837 100644 --- a/src/core/components/HashPopover.tsx +++ b/src/core/components/HashPopover.tsx @@ -33,12 +33,14 @@ interface Props { address: string; shortHash?: boolean; textColor?: 'primary' | 'secondary'; + paper?: boolean; } export const HashPopover: React.FC = ({ address, shortHash, textColor = 'primary', + paper, }) => { const classes = useStyles(); const [open, setOpen] = useState(false); @@ -49,7 +51,7 @@ export const HashPopover: React.FC = ({ label={shortHash ? getShortHash(address) : address} sx={{ width: shortHash ? 110 : 200 }} className={clsx( - classes.chip, + paper ? classes.paperChip : classes.chip, textColor === 'secondary' && classes.addressSecondaryText )} onClick={(event) => { @@ -119,4 +121,11 @@ const useStyles = makeStyles((theme) => ({ addressSecondaryText: { color: theme.palette.text.secondary, }, + paperChip: { + borderRadius: 2, + backgroundColor: theme.palette.background.paper, + '&:hover, &:focus': { + backgroundColor: theme.palette.background.paper, + }, + }, })); diff --git a/src/core/components/TransactionStatus.tsx b/src/core/components/TransactionStatus.tsx new file mode 100644 index 00000000..9cfdb397 --- /dev/null +++ b/src/core/components/TransactionStatus.tsx @@ -0,0 +1,79 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Grid, Paper, Typography } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ITransactionStatus } from '../interfaces'; +import dayjs from 'dayjs'; +import { HashPopover } from './HashPopover'; + +interface Props { + status: ITransactionStatus; +} + +export const TransactionStatus: React.FC = ({ status }) => { + const { t } = useTranslation(); + const classes = useStyles(); + + return ( + <> + {t('details')} + + {status.details.map((item) => ( + + + + + + + {item.type} + + + {item.status} + + + + + {item.id ? ( + + ) : undefined} + + {item.timestamp && ( + + + {dayjs(item.timestamp).format('MM/DD/YYYY h:mm A')} + + + )} + + + + + + ))} + + + ); +}; + +const useStyles = makeStyles((theme) => ({ + paper: { + backgroundColor: theme.palette.background.default, + padding: theme.spacing(2), + }, +})); diff --git a/src/core/interfaces.ts b/src/core/interfaces.ts index 37b26bf3..98fb4afb 100644 --- a/src/core/interfaces.ts +++ b/src/core/interfaces.ts @@ -44,6 +44,14 @@ export enum FFColors { Red = '#FF0000', } +export enum TransactionType { + None = 'none', + BatchPin = 'batch_pin', + TokenPool = 'token_pool', + TokenTransfer = 'token_transfer', + ContractInvoke = 'contract_invoke', +} + export type CreatedFilterOptions = '1hour' | '24hours' | '7days' | '30days'; export type FilterOptions = CreatedFilterOptions; @@ -150,18 +158,22 @@ export interface INamespace { export interface ITransaction { created: number; - hash: string; id: string; - protocolId?: string; - sequence: number; - status: TXStatus; - info?: IEthTransactionInfo; - subject: { - signer: string; - namespace: string; + namespace: string; + type: TransactionType; +} + +export interface ITransactionStatus { + status: OPStatus; + details: { + error?: string; + id?: string; + status: string; + subtype?: string; + timestamp?: string; type: string; - reference: string; - }; + info: any; + }[]; } export interface IEthTransactionInfo { diff --git a/src/core/translations/en.json b/src/core/translations/en.json index c517195f..7c45a5fb 100644 --- a/src/core/translations/en.json +++ b/src/core/translations/en.json @@ -6,7 +6,7 @@ "last7Days": "Last 7 Days", "last30Days": "Last 30 Days", "recentTransactions": "Recent Transactions", - "messages":"Messages", + "messages": "Messages", "namespace": "Namespace", "transactions": "Transactions", "addFilter": "Add Filter", @@ -28,5 +28,7 @@ "caseSensitive": "Case sensitive", "rule": "Rule", "matches": "Matches", - "notMatches": "Does not match" + "notMatches": "Does not match", + "status": "Status", + "details": "Details" } diff --git a/src/modules/data/registration.ts b/src/modules/data/registration.ts index 83942561..4633da27 100644 --- a/src/modules/data/registration.ts +++ b/src/modules/data/registration.ts @@ -29,7 +29,6 @@ import { Data } from './views/Data/Data'; import { Events } from './views/Events/Events'; import { MessageDetails } from './views/Messages/MessageDetails'; import { Messages } from './views/Messages/Messages'; -import { TransactionDetails } from './views/Transactions/TransactionDetails'; import { Transactions } from './views/Transactions/Transactions'; import { Types } from './views/Types/Types'; @@ -152,11 +151,6 @@ export const DataRoutes: IRoute[] = [ route: `${DATA_ROUTE_PREFIX}/transactions`, component: Transactions, }, - { - exact: true, - route: `${DATA_ROUTE_PREFIX}/transactions/:id`, - component: TransactionDetails, - }, { exact: true, route: `${DATA_ROUTE_PREFIX}/events`, diff --git a/src/modules/data/translations/en.json b/src/modules/data/translations/en.json index 8e257143..5db065b2 100644 --- a/src/modules/data/translations/en.json +++ b/src/modules/data/translations/en.json @@ -10,7 +10,6 @@ "dataDetails": "Data Details", "dataExplorer": "Data Explorer", "dataHash": "Data Hash", - "dateMined": "Date Mined", "dataTypes": "Data Types", "details": "Details", "emptyPlaceholder": "--", @@ -19,7 +18,7 @@ "filter": "Filter", "hash": "Hash", "id": "ID", - "last1Hour": "Last 1 Hour", + "last1Hour": "Last 1 Hour", "last24Hours": "Last 24 Hours", "last30Days": "Last 30 Days", "last7Days": "Last 7 Days", @@ -58,5 +57,6 @@ "type": "Type", "version": "Version", "viewTx": "View TX", - "yes": "Yes" -} \ No newline at end of file + "yes": "Yes", + "created": "Created" +} diff --git a/src/modules/data/views/Dashboard.tsx b/src/modules/data/views/Dashboard.tsx index ce5ef138..2ac0e401 100644 --- a/src/modules/data/views/Dashboard.tsx +++ b/src/modules/data/views/Dashboard.tsx @@ -42,7 +42,6 @@ import { IMetric, IPieChartElement, MSGStatus, - TXStatus, } from '../../../core/interfaces'; import { fetchWithCredentials, getCreatedFilter } from '../../../core/utils'; import { useDataTranslation } from '../registration'; @@ -66,11 +65,6 @@ const MSG_STATUS_COLORS: { [key in MSGStatus]: string } = { staged: FFColors.Yellow, rejected: FFColors.Red, }; -const TX_STATUS_COLORS: { [key in TXStatus]: string } = { - Succeeded: FFColors.Blue, - Pending: FFColors.Yellow, - Error: FFColors.Red, -}; export const Dashboard: () => JSX.Element = () => { const classes = useStyles(); @@ -83,9 +77,6 @@ export const Dashboard: () => JSX.Element = () => { const [latestMsgs, setLatestMsgs] = useState< IGenericPagedResponse[] | undefined >(undefined); - const [latestTx, setLatestTx] = useState( - undefined - ); // Metrics const [msgMetrics, setMsgMetrics] = useState([]); const [txMetrics, setTxMetrics] = useState([]); @@ -136,26 +127,6 @@ export const Dashboard: () => JSX.Element = () => { setLatestMsgs(msgStatusResponseJsons); } }); - // Tx Pie Chart - const txStatusObject: { [key: string]: string } = TXStatus; - const txEnums = Object.keys(TXStatus).map( - (key: string) => txStatusObject[key] - ); - Promise.all( - txEnums.map((e) => - fetchWithCredentials( - `/api/v1/namespaces/${namespace}/transactions?count${createdFilterObject.filterString}&limit=1&status=${e}` - ) - ) - ).then(async (txStatusResponses) => { - if (txStatusResponses.every((e) => e.ok)) { - const txStatusResponseJsons: IGenericPagedResponse[] = []; - for (let i = 0; i < txStatusResponses.length; i++) { - await txStatusResponseJsons.push(await txStatusResponses[i].json()); - } - setLatestTx(txStatusResponseJsons); - } - }); }, [namespace, lastEvent, createdFilter]); useEffect(() => { @@ -227,21 +198,6 @@ export const Dashboard: () => JSX.Element = () => { latestMsgs && mapPieChartData(latestMsgs, MSG_STATUS_COLORS, MSGStatus), title: t('latestMessages'), }, - { - chart: - latestTx !== undefined ? ( - <> - - - ) : ( - - ), - data: latestTx && mapPieChartData(latestTx, TX_STATUS_COLORS, TXStatus), - title: t('latestTransactions'), - }, ]; const pieChartPanel = (data: IChartPanel): JSX.Element => ( @@ -367,7 +323,6 @@ export const Dashboard: () => JSX.Element = () => { container item md={12} - lg={6} key={idx} className={classes.chartPanel} > diff --git a/src/modules/data/views/Messages/MessageDetails.tsx b/src/modules/data/views/Messages/MessageDetails.tsx index 96c1dc54..58047a94 100644 --- a/src/modules/data/views/Messages/MessageDetails.tsx +++ b/src/modules/data/views/Messages/MessageDetails.tsx @@ -13,12 +13,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import LaunchIcon from '@mui/icons-material/Launch'; import { Breadcrumbs, CircularProgress, Grid, - IconButton, Link, List, Modal, @@ -126,20 +124,7 @@ export const MessageDetails: () => JSX.Element = () => { { label: t('transactionID'), value: batch?.payload.tx.id && ( - <> - - { - history.push( - `/namespace/${selectedNamespace}/data/transactions/${batch.payload.tx.id}` - ); - }} - > - - - + ), }, diff --git a/src/modules/data/views/Transactions/TransactionDetails.tsx b/src/modules/data/views/Transactions/TransactionDetails.tsx index cb48136e..7df60451 100644 --- a/src/modules/data/views/Transactions/TransactionDetails.tsx +++ b/src/modules/data/views/Transactions/TransactionDetails.tsx @@ -14,230 +14,122 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, { useState, useEffect, useContext } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; -import { ITransaction } from '../../../../core/interfaces'; -import { NamespaceContext } from '../../../../core/contexts/NamespaceContext'; -import ChevronLeftIcon from 'mdi-react/ChevronLeftIcon'; -import { HashPopover } from '../../../../core/components/HashPopover'; -import { StatusChip } from '../../../../core/components/StatusChip'; -import { - Grid, - Box, - Typography, - Button, - CircularProgress, - Card, - CardContent, -} from '@mui/material'; +import React, { useContext, useEffect, useState } from 'react'; +import { DisplaySlide } from '../../../../core/components/Display/DisplaySlide'; +import { Typography, Grid, CircularProgress } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; -import { fetchWithCredentials } from '../../../../core/utils'; -import { SnackbarContext } from '../../../../core/contexts/SnackbarContext'; +import { ITransaction, ITransactionStatus } from '../../../../core/interfaces'; import { useDataTranslation } from '../../registration'; +import { HashPopover } from '../../../../core/components/HashPopover'; +import dayjs from 'dayjs'; +import { TransactionStatus } from '../../../../core/components/TransactionStatus'; +import { fetchWithCredentials } from '../../../../core/utils'; +import { NamespaceContext } from '../../../../core/contexts/NamespaceContext'; -export const TransactionDetails: () => JSX.Element = () => { - const history = useHistory(); - const { id } = useParams<{ id: string }>(); +interface Props { + data: ITransaction; + open: boolean; + onClose: () => void; +} + +export const TransactionDetails: React.FC = ({ + data, + open, + onClose, +}) => { const { t } = useDataTranslation(); + const [status, setStatus] = useState(); const { selectedNamespace } = useContext(NamespaceContext); - const [transaction, setTransaction] = useState(); - const [loading, setLoading] = useState(false); const classes = useStyles(); - const { setMessage, setMessageType } = useContext(SnackbarContext); - - const detailItem = (label: string, value: string | JSX.Element) => ( - <> - - {label} - - - {value} - - - ); + const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); fetchWithCredentials( - `/api/v1/namespaces/${selectedNamespace}/transactions/${id}` + `/api/v1/namespaces/${selectedNamespace}/transactions/${data.id}/status` ) .then(async (response) => { if (response.ok) { - setTransaction(await response.json()); - } else { - setMessageType('error'); - setMessage(`Error loading transaction ${id}`); + setStatus(await response.json()); } }) .finally(() => { setLoading(false); }); - }, [selectedNamespace, id, setMessageType, setMessage]); + }, [selectedNamespace, data.id]); - if (loading) { - return ( - - - - ); - } + const detailItem = (label: string, value: string | JSX.Element | number) => ( + <> + + {label} + + + {value} + + + ); - if (!transaction) { - return <>; - } + const renderStatus = () => { + if (status) { + return ( + + + + + + ); + } + }; return ( - - - - - - - - {t('transactionDetails')} - - - - - - - - {detailItem( - 'hash', - - )} - - {transaction.info?.blockNumber && ( - - {detailItem( - 'Block', - - {transaction.info.blockNumber} - - )} - - )} - - {detailItem( - 'From', - - )} - - - {detailItem( - 'Status', - - )} - - - {detailItem( - 'Timestamp', - - {transaction.created} - - )} - - - {detailItem( - 'sequence', - - {transaction.sequence} - - )} - - {transaction.info?.signature && ( - - {detailItem( - 'methods', - - {transaction.info.signature} - - )} - - )} - - + <> + + + + + {t('transactionDetails')} + - - - - {transaction.info?.address && ( - - {detailItem( - 'address', - - {transaction.info.address} - - )} - - )} - - {detailItem( - 'type', - - {transaction.subject.type} - - )} - - - {detailItem( - 'namespace', - - {transaction.subject.namespace} - - )} - - - {detailItem( - 'reference', - - {transaction.subject.reference} - - )} - - - + + + {detailItem(t('id'), )} + + + {detailItem(t('type'), data.type)} + + {status && ( + + {detailItem(t('status'), status.status)} + + )} + + {detailItem( + t('created'), + dayjs(data.created).format('MM/DD/YYYY h:mm A') + )} + - - + {loading ? : renderStatus()} + + ); }; const useStyles = makeStyles((theme) => ({ - bold: { - fontWeight: 'bold', + detailsContainer: { + padding: theme.spacing(3), }, - centeredContent: { - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - height: 'calc(100vh - 300px)', - overflow: 'auto', + detailItem: { + paddingBottom: theme.spacing(1), }, - backButton: { - color: theme.palette.text.secondary, - textTransform: 'capitalize', - paddingLeft: 0, + header: { + fontWeight: 'bold', }, - paddingBottom: { - paddingBottom: theme.spacing(2), - }, - content: { - padding: theme.spacing(3), + headerContainer: { + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), + paddingTop: theme.spacing(3), }, detailLabel: { fontSize: 10, @@ -245,12 +137,30 @@ const useStyles = makeStyles((theme) => ({ textTransform: 'uppercase', }, detailValue: { - color: theme.palette.text.secondary, + fontSize: 14, }, - detailGrid: { - padding: theme.spacing(1), + divider: { + backgroundColor: theme.palette.background.default, + height: 2, }, - gridPadding: { - paddingBottom: theme.spacing(1), + copyButton: { + backgroundColor: theme.palette.primary.dark, + borderRadius: 20, + fontSize: 10, + }, + paddingRight: { + paddingRight: theme.spacing(1), + }, + centeredContent: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + height: 'calc(100vh - 300px)', + overflow: 'auto', + }, + paper: { + backgroundColor: theme.palette.background.default, + minWidth: '40vw', }, })); diff --git a/src/modules/data/views/Transactions/TransactionList.tsx b/src/modules/data/views/Transactions/TransactionList.tsx index 48d1acd8..62dd282d 100644 --- a/src/modules/data/views/Transactions/TransactionList.tsx +++ b/src/modules/data/views/Transactions/TransactionList.tsx @@ -18,7 +18,6 @@ import React, { useState, useEffect, useContext } from 'react'; import { Card, Grid, TablePagination } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import dayjs from 'dayjs'; -import { useHistory } from 'react-router-dom'; import { FFColors, ICreatedFilter, @@ -39,10 +38,13 @@ const PAGE_LIMITS = [10, 25]; interface Props { filterString?: string; + setDetailsTx: React.Dispatch>; } -export const TransactionList: React.FC = ({ filterString }) => { - const history = useHistory(); +export const TransactionList: React.FC = ({ + filterString, + setDetailsTx, +}) => { const { t } = useDataTranslation(); const classes = useStyles(); const [transactions, setTransactions] = useState([]); @@ -52,13 +54,7 @@ export const TransactionList: React.FC = ({ filterString }) => { const [rowsPerPage, setRowsPerPage] = useState(PAGE_LIMITS[0]); const { createdFilter, lastEvent } = useContext(ApplicationContext); - const columnHeaders = [ - t('hash'), - t('blockNumber'), - t('signer'), - t('status'), - t('dateMined'), - ]; + const columnHeaders = [t('id'), t('type'), t('created')]; useEffect(() => { const createdFilterObject: ICreatedFilter = getCreatedFilter(createdFilter); @@ -132,16 +128,8 @@ export const TransactionList: React.FC = ({ filterString }) => { return transactions.map((tx: ITransaction) => ({ key: tx.id, columns: [ - { - value: , - }, - { value: tx.info?.blockNumber }, - { - value: ( - - ), - }, - { value: tx.status }, + { value: }, + { value: tx.type }, { value: tx.created ? dayjs(tx.created).format('MM/DD/YYYY h:mm A') @@ -149,9 +137,7 @@ export const TransactionList: React.FC = ({ filterString }) => { }, ], onClick: () => { - history.push( - `/namespace/${selectedNamespace}/data/transactions/${tx.id}` - ); + setDetailsTx(tx); }, })); }; diff --git a/src/modules/data/views/Transactions/TransactionTimeline.tsx b/src/modules/data/views/Transactions/TransactionTimeline.tsx index 10f10a1b..d1d5ffe9 100644 --- a/src/modules/data/views/Transactions/TransactionTimeline.tsx +++ b/src/modules/data/views/Transactions/TransactionTimeline.tsx @@ -17,10 +17,9 @@ import React, { useEffect, useContext, useRef } from 'react'; import { ICreatedFilter, - IHistory, IPagedTransactionResponse, + ITransaction, } from '../../../../core/interfaces'; -import { useHistory } from 'react-router'; import dayjs from 'dayjs'; import BroadcastIcon from 'mdi-react/BroadcastIcon'; import { DataTimeline } from '../../../../core/components/DataTimeline/DataTimeline'; @@ -29,11 +28,7 @@ import { NamespaceContext } from '../../../../core/contexts/NamespaceContext'; import { InfiniteData, useInfiniteQuery, useQueryClient } from 'react-query'; import useIntersectionObserver from '../../../../core/hooks/useIntersectionObserver'; import { SnackbarContext } from '../../../../core/contexts/SnackbarContext'; -import { - fetchWithCredentials, - getCreatedFilter, - getShortHash, -} from '../../../../core/utils'; +import { fetchWithCredentials, getCreatedFilter } from '../../../../core/utils'; import { DataTableEmptyState } from '../../../../core/components/DataTable/DataTableEmptyState'; import { useDataTranslation } from '../../registration'; @@ -41,11 +36,14 @@ const ROWS_PER_PAGE = 25; interface Props { filterString?: string; + setDetailsTx: React.Dispatch>; } -export const TransactionTimeline: React.FC = ({ filterString }) => { +export const TransactionTimeline: React.FC = ({ + filterString, + setDetailsTx, +}) => { const { t } = useDataTranslation(); - const history = useHistory(); const loadingRef = useRef(null); const observer = useIntersectionObserver(loadingRef, {}); const isVisible = !!observer?.isIntersecting; @@ -109,15 +107,11 @@ export const TransactionTimeline: React.FC = ({ filterString }) => { const pages = data.pages.map((page) => page.items); return pages.flat().map((tx) => ({ key: tx.id, - title: getShortHash(tx.hash), - description: tx.status, + title: tx.type, time: dayjs(tx.created).format('MM/DD/YYYY h:mm A'), icon: , - author: tx.subject.signer, onClick: () => { - history.push( - `/namespace/${selectedNamespace}/data/transactions/${tx.id}` - ); + setDetailsTx(tx); }, })); } else { diff --git a/src/modules/data/views/Transactions/Transactions.tsx b/src/modules/data/views/Transactions/Transactions.tsx index d425dd65..eb1b4d9a 100644 --- a/src/modules/data/views/Transactions/Transactions.tsx +++ b/src/modules/data/views/Transactions/Transactions.tsx @@ -26,6 +26,8 @@ import { FilterDisplay } from '../../../../core/components/FilterDisplay'; import { ArrayParam, withDefault, useQueryParam } from 'use-query-params'; import { FilterModal } from '../../../../core/components/FilterModal'; import { DatePicker } from '../../../../core/components/DatePicker'; +import { ITransaction } from '../../../../core/interfaces'; +import { TransactionDetails } from './TransactionDetails'; export const Transactions: () => JSX.Element = () => { const { t } = useDataTranslation(); @@ -40,19 +42,8 @@ export const Transactions: () => JSX.Element = () => { 'filters', withDefault(ArrayParam, []) ); - - const filterFields = [ - 'created', - 'id', - 'info', - 'namespace', - 'protocolid', - 'reference', - 'sequence', - 'signer', - 'status', - 'type', - ]; + const [detailsTx, setDetailsTx] = useState(); + const filterFields = ['created', 'id', 'namespace', 'status', 'type']; const handleOpenFilter = (event: React.MouseEvent) => { setFilterAnchor(event.currentTarget); @@ -117,12 +108,18 @@ export const Transactions: () => JSX.Element = () => { )} {dataView === 'timeline' && ( - + )} {dataView === 'list' && ( - + )} @@ -137,6 +134,15 @@ export const Transactions: () => JSX.Element = () => { addFilter={handleAddFilter} /> )} + {detailsTx && ( + { + setDetailsTx(undefined); + }} + data={detailsTx} + /> + )} ); }; diff --git a/src/modules/home/translations/en.json b/src/modules/home/translations/en.json index 6838a0b0..ef64ec6f 100644 --- a/src/modules/home/translations/en.json +++ b/src/modules/home/translations/en.json @@ -17,14 +17,13 @@ "messages": "Messages", "modules": "Modules", "monitoring": "Monitoring", - "monitoringDescription" : "Monitor operations within your network", + "monitoringDescription": "Monitor operations within your network", "networkMap": "Network Map", "networkMapDescription": "View organizations and nodes within your network", "networkMembers": "Network Members", "reference": "Reference", - "tokens":"Tokens", + "tokens": "Tokens", "tokenTransfers": "Token Transfers", "tokensDescription": "View tokens, accounts, and transfers within your network", - "welcome": "Welcome!", - "tokens": "Tokens" -} \ No newline at end of file + "welcome": "Welcome!" +} diff --git a/src/modules/monitoring/translations/en.json b/src/modules/monitoring/translations/en.json index 8bab6da1..c8f7d3bb 100644 --- a/src/modules/monitoring/translations/en.json +++ b/src/modules/monitoring/translations/en.json @@ -5,7 +5,7 @@ "emptyPlaceholder": "--", "filter": "Filter", "id": "ID", - "last1Hour": "Last 1 Hour", + "last1Hour": "Last 1 Hour", "last24Hours": "Last 24 Hours", "last30Days": "Last 30 Days", "last7Days": "Last 7 Days", @@ -22,4 +22,4 @@ "time": "Time", "timestamp": "Timestamp", "type": "Type" -} \ No newline at end of file +}