Commit d8ba0b51 authored by Bogdan Bodnar's avatar Bogdan Bodnar

Merge branch 'notifications-settings' into 'dev'

Notifications settings

Closes #17

See merge request !2
parents 6c6d8ee4 dec8c6a3
......@@ -6,7 +6,7 @@
.PHONY: all install run run-js test test-web clean
SHELL=/bin/bash
PYTHON=python3.7
PYTHON=python3.6
FLASK=flask
VENV_NAME?=venv
......@@ -45,6 +45,10 @@ install-js: js/package.json
run:
${FLASK} run --host="0.0.0.0" --port=81
run-ws:
foris-ws --host "0.0.0.0" --port 9081 -a filesystem -d mqtt --mqtt-host localhost --mqtt-port 11883 \
--mqtt-passwd-file '/etc/fosquitto/credentials.plain';
watch-js:
cd ${JS_DIR};\
npx watchify ./src/app.js -o ../reforis/static/js/app.min.js \
......
......@@ -8,11 +8,11 @@
import React from 'react';
import {render} from 'react-dom';
import NotificationsDropdown from './notifications/NotificationsDropdown/NotificationsDropdown';
import NotificationsCenter from './notifications/NotificationsCenter/NotificationsCenter';
import WiFi from './wifi/WiFi';
import WAN from './wan/WAN';
import LAN from './lan/LAN';
import NotificationsDropdown from './notifications/NotificationsDropdown';
import NotificationsCenter from './notifications/NotificationsCenter';
import ConnectionTest from './connectionTest/ConnectionTest';
import webSockets from './webSockets';
......
/*
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
*
* This is free software, licensed under the GNU General Public License v3.
* See /LICENSE for more information.
*/
import React from 'react';
import Input from './Input';
import PropTypes from 'prop-types';
const EmailInput = Input('email');
EmailInput.propTypes = {
label: PropTypes.string.isRequired,
error: PropTypes.string,
helpText: PropTypes.string,
value: PropTypes.string,
};
export default EmailInput;
......@@ -5,7 +5,7 @@ exports[`<Checkbox/> Render checkbox 1`] = `
class="form-group row"
>
<div
class="form-label col-sm-2"
class="form-label col-sm-3"
>
<label
class="form-label"
......@@ -15,7 +15,7 @@ exports[`<Checkbox/> Render checkbox 1`] = `
</label>
</div>
<div
class="form-check col-sm-9"
class="form-check col-sm-8"
>
<input
checked=""
......@@ -36,7 +36,7 @@ exports[`<Checkbox/> Render uncheked checkbox 1`] = `
class="form-group row"
>
<div
class="form-label col-sm-2"
class="form-label col-sm-3"
>
<label
class="form-label"
......@@ -46,7 +46,7 @@ exports[`<Checkbox/> Render uncheked checkbox 1`] = `
</label>
</div>
<div
class="form-check col-sm-9"
class="form-check col-sm-8"
>
<input
id="2"
......
......@@ -5,13 +5,13 @@ exports[`<PasswordInput/> Render number input 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="1"
>
Test label
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......
......@@ -5,13 +5,13 @@ exports[`<PasswordInput/> Render password input 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="1"
>
Test label
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......
......@@ -6,7 +6,7 @@ exports[`<RadioSet/> Render radio set 1`] = `
id="1"
>
<div
class="form-label col-sm-2"
class="form-label col-sm-3"
>
<label
class="form-label"
......@@ -16,7 +16,7 @@ exports[`<RadioSet/> Render radio set 1`] = `
</label>
</div>
<div
class="col-sm-9"
class="col-sm-8"
>
<div
class="form-check form-check-inline"
......
......@@ -5,13 +5,13 @@ exports[`<Select/> Render select 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="1"
>
Test label
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......
......@@ -5,13 +5,13 @@ exports[`<TextInput/> Render text input 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="1"
>
Test label
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......
......@@ -5,5 +5,5 @@
* See /LICENSE for more information.
*/
export const LABEL_SIZE = 2;
export const FIELD_SIZE = 9;
export const LABEL_SIZE = 3;
export const FIELD_SIZE = 8;
......@@ -10,6 +10,7 @@ import API from './utils';
const API_URL = '/api';
const ENDPOINTS = [
{name: 'notifications', url: '/notifications', methods: ['get', 'post']},
{name: 'router_notifications', url: '/notifications-settings', methods: ['get', 'post']},
{name: 'wifi', url: '/wifi', methods: ['get', 'post']},
{name: 'wan', url: '/wan', methods: ['get', 'post']},
{name: 'lan', url: '/lan', methods: ['get', 'post']},
......
......@@ -20,7 +20,7 @@ ForisForm.propTypes = {
children: propTypes.node.isRequired,
};
export default function ForisForm({ws, module, prepData, prepDataToSubmit, validator, children}) {
export default function ForisForm({ws, module, prepData, prepDataToSubmit, validator, anotherWSLogic, children}) {
const [
formData,
formErrors,
......@@ -30,7 +30,7 @@ export default function ForisForm({ws, module, prepData, prepDataToSubmit, valid
setFormValue,
onSubmit,
] = useForisForm(ws, module, prepData, prepDataToSubmit, validator);
] = useForisForm(ws, module, prepData, prepDataToSubmit, validator, anotherWSLogic);
if (JSON.stringify(formData) === '{}')
return null;
......
......@@ -56,12 +56,14 @@ function getChangedValue(target) {
let value = target.value;
if (target.type === 'checkbox')
value = target.checked;
else if (target.type === 'number')
value = parseInt(value);
else if (target.type === 'number') {
const parsedValue = parseInt(value);
value = isNaN(parsedValue) ? value : parsedValue;
}
return value
}
function useForisFormWS(ws, loadFormData, setFormState) {
function useForisFormWS(ws, module, loadFormData, setFormState) {
const [remindsToNWRestart, setRemindsToNWRestart] = useState(null);
useEffect(() => {
......@@ -81,7 +83,7 @@ function useForisFormWS(ws, loadFormData, setFormState) {
return remindsToNWRestart
}
export function useForisForm(ws, module, prepData, prepDataToSubmit, validator) {
export function useForisForm(ws, module, prepData, prepDataToSubmit, validator, anotherWSLogic) {
const [
formData,
formErrors,
......@@ -97,10 +99,17 @@ export function useForisForm(ws, module, prepData, prepDataToSubmit, validator)
useEffect(() => loadFormData(), []);
useEffect(() => setFormState(isReady ? FORM_STATES.READY : FORM_STATES.LOAD), [isReady,]);
const remindsToNWRestart = useForisFormWS(ws, loadFormData, setFormState);
const remindsToNWRestart = useForisFormWS(ws, module, loadFormData, setFormState);
const postData = useAPIPostData(module);
// It's useful when the module has different update logic (see notifications email setting.)
// If there will be more module with the same logic then it can be split into a few hooks.
useEffect(() => {
if (anotherWSLogic)
anotherWSLogic(setFormState, loadFormData)
}, []);
function onSubmit(e) {
e.preventDefault();
setFormState(FORM_STATES.UPDATE);
......
......@@ -12,6 +12,7 @@ export const ERROR_MESSAGES = {
domain: _('This is not a valid domain name.'),
DUID: _('This is not a valid DUID.'),
MAC: _('This is not a valid MAC address.'),
MultipleEmails: _("Doesn't contain a list of emails separated by commas."),
};
const REs = {
......@@ -21,6 +22,7 @@ const REs = {
domain: /^([a-zA-Z0-9-]{1,63}\.?)*$/,
DUID: /^([0-9a-fA-F]{2}){4}([0-9a-fA-F]{2})*$/,
MAC: /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/,
MultipleEmails: /^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( *, *[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$/,
};
......@@ -36,4 +38,5 @@ module.exports = {
validateDomain: createValidator('domain'),
validateDUID: createValidator('DUID'),
validateMAC: createValidator('MAC'),
validateMultipleEmails: createValidator('MultipleEmails'),
};
\ No newline at end of file
......@@ -9,13 +9,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="62"
>
LAN mode
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -43,13 +43,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="75"
>
Router IP address
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -72,13 +72,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="76"
>
Network mask
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -99,7 +99,7 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<div
class="form-label col-sm-2"
class="form-label col-sm-3"
>
<label
class="form-label"
......@@ -109,7 +109,7 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
</label>
</div>
<div
class="form-check col-sm-9"
class="form-check col-sm-8"
>
<input
id="77"
......@@ -126,13 +126,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="82"
>
DHCP start
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -154,13 +154,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="83"
>
DHCP max leases
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -182,13 +182,13 @@ exports[`<LAN/> Snapshot managed with enabled DHCP. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="84"
>
Lease time (hours)
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -225,13 +225,13 @@ exports[`<LAN/> Snapshot managed. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="46"
>
LAN mode
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -259,13 +259,13 @@ exports[`<LAN/> Snapshot managed. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="59"
>
Router IP address
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -288,13 +288,13 @@ exports[`<LAN/> Snapshot managed. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="60"
>
Network mask
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -315,7 +315,7 @@ exports[`<LAN/> Snapshot managed. 1`] = `
class="form-group row"
>
<div
class="form-label col-sm-2"
class="form-label col-sm-3"
>
<label
class="form-label"
......@@ -325,7 +325,7 @@ exports[`<LAN/> Snapshot managed. 1`] = `
</label>
</div>
<div
class="form-check col-sm-9"
class="form-check col-sm-8"
>
<input
id="61"
......@@ -357,13 +357,13 @@ exports[`<LAN/> Snapshot unmanaged (dhcp). 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="1"
>
LAN mode
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -391,13 +391,13 @@ exports[`<LAN/> Snapshot unmanaged (dhcp). 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="2"
>
IPv4 protocol
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -428,13 +428,13 @@ exports[`<LAN/> Snapshot unmanaged (dhcp). 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="3"
>
DHCP hostname
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -471,13 +471,13 @@ exports[`<LAN/> Snapshot unmanaged none. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="32"
>
LAN mode
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -505,13 +505,13 @@ exports[`<LAN/> Snapshot unmanaged none. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="33"
>
IPv4 protocol
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -557,13 +557,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="13"
>
LAN mode
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -591,13 +591,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="14"
>
IPv4 protocol
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<select
class="form-control"
......@@ -628,13 +628,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="27"
>
IP address
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -655,13 +655,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="28"
>
Network mask
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -682,13 +682,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="29"
>
Gateway
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -709,13 +709,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="30"
>
DNS server 1
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......@@ -737,13 +737,13 @@ exports[`<LAN/> Snapshot unmanaged static. 1`] = `
class="form-group row"
>
<label
class="form-control-label col-sm-2"
class="form-control-label col-sm-3"
for="31"
>
DNS server 2
</label>
<div
class="col-sm-9"
class="col-sm-8"
>
<input
class="form-control "
......
......@@ -9,8 +9,10 @@
import React from 'react';
import propTypes from 'prop-types';
import useNotifications from './hooks';
import useNotifications from '../hooks';
import NotificationsCenterList from './NotificationsCenterList';
import NotificationsEmailSettings from './NotificationsEmailSettings/NotificationsEmailSettings';
NotificationsCenter.propTypes = {
ws: propTypes.object.isRequired
......@@ -31,12 +33,12 @@ export default function NotificationsCenter({ws}) {
}
return <div id='notifications-center'>
<h3>{_('Settings')}</h3>
<NotificationsEmailSettings ws={ws}/>
<h3>{_('Notifications')}</h3>
{
notifications.length !== 0 ?
getDismissAllButton() :
<p className='text-muted text-center'>{_('No notifications')}</p>
getDismissAllButton() : <p className='text-muted text-center'>{_('No notifications')}</p>
}
<NotificationsCenterList
......
......@@ -8,8 +8,9 @@
import React, {useRef, useEffect} from 'react';
import propTypes from 'prop-types';
import {NOTIFICATION_PROP_TYPES, toLocaleDateString} from './utils';
import NotificationIcon from './NotificationIcon';
import {NOTIFICATION_PROP_TYPES, toLocaleDateString} from '../utils';
import NotificationIcon from '../NotificationIcon';
NotificationsCenterList.propTypes = {
notifications: propTypes.arrayOf(NOTIFICATION_PROP_TYPES),
......
/*
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
*
* This is free software, licensed under the GNU General Public License v3.
* See /LICENSE for more information.
*/
import React from 'react';
import {FORM_STATES} from '../../../forisForm/hooks';
import ForisForm from '../../../forisForm/ForisForm';
import NotificationsEmailSettingsForm from './forms/NotificationsEmailSettingsForm';
import validator from './validator';
export default function NotificationsEmailSettings({ws}) {
const module = 'router_notifications';
// The notifications email setting has different WS notifications action, moreover it should be updated after
// getting update_email_settings notification.
function notificationsEmailSettingWSLogic(setFormState, loadData) {
ws.subscribe(module)
.bind(module, 'update_email_settings', () => {
setFormState(FORM_STATES.UPDATE);
loadData()
});
}
return <>
<ForisForm
ws={ws}
module={module}
prepData={prepData}
prepDataToSubmit={prepDataToSubmit}
validator={validator}
anotherWSLogic={notificationsEmailSettingWSLogic}
>
<NotificationsEmailSettingsForm/>
</ForisForm>
</>
}
function prepData(formData) {
formData = formData.emails;
formData.common.to = formData.common.to.join(', ');