diff --git a/app/soapbox/features/admin/components/admin_nav.js b/app/soapbox/features/admin/components/admin_nav.js new file mode 100644 index 000000000..95a50e38e --- /dev/null +++ b/app/soapbox/features/admin/components/admin_nav.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Icon from 'soapbox/components/icon'; +import { NavLink } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; + +export default +class AdminNav extends React.PureComponent { + + render() { + return ( +
+
+ + + + +
+
+ ); + } + +} diff --git a/app/soapbox/features/admin/index.js b/app/soapbox/features/admin/index.js new file mode 100644 index 000000000..96115b7b6 --- /dev/null +++ b/app/soapbox/features/admin/index.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl'; +import { connect } from 'react-redux'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Column from '../ui/components/column'; +import { parseVersion } from 'soapbox/utils/features'; + +const messages = defineMessages({ + heading: { id: 'column.admin.dashboard', defaultMessage: 'Dashboard' }, +}); + +const mapStateToProps = (state, props) => ({ + instance: state.get('instance'), +}); + +export default @connect(mapStateToProps) +@injectIntl +class Dashboard extends ImmutablePureComponent { + + static propTypes = { + intl: PropTypes.object.isRequired, + instance: ImmutablePropTypes.map.isRequired, + }; + + render() { + const { intl, instance } = this.props; + const v = parseVersion(instance.get('version')); + + return ( + +
+
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

+
    +
  • Soapbox FE 1.1.0
  • +
  • {v.software} {v.version}
  • +
+
+
+
+ ); + } + +} diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 8b714fe7e..6d6080ef7 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -39,6 +39,7 @@ import Icon from 'soapbox/components/icon'; import { isStaff } from 'soapbox/utils/accounts'; import ChatPanes from 'soapbox/features/chats/components/chat_panes'; import ProfileHoverCard from 'soapbox/components/profile_hover_card'; +import AdminNav from 'soapbox/features/admin/components/admin_nav'; import { Status, @@ -86,6 +87,7 @@ import { ChatIndex, ChatRoom, ServerInfo, + Dashboard, } from './util/async-components'; // Dummy import, to make sure that ends up in the application bundle. @@ -154,6 +156,14 @@ const LAYOUT = { , ], }, + ADMIN: { + LEFT: [ + , + ], + RIGHT: [ + , + ], + }, STATUS: { TOP: null, LEFT: null, @@ -274,6 +284,8 @@ class SwitchingColumnsArea extends React.PureComponent { + + diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js index 3cb9e5142..3421f2d9e 100644 --- a/app/soapbox/features/ui/util/async-components.js +++ b/app/soapbox/features/ui/util/async-components.js @@ -217,3 +217,7 @@ export function ChatRoom() { export function ServerInfo() { return import(/* webpackChunkName: "features/server_info" */'../../server_info'); } + +export function Dashboard() { + return import(/* webpackChunkName: "features/admin" */'../../admin'); +} diff --git a/app/styles/application.scss b/app/styles/application.scss index 3841d231e..91f5ec192 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -79,6 +79,7 @@ @import 'components/snackbar'; @import 'components/accordion'; @import 'components/server-info'; +@import 'components/admin'; // Holiday @import 'holiday/halloween'; diff --git a/app/styles/components/admin.scss b/app/styles/components/admin.scss new file mode 100644 index 000000000..5d04d77ea --- /dev/null +++ b/app/styles/components/admin.scss @@ -0,0 +1,69 @@ +.dashcounters { + display: flex; + flex-wrap: wrap; + margin: 0 -5px 0; + padding: 20px; +} + +.dashcounter { + box-sizing: border-box; + flex: 0 0 33.333%; + padding: 0 5px; + margin-bottom: 10px; + + > a, + > div { + text-decoration: none; + color: inherit; + display: block; + padding: 20px; + background: var(--accent-color--faint); + border-radius: 4px; + transition: 0.2s; + } + + > a:hover { + background: var(--accent-color--med); + transform: translateY(-2px); + } + + &__num, + &__text { + text-align: center; + font-weight: 500; + font-size: 24px; + line-height: 30px; + color: var(--primary-text-color); + margin-bottom: 10px; + } + + &__label { + font-size: 14px; + color: hsla(var(--primary-text-color_hsl), 0.6); + text-align: center; + font-weight: 500; + } +} + +.dashwidgets { + display: flex; + flex-wrap: wrap; + margin: 0 -5px; + padding: 0 20px 20px 20px; +} + +.dashwidget { + flex: 1; + margin-bottom: 20px; + padding: 0 5px; + + h4 { + text-transform: uppercase; + font-size: 13px; + font-weight: 700; + color: #999; + padding-bottom: 8px; + margin-bottom: 8px; + border-bottom: 1px solid #cfeaf3; + } +}