Convert Log FIles to TypeScript

pull/7605/head
Mark McDowall 2 months ago
parent 1e5932d89a
commit 64160866c3
No known key found for this signature in database

@ -1,5 +1,6 @@
import DiskSpace from 'typings/DiskSpace';
import Health from 'typings/Health';
import LogFile from 'typings/LogFile';
import SystemStatus from 'typings/SystemStatus';
import Task from 'typings/Task';
import Update from 'typings/Update';
@ -11,15 +12,18 @@ export type DiskSpaceAppState = AppSectionState<DiskSpace>;
export type HealthAppState = AppSectionState<Health>;
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
export type TaskAppState = AppSectionState<Task>;
export type LogFilesAppState = AppSectionState<LogFile>;
export type UpdateAppState = AppSectionState<Update>;
interface SystemAppState {
backups: BackupAppState;
diskSpace: DiskSpaceAppState;
health: HealthAppState;
logFiles: LogFilesAppState;
logs: LogsAppState;
status: SystemStatusAppState;
tasks: TaskAppState;
updateLogFiles: LogFilesAppState;
updates: UpdateAppState;
}

@ -0,0 +1,51 @@
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 { executeCommand } from 'Store/Actions/commandActions';
import { fetchLogFiles } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import LogFiles from '../LogFiles';
function AppLogFiles() {
const dispatch = useDispatch();
const { isFetching, items } = useSelector(
(state: AppState) => state.system.logFiles
);
const isDeleteFilesExecuting = useSelector(
createCommandExecutingSelector(commandNames.DELETE_LOG_FILES)
);
const handleRefreshPress = useCallback(() => {
dispatch(fetchLogFiles());
}, [dispatch]);
const handleDeleteFilesPress = useCallback(() => {
dispatch(
executeCommand({
name: commandNames.DELETE_LOG_FILES,
commandFinished: () => {
dispatch(fetchLogFiles());
},
})
);
}, [dispatch]);
useEffect(() => {
dispatch(fetchLogFiles());
}, [dispatch]);
return (
<LogFiles
isDeleteFilesExecuting={isDeleteFilesExecuting}
isFetching={isFetching}
items={items}
type="app"
onRefreshPress={handleRefreshPress}
onDeleteFilesPress={handleDeleteFilesPress}
/>
);
}
export default AppLogFiles;

@ -1,144 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
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 PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import LogsNavMenu from '../LogsNavMenu';
import LogFilesTableRow from './LogFilesTableRow';
const columns = [
{
name: 'filename',
label: () => translate('Filename'),
isVisible: true
},
{
name: 'lastWriteTime',
label: () => translate('LastWriteTime'),
isVisible: true
},
{
name: 'download',
isVisible: true
}
];
class LogFiles extends Component {
//
// Render
render() {
const {
isFetching,
items,
deleteFilesExecuting,
currentLogView,
location,
onRefreshPress,
onDeleteFilesPress,
...otherProps
} = this.props;
return (
<PageContent title={translate('LogFiles')}>
<PageToolbar>
<PageToolbarSection>
<LogsNavMenu current={currentLogView} />
<PageToolbarSeparator />
<PageToolbarButton
label={translate('Refresh')}
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
isSpinning={isFetching}
onPress={onRefreshPress}
/>
<PageToolbarButton
label={translate('Clear')}
iconName={icons.CLEAR}
isSpinning={deleteFilesExecuting}
onPress={onDeleteFilesPress}
/>
</PageToolbarSection>
</PageToolbar>
<PageContentBody>
<Alert>
<div>
{translate('LogFilesLocation', {
location
})}
</div>
{
currentLogView === 'Log Files' &&
<div>
<InlineMarkdown data={translate('TheLogLevelDefault')} />
</div>
}
</Alert>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!items.length &&
<div>
<Table
columns={columns}
{...otherProps}
>
<TableBody>
{
items.map((item) => {
return (
<LogFilesTableRow
key={item.id}
{...item}
/>
);
})
}
</TableBody>
</Table>
</div>
}
{
!isFetching && !items.length &&
<Alert kind={kinds.INFO}>
{translate('NoLogFiles')}
</Alert>
}
</PageContentBody>
</PageContent>
);
}
}
LogFiles.propTypes = {
isFetching: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
deleteFilesExecuting: PropTypes.bool.isRequired,
currentLogView: PropTypes.string.isRequired,
location: PropTypes.string.isRequired,
onRefreshPress: PropTypes.func.isRequired,
onDeleteFilesPress: PropTypes.func.isRequired
};
export default LogFiles;

@ -1,92 +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 { executeCommand } from 'Store/Actions/commandActions';
import { fetchLogFiles } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import combinePath from 'Utilities/String/combinePath';
import translate from 'Utilities/String/translate';
import LogFiles from './LogFiles';
function createMapStateToProps() {
return createSelector(
(state) => state.system.logFiles,
(state) => state.system.status.item,
createCommandExecutingSelector(commandNames.DELETE_LOG_FILES),
(logFiles, status, deleteFilesExecuting) => {
const {
isFetching,
items
} = logFiles;
const {
appData,
isWindows
} = status;
return {
isFetching,
items,
deleteFilesExecuting,
currentLogView: translate('LogFiles'),
location: combinePath(isWindows, appData, ['logs'])
};
}
);
}
const mapDispatchToProps = {
fetchLogFiles,
executeCommand
};
class LogFilesConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchLogFiles();
}
//
// Listeners
onRefreshPress = () => {
this.props.fetchLogFiles();
};
onDeleteFilesPress = () => {
this.props.executeCommand({
name: commandNames.DELETE_LOG_FILES,
commandFinished: this.onCommandFinished
});
};
onCommandFinished = () => {
this.props.fetchLogFiles();
};
//
// Render
render() {
return (
<LogFiles
onRefreshPress={this.onRefreshPress}
onDeleteFilesPress={this.onDeleteFilesPress}
{...this.props}
/>
);
}
}
LogFilesConnector.propTypes = {
deleteFilesExecuting: PropTypes.bool.isRequired,
fetchLogFiles: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(LogFilesConnector);

@ -1,51 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Link from 'Components/Link/Link';
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import translate from 'Utilities/String/translate';
import styles from './LogFilesTableRow.css';
class LogFilesTableRow extends Component {
//
// Render
render() {
const {
filename,
lastWriteTime,
downloadUrl
} = this.props;
return (
<TableRow>
<TableRowCell>{filename}</TableRowCell>
<RelativeDateCell
date={lastWriteTime}
/>
<TableRowCell className={styles.download}>
<Link
to={downloadUrl}
target="_blank"
noRouter={true}
>
{translate('Download')}
</Link>
</TableRowCell>
</TableRow>
);
}
}
LogFilesTableRow.propTypes = {
filename: PropTypes.string.isRequired,
lastWriteTime: PropTypes.string.isRequired,
downloadUrl: PropTypes.string.isRequired
};
export default LogFilesTableRow;

@ -0,0 +1,133 @@
import React from 'react';
import { useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
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 PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons, kinds } from 'Helpers/Props';
import LogFile from 'typings/LogFile';
import combinePath from 'Utilities/String/combinePath';
import translate from 'Utilities/String/translate';
import LogFilesTableRow from './LogFilesTableRow';
import LogsNavMenu from './LogsNavMenu';
const columns: Column[] = [
{
name: 'filename',
label: () => translate('Filename'),
isVisible: true,
},
{
name: 'lastWriteTime',
label: () => translate('LastWriteTime'),
isVisible: true,
},
{
name: 'download',
label: '',
isVisible: true,
},
];
type LogFileType = 'app' | 'update';
interface LogFilesProps {
isFetching: boolean;
items: LogFile[];
isDeleteFilesExecuting: boolean;
type: LogFileType;
onRefreshPress: () => void;
onDeleteFilesPress: () => void;
}
function LogFiles({
isFetching,
items,
isDeleteFilesExecuting,
type,
onRefreshPress,
onDeleteFilesPress,
...otherProps
}: LogFilesProps) {
const { appData, isWindows } = useSelector(
(state: AppState) => state.system.status.item
);
const currentLogView =
type === 'update' ? translate('UpdaterLogFiles') : translate('LogFiles');
const location = combinePath(isWindows, appData, [
type === 'update' ? 'UpdateLogs' : 'logs',
]);
return (
<PageContent title={translate('LogFiles')}>
<PageToolbar>
<PageToolbarSection>
<LogsNavMenu current={currentLogView} />
<PageToolbarSeparator />
<PageToolbarButton
label={translate('Refresh')}
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
isSpinning={isFetching}
onPress={onRefreshPress}
/>
<PageToolbarButton
label={translate('Clear')}
iconName={icons.CLEAR}
isSpinning={isDeleteFilesExecuting}
onPress={onDeleteFilesPress}
/>
</PageToolbarSection>
</PageToolbar>
<PageContentBody>
<Alert>
<div>
{translate('LogFilesLocation', {
location,
})}
</div>
{currentLogView === 'Log Files' ? (
<div>
<InlineMarkdown data={translate('TheLogLevelDefault')} />
</div>
) : null}
</Alert>
{isFetching ? <LoadingIndicator /> : null}
{!isFetching && items.length ? (
<div>
<Table columns={columns} {...otherProps}>
<TableBody>
{items.map((item) => {
return <LogFilesTableRow key={item.id} {...item} />;
})}
</TableBody>
</Table>
</div>
) : null}
{!isFetching && !items.length ? (
<Alert kind={kinds.INFO}>{translate('NoLogFiles')}</Alert>
) : null}
</PageContentBody>
</PageContent>
);
}
export default LogFiles;

@ -0,0 +1,35 @@
import React from 'react';
import Link from 'Components/Link/Link';
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import translate from 'Utilities/String/translate';
import styles from './LogFilesTableRow.css';
interface LogFilesTableRowProps {
filename: string;
lastWriteTime: string;
downloadUrl: string;
}
function LogFilesTableRow({
filename,
lastWriteTime,
downloadUrl,
}: LogFilesTableRowProps) {
return (
<TableRow>
<TableRowCell>{filename}</TableRowCell>
<RelativeDateCell date={lastWriteTime} />
<TableRowCell className={styles.download}>
<Link to={downloadUrl} target="_blank" noRouter={true}>
{translate('Download')}
</Link>
</TableRowCell>
</TableRow>
);
}
export default LogFilesTableRow;

@ -1,30 +0,0 @@
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import Switch from 'Components/Router/Switch';
import LogFilesConnector from './Files/LogFilesConnector';
import UpdateLogFilesConnector from './Updates/UpdateLogFilesConnector';
class Logs extends Component {
//
// Render
render() {
return (
<Switch>
<Route
exact={true}
path="/system/logs/files"
component={LogFilesConnector}
/>
<Route
path="/system/logs/files/update"
component={UpdateLogFilesConnector}
/>
</Switch>
);
}
}
export default Logs;

@ -0,0 +1,17 @@
import React from 'react';
import { Route } from 'react-router-dom';
import Switch from 'Components/Router/Switch';
import AppLogFiles from './App/AppLogFiles';
import UpdateLogFiles from './Update/UpdateLogFiles';
function Logs() {
return (
<Switch>
<Route exact={true} path="/system/logs/files" component={AppLogFiles} />
<Route path="/system/logs/files/update" component={UpdateLogFiles} />
</Switch>
);
}
export default Logs;

@ -1,72 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Menu from 'Components/Menu/Menu';
import MenuButton from 'Components/Menu/MenuButton';
import MenuContent from 'Components/Menu/MenuContent';
import MenuItem from 'Components/Menu/MenuItem';
import translate from 'Utilities/String/translate';
class LogsNavMenu extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isMenuOpen: false
};
}
//
// Listeners
onMenuButtonPress = () => {
this.setState({ isMenuOpen: !this.state.isMenuOpen });
};
onMenuItemPress = () => {
this.setState({ isMenuOpen: false });
};
//
// Render
render() {
const {
current
} = this.props;
return (
<Menu>
<MenuButton
onPress={this.onMenuButtonPress}
>
{current}
</MenuButton>
<MenuContent
isOpen={this.state.isMenuOpen}
>
<MenuItem
to={'/system/logs/files'}
>
{translate('LogFiles')}
</MenuItem>
<MenuItem
to={'/system/logs/files/update'}
>
{translate('UpdaterLogFiles')}
</MenuItem>
</MenuContent>
</Menu>
);
}
}
LogsNavMenu.propTypes = {
current: PropTypes.string.isRequired
};
export default LogsNavMenu;

@ -0,0 +1,33 @@
import React, { useCallback, useState } from 'react';
import Menu from 'Components/Menu/Menu';
import MenuButton from 'Components/Menu/MenuButton';
import MenuContent from 'Components/Menu/MenuContent';
import MenuItem from 'Components/Menu/MenuItem';
import translate from 'Utilities/String/translate';
interface LogsNavMenuProps {
current: string;
}
function LogsNavMenu({ current }: LogsNavMenuProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const handleMenuButtonPress = useCallback(() => {
setIsMenuOpen((prevIsMenuOpen) => !prevIsMenuOpen);
}, []);
return (
<Menu>
<MenuButton onPress={handleMenuButtonPress}>{current}</MenuButton>
<MenuContent isOpen={isMenuOpen}>
<MenuItem to="/system/logs/files">{translate('LogFiles')}</MenuItem>
<MenuItem to="/system/logs/files/update">
{translate('UpdaterLogFiles')}
</MenuItem>
</MenuContent>
</Menu>
);
}
export default LogsNavMenu;

@ -0,0 +1,51 @@
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 { executeCommand } from 'Store/Actions/commandActions';
import { fetchUpdateLogFiles } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import LogFiles from '../LogFiles';
function UpdateLogFiles() {
const dispatch = useDispatch();
const { isFetching, items } = useSelector(
(state: AppState) => state.system.updateLogFiles
);
const isDeleteFilesExecuting = useSelector(
createCommandExecutingSelector(commandNames.DELETE_UPDATE_LOG_FILES)
);
const handleRefreshPress = useCallback(() => {
dispatch(fetchUpdateLogFiles());
}, [dispatch]);
const handleDeleteFilesPress = useCallback(() => {
dispatch(
executeCommand({
name: commandNames.DELETE_UPDATE_LOG_FILES,
commandFinished: () => {
dispatch(fetchUpdateLogFiles());
},
})
);
}, [dispatch]);
useEffect(() => {
dispatch(fetchUpdateLogFiles());
}, [dispatch]);
return (
<LogFiles
isDeleteFilesExecuting={isDeleteFilesExecuting}
isFetching={isFetching}
items={items}
type="update"
onRefreshPress={handleRefreshPress}
onDeleteFilesPress={handleDeleteFilesPress}
/>
);
}
export default UpdateLogFiles;

@ -1,90 +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 { executeCommand } from 'Store/Actions/commandActions';
import { fetchUpdateLogFiles } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import combinePath from 'Utilities/String/combinePath';
import LogFiles from '../Files/LogFiles';
function createMapStateToProps() {
return createSelector(
(state) => state.system.updateLogFiles,
(state) => state.system.status.item,
createCommandExecutingSelector(commandNames.DELETE_UPDATE_LOG_FILES),
(updateLogFiles, status, deleteFilesExecuting) => {
const {
isFetching,
items
} = updateLogFiles;
const {
appData,
isWindows
} = status;
return {
isFetching,
items,
deleteFilesExecuting,
currentLogView: 'Updater Log Files',
location: combinePath(isWindows, appData, ['UpdateLogs'])
};
}
);
}
const mapDispatchToProps = {
fetchUpdateLogFiles,
executeCommand
};
class UpdateLogFilesConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchUpdateLogFiles();
}
componentDidUpdate(prevProps) {
if (prevProps.deleteFilesExecuting && !this.props.deleteFilesExecuting) {
this.props.fetchUpdateLogFiles();
}
}
//
// Listeners
onRefreshPress = () => {
this.props.fetchUpdateLogFiles();
};
onDeleteFilesPress = () => {
this.props.executeCommand({ name: commandNames.DELETE_UPDATE_LOG_FILES });
};
//
// Render
render() {
return (
<LogFiles
onRefreshPress={this.onRefreshPress}
onDeleteFilesPress={this.onDeleteFilesPress}
{...this.props}
/>
);
}
}
UpdateLogFilesConnector.propTypes = {
deleteFilesExecuting: PropTypes.bool.isRequired,
fetchUpdateLogFiles: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(UpdateLogFilesConnector);

@ -0,0 +1,10 @@
import ModelBase from 'App/ModelBase';
interface LogFile extends ModelBase {
filename: string;
lastWriteTime: string;
contentsUrl: string;
downloadUrl: string;
}
export default LogFile;
Loading…
Cancel
Save