From 64160866c3338003124851ca4d61e66e7f8efc55 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 24 Dec 2024 21:08:24 -0800 Subject: [PATCH] Convert Log FIles to TypeScript --- frontend/src/App/State/SystemAppState.ts | 4 + frontend/src/System/Logs/App/AppLogFiles.tsx | 51 +++++++ frontend/src/System/Logs/Files/LogFiles.js | 144 ------------------ .../System/Logs/Files/LogFilesConnector.js | 92 ----------- .../src/System/Logs/Files/LogFilesTableRow.js | 51 ------- frontend/src/System/Logs/LogFiles.tsx | 133 ++++++++++++++++ .../Logs/{Files => }/LogFilesTableRow.css | 0 .../{Files => }/LogFilesTableRow.css.d.ts | 0 frontend/src/System/Logs/LogFilesTableRow.tsx | 35 +++++ frontend/src/System/Logs/Logs.js | 30 ---- frontend/src/System/Logs/Logs.tsx | 17 +++ frontend/src/System/Logs/LogsNavMenu.js | 72 --------- frontend/src/System/Logs/LogsNavMenu.tsx | 33 ++++ .../src/System/Logs/Update/UpdateLogFiles.tsx | 51 +++++++ .../Logs/Updates/UpdateLogFilesConnector.js | 90 ----------- frontend/src/typings/LogFile.ts | 10 ++ 16 files changed, 334 insertions(+), 479 deletions(-) create mode 100644 frontend/src/System/Logs/App/AppLogFiles.tsx delete mode 100644 frontend/src/System/Logs/Files/LogFiles.js delete mode 100644 frontend/src/System/Logs/Files/LogFilesConnector.js delete mode 100644 frontend/src/System/Logs/Files/LogFilesTableRow.js create mode 100644 frontend/src/System/Logs/LogFiles.tsx rename frontend/src/System/Logs/{Files => }/LogFilesTableRow.css (100%) rename frontend/src/System/Logs/{Files => }/LogFilesTableRow.css.d.ts (100%) create mode 100644 frontend/src/System/Logs/LogFilesTableRow.tsx delete mode 100644 frontend/src/System/Logs/Logs.js create mode 100644 frontend/src/System/Logs/Logs.tsx delete mode 100644 frontend/src/System/Logs/LogsNavMenu.js create mode 100644 frontend/src/System/Logs/LogsNavMenu.tsx create mode 100644 frontend/src/System/Logs/Update/UpdateLogFiles.tsx delete mode 100644 frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js create mode 100644 frontend/src/typings/LogFile.ts diff --git a/frontend/src/App/State/SystemAppState.ts b/frontend/src/App/State/SystemAppState.ts index 81be2ab24..6f13b61e1 100644 --- a/frontend/src/App/State/SystemAppState.ts +++ b/frontend/src/App/State/SystemAppState.ts @@ -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; export type HealthAppState = AppSectionState; export type SystemStatusAppState = AppSectionItemState; export type TaskAppState = AppSectionState; +export type LogFilesAppState = AppSectionState; export type UpdateAppState = AppSectionState; interface SystemAppState { backups: BackupAppState; diskSpace: DiskSpaceAppState; health: HealthAppState; + logFiles: LogFilesAppState; logs: LogsAppState; status: SystemStatusAppState; tasks: TaskAppState; + updateLogFiles: LogFilesAppState; updates: UpdateAppState; } diff --git a/frontend/src/System/Logs/App/AppLogFiles.tsx b/frontend/src/System/Logs/App/AppLogFiles.tsx new file mode 100644 index 000000000..ae0e08172 --- /dev/null +++ b/frontend/src/System/Logs/App/AppLogFiles.tsx @@ -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 ( + + ); +} + +export default AppLogFiles; diff --git a/frontend/src/System/Logs/Files/LogFiles.js b/frontend/src/System/Logs/Files/LogFiles.js deleted file mode 100644 index e185e03cb..000000000 --- a/frontend/src/System/Logs/Files/LogFiles.js +++ /dev/null @@ -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 ( - - - - - - - - - - - - - - -
- {translate('LogFilesLocation', { - location - })} -
- - { - currentLogView === 'Log Files' && -
- -
- } -
- - { - isFetching && - - } - - { - !isFetching && !!items.length && -
- - - { - items.map((item) => { - return ( - - ); - }) - } - -
-
- } - - { - !isFetching && !items.length && - - {translate('NoLogFiles')} - - } -
-
- ); - } - -} - -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; diff --git a/frontend/src/System/Logs/Files/LogFilesConnector.js b/frontend/src/System/Logs/Files/LogFilesConnector.js deleted file mode 100644 index 75921f346..000000000 --- a/frontend/src/System/Logs/Files/LogFilesConnector.js +++ /dev/null @@ -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 ( - - ); - } -} - -LogFilesConnector.propTypes = { - deleteFilesExecuting: PropTypes.bool.isRequired, - fetchLogFiles: PropTypes.func.isRequired, - executeCommand: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(LogFilesConnector); diff --git a/frontend/src/System/Logs/Files/LogFilesTableRow.js b/frontend/src/System/Logs/Files/LogFilesTableRow.js deleted file mode 100644 index 1e5ad552d..000000000 --- a/frontend/src/System/Logs/Files/LogFilesTableRow.js +++ /dev/null @@ -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 ( - - {filename} - - - - - - {translate('Download')} - - - - ); - } - -} - -LogFilesTableRow.propTypes = { - filename: PropTypes.string.isRequired, - lastWriteTime: PropTypes.string.isRequired, - downloadUrl: PropTypes.string.isRequired -}; - -export default LogFilesTableRow; diff --git a/frontend/src/System/Logs/LogFiles.tsx b/frontend/src/System/Logs/LogFiles.tsx new file mode 100644 index 000000000..0bcd203b9 --- /dev/null +++ b/frontend/src/System/Logs/LogFiles.tsx @@ -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 ( + + + + + + + + + + + + + + +
+ {translate('LogFilesLocation', { + location, + })} +
+ + {currentLogView === 'Log Files' ? ( +
+ +
+ ) : null} +
+ + {isFetching ? : null} + + {!isFetching && items.length ? ( +
+ + + {items.map((item) => { + return ; + })} + +
+
+ ) : null} + + {!isFetching && !items.length ? ( + {translate('NoLogFiles')} + ) : null} +
+
+ ); +} + +export default LogFiles; diff --git a/frontend/src/System/Logs/Files/LogFilesTableRow.css b/frontend/src/System/Logs/LogFilesTableRow.css similarity index 100% rename from frontend/src/System/Logs/Files/LogFilesTableRow.css rename to frontend/src/System/Logs/LogFilesTableRow.css diff --git a/frontend/src/System/Logs/Files/LogFilesTableRow.css.d.ts b/frontend/src/System/Logs/LogFilesTableRow.css.d.ts similarity index 100% rename from frontend/src/System/Logs/Files/LogFilesTableRow.css.d.ts rename to frontend/src/System/Logs/LogFilesTableRow.css.d.ts diff --git a/frontend/src/System/Logs/LogFilesTableRow.tsx b/frontend/src/System/Logs/LogFilesTableRow.tsx new file mode 100644 index 000000000..021862dcc --- /dev/null +++ b/frontend/src/System/Logs/LogFilesTableRow.tsx @@ -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 ( + + {filename} + + + + + + {translate('Download')} + + + + ); +} + +export default LogFilesTableRow; diff --git a/frontend/src/System/Logs/Logs.js b/frontend/src/System/Logs/Logs.js deleted file mode 100644 index fa0be453e..000000000 --- a/frontend/src/System/Logs/Logs.js +++ /dev/null @@ -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 ( - - - - - - ); - } -} - -export default Logs; diff --git a/frontend/src/System/Logs/Logs.tsx b/frontend/src/System/Logs/Logs.tsx new file mode 100644 index 000000000..4dd2e5ba1 --- /dev/null +++ b/frontend/src/System/Logs/Logs.tsx @@ -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 ( + + + + + + ); +} + +export default Logs; diff --git a/frontend/src/System/Logs/LogsNavMenu.js b/frontend/src/System/Logs/LogsNavMenu.js deleted file mode 100644 index 923e4f41c..000000000 --- a/frontend/src/System/Logs/LogsNavMenu.js +++ /dev/null @@ -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 ( - - - {current} - - - - {translate('LogFiles')} - - - - {translate('UpdaterLogFiles')} - - - - ); - } -} - -LogsNavMenu.propTypes = { - current: PropTypes.string.isRequired -}; - -export default LogsNavMenu; diff --git a/frontend/src/System/Logs/LogsNavMenu.tsx b/frontend/src/System/Logs/LogsNavMenu.tsx new file mode 100644 index 000000000..5a6b50f7a --- /dev/null +++ b/frontend/src/System/Logs/LogsNavMenu.tsx @@ -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 ( + + {current} + + {translate('LogFiles')} + + + {translate('UpdaterLogFiles')} + + + + ); +} + +export default LogsNavMenu; diff --git a/frontend/src/System/Logs/Update/UpdateLogFiles.tsx b/frontend/src/System/Logs/Update/UpdateLogFiles.tsx new file mode 100644 index 000000000..4b97c4584 --- /dev/null +++ b/frontend/src/System/Logs/Update/UpdateLogFiles.tsx @@ -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 ( + + ); +} + +export default UpdateLogFiles; diff --git a/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js b/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js deleted file mode 100644 index 537816014..000000000 --- a/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js +++ /dev/null @@ -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 ( - - ); - } -} - -UpdateLogFilesConnector.propTypes = { - deleteFilesExecuting: PropTypes.bool.isRequired, - fetchUpdateLogFiles: PropTypes.func.isRequired, - executeCommand: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(UpdateLogFilesConnector); diff --git a/frontend/src/typings/LogFile.ts b/frontend/src/typings/LogFile.ts new file mode 100644 index 000000000..4f8085727 --- /dev/null +++ b/frontend/src/typings/LogFile.ts @@ -0,0 +1,10 @@ +import ModelBase from 'App/ModelBase'; + +interface LogFile extends ModelBase { + filename: string; + lastWriteTime: string; + contentsUrl: string; + downloadUrl: string; +} + +export default LogFile;