Initial Commit Rework

This commit is contained in:
Qstick 2017-09-03 22:20:56 -04:00
parent 74a4cc048c
commit 95051cbd63
2483 changed files with 101351 additions and 111396 deletions

View file

@ -0,0 +1,71 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import titleCase from 'Utilities/String/titleCase';
import FieldSet from 'Components/FieldSet';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
class About extends Component {
//
// Render
render() {
const {
version,
isMonoRuntime,
runtimeVersion,
appData,
startupPath,
mode
} = this.props;
return (
<FieldSet
legend="About"
>
<DescriptionList>
<DescriptionListItem
title="Version"
data={version}
/>
{
isMonoRuntime &&
<DescriptionListItem
title="Mono Version"
data={runtimeVersion}
/>
}
<DescriptionListItem
title="AppData directory"
data={appData}
/>
<DescriptionListItem
title="Startup directory"
data={startupPath}
/>
<DescriptionListItem
title="Mode"
data={titleCase(mode)}
/>
</DescriptionList>
</FieldSet>
);
}
}
About.propTypes = {
version: PropTypes.string,
isMonoRuntime: PropTypes.bool,
runtimeVersion: PropTypes.string,
appData: PropTypes.string,
startupPath: PropTypes.string,
mode: PropTypes.string
};
export default About;

View file

@ -0,0 +1,48 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchStatus } from 'Store/Actions/systemActions';
import About from './About';
function createMapStateToProps() {
return createSelector(
(state) => state.system.status,
(status) => {
return {
...status.item
};
}
);
}
const mapDispatchToProps = {
fetchStatus
};
class AboutConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchStatus();
}
//
// Render
render() {
return (
<About
{...this.props}
/>
);
}
}
AboutConnector.propTypes = {
fetchStatus: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(AboutConnector);

View file

@ -0,0 +1,5 @@
.space {
composes: cell from 'Components/Table/Cells/TableRowCell.css';
width: 150px;
}

View file

@ -0,0 +1,122 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { kinds, sizes } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
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 ProgressBar from 'Components/ProgressBar';
import styles from './DiskSpace.css';
const columns = [
{
name: 'path',
label: 'Location',
isVisible: true
},
{
name: 'freeSpace',
label: 'Free Space',
isVisible: true
},
{
name: 'totalSpace',
label: 'Total Space',
isVisible: true
},
{
name: 'progress',
isVisible: true
}
];
class DiskSpace extends Component {
//
// Render
render() {
const {
isFetching,
items
} = this.props;
return (
<FieldSet
legend="Disk Space"
>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching &&
<Table
columns={columns}
>
<TableBody>
{
items.map((item) => {
const {
freeSpace,
totalSpace
} = item;
const diskUsage = (100 - freeSpace / totalSpace * 100);
let diskUsageKind = kinds.PRIMARY;
if (diskUsage > 90) {
diskUsageKind = kinds.DANGER;
} else if (diskUsage > 80) {
diskUsageKind = kinds.WARNING;
}
return (
<TableRow key={item.path}>
<TableRowCell>
{item.path}
{
item.label &&
` (${item.label})`
}
</TableRowCell>
<TableRowCell className={styles.space}>
{formatBytes(freeSpace)}
</TableRowCell>
<TableRowCell className={styles.space}>
{formatBytes(totalSpace)}
</TableRowCell>
<TableRowCell className={styles.space}>
<ProgressBar
progress={diskUsage}
kind={diskUsageKind}
size={sizes.MEDIUM}
/>
</TableRowCell>
</TableRow>
);
})
}
</TableBody>
</Table>
}
</FieldSet>
);
}
}
DiskSpace.propTypes = {
isFetching: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired
};
export default DiskSpace;

View file

@ -0,0 +1,54 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchDiskSpace } from 'Store/Actions/systemActions';
import DiskSpace from './DiskSpace';
function createMapStateToProps() {
return createSelector(
(state) => state.system.diskSpace,
(diskSpace) => {
const {
isFetching,
items
} = diskSpace;
return {
isFetching,
items
};
}
);
}
const mapDispatchToProps = {
fetchDiskSpace
};
class DiskSpaceConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchDiskSpace();
}
//
// Render
render() {
return (
<DiskSpace
{...this.props}
/>
);
}
}
DiskSpaceConnector.propTypes = {
fetchDiskSpace: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(DiskSpaceConnector);

View file

@ -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;
}

View file

@ -0,0 +1,170 @@
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 Link from 'Components/Link/Link';
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 (
<Link to="/settings/indexers">
Settings
</Link>
);
case 'DownloadClientCheck':
case 'ImportMechanismCheck':
return (
<Link to="/settings/downloadclients">
Settings
</Link>
);
case 'RootFolderCheck':
return (
<div>
<Link to="/serieseditor">
Series Editor
</Link>
</div>
);
case 'UpdateCheck':
return (
<Link to="/system/updates">
Updates
</Link>
);
default:
return;
}
}
const columns = [
{
className: styles.status,
name: 'type',
isVisible: true
},
{
name: 'message',
label: 'Message',
isVisible: true
},
{
name: 'wikiLink',
label: 'Wiki',
isVisible: true
},
{
name: 'internalLink',
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);
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>
<Link
to={item.wikiUrl}
title="Read the Wiki for more information"
>
Wiki
</Link>
</TableRowCell>
<TableRowCell>
{
internalLink
}
</TableRowCell>
</TableRow>
);
})
}
</TableBody>
</Table>
}
</FieldSet>
);
}
}
Health.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired
};
export default Health;

View file

@ -0,0 +1,56 @@
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 Health from './Health';
function createMapStateToProps() {
return createSelector(
(state) => state.system.health,
(health) => {
const {
isFetching,
isPopulated,
items
} = health;
return {
isFetching,
isPopulated,
items
};
}
);
}
const mapDispatchToProps = {
fetchHealth
};
class HealthConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchHealth();
}
//
// Render
render() {
return (
<Health
{...this.props}
/>
);
}
}
HealthConnector.propTypes = {
fetchHealth: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(HealthConnector);

View file

@ -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);

View file

@ -0,0 +1,69 @@
import React, { Component } from 'react';
import Link from 'Components/Link/Link';
import FieldSet from 'Components/FieldSet';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
class MoreInfo extends Component {
//
// Render
render() {
return (
<FieldSet
legend="More Info"
>
<DescriptionList>
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://lidarr.audio/">lidarr.audio</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/Wiki">wiki.lidarr.audio</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Reddit</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://www.reddit.com/r/Lidarr/">Lidarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://discord.gg/8Y7rDc9">#lidarr on Discord</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Donations</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://www.paypal.me/Lidarr">Donate to Lidarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Donations (Sonarr)</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="ttps://sonarr.tv/donate">Donate to Sonarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Source</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/">github.com/Lidarr/Lidarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Feature Requests</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://github.com/Lidarr/Lidarr/issues">github.com/Lidarr/Lidarr/issues</Link>
</DescriptionListItemDescription>
</DescriptionList>
</FieldSet>
);
}
}
MoreInfo.propTypes = {
};
export default MoreInfo;

View file

@ -0,0 +1,29 @@
import React, { Component } from 'react';
import PageContent from 'Components/Page/PageContent';
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
import HealthConnector from './Health/HealthConnector';
import DiskSpaceConnector from './DiskSpace/DiskSpaceConnector';
import AboutConnector from './About/AboutConnector';
import MoreInfo from './MoreInfo/MoreInfo';
class Status extends Component {
//
// Render
render() {
return (
<PageContent title="Status">
<PageContentBodyConnector>
<HealthConnector />
<DiskSpaceConnector />
<AboutConnector />
<MoreInfo />
</PageContentBodyConnector>
</PageContent>
);
}
}
export default Status;