[TASK] rewrite table (#10)

This commit is contained in:
Geno 2018-08-10 11:52:46 +02:00 committed by GitHub
parent 7c5d117784
commit 861531712c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 301 additions and 387 deletions

View File

@ -2,15 +2,13 @@ db_type = "sqlite3"
db_connection = "/tmp/freifunkmanager.db"
webserver_bind = ":8080"
webroot = "./webroot"
webroot = "./webroot/"
secret = "passw0rd"
ssh_key = "~/.ssh/id_rsa"
ssh_ipaddress_suffix = "fd2f:"
ssh_timeout = "1m"
ssh_config_all = false
ssh_yanic_update = true
# enable receiving
yanic_enable = true

View File

@ -41,14 +41,18 @@ table th > input {
font-weight: bold;
background: #fff;
}
table th.sortable.sort-down:after {
table th.sort-header:after {
font-size: 0.6em;
}
table th.sort-header.sort-down:after {
content: " \25BC"
}
table th.sortable.sort-up:after {
table th.sort-header.sort-up:after {
content: " \25B2"
}
table th.sortable:not(.sort-down):not(.sort-up):after {
table th.sort-header:not(.sort-down):not(.sort-up):after {
content: " \25B4\25BE";
color: grey;
}
table.nodes, table.stats {
width: 100%;
@ -66,11 +70,11 @@ table.nodes tbody tr:nth-child(odd) {
table.nodes tbody tr:hover {
background: #ccc;
}
table.nodes tbody tr.offline td:first-child,
table.nodes tbody tr.offline:hover td:first-child{
table.nodes tbody tr td.offline,
table.nodes tbody tr td.offline:hover{
background: #dc0067;
}
table.nodes tbody tr.unseen:first-child{
table.nodes tbody tr td.unseen{
background: #009ee0;
}
@ -103,9 +107,10 @@ a {
}
input[readonly] {
input {
border: none;
border-bottom: 1px solid;
border-bottom: 1px solid grey;
background: none;
padding: 0px;
font-size: 15px;

View File

@ -16,6 +16,7 @@ export function getNode (nodeid) {
// eslint-disable-next-line no-underscore-dangle
node._wireless = cNode.wireless;
node.lastseen = cNode.lastseen;
node.statistics = cNode.statistics;
}
return node;
};

96
webroot/js/table.js Normal file
View File

@ -0,0 +1,96 @@
import * as V from 'superfine';
export default class Table {
constructor (el, properties, headings, sortIndex, renderRow) {
this.sortReverse = false;
this.el = el;
this.headings = headings;
this.sortIndex = sortIndex;
this.renderRow = renderRow;
this.properties = properties;
this.maxDisplayedRows = localStorage.getItem("maxDisplayedRows");
if (this.maxDisplayedRows == null) {
this.maxDisplayedRows = 20;
}
}
sortTable(i) {
this.sortReverse = i === this.sortIndex ? !this.sortReverse : false;
this.sortIndex = i;
this.render();
}
render () {
const children = [],
self = this;
if (this.data.length !== 0) {
const th = this.headings.map((d, i) => {
const properties = {};
let name = d.name;
if (d.class) {
properties.class += ' ' + d.class;
properties.title = name;
name = '';
}
if (!d.sort) {
return V.h('th', properties, name);
}
properties.onclick = () => self.sortTable(i);
properties.class = 'sort-header';
if (self.sortIndex === i) {
properties.class += self.sortReverse ? ' sort-up' : ' sort-down';
}
return V.h('th', properties, name);
});
let rows = this.data.slice(0).sort(this.headings[this.sortIndex].sort);
if (this.headings[this.sortIndex].reverse ? !this.sortReverse : this.sortReverse) {
rows = rows.reverse();
}
var maxDisplayedRows = rows.length;
if (this.maxDisplayedRows != -1) {
maxDisplayedRows = Math.min(this.maxDisplayedRows, maxDisplayedRows);
}
var visibleRows = [];
for (let i = 0; i < maxDisplayedRows; i += 1) {
visibleRows.push(rows[i]);
}
children.push(V.h('thead', {}, V.h('tr', {}, th)));
children.push(V.h('tbody', {}, visibleRows.map(this.renderRow)));
children.push(V.h('tfoot', {}, V.h('tr', {}, V.h('td',{colspan: th.length},
[V.h('span',{}, maxDisplayedRows + " of " + rows.length + " nodes. Show: ")].concat(
[["5", 5], ["10", 10], ["20", 20], ["50", 50], ["All", -1]].map((item)=>
V.h('a',{
class: 'btn',
onclick:()=> {
this.maxDisplayedRows = item[1];
localStorage.setItem("maxDisplayedRows", this.maxDisplayedRows);
}
}, item[0])
))
))));
}
V.render(this.vel, this.vel = V.h('table',this.properties, children), this.el);
}
setData(d) {
this.data = d;
this.render();
}
}

View File

@ -3,6 +3,8 @@ import * as gui from '../gui';
import * as lib from '../lib';
import * as socket from '../socket';
import * as store from '../store';
import * as V from 'superfine';
import Table from '../table';
import config from '../config';
import View from '../view';
import {FromNowAgo} from '../lib';
@ -11,374 +13,212 @@ export class ListView extends View {
constructor () {
super();
this.debouncer = new lib.Debouncer(1000, "list render");
this.maxDisplayedNodes = localStorage.getItem("maxDisplayedNodes");
if (this.maxDisplayedNodes == null) {
this.maxDisplayedNodes = 20;
}
const table = domlib.newAt(this.el, 'table'),
thead = domlib.newAt(table, 'thead');
this.tbody = domlib.newAt(table, 'tbody');
// eslint-disable-next-line one-var
const tr = domlib.newAt(thead, 'tr'),
cell1 = domlib.newAt(tr, 'th'),
cell2 = domlib.newAt(tr, 'th'),
cell3 = domlib.newAt(tr, 'th'),
cell4 = domlib.newAt(tr, 'th'),
cell5 = domlib.newAt(tr, 'th'),
cell6 = domlib.newAt(tr, 'th'),
cell7 = domlib.newAt(tr, 'th'),
cell8 = domlib.newAt(tr, 'th'),
cell9 = domlib.newAt(tr, 'th'),
cell10 = domlib.newAt(tr, 'th'),
cell11 = domlib.newAt(tr, 'th');
cell1.innerHTML = 'Lastseen';
cell1.addEventListener('click', () => {
this.sortTable(cell1);
this.filter = domlib.newAt(this.el, 'input', {
'placeholder': 'Search',
'style': 'width: 100%;border: 0px; border-bottom: 2px inset;',
});
cell2.classList.add('sortable');
this.nodeidFilter = domlib.newAt(cell2, 'input');
this.nodeidFilter.setAttribute('placeholder', 'NodeID');
this.nodeidFilter.setAttribute('size', '9');
this.nodeidFilter.addEventListener('keyup', () => {
this.render();
});
cell2.addEventListener('dblclick', () => {
this.sortTable(cell2);
});
this.nodeidFilter.addEventListener('focusin', () => {
this.editing = true;
});
this.nodeidFilter.addEventListener('focusout', () => {
this.editing = false;
this.filter.addEventListener('keyup', () => {
this.render();
});
cell3.classList.add('sortable');
cell3.classList.add('hostname');
this.hostnameFilter = domlib.newAt(cell3, 'input');
this.hostnameFilter.setAttribute('placeholder', 'Hostname');
this.hostnameFilter.addEventListener('keyup', () => {
this.render();
});
cell3.addEventListener('dblclick', () => {
this.sortTable(cell3);
});
this.hostnameFilter.addEventListener('focusin', () => {
this.editing = true;
});
this.hostnameFilter.addEventListener('focusout', () => {
this.editing = false;
this.render();
});
cell4.innerHTML = 'Freq';
cell5.innerHTML = 'CurChannel';
cell5.classList.add('sortable');
cell5.addEventListener('click', () => {
this.sortTable(cell5);
});
cell6.innerHTML = 'Channel';
cell6.classList.add('sortable');
cell6.addEventListener('click', () => {
this.sortTable(cell6);
});
cell7.innerHTML = 'CurPower';
cell7.classList.add('sortable');
cell7.addEventListener('click', () => {
this.sortTable(cell7);
});
cell8.innerHTML = 'Power';
cell8.classList.add('sortable');
cell8.addEventListener('click', () => {
this.sortTable(cell8);
});
cell9.innerHTML = 'Clients';
cell9.classList.add('sortable');
cell9.addEventListener('click', () => {
this.sortTable(cell9);
});
cell10.innerHTML = 'ChanUtil';
cell10.classList.add('sortable');
cell10.addEventListener('click', () => {
this.sortTable(cell10);
});
cell11.innerHTML = 'Option';
table.classList.add('nodes');
this.footerNote = domlib.newAt(this.el, 'span');
var footerLinkContents = [["5", 5], ["10", 10], ["20", 20], ["50", 50], ["All", -1]];
for (var i = 0; i < footerLinkContents.length; i++) {
var link = domlib.newAt(this.el, 'a', null, footerLinkContents[i][0]);
link.classList.add('btn');
const newValue = footerLinkContents[i][1];
link.addEventListener('click', () => {
this.maxDisplayedNodes = newValue;
localStorage.setItem("maxDisplayedNodes", this.maxDisplayedNodes);
this.render();
});
}
}
// eslint-disable-next-line id-length
sort (sortIndex, sortReverse) {
function sortNumber (aNum, bNum) {
return aNum - bNum;
}
return (a, b) => {
if (!sortIndex) {
return a.node_id.localeCompare(b.node_id);
}
if (sortIndex.classList.contains("hostname")) {
return a.hostname.localeCompare(b.hostname);
}
switch (sortIndex.innerText) {
case 'Lastseen':
return a.lastseen - b.lastseen;
case 'CurPower':
// eslint-disable-next-line no-underscore-dangle
return a._wireless.txpower24 - b._wireless.txpower24;
case 'Power':
return a.wireless.txpower24 - b.wireless.txpower24;
case 'CurChannel':
// eslint-disable-next-line no-underscore-dangle
return a._wireless.channel24 - b._wireless.channel24;
case 'Channel':
return a.wireless.channel24 - b.wireless.channel24;
case 'Clients':
return a.statistics.clients.wifi24 - b.statistics.clients.wifi24;
// eslint-disable-next-line no-case-declarations
case 'ChanUtil':
if(a.statistics.wireless === null) return 1;
if(b.statistics.wireless === null) return -1;
// eslint-disable-next-line id-length
let aMax = a.statistics.wireless.map((d) => d.ChanUtil).sort(sortNumber),
// eslint-disable-next-line id-length
bMax = b.statistics.wireless.map((d) => d.ChanUtil).sort(sortNumber);
if (!sortReverse) {
aMax = aMax.reverse();
bMax = bMax.reverse();
}
return bMax[0] - aMax[0];
default:
return a.node_id.localeCompare(b.node_id);
}
}
this.table = new Table(domlib.newAt(this.el, 'div'),{
class: 'nodes',
},[
{
name:'Lastseen',
sort: (a, b) => new Date(a.lastseen) - new Date(b.lastseen),
reverse: false
},
{
name:'NodeID',
sort: (a, b) => a.node_id.localeCompare(b.node_id),
reverse: false
},
{
name:'Hostname',
sort: (a, b) => a.hostname.localeCompare(b.hostname),
reverse: false
},
{ name:'Freq' },
{
name:'CurCh',
sort: (a, b) => (a._wireless ? a._wireless.channel24 : 0) - (b._wireless ? b._wireless.channel24 : 0),
reverse: false
},
{
name:'Channel',
sort: (a, b) => a.wireless.channel24 - b.wireless.channel24,
reverse: false
},
{
name:'CurPW',
sort: (a, b) => (a._wireless ? a._wireless.txpower24 : 0) - (b._wireless ? b._wireless.txpower24 : 0),
reverse: false
},
{
name:'Power',
sort: (a, b) => a.wireless.txpower24 - b.wireless.txpower24,
reverse: false
},
{
name:'Clients',
sort: (a, b) => a.statistics.clients.wifi24 - b.statistics.clients.wifi24,
reverse: false
},
{
name:'ChanUtil',
sort: (a, b) => (a.statistics.wireless ? a.statistics.wireless.filter((d) => d.frequency < 5000)[0].ChanUtil || 0 : 0) - (b.statistics.wireless ? b.statistics.wireless.filter((d) => d.frequency < 5000)[0].ChanUtil || 0 : 0),
reverse: false
},
{ name:'Options' }
], 1, this.renderRow.bind(this));
}
renderRow (node) {
const startdate = new Date(),
tr = document.createElement('tr'),
lastseen = domlib.newAt(tr, 'td'),
nodeID = domlib.newAt(tr, 'td'),
hostname = domlib.newAt(tr, 'td'),
hostnameInput = domlib.newAt(hostname, 'input'),
freq = domlib.newAt(tr, 'td'),
curchannel = domlib.newAt(tr, 'td'),
channel = domlib.newAt(tr, 'td'),
channel24Input = domlib.newAt(domlib.newAt(channel, 'span'), 'select'),
channel5Input = domlib.newAt(domlib.newAt(channel, 'span'), 'select'),
curpower = domlib.newAt(tr, 'td'),
power = domlib.newAt(tr, 'td'),
power24Input = domlib.newAt(domlib.newAt(power, 'span'), 'input'),
power5Input = domlib.newAt(domlib.newAt(power, 'span'), 'input'),
client = domlib.newAt(tr, 'td'),
chanUtil = domlib.newAt(tr, 'td'),
option = domlib.newAt(tr, 'td'),
edit = domlib.newAt(option, 'div');
channel24Options = [],
channel5Options = [];
startdate.setMinutes(startdate.getMinutes() - config.node.offline);
if (new Date(node.lastseen) < startdate) {
tr.classList.add('offline');
// eslint-disable-next-line no-underscore-dangle
} else if (!node._wireless) {
tr.classList.add('unseen');
}
lastseen.textContent = FromNowAgo(node.lastseen);
nodeID.textContent = node.node_id;
hostnameInput.value = node.hostname;
hostnameInput.readOnly = true;
hostnameInput.setAttribute('placeholder', 'Hostname');
hostnameInput.addEventListener('dblclick', () => {
this.editing = true;
hostnameInput.readOnly = false;
});
hostnameInput.addEventListener('focusout', () => {
if (hostnameInput.readOnly) {
return;
}
this.editing = false;
hostnameInput.readOnly = true;
const old = node.hostname;
node.hostname = hostnameInput.value;
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.hostname = old;
hostnameInput.value = old;
}
});
});
domlib.newAt(freq, 'span').textContent = '2.4 Ghz';
domlib.newAt(freq, 'span').textContent = '5 Ghz';
/* eslint-disable no-underscore-dangle */
if (node._wireless) {
domlib.newAt(curchannel, 'span').textContent = node._wireless.channel24 || '-';
domlib.newAt(curchannel, 'span').textContent = node._wireless.channel5 || '-';
}
let i = 0;
for (i = 0; i < store.channelsWifi24.length; i++) {
const opt = domlib.newAt(channel24Input,'option',{
channel24Options.push(V.h('option', {
'value': store.channelsWifi24[i],
},store.channelsWifi24[i]);
if(store.channelsWifi24[i] === node.wireless.channel24) {
opt.selected = true;
}
'selected': (store.channelsWifi24[i] === node.wireless.channel24),
}, store.channelsWifi24[i]));
}
channel24Input.addEventListener('focusin', () => {
this.editing = true;
});
channel24Input.addEventListener('focusout', () => {
this.editing = false;
const old = node.wireless.channel24;
node.wireless.channel24 = parseInt(channel24Input.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.channel24 = old;
channel24Input.value = old;
}
});
});
for (i = 0; i < store.channelsWifi5.length; i++) {
const opt = domlib.newAt(channel5Input,'option',{
channel5Options.push(V.h('option', {
'value': store.channelsWifi5[i],
},store.channelsWifi5[i]);
if(store.channelsWifi5[i] === node.wireless.channel5) {
opt.selected = true;
}
'selected': (store.channelsWifi5[i] === node.wireless.channel5),
}, store.channelsWifi5[i]));
}
channel5Input.addEventListener('focusin', () => {
this.editing = true;
});
channel5Input.addEventListener('focusout', () => {
this.editing = false;
const old = node.wireless.channel5;
node.wireless.channel5 = parseInt(channel5Input.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.channel5 = old;
channel5Input.value = old;
}
});
});
/* eslint-disable no-underscore-dangle */
if (node._wireless) {
domlib.newAt(curpower, 'span').textContent = node._wireless.txpower24 || '-';
domlib.newAt(curpower, 'span').textContent = node._wireless.txpower5 || '-';
}
/* eslint-enable no-underscore-dangle */
power24Input.value = node.wireless.txpower24 || '';
power24Input.type = 'number';
power24Input.min = 1;
power24Input.max = 23;
power24Input.setAttribute('placeholder', '-');
power24Input.addEventListener('focusout', () => {
this.editing = false;
const old = node.wireless.txpower24;
node.wireless.txpower24 = parseInt(power24Input.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.txpower24 = old;
power24Input.value = old;
}
});
});
power5Input.value = node.wireless.txpower5 || '';
power5Input.type = 'number';
power5Input.min = 1;
power5Input.max = 23;
power5Input.setAttribute('placeholder', '-');
power5Input.addEventListener('focusout', () => {
this.editing = false;
const old = node.wireless.txpower5;
node.wireless.txpower5 = parseInt(power5Input.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.txpower5 = old;
power5Input.value = old;
}
});
});
domlib.newAt(client, 'span').textContent = node.statistics.clients.wifi24;
domlib.newAt(client, 'span').textContent = node.statistics.clients.wifi5;
/* eslint-disable id-length, no-magic-numbers,one-var */
const chanUtil24 = node.statistics.wireless
? node.statistics.wireless.filter((d) => d.frequency < 5000)[0] || {}
: {},
chanUtil5 = node.statistics.wireless
? node.statistics.wireless.filter((d) => d.frequency > 5000)[0] || {}
: {};
/* eslint-enable id-length, no-magic-numbers,one-var */
domlib.newAt(chanUtil, 'span').textContent = chanUtil24.ChanUtil || '-';
domlib.newAt(chanUtil, 'span').textContent = chanUtil5.ChanUtil || '-';
edit.classList.add('btn');
edit.textContent = 'Edit';
edit.addEventListener('click', () => {
gui.router.navigate(gui.router.generate('node', {'nodeID': node.node_id}));
});
return tr;
}
return V.h('tr', {},[
V.h('td', {
'class':(new Date(node.lastseen) < startdate)?'offline':(!node._wireless)?'unseen':''
}, FromNowAgo(node.lastseen)),
V.h('td', {}, node.node_id),
V.h('td', {}, V.h('input',{
'value': this._hostname || node.hostname,
'oninput':(e) => {
this._hostname = e.target.value;
},
'onfocusout':(e) => {
delete this._hostname;
sortTable (head) {
if (this.sortIndex) {
this.sortIndex.classList.remove('sort-up', 'sort-down');
}
this.sortReverse = head === this.sortIndex
? !this.sortReverse
: false;
this.sortIndex = head;
const old = node.hostname;
node.hostname = e.target.value;
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.hostname = old;
e.target.value = old;
}
});
}
})),
V.h('td', {}, [
V.h('span', {},'2.4 GHz'),
V.h('span', {},'5 GHz')
]),
V.h('td', {}, [
V.h('span', {}, node._wireless ? node._wireless.channel24 || '-':'-'),
V.h('span', {}, node._wireless ? node._wireless.channel5 || '-':'-')
]),
V.h('td', {}, [
V.h('span', {}, V.h('select',{
'onfocusout':(e) => {
const old = node.wireless.channel24;
node.wireless.channel24 = parseInt(e.target.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.channel24 = old;
e.target.value = old;
}
});
}
}, channel24Options)),
V.h('span', {}, V.h('select',{
'onfocusout':(e) => {
const old = node.wireless.channel5;
node.wireless.channel5 = parseInt(e.target.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.channel5 = old;
e.target.value = old;
}
});
}
}, channel5Options))
]),
V.h('td', {}, [
V.h('span', {}, node._wireless ? node._wireless.txpower24 || '-':'-'),
V.h('span', {}, node._wireless ? node._wireless.txpower5 || '-':'-')
]),
V.h('td', {}, [
V.h('span', {}, V.h('input',{
'type': 'number',
'min': 0,
'max': 23,
'value': this._txpower24 || node.wireless.txpower24,
'oninput':(e) => {
this._txpower24 = e.target.value;
},
'onfocusout':(e) => {
delete this._txpower24;
this.sortIndex.classList.add(this.sortReverse
? 'sort-up'
: 'sort-down');
const old = node.wireless.txpower24;
node.wireless.txpower24 = parseInt(e.target.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.txpower24 = old;
e.target.value = old;
}
});
}
})),
V.h('span', {}, V.h('input',{
'type': 'number',
'min': 0,
'max': 23,
'value': this._txpower5 || node.wireless.txpower5,
'oninput':(e) => {
this._txpower5 = e.target.value;
},
'onfocusout':(e) => {
delete this._txpower5;
this.render();
const old = node.wireless.txpower5;
node.wireless.txpower5 = parseInt(e.target.value, 10);
socket.sendnode(node, (msg)=>{
if (!msg.body) {
node.wireless.txpower5 = old;
e.target.value = old;
}
});
}
}))
]),
V.h('td', {}, [
V.h('span', {}, node.statistics.clients.wifi24),
V.h('span', {}, node.statistics.clients.wifi5)
]),
V.h('td', {}, [
V.h('span', {}, node.statistics.wireless ? (node.statistics.wireless.filter((d) => d.frequency < 5000)[0] || {ChanUtil: '-'}).ChanUtil : '-'),
V.h('span', {}, node.statistics.wireless ? (node.statistics.wireless.filter((d) => d.frequency > 5000)[0] || {ChanUtil: '-'}).ChanUtil : '-'),
]),
V.h('td', {}, [
V.h('a',{
'class':'btn',
'href':gui.router.generate('node', {'nodeID': node.node_id})
}, 'Edit')
]),
]);
}
render () {
@ -386,41 +226,17 @@ export class ListView extends View {
}
renderView () {
if (this.editing && this.tbody) {
return;
}
while(this.tbody.hasChildNodes()) {
this.tbody.removeChild(this.tbody.firstElementChild);
}
let nodes = store.getNodes();
if (this.hostnameFilter && this.hostnameFilter.value !== '') {
if (this.filter && this.filter.value !== '') {
// eslint-disable-next-line id-length
nodes = nodes.filter((d) => d.hostname.toLowerCase().indexOf(this.hostnameFilter.value.toLowerCase()) > -1);
}
if (this.nodeidFilter && this.nodeidFilter.value !== '') {
// eslint-disable-next-line id-length
nodes = nodes.filter((d) => d.node_id.indexOf(this.nodeidFilter.value.toLowerCase()) > -1);
nodes = nodes.filter((d) => {
return d.node_id.toLowerCase().indexOf(this.filter.value.toLowerCase()) > -1 ||
d.hostname.toLowerCase().indexOf(this.filter.value.toLowerCase()) > -1 ||
d.owner.toLowerCase().indexOf(this.filter.value.toLowerCase()) > -1
});
}
nodes = nodes.sort(this.sort(this.sortIndex, this.sortReverse));
if (this.sortReverse) {
nodes = nodes.reverse();
}
var numDisplayedNodes = nodes.length;
if (this.maxDisplayedNodes != -1) {
numDisplayedNodes = Math.min(this.maxDisplayedNodes, numDisplayedNodes);
}
var fragment = document.createDocumentFragment();
for (let i = 0; i < numDisplayedNodes; i += 1) {
const row = this.renderRow(nodes[i]);
fragment.appendChild(row);
}
this.tbody.appendChild(fragment);
this.footerNote.innerHTML = numDisplayedNodes + " of " + nodes.length + " nodes. Show: ";
this.table.setData(nodes);
}
}

View File

@ -1,5 +1,3 @@
import * as domlib from '../domlib';
import * as gui from '../gui';
import * as socket from '../socket';
import * as store from '../store';