+ client: login page

This commit is contained in:
Ildar Kamalov
2019-09-05 19:07:14 +03:00
parent 1e4edf0669
commit 66bd06cf69
26 changed files with 607 additions and 118 deletions

View File

@@ -64,7 +64,7 @@ class App extends Component {
};
render() {
const { dashboard, encryption } = this.props;
const { dashboard, encryption, getVersion } = this.props;
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
return (
@@ -109,7 +109,12 @@ class App extends Component {
</Fragment>
)}
</div>
<Footer />
<Footer
dnsVersion={dashboard.dnsVersion}
dnsPort={dashboard.dnsPort}
processingVersion={dashboard.processingVersion}
getVersion={getVersion}
/>
<Toasts />
<Icons />
</Fragment>
@@ -127,6 +132,7 @@ App.propTypes = {
error: PropTypes.string,
changeLanguage: PropTypes.func,
encryption: PropTypes.object,
getVersion: PropTypes.func,
};
export default withNamespaces()(App);

View File

@@ -29,6 +29,7 @@
.nav-tabs .nav-link {
width: 100%;
border: 0;
padding: 20px 0;
}
.header {
@@ -68,42 +69,8 @@
overflow: hidden;
}
.nav-version {
padding: 7px 0;
font-size: 0.80rem;
text-align: right;
}
.nav-version__value {
max-width: 110px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
@media screen and (min-width: 992px) {
.nav-version__value {
max-width: 100%;
overflow: visible;
}
}
.nav-version__link {
position: relative;
display: inline-block;
border-bottom: 1px dashed #495057;
cursor: pointer;
}
.nav-version__text {
display: flex;
align-items: center;
justify-content: flex-end;
}
.header-brand-img {
height: 32px;
height: 24px;
}
.nav-tabs .nav-item.show .nav-link {
@@ -112,6 +79,56 @@
border-bottom-color: #66b574;
}
.header__right {
display: flex;
align-items: center;
justify-content: flex-end;
min-width: 100px;
}
.header__logout {
display: inline-flex;
align-items: center;
justify-content: center;
width: 25px;
height: 25px;
min-width: 25px;
padding: 2px;
margin-left: 10px;
color: #9aa0ac;
}
.header__logout:hover,
.header__logout:focus {
color: #6e7687;
}
.header__logout-icon {
height: 100%;
}
.header__row {
display: flex;
align-items: center;
}
.header__container {
width: 100%;
max-width: 1200px;
padding-right: 0.75rem;
padding-left: 0.75rem;
margin-right: auto;
margin-left: auto;
}
.header__column:last-child {
margin-left: auto;
}
.nav-tabs {
margin: 0;
}
@media screen and (min-width: 992px) {
.header {
padding: 0;
@@ -139,13 +156,31 @@
box-shadow: none;
}
.nav-version {
padding: 0;
}
.nav-icon {
display: none;
}
.header-brand-img {
height: 32px;
}
.header__logout {
width: 35px;
height: 35px;
padding: 5px;
}
.header__row {
justify-content: space-between;
}
.header__column:last-child {
margin-left: 0;
}
.nav-tabs {
margin: 0 -0.75rem;
}
}
@media screen and (min-width: 1280px) {
@@ -153,10 +188,6 @@
font-size: 14px;
}
.nav-version {
font-size: 0.85rem;
}
.nav-icon {
display: block;
}

View File

@@ -26,7 +26,7 @@ class Menu extends Component {
render() {
const menuClass = classnames({
'col-lg-6 mobile-menu': true,
'header__column mobile-menu': true,
'mobile-menu--active': this.props.isMenuOpen,
});

View File

@@ -5,7 +5,6 @@ import classnames from 'classnames';
import { Trans, withNamespaces } from 'react-i18next';
import Menu from './Menu';
import Version from './Version';
import logo from '../ui/svg/logo.svg';
import './Header.css';
@@ -23,7 +22,7 @@ class Header extends Component {
};
render() {
const { dashboard, getVersion, location } = this.props;
const { dashboard, location } = this.props;
const { isMenuOpen } = this.state;
const badgeClass = classnames({
'badge dns-status': true,
@@ -33,21 +32,24 @@ class Header extends Component {
return (
<div className="header">
<div className="container">
<div className="row align-items-center">
<div className="header-toggler d-lg-none ml-2 ml-lg-0 collapsed" onClick={this.toggleMenuOpen}>
<div className="header__container">
<div className="header__row">
<div
className="header-toggler d-lg-none ml-lg-0 collapsed"
onClick={this.toggleMenuOpen}
>
<span className="header-toggler-icon"></span>
</div>
<div className="col col-lg-3">
<div className="header__column">
<div className="d-flex align-items-center">
<Link to="/" className="nav-link pl-0 pr-1">
<img src={logo} alt="" className="header-brand-img" />
</Link>
{!dashboard.proccessing && dashboard.isCoreRunning &&
{!dashboard.proccessing && dashboard.isCoreRunning && (
<span className={badgeClass}>
<Trans>{dashboard.protectionEnabled ? 'on' : 'off'}</Trans>
</span>
}
)}
</div>
</div>
<Menu
@@ -56,14 +58,13 @@ class Header extends Component {
toggleMenuOpen={this.toggleMenuOpen}
closeMenu={this.closeMenu}
/>
{!dashboard.processing &&
<div className="col col-sm-6 col-lg-3">
<Version
{ ...dashboard }
getVersion={getVersion}
/>
<div className="header__column">
<div className="header__right">
<a href="/control/logout" className="btn btn-sm btn-outline-secondary">
<Trans>logout</Trans>
</a>
</div>
}
</div>
</div>
</div>
</div>
@@ -75,6 +76,7 @@ Header.propTypes = {
dashboard: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
getVersion: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withNamespaces()(Header);

View File

@@ -1,3 +1,7 @@
.footer {
padding: 1rem 0;
}
.footer__row {
display: flex;
align-items: center;
@@ -8,6 +12,12 @@
margin-bottom: 15px;
}
.footer__column--links {
display: flex;
flex-direction: column;
align-items: center;
}
.footer__column--language {
min-width: 220px;
margin-bottom: 0;
@@ -16,7 +26,7 @@
.footer__link {
display: inline-block;
vertical-align: middle;
margin-right: 15px;
margin-bottom: 8px;
}
.footer__link--report {
@@ -42,4 +52,12 @@
min-width: initial;
margin-left: auto;
}
.footer__column--links {
display: block;
}
.footer__link {
margin: 0 20px 0 0;
}
}

View File

@@ -1,8 +1,10 @@
import React, { Component } from 'react';
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import { REPOSITORY, LANGUAGES, PRIVACY_POLICY_LINK } from '../../helpers/constants';
import i18n from '../../i18n';
import Version from './Version';
import './Footer.css';
import './Select.css';
@@ -14,42 +16,98 @@ class Footer extends Component {
changeLanguage = (event) => {
i18n.changeLanguage(event.target.value);
}
};
render() {
const {
dnsVersion, processingVersion, getVersion,
} = this.props;
return (
<footer className="footer">
<div className="container">
<div className="footer__row">
<div className="footer__column">
<div className="footer__copyright">
<Trans>copyright</Trans> &copy; {this.getYear()} <a href="https://adguard.com/">AdGuard</a>
<Fragment>
<footer className="footer">
<div className="container">
<div className="footer__row">
{!dnsVersion && (
<div className="footer__column">
<div className="footer__copyright">
<Trans>copyright</Trans> &copy; {this.getYear()}{' '}
<a href="https://adguard.com/">AdGuard</a>
</div>
</div>
)}
<div className="footer__column footer__column--links">
<a
href={REPOSITORY.URL}
className="footer__link"
target="_blank"
rel="noopener noreferrer"
>
<Trans>homepage</Trans>
</a>
<a
href={PRIVACY_POLICY_LINK}
className="footer__link"
target="_blank"
rel="noopener noreferrer"
>
<Trans>privacy_policy</Trans>
</a>
<a
href={REPOSITORY.ISSUES}
className="btn btn-outline-primary btn-sm footer__link footer__link--report"
target="_blank"
rel="noopener noreferrer"
>
<Trans>report_an_issue</Trans>
</a>
</div>
<div className="footer__column footer__column--language">
<select
className="form-control select select--language"
value={i18n.language}
onChange={this.changeLanguage}
>
{LANGUAGES.map(language => (
<option key={language.key} value={language.key}>
{language.name}
</option>
))}
</select>
</div>
</div>
<div className="footer__column">
<a href={REPOSITORY.URL} className="footer__link" target="_blank" rel="noopener noreferrer">
<Trans>homepage</Trans>
</a>
<a href={PRIVACY_POLICY_LINK} className="footer__link" target="_blank" rel="noopener noreferrer">
<Trans>privacy_policy</Trans>
</a>
<a href={REPOSITORY.ISSUES} className="btn btn-outline-primary btn-sm footer__link footer__link--report" target="_blank" rel="noopener noreferrer">
<Trans>report_an_issue</Trans>
</a>
</div>
<div className="footer__column footer__column--language">
<select className="form-control select select--language" value={i18n.language} onChange={this.changeLanguage}>
{LANGUAGES.map(language =>
<option key={language.key} value={language.key}>
{language.name}
</option>)}
</select>
</div>
</footer>
{dnsVersion && (
<div className="footer">
<div className="container">
<div className="footer__row">
<div className="footer__column">
<div className="footer__copyright">
<Trans>copyright</Trans> &copy; {this.getYear()}{' '}
<a href="https://adguard.com/">AdGuard</a>
</div>
</div>
<div className="footer__column footer__column--language">
<Version
dnsVersion={dnsVersion}
processingVersion={processingVersion}
getVersion={getVersion}
/>
</div>
</div>
</div>
</div>
</div>
</footer>
)}
</Fragment>
);
}
}
Footer.propTypes = {
dnsVersion: PropTypes.string,
processingVersion: PropTypes.bool,
getVersion: PropTypes.func,
};
export default withNamespaces()(Footer);

View File

@@ -131,6 +131,10 @@ const Icons = () => (
<symbol id="question" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12" y2="17" />
</symbol>
<symbol id="logout" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line>
</symbol>
</svg>
);

View File

@@ -4,7 +4,13 @@
}
.page-title__actions {
display: inline-block;
vertical-align: baseline;
margin-left: 20px;
display: block;
}
@media screen and (min-width: 768px) {
.page-title__actions {
display: inline-block;
vertical-align: baseline;
margin-left: 20px;
}
}

View File

@@ -78,7 +78,7 @@ section {
body {
margin: 0;
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
font-size: 0.9375rem;
font-weight: 400;
line-height: 1.5;

View File

@@ -0,0 +1,40 @@
.version {
font-size: 0.80rem;
}
@media screen and (min-width: 1280px) {
.version {
font-size: 0.85rem;
}
}
.version__value {
font-weight: 600;
}
@media screen and (min-width: 992px) {
.version__value {
max-width: 100%;
overflow: visible;
}
}
.version__link {
position: relative;
display: inline-block;
border-bottom: 1px dashed #495057;
cursor: pointer;
}
.version__text {
display: flex;
align-items: center;
justify-content: center;
}
@media screen and (min-width: 992px) {
.version__text {
justify-content: flex-end;
}
}

View File

@@ -2,15 +2,17 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import './Version.css';
const Version = (props) => {
const {
dnsVersion, dnsAddresses, processingVersion, t,
dnsVersion, processingVersion, t,
} = props;
return (
<div className="nav-version">
<div className="nav-version__text">
<Trans>version</Trans>:&nbsp;<span className="nav-version__value" title={dnsVersion}>{dnsVersion}</span>
<div className="version">
<div className="version__text">
<Trans>version</Trans>:&nbsp;<span className="version__value" title={dnsVersion}>{dnsVersion}</span>
<button
type="button"
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
@@ -23,24 +25,12 @@ const Version = (props) => {
</svg>
</button>
</div>
<div className="nav-version__link">
<div className="popover__trigger popover__trigger--address">
<Trans>dns_addresses</Trans>
</div>
<div className="popover__body popover__body--address">
<div className="popover__list">
{dnsAddresses.map(ip => <li key={ip}>{ip}</li>)}
</div>
</div>
</div>
</div>
);
};
Version.propTypes = {
dnsVersion: PropTypes.string.isRequired,
dnsAddresses: PropTypes.array.isRequired,
dnsPort: PropTypes.number.isRequired,
getVersion: PropTypes.func.isRequired,
processingVersion: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,