'use strict'; import { h, render, useState, useEffect, useRef, html, Router } from './bundle.js'; const Logo = props => html` `; const IconHeart = props => html``; const IconDownArrowBox = props => html` `; const IconUpArrowBox = props => html` `; const IconSettings = props => html` `; const IconDesktop = props => html` `; const IconScan = props => html` `; const IconAlert = props => html` `; const IconRefresh = props => html` `; const IconBars4 = props => html` `; const IconBars3 = props => html` `; const IconLogout = props => html` `; const IconSave = props => html` `; const IconEmail = props => html` `; const IconExpand = props => html` `; const IconShrink = props => html` `; const IconOk = props => html` `; const IconFailed = props => html` `; const IconUpload = props => html` `; const IconDownload = props => html` `; const IconBolt = props => html` `; const IconHome = props => html` `; const IconLink = props => html` `; const IconShield = props => html` `; const IconBarsDown = props => html` `; const IconArrowDown = props => html` `; const IconArrowUp = props => html` `; const IconExclamationTriangle = props => html` `; const tipColors = { green: 'bg-green-100 text-green-900', yellow: 'bg-yellow-100 text-yellow-900', red: 'bg-red-100 text-red-900', }; function Button({title, onclick, disabled, cls, icon, ref, colors, hovercolor, disabledcolor}) { const [spin, setSpin] = useState(false); const cb = function(ev) { const res = onclick ? onclick() : null; if (res && typeof (res.catch) === 'function') { setSpin(true); res.catch(() => false).then(() => setSpin(false)); } }; if (!colors) colors = 'bg-blue-600 hover:bg-blue-500 disabled:bg-blue-400'; return html` ${title} <${spin ? IconRefresh : icon} class="w-4 ${spin ? 'animate-spin' : ''}" /> />` }; function Login({login}) { const [user, setUser] = useState(''); const [pass, setPass] = useState(''); const onsubmit = function(ev) { const authhdr = 'Basic ' + btoa(user + ':' + pass); const headers = {Authorization: authhdr}; return fetch('/api/login', {headers}).then(login).finally(r => setPass('')); }; return html` <${Logo} class="h-12 stroke-cyan-600 stroke-1" /> WiFi Router Login/> /> Username setUser(ev.target.value)} value=${user} /> /> Password setPass(ev.target.value)} value=${pass} onchange=${onsubmit} /> /> <${Button} title="Sign In" icon=${IconLogout} onclick=${onsubmit} cls="flex w-full justify-center" /> /> To login, use: admin/admin, user1/user1, user2/user2 /> /> />`; }; function Header({logout, user, setShowSidebar, showSidebar}) { return html` setShowSidebar(v => !v)} class="text-slate-400"> <${IconBars3} class="h-6" /> /> /> logged in as: ${user}/> /> <${Button} title="Logout" icon=${IconLogout} onclick=${logout} /> /> /> /> />`; }; const NavLink = ({title, icon, href, url}) => html` <${icon} class="w-6 h-6"/> ${title} //> />`; function Sidebar({url, show}) { return html` <${Logo} class="h-full"/> Your Brand /> <${NavLink} title="Dashboard" icon=${IconHome} href="/" url=${url} /> <${NavLink} title="Connected Devices" icon=${IconLink} href="/devices" url=${url} /> <${NavLink} title="Firewall" icon=${IconShield} href="/firewall" url=${url} /> <${NavLink} title="DHCP" icon=${IconBarsDown} href="/dhcp" url=${url} /> <${NavLink} title="Administration" icon=${IconSettings} href="/admin" url=${url} /> /> /> />`; }; function Colored({icon, text, colors}) { return html` ${icon && html`<${icon} class="w-5 h-5" />`} ${text}/> />`; }; function Stat({title, text, tipText, tipIcon, tipColors}) { return html` ${title} /> ${text} /> <${Colored} text=${tipText} icon=${tipIcon} colors=${tipColors} /> /> /> /> />`; }; function Events({}) { const [events, setEvents] = useState([]); const refresh = () => fetch('/api/events/get').then(r => r.json()).then(r => setEvents(r)).catch(e => console.log(e)); useEffect(refresh, []); const Th = props => html`${props.title}`; const Td = props => html`${props.text}`; const Prio = ({prio}) => { const text = ['high', 'medium', 'low'][prio]; const colors = [tipColors.red, tipColors.yellow, tipColors.green][prio]; return html`<${Colored} colors=${colors} text=${text} />`; }; const Event = ({e}) => html` <${Td} text=${['network', 'optic', 'power', 'routing'][e.type]} /> <${Td} text=${html`<${Prio} prio=${e.prio}/>`} /> <${Td} text=${e.time || '1970-01-01'} /> <${Td} text=${e.text} /> />`; //console.log(events); return html` Event Log /> <${Th} title="Type" /> <${Th} title="Prio" /> <${Th} title="Time" /> <${Th} title="Description" /> ${events.map(e => h(Event, {e}))} /> />`; }; function ModemStatus({}) { const th = props => html` ${props.title} />`; const td = props => html` ${props.text} />`; const tr = props => html`${props.cols.map(text => h(td, {text}))}/>`; return html` Cable Modem Status /> <${th} title="" /> <${th} title="Status" /> <${th} title="Comment" /> <${tr} cols=${['Downstream Channel (Hz)', '4820000000', 'Locked']} /> <${tr} cols=${['Upstream Channel (Hz)', '3280000', 'Ranged']} /> <${tr} cols=${['Provisioning State', 'Online', 'Operational']} /> /> />`; }; const range = (start, size, step) => Array.from({length: size}, (_, i) => i * (step || 1) + start); function SpeedChart({stats}) { const us = stats.upload_speed, ds = stats.download_speed; const n = us.length /* entries */, w = 20 /* entry width */, ls = 15/* left space */; const h = 100 /* graph height */, yticks = 5 /* Y axis ticks */, bs = 10 /* bottom space */; const ymax = 500; const yt = i => (h - bs) / yticks * (i + 1); const bh = p => (h - bs) * p / 100; // Bar height const by = p => (h - bs) - bh(p); // console.log(ds); return html` Internet Speed, last 24h /> ${range(0, yticks).map(i => html` ${ymax-ymax/yticks*(i+1)}/> `)} ${range(0, n).map(x => html` ${x*2}:00/> `)} /> /> />`; }; function Main({}) { const [stats, setStats] = useState(null); const refresh = () => fetch('/api/stats/get').then(r => r.json()).then(r => setStats(r)); useEffect(refresh, []); if (!stats) return ''; return html` <${Stat} title="Download Speed" text="${stats.download_speed[0]} Mbps" tipText="slow" tipIcon=${IconAlert} tipColors=${tipColors.red} /> <${Stat} title="Upload Speed" text="${stats.upload_speed[0]} Mbps" tipText="good" tipIcon=${IconOk} tipColors=${tipColors.green} /> <${Stat} title="Daily Downloaded" text="${stats.downloaded} Mb" tipText="-2.2%" tipIcon=${IconArrowDown} /> <${Stat} title="Daily Uploaded" text="${stats.uploaded} Mb" tipText="+12.7%" tipIcon=${IconArrowUp} /> /> /> <${SpeedChart} stats=${stats} /> <${Events} /> <${ModemStatus} /> /> />`; }; function Devices({}) { return html`Devices/>`; }; const App = function({}) { const [loading, setLoading] = useState(true); const [url, setUrl] = useState('/'); const [user, setUser] = useState(''); const [showSidebar, setShowSidebar] = useState(true); const logout = () => fetch('/api/logout').then(r => setUser('')); const login = r => !r.ok ? setLoading(false) : r.json() .then(r => setUser(r.user)) .finally(r => setLoading(false)); useEffect(() => fetch('/api/login').then(login), []); if (loading) return ''; // Show blank page on initial load if (!user) return html`<${Login} login=${login} />`; // If not logged in, show login screen return html` <${Sidebar} url=${url} show=${showSidebar} /> <${Header} logout=${logout} user=${user} showSidebar=${showSidebar} setShowSidebar=${setShowSidebar} /> <${Router} onChange=${ev => setUrl(ev.url)} history=${History.createHashHistory()} > <${Main} default=${true} /> <${Devices} path="aa" /> /> /> />`; }; window.onload = ev => render(h(App), document.body);
${title}