1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-21 22:05:43 -04:00

New: Project Aphrodite

This commit is contained in:
Qstick
2018-11-23 02:04:42 -05:00
parent 65efa15551
commit 8430cb40ab
1080 changed files with 73015 additions and 0 deletions
@@ -0,0 +1,21 @@
.legend {
display: flex;
justify-content: space-between;
}
.loading {
composes: loading from 'Components/Loading/LoadingIndicator.css';
margin-top: 2px;
margin-left: 10px;
text-align: left;
}
.status {
width: 20px;
}
.healthOk {
margin-bottom: 25px;
}
+206
View File
@@ -0,0 +1,206 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import titleCase from 'Utilities/String/titleCase';
import { icons, kinds } from 'Helpers/Props';
import Icon from 'Components/Icon';
import IconButton from 'Components/Link/IconButton';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FieldSet from 'Components/FieldSet';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableRow from 'Components/Table/TableRow';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import styles from './Health.css';
function getInternalLink(source) {
switch (source) {
case 'IndexerRssCheck':
case 'IndexerSearchCheck':
case 'IndexerStatusCheck':
return (
<IconButton
name={icons.SETTINGS}
title="Settings"
to="/settings/indexers"
/>
);
case 'DownloadClientCheck':
case 'ImportMechanismCheck':
return (
<IconButton
name={icons.SETTINGS}
title="Settings"
to="/settings/downloadclients"
/>
);
case 'RootFolderCheck':
return (
<IconButton
name={icons.PLAY}
title="Movie Editor"
to="/movieeditor"
/>
);
case 'UpdateCheck':
return (
<IconButton
name={icons.UPDATE}
title="Updates"
to="/system/updates"
/>
);
default:
return;
}
}
function getTestLink(source, props) {
switch (source) {
case 'IndexerStatusCheck':
return (
<SpinnerIconButton
name={icons.TEST}
title="Test All"
isSpinning={props.isTestingAllIndexers}
onPress={props.dispatchTestAllIndexers}
/>
);
case 'DownloadClientCheck':
return (
<SpinnerIconButton
name={icons.TEST}
title="Test All"
isSpinning={props.isTestingAllDownloadClients}
onPress={props.dispatchTestAllDownloadClients}
/>
);
default:
break;
}
}
const columns = [
{
className: styles.status,
name: 'type',
isVisible: true
},
{
name: 'message',
label: 'Message',
isVisible: true
},
{
name: 'actions',
label: 'Actions',
isVisible: true
}
];
class Health extends Component {
//
// Render
render() {
const {
isFetching,
isPopulated,
items
} = this.props;
const healthIssues = !!items.length;
return (
<FieldSet
legend={
<div className={styles.legend}>
Health
{
isFetching && isPopulated &&
<LoadingIndicator
className={styles.loading}
size={20}
/>
}
</div>
}
>
{
isFetching && !isPopulated &&
<LoadingIndicator />
}
{
!healthIssues &&
<div className={styles.healthOk}>
No issues with your configuration
</div>
}
{
healthIssues &&
<Table
columns={columns}
>
<TableBody>
{
items.map((item) => {
const internalLink = getInternalLink(item.source);
const testLink = getTestLink(item.source, this.props);
return (
<TableRow key={`health${item.message}`}>
<TableRowCell>
<Icon
name={icons.DANGER}
kind={item.type.toLowerCase() === 'error' ? kinds.DANGER : kinds.WARNING}
title={titleCase(item.type)}
/>
</TableRowCell>
<TableRowCell>{item.message}</TableRowCell>
<TableRowCell>
<IconButton
name={icons.WIKI}
to={item.wikiUrl}
title="Read the Wiki for more information"
/>
{
internalLink
}
{
!!testLink &&
testLink
}
</TableRowCell>
</TableRow>
);
})
}
</TableBody>
</Table>
}
</FieldSet>
);
}
}
Health.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
isTestingAllDownloadClients: PropTypes.bool.isRequired,
isTestingAllIndexers: PropTypes.bool.isRequired,
dispatchTestAllDownloadClients: PropTypes.func.isRequired,
dispatchTestAllIndexers: PropTypes.func.isRequired
};
export default Health;
@@ -0,0 +1,68 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchHealth } from 'Store/Actions/systemActions';
import { testAllDownloadClients, testAllIndexers } from 'Store/Actions/settingsActions';
import Health from './Health';
function createMapStateToProps() {
return createSelector(
(state) => state.system.health,
(state) => state.settings.downloadClients.isTestingAll,
(state) => state.settings.indexers.isTestingAll,
(health, isTestingAllDownloadClients, isTestingAllIndexers) => {
const {
isFetching,
isPopulated,
items
} = health;
return {
isFetching,
isPopulated,
items,
isTestingAllDownloadClients,
isTestingAllIndexers
};
}
);
}
const mapDispatchToProps = {
dispatchFetchHealth: fetchHealth,
dispatchTestAllDownloadClients: testAllDownloadClients,
dispatchTestAllIndexers: testAllIndexers
};
class HealthConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.dispatchFetchHealth();
}
//
// Render
render() {
const {
dispatchFetchHealth,
...otherProps
} = this.props;
return (
<Health
{...otherProps}
/>
);
}
}
HealthConnector.propTypes = {
dispatchFetchHealth: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(HealthConnector);
@@ -0,0 +1,79 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchHealth } from 'Store/Actions/systemActions';
import PageSidebarStatus from 'Components/Page/Sidebar/PageSidebarStatus';
function createMapStateToProps() {
return createSelector(
(state) => state.app,
(state) => state.system.health,
(app, health) => {
const count = health.items.length;
let errors = false;
let warnings = false;
health.items.forEach((item) => {
if (item.type === 'error') {
errors = true;
}
if (item.type === 'warning') {
warnings = true;
}
});
return {
isConnected: app.isConnected,
isReconnecting: app.isReconnecting,
isPopulated: health.isPopulated,
count,
errors,
warnings
};
}
);
}
const mapDispatchToProps = {
fetchHealth
};
class HealthStatusConnector extends Component {
//
// Lifecycle
componentDidMount() {
if (!this.props.isPopulated) {
this.props.fetchHealth();
}
}
componentDidUpdate(prevProps) {
if (this.props.isConnected && prevProps.isReconnecting) {
this.props.fetchHealth();
}
}
//
// Render
render() {
return (
<PageSidebarStatus
{...this.props}
/>
);
}
}
HealthStatusConnector.propTypes = {
isConnected: PropTypes.bool.isRequired,
isReconnecting: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
fetchHealth: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(HealthStatusConnector);