parent
9276bd7a16
commit
1e5932d89a
@ -0,0 +1,14 @@
|
||||
import AppSectionState, {
|
||||
AppSectionFilterState,
|
||||
PagedAppSectionState,
|
||||
TableAppSectionState,
|
||||
} from 'App/State/AppSectionState';
|
||||
import LogEvent from 'typings/LogEvent';
|
||||
|
||||
interface LogsAppState
|
||||
extends AppSectionState<LogEvent>,
|
||||
AppSectionFilterState<LogEvent>,
|
||||
PagedAppSectionState,
|
||||
TableAppSectionState {}
|
||||
|
||||
export default LogsAppState;
|
@ -1,141 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import LogsTableRow from './LogsTableRow';
|
||||
|
||||
function LogsTable(props) {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
columns,
|
||||
selectedFilterKey,
|
||||
filters,
|
||||
totalRecords,
|
||||
clearLogExecuting,
|
||||
onRefreshPress,
|
||||
onClearLogsPress,
|
||||
onFilterSelect,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Logs')}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isFetching}
|
||||
onPress={onRefreshPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('Clear')}
|
||||
iconName={icons.CLEAR}
|
||||
isSpinning={clearLogExecuting}
|
||||
onPress={onClearLogsPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
canModifyColumns={false}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
|
||||
<FilterMenu
|
||||
alignMenu={align.RIGHT}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={[]}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
|
||||
<PageContentBody>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !error && !items.length &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('NoEventsFound')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !error && !!items.length &&
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
canModifyColumns={false}
|
||||
{...otherProps}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<LogsTableRow
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
{...item}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePager
|
||||
totalRecords={totalRecords}
|
||||
isFetching={isFetching}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
LogsTable.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
clearLogExecuting: PropTypes.bool.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onRefreshPress: PropTypes.func.isRequired,
|
||||
onClearLogsPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default LogsTable;
|
@ -0,0 +1,205 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import Alert from 'Components/Alert';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import usePaging from 'Components/Table/usePaging';
|
||||
import useCurrentPage from 'Helpers/Hooks/useCurrentPage';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import {
|
||||
fetchLogs,
|
||||
gotoLogsFirstPage,
|
||||
gotoLogsPage,
|
||||
setLogsFilter,
|
||||
setLogsSort,
|
||||
setLogsTableOption,
|
||||
} from 'Store/Actions/systemActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { TableOptionsChangePayload } from 'typings/Table';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import LogsTableRow from './LogsTableRow';
|
||||
|
||||
function LogsTable() {
|
||||
const dispatch = useDispatch();
|
||||
const requestCurrentPage = useCurrentPage();
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
columns,
|
||||
page,
|
||||
pageSize,
|
||||
totalPages,
|
||||
totalRecords,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
filters,
|
||||
selectedFilterKey,
|
||||
} = useSelector((state: AppState) => state.system.logs);
|
||||
|
||||
const isClearLogExecuting = useSelector(
|
||||
createCommandExecutingSelector(commandNames.CLEAR_LOGS)
|
||||
);
|
||||
|
||||
const {
|
||||
handleFirstPagePress,
|
||||
handlePreviousPagePress,
|
||||
handleNextPagePress,
|
||||
handleLastPagePress,
|
||||
handlePageSelect,
|
||||
} = usePaging({
|
||||
page,
|
||||
totalPages,
|
||||
gotoPage: gotoLogsPage,
|
||||
});
|
||||
|
||||
const handleFilterSelect = useCallback(
|
||||
(selectedFilterKey: string | number) => {
|
||||
dispatch(setLogsFilter({ selectedFilterKey }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleSortPress = useCallback(
|
||||
(sortKey: string) => {
|
||||
dispatch(setLogsSort({ sortKey }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleTableOptionChange = useCallback(
|
||||
(payload: TableOptionsChangePayload) => {
|
||||
dispatch(setLogsTableOption(payload));
|
||||
|
||||
if (payload.pageSize) {
|
||||
dispatch(gotoLogsFirstPage({ page: 1 }));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleRefreshPress = useCallback(() => {
|
||||
dispatch(gotoLogsFirstPage());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClearLogsPress = useCallback(() => {
|
||||
dispatch(
|
||||
executeCommand({
|
||||
name: commandNames.CLEAR_LOGS,
|
||||
commandFinished: () => {
|
||||
dispatch(gotoLogsFirstPage());
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (requestCurrentPage) {
|
||||
dispatch(fetchLogs());
|
||||
} else {
|
||||
dispatch(gotoLogsFirstPage({ page: 1 }));
|
||||
}
|
||||
}, [requestCurrentPage, dispatch]);
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Logs')}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isFetching}
|
||||
onPress={handleRefreshPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('Clear')}
|
||||
iconName={icons.CLEAR}
|
||||
isSpinning={isClearLogExecuting}
|
||||
onPress={handleClearLogsPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
canModifyColumns={false}
|
||||
columns={columns}
|
||||
pageSize={pageSize}
|
||||
onTableOptionChange={handleTableOptionChange}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
|
||||
<FilterMenu
|
||||
alignMenu={align.RIGHT}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={[]}
|
||||
onFilterSelect={handleFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
|
||||
<PageContentBody>
|
||||
{isFetching && !isPopulated ? <LoadingIndicator /> : null}
|
||||
|
||||
{isPopulated && !error && !items.length ? (
|
||||
<Alert kind={kinds.INFO}>{translate('NoEventsFound')}</Alert>
|
||||
) : null}
|
||||
|
||||
{isPopulated && !error && items.length ? (
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
pageSize={pageSize}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onTableOptionChange={handleTableOptionChange}
|
||||
onSortPress={handleSortPress}
|
||||
>
|
||||
<TableBody>
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<LogsTableRow key={item.id} columns={columns} {...item} />
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePager
|
||||
page={page}
|
||||
totalPages={totalPages}
|
||||
totalRecords={totalRecords}
|
||||
isFetching={isFetching}
|
||||
onFirstPagePress={handleFirstPagePress}
|
||||
onPreviousPagePress={handlePreviousPagePress}
|
||||
onNextPagePress={handleNextPagePress}
|
||||
onLastPagePress={handleLastPagePress}
|
||||
onPageSelect={handlePageSelect}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogsTable;
|
@ -1,148 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as systemActions from 'Store/Actions/systemActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import LogsTable from './LogsTable';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.system.logs,
|
||||
createCommandExecutingSelector(commandNames.CLEAR_LOGS),
|
||||
(logs, clearLogExecuting) => {
|
||||
return {
|
||||
clearLogExecuting,
|
||||
...logs
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
executeCommand,
|
||||
...systemActions
|
||||
};
|
||||
|
||||
class LogsTableConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchLogs,
|
||||
gotoLogsFirstPage
|
||||
} = this.props;
|
||||
|
||||
if (useCurrentPage) {
|
||||
fetchLogs();
|
||||
} else {
|
||||
gotoLogsFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.clearLogExecuting && !this.props.clearLogExecuting) {
|
||||
this.props.gotoLogsFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoLogsFirstPage();
|
||||
};
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoLogsPreviousPage();
|
||||
};
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoLogsNextPage();
|
||||
};
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoLogsLastPage();
|
||||
};
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoLogsPage({ page });
|
||||
};
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setLogsSort({ sortKey });
|
||||
};
|
||||
|
||||
onFilterSelect = (selectedFilterKey) => {
|
||||
this.props.setLogsFilter({ selectedFilterKey });
|
||||
};
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setLogsTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoLogsFirstPage();
|
||||
}
|
||||
};
|
||||
|
||||
onRefreshPress = () => {
|
||||
this.props.gotoLogsFirstPage();
|
||||
};
|
||||
|
||||
onClearLogsPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.CLEAR_LOGS,
|
||||
commandFinished: this.onCommandFinished
|
||||
});
|
||||
};
|
||||
|
||||
onCommandFinished = () => {
|
||||
this.props.gotoLogsFirstPage();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LogsTable
|
||||
onFirstPagePress={this.onFirstPagePress}
|
||||
onPreviousPagePress={this.onPreviousPagePress}
|
||||
onNextPagePress={this.onNextPagePress}
|
||||
onLastPagePress={this.onLastPagePress}
|
||||
onPageSelect={this.onPageSelect}
|
||||
onSortPress={this.onSortPress}
|
||||
onFilterSelect={this.onFilterSelect}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onRefreshPress={this.onRefreshPress}
|
||||
onClearLogsPress={this.onClearLogsPress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LogsTableConnector.propTypes = {
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
clearLogExecuting: PropTypes.bool.isRequired,
|
||||
fetchLogs: PropTypes.func.isRequired,
|
||||
gotoLogsFirstPage: PropTypes.func.isRequired,
|
||||
gotoLogsPreviousPage: PropTypes.func.isRequired,
|
||||
gotoLogsNextPage: PropTypes.func.isRequired,
|
||||
gotoLogsLastPage: PropTypes.func.isRequired,
|
||||
gotoLogsPage: PropTypes.func.isRequired,
|
||||
setLogsSort: PropTypes.func.isRequired,
|
||||
setLogsFilter: PropTypes.func.isRequired,
|
||||
setLogsTableOption: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
connect(createMapStateToProps, mapDispatchToProps)(LogsTableConnector)
|
||||
);
|
@ -1,79 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
import { scrollDirections } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './LogsTableDetailsModal.css';
|
||||
|
||||
function LogsTableDetailsModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
message,
|
||||
exception,
|
||||
onModalClose
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
{translate('Details')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
{translate('Message')}
|
||||
</div>
|
||||
|
||||
<Scroller
|
||||
className={styles.detailsText}
|
||||
scrollDirection={scrollDirections.HORIZONTAL}
|
||||
>
|
||||
{message}
|
||||
</Scroller>
|
||||
|
||||
{
|
||||
!!exception &&
|
||||
<div>
|
||||
<div>
|
||||
{translate('Exception')}
|
||||
</div>
|
||||
<Scroller
|
||||
className={styles.detailsText}
|
||||
scrollDirection={scrollDirections.HORIZONTAL}
|
||||
>
|
||||
{exception}
|
||||
</Scroller>
|
||||
</div>
|
||||
}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
LogsTableDetailsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
message: PropTypes.string.isRequired,
|
||||
exception: PropTypes.string,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default LogsTableDetailsModal;
|
@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
import { scrollDirections } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './LogsTableDetailsModal.css';
|
||||
|
||||
interface LogsTableDetailsModalProps {
|
||||
isOpen: boolean;
|
||||
message: string;
|
||||
exception?: string;
|
||||
onModalClose: (...args: unknown[]) => unknown;
|
||||
}
|
||||
|
||||
function LogsTableDetailsModal({
|
||||
isOpen,
|
||||
message,
|
||||
exception,
|
||||
onModalClose,
|
||||
}: LogsTableDetailsModalProps) {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>{translate('Details')}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>{translate('Message')}</div>
|
||||
|
||||
<Scroller
|
||||
className={styles.detailsText}
|
||||
scrollDirection={scrollDirections.HORIZONTAL}
|
||||
>
|
||||
{message}
|
||||
</Scroller>
|
||||
|
||||
{!!exception && (
|
||||
<div>
|
||||
<div>{translate('Exception')}</div>
|
||||
<Scroller
|
||||
className={styles.detailsText}
|
||||
scrollDirection={scrollDirections.HORIZONTAL}
|
||||
>
|
||||
{exception}
|
||||
</Scroller>
|
||||
</div>
|
||||
)}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogsTableDetailsModal;
|
@ -1,158 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRowButton from 'Components/Table/TableRowButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import LogsTableDetailsModal from './LogsTableDetailsModal';
|
||||
import styles from './LogsTableRow.css';
|
||||
|
||||
function getIconName(level) {
|
||||
switch (level) {
|
||||
case 'trace':
|
||||
case 'debug':
|
||||
case 'info':
|
||||
return icons.INFO;
|
||||
case 'warn':
|
||||
return icons.DANGER;
|
||||
case 'error':
|
||||
return icons.BUG;
|
||||
case 'fatal':
|
||||
return icons.FATAL;
|
||||
default:
|
||||
return icons.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
class LogsTableRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isDetailsModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
// Don't re-open the modal if it's already open
|
||||
if (!this.state.isDetailsModalOpen) {
|
||||
this.setState({ isDetailsModalOpen: true });
|
||||
}
|
||||
};
|
||||
|
||||
onModalClose = () => {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
level,
|
||||
time,
|
||||
logger,
|
||||
message,
|
||||
exception,
|
||||
columns
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TableRowButton
|
||||
overlayContent={true}
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'level') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.level}
|
||||
>
|
||||
<Icon
|
||||
className={styles[level]}
|
||||
name={getIconName(level)}
|
||||
title={level}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'time') {
|
||||
return (
|
||||
<RelativeDateCell
|
||||
key={name}
|
||||
date={time}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'logger') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{logger}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'message') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{message}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.actions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<LogsTableDetailsModal
|
||||
isOpen={this.state.isDetailsModalOpen}
|
||||
message={message}
|
||||
exception={exception}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogsTableRow.propTypes = {
|
||||
level: PropTypes.string.isRequired,
|
||||
time: PropTypes.string.isRequired,
|
||||
logger: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired,
|
||||
exception: PropTypes.string,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
|
||||
export default LogsTableRow;
|
@ -0,0 +1,102 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Column from 'Components/Table/Column';
|
||||
import TableRowButton from 'Components/Table/TableRowButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { LogEventLevel } from 'typings/LogEvent';
|
||||
import LogsTableDetailsModal from './LogsTableDetailsModal';
|
||||
import styles from './LogsTableRow.css';
|
||||
|
||||
interface LogsTableRowProps {
|
||||
level: LogEventLevel;
|
||||
time: string;
|
||||
logger: string;
|
||||
message: string;
|
||||
exception?: string;
|
||||
columns: Column[];
|
||||
}
|
||||
|
||||
function LogsTableRow({
|
||||
level,
|
||||
time,
|
||||
logger,
|
||||
message,
|
||||
exception,
|
||||
columns,
|
||||
}: LogsTableRowProps) {
|
||||
const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false);
|
||||
|
||||
const iconName = useMemo(() => {
|
||||
switch (level) {
|
||||
case 'trace':
|
||||
case 'debug':
|
||||
case 'info':
|
||||
return icons.INFO;
|
||||
case 'warn':
|
||||
return icons.DANGER;
|
||||
case 'error':
|
||||
return icons.BUG;
|
||||
case 'fatal':
|
||||
return icons.FATAL;
|
||||
default:
|
||||
return icons.UNKNOWN;
|
||||
}
|
||||
}, [level]);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
setIsDetailsModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleDetailsModalClose = useCallback(() => {
|
||||
setIsDetailsModalOpen(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TableRowButton overlayContent={true} onPress={handlePress}>
|
||||
{columns.map((column) => {
|
||||
const { name, isVisible } = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'level') {
|
||||
return (
|
||||
<TableRowCell key={name} className={styles.level}>
|
||||
<Icon className={styles[level]} name={iconName} title={level} />
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'time') {
|
||||
return <RelativeDateCell key={name} date={time} />;
|
||||
}
|
||||
|
||||
if (name === 'logger') {
|
||||
return <TableRowCell key={name}>{logger}</TableRowCell>;
|
||||
}
|
||||
|
||||
if (name === 'message') {
|
||||
return <TableRowCell key={name}>{message}</TableRowCell>;
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return <TableRowCell key={name} className={styles.actions} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
|
||||
<LogsTableDetailsModal
|
||||
isOpen={isDetailsModalOpen}
|
||||
message={message}
|
||||
exception={exception}
|
||||
onModalClose={handleDetailsModalClose}
|
||||
/>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogsTableRow;
|
@ -0,0 +1,18 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
export type LogEventLevel =
|
||||
| 'trace'
|
||||
| 'debug'
|
||||
| 'info'
|
||||
| 'warn'
|
||||
| 'error'
|
||||
| 'fatal';
|
||||
|
||||
interface LogEvent extends ModelBase {
|
||||
time: string;
|
||||
level: LogEventLevel;
|
||||
logger: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default LogEvent;
|
Loading…
Reference in new issue