mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-12-15 19:31:45 +03:00
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:
commit
1a3d98d3bf
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
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">
|
||||
|
@ -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'
|
||||
>
|
||||
|
47
client/src/components/Settings/Dhcp/index.css
Normal file
47
client/src/components/Settings/Dhcp/index.css
Normal 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;
|
||||
}
|
||||
}
|
@ -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,9 +172,7 @@ const Dhcp = () => {
|
||||
const toggleDhcpButton = getToggleDhcpButton();
|
||||
|
||||
return <>
|
||||
<PageTitle title={t('dhcp_settings')} subtitle={t('dhcp_description')}>
|
||||
<div className="page-title__actions">
|
||||
<div className="mb-3">
|
||||
<PageTitle title={t('dhcp_settings')} subtitle={t('dhcp_description')} containerClass="page-title--dhcp">
|
||||
{toggleDhcpButton}
|
||||
<button
|
||||
type="button"
|
||||
@ -185,14 +184,12 @@ const Dhcp = () => {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className='btn btn-sm mx-2 btn-outline-secondary'
|
||||
className='btn btn-sm btn-outline-secondary'
|
||||
disabled={!enteredSomeValue || processingConfig}
|
||||
onClick={clear}
|
||||
>
|
||||
<Trans>reset_settings</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</PageTitle>
|
||||
{!processing && !processingInterfaces
|
||||
&& <>
|
||||
|
11
client/src/components/Settings/FormButton.css
Normal file
11
client/src/components/Settings/FormButton.css
Normal 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;
|
||||
}
|
||||
}
|
@ -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}
|
||||
>
|
||||
|
@ -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}
|
||||
>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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}
|
||||
const PageTitle = ({
|
||||
title, subtitle, children, containerClass,
|
||||
}) => <div className="page-header">
|
||||
<div className={containerClass}>
|
||||
<h1 className="page-title pr-2">{title}</h1>
|
||||
{children}
|
||||
</h1>
|
||||
{subtitle && (
|
||||
<div className="page-subtitle">
|
||||
</div>
|
||||
{subtitle && <div className="page-subtitle">
|
||||
{subtitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
</div>}
|
||||
</div>;
|
||||
|
||||
PageTitle.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
containerClass: PropTypes.string,
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
|
@ -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>}
|
||||
</>;
|
||||
|
Loading…
Reference in New Issue
Block a user