Pull request #796: + client: 2152 Smartphone compatible design for user interface

#2152

* commit 'be82502ba78d52068184b03db5fd4bb044a26583':
  Fix margins
  Fix markup
  Fix dhcp interfaces markup
  + client: 2152 Smartphone compatible design for user interface
This commit is contained in:
Artem Baskal 2020-10-08 12:10:40 +03:00
commit 1a3d98d3bf
12 changed files with 149 additions and 90 deletions

View file

@ -63,10 +63,6 @@ body {
cursor: not-allowed;
}
.select--no-warning {
margin-bottom: 1.375rem;
}
.button-action {
visibility: hidden;
}
@ -75,3 +71,9 @@ body {
.button-action--active {
visibility: visible;
}
@media (max-width: 500px) {
.dashboard .button-action {
visibility: visible;
}
}

View file

@ -33,3 +33,24 @@
right: 0;
}
}
.page-title--dashboard {
display: flex;
align-items: center;
}
.dashboard-title__button{
margin: 0 0.5rem;
}
@media (max-width: 767.98px) {
.page-title--dashboard {
flex-direction: column;
align-items: flex-start;
}
.dashboard-title__button{
margin: 0.5rem 0;
display: block;
}
}

View file

@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { Trans, useTranslation } from 'react-i18next';
import classNames from 'classnames';
import Statistics from './Statistics';
import Counters from './Counters';
import Clients from './Clients';
@ -17,6 +17,7 @@ const Dashboard = ({
getStats,
getStatsConfig,
dashboard,
dashboard: { protectionEnabled, processingProtection },
toggleProtection,
stats,
access,
@ -33,20 +34,12 @@ const Dashboard = ({
getAllStats();
}, []);
const getToggleFilteringButton = () => {
const { protectionEnabled, processingProtection } = dashboard;
const buttonText = protectionEnabled ? 'disable_protection' : 'enable_protection';
const buttonClass = protectionEnabled ? 'btn-gray' : 'btn-success';
const buttonText = protectionEnabled ? 'disable_protection' : 'enable_protection';
return <button
type="button"
className={`btn btn-sm mr-2 ${buttonClass}`}
onClick={() => toggleProtection(protectionEnabled)}
disabled={processingProtection}
>
<Trans>{buttonText}</Trans>
</button>;
};
const buttonClass = classNames('btn btn-sm dashboard-title__button', {
'btn-gray': protectionEnabled,
'btn-success': !protectionEnabled,
});
const refreshButton = <button
type="button"
@ -62,24 +55,27 @@ const Dashboard = ({
? t('for_last_24_hours')
: t('for_last_days', { count: stats.interval });
const refreshFullButton = <button
type="button"
className="btn btn-outline-primary btn-sm"
onClick={() => getAllStats()}
>
<Trans>refresh_statics</Trans>
</button>;
const statsProcessing = stats.processingStats
|| stats.processingGetConfig
|| access.processing;
return <>
<PageTitle title={t('dashboard')}>
<div className="page-title__actions">
{getToggleFilteringButton()}
{refreshFullButton}
</div>
<PageTitle title={t('dashboard')} containerClass="page-title--dashboard">
<button
type="button"
className={buttonClass}
onClick={() => toggleProtection(protectionEnabled)}
disabled={processingProtection}
>
<Trans>{buttonText}</Trans>
</button>
<button
type="button"
className="btn btn-outline-primary btn-sm"
onClick={getAllStats}
>
<Trans>refresh_statics</Trans>
</button>
</PageTitle>
{statsProcessing && <Loading />}
{!statsProcessing && <div className="row row-cards dashboard">

View file

@ -46,7 +46,7 @@ const renderInterfaceValues = ({
gateway_ip,
hardware_address,
ip_addresses,
}) => <div className='d-flex align-items-end col-6'>
}) => <div className='d-flex align-items-end dhcp__interfaces-info'>
<ul className="list-unstyled m-0">
{getInterfaceValues({
gateway_ip,
@ -77,12 +77,12 @@ const Interfaces = () => {
return !processingInterfaces
&& interfaces
&& <>
<div className="row align-items-center pb-2">
<div className="col-6">
<div className="row dhcp__interfaces">
<div className="col col__dhcp">
<Field
name="interface_name"
component={renderSelectField}
className="form-control custom-select"
className="form-control custom-select pl-4 col-md"
validate={[validateRequiredValue]}
label='dhcp_interface_select'
>

View file

@ -0,0 +1,47 @@
.dhcp-form__button {
margin: 0 1rem;
}
.page-title--dhcp {
display: flex;
align-items: center;
}
.col__dhcp {
flex: 0 0 50%;
max-width: 50%;
padding-right: 0;
}
.dhcp__interfaces {
padding-bottom: 1rem;
}
.dhcp__interfaces-info {
padding: 0.5rem 0.75rem 0;
line-break: anywhere;
}
@media (max-width: 991.98px) {
.dhcp-form__button {
margin: 0.5rem 0;
display: block;
}
.page-title--dhcp {
flex-direction: column;
align-items: flex-start;
padding-bottom: 0.5rem;
}
.col__dhcp {
flex: 0 0 100%;
max-width: 100%;
padding-right: 0.75rem;
}
.dhcp__interfaces {
flex-direction: column;
padding-bottom: 0.5rem;
}
}

View file

@ -31,6 +31,7 @@ import {
calculateDhcpPlaceholdersIpv4,
calculateDhcpPlaceholdersIpv6,
} from '../../../helpers/helpers';
import './index.css';
const Dhcp = () => {
const { t } = useTranslation();
@ -114,7 +115,7 @@ const Dhcp = () => {
.every(Boolean) || Object.values(v6)
.every(Boolean));
const className = classNames('btn btn-sm mr-2', {
const className = classNames('btn btn-sm', {
'btn-gray': enabled,
'btn-outline-success': !enabled,
});
@ -141,7 +142,7 @@ const Dhcp = () => {
</button>;
};
const statusButtonClass = classNames('btn btn-sm mx-2', {
const statusButtonClass = classNames('btn btn-sm dhcp-form__button', {
'btn-loading btn-primary': processingStatus,
'btn-outline-primary': !processingStatus,
});
@ -171,28 +172,24 @@ const Dhcp = () => {
const toggleDhcpButton = getToggleDhcpButton();
return <>
<PageTitle title={t('dhcp_settings')} subtitle={t('dhcp_description')}>
<div className="page-title__actions">
<div className="mb-3">
{toggleDhcpButton}
<button
type="button"
className={statusButtonClass}
onClick={onClick}
disabled={enabled || !interface_name || processingConfig}
>
<Trans>check_dhcp_servers</Trans>
</button>
<button
type="button"
className='btn btn-sm mx-2 btn-outline-secondary'
disabled={!enteredSomeValue || processingConfig}
onClick={clear}
>
<Trans>reset_settings</Trans>
</button>
</div>
</div>
<PageTitle title={t('dhcp_settings')} subtitle={t('dhcp_description')} containerClass="page-title--dhcp">
{toggleDhcpButton}
<button
type="button"
className={statusButtonClass}
onClick={onClick}
disabled={enabled || !interface_name || processingConfig}
>
<Trans>check_dhcp_servers</Trans>
</button>
<button
type="button"
className='btn btn-sm btn-outline-secondary'
disabled={!enteredSomeValue || processingConfig}
onClick={clear}
>
<Trans>reset_settings</Trans>
</button>
</PageTitle>
{!processing && !processingInterfaces
&& <>

View file

@ -0,0 +1,11 @@
.form__button {
margin-left: 1.5rem;
}
@media (max-width: 500px) {
.form__button {
margin-left: 0;
margin-top: 1rem;
display: block;
}
}

View file

@ -6,6 +6,7 @@ import flow from 'lodash/flow';
import { CheckboxField, renderRadioField, toNumber } from '../../../helpers/form';
import { FORM_NAME, QUERY_LOG_INTERVALS_DAYS } from '../../../helpers/constants';
import '../FormButton.css';
const getIntervalFields = (processing, t, toNumber) => QUERY_LOG_INTERVALS_DAYS.map((interval) => {
const title = interval === 1 ? t('interval_24_hour') : t('interval_days', { count: interval });
@ -68,7 +69,7 @@ const Form = (props) => {
</button>
<button
type="button"
className="btn btn-outline-secondary btn-standard ml-5"
className="btn btn-outline-secondary btn-standard form__button"
onClick={() => handleClear()}
disabled={processingClear}
>

View file

@ -6,6 +6,7 @@ import flow from 'lodash/flow';
import { renderRadioField, toNumber } from '../../../helpers/form';
import { FORM_NAME, STATS_INTERVALS_DAYS } from '../../../helpers/constants';
import '../FormButton.css';
const getIntervalFields = (processing, t, toNumber) => STATS_INTERVALS_DAYS.map((interval) => {
const title = interval === 1 ? t('interval_24_hour') : t('interval_days', { count: interval });
@ -52,7 +53,7 @@ const Form = (props) => {
</button>
<button
type="button"
className="btn btn-outline-secondary btn-standard ml-5"
className="btn btn-outline-secondary btn-standard form__button"
onClick={() => handleReset()}
disabled={processingReset}
>

View file

@ -36,15 +36,3 @@
font-size: 36px;
line-height: 46px;
}
.page-title__actions {
display: block;
}
@media screen and (min-width: 768px) {
.page-title__actions {
display: inline-block;
vertical-align: baseline;
margin-left: 20px;
}
}

View file

@ -3,24 +3,23 @@ import PropTypes from 'prop-types';
import './PageTitle.css';
const PageTitle = ({ title, subtitle, children }) => (
<div className="page-header">
<h1 className="page-title">
{title}
{children}
</h1>
{subtitle && (
<div className="page-subtitle">
{subtitle}
</div>
)}
const PageTitle = ({
title, subtitle, children, containerClass,
}) => <div className="page-header">
<div className={containerClass}>
<h1 className="page-title pr-2">{title}</h1>
{children}
</div>
);
{subtitle && <div className="page-subtitle">
{subtitle}
</div>}
</div>;
PageTitle.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
children: PropTypes.node,
containerClass: PropTypes.string,
};
export default PageTitle;

View file

@ -1,7 +1,6 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
import classNames from 'classnames';
import { createOnBlurHandler } from './helpers';
import { R_UNIX_ABSOLUTE_PATH, R_WIN_ABSOLUTE_PATH } from './constants';
@ -203,13 +202,10 @@ export const renderSelectField = ({
label,
}) => {
const showWarning = touched && error;
const selectClass = classNames('form-control custom-select', {
'select--no-warning': !showWarning,
});
return <>
{label && <label><Trans>{label}</Trans></label>}
<select {...input} className={selectClass}>{children}</select>
<select {...input} className='form-control custom-select'>{children}</select>
{showWarning
&& <span className="form__message form__message--error form__message--left-pad"><Trans>{error}</Trans></span>}
</>;