diff --git a/projects/tentacles/src/python/tentacles/__main__.py b/projects/tentacles/src/python/tentacles/__main__.py index 8db9dff..7999a88 100644 --- a/projects/tentacles/src/python/tentacles/__main__.py +++ b/projects/tentacles/src/python/tentacles/__main__.py @@ -50,12 +50,6 @@ def user_session(): ctx.gid = gid ctx.username = name ctx.is_admin = gid == 0 - else: - ctx.sid = None - ctx.uid = None - ctx.gid = None - ctx.username = None - ctx.is_admin = False @cli.command() diff --git a/projects/tentacles/src/python/tentacles/static/css/_tirefire.scss b/projects/tentacles/src/python/tentacles/static/css/_tirefire.scss index 89a12b9..6b69451 100644 --- a/projects/tentacles/src/python/tentacles/static/css/_tirefire.scss +++ b/projects/tentacles/src/python/tentacles/static/css/_tirefire.scss @@ -3,7 +3,178 @@ $beige: #F4F8EE; $red: #BB2D2E; $orange: #CA4F1F; $yellow: #EDB822; +$clear: rgba(255, 255, 255, 255); + +$secondary_red: red; $secondary_blue: #288BC2; $secondary_green: #A5C426; $secondary_light_grey: #CACBCA; $secondary_dark_grey: #9A9A9A; + +@font-face { + font-family: 'Aaux Next'; + font-style: normal; + font-weight: 400; + src: local('Aaux Next'), url('/static/font/AauxNextBlk.otf') format('otf'); +} + +@font-face { + font-family: 'Aaux Next'; + font-style: normal; + font-weight: 400; + src: local('Aaux Next'), url('/static/font/aauxnextbdwebfont.otf') format('otf'); +} + +@font-face { + font-family: 'Aaux Next'; + font-style: normal; + font-weight: 400; + src: local('Aaux Next'), url('/static/font/aauxnextltwebfont.otf') format('otf'); +} + +@font-face { + font-family: 'Aaux Next'; + font-style: normal; + font-weight: 400; + src: local('Aaux Next'), url('/static/font/aauxnextmdwebfont.otf') format('otf'); +} + +@import url(https://fonts.googleapis.com/css?family=Raleway); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Hidable alerts +.alert .inner { + display: block; + padding: 6px; + margin: 6px; + border-radius: 3px; + border: 1px solid rgb(180,180,180); + background-color: rgb(212,212,212); +} + +.alert .close { + float: right; + margin: 3px 12px 0px 0px; + cursor: pointer; +} + +.alert .inner,.alert .close { + color: rgb(88,88,88); +} + +.alert input { + display: none; +} + +.alert input:checked ~ * { + animation-name: dismiss,hide; + animation-duration: 300ms; + animation-iteration-count: 1; + animation-timing-function: ease; + animation-fill-mode: forwards; + animation-delay: 0s,100ms; +} + +.alert.error .inner { + border: 1px solid rgb(238,211,215); + background-color: rgb(242,222,222); +} + +.alert.error .inner,.alert.error .close { + color: rgb(185,74,72); +} + +.alert.success .inner { + border: 1px solid rgb(214,233,198); + background-color: rgb(223,240,216); +} + +.alert.success .inner,.alert.success .close { + color: rgb(70,136,71); +} + +.alert.info .inner { + border: 1px solid rgb(188,232,241); + background-color: rgb(217,237,247); +} + +.alert.info .inner,.alert.info .close { + color: rgb(58,135,173); +} + +.alert.warning .inner { + border: 1px solid rgb(251,238,213); + background-color: rgb(252,248,227); +} + +.alert.warning .inner,.alert.warning .close { + color: rgb(192,152,83); +} + +@keyframes dismiss { + 0% { + opacity: 1; + } + 90%, 100% { + opacity: 0; + font-size: 0.1px; + transform: scale(0); + } +} + +@keyframes hide { + 100% { + height: 0px; + width: 0px; + overflow: hidden; + margin: 0px; + padding: 0px; + border: 0px; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// A timer animation +.timer { + background: -webkit-linear-gradient(left, skyBlue 50%, #eee 50%); + border-radius: 100%; + height: calc(var(--size) * 1px); + width: calc(var(--size) * 1px); + position: relative; + -webkit-animation: time calc(var(--duration) * 1s) steps(1000, start) infinite; + -webkit-mask: radial-gradient(transparent 50%,#000 50%); + mask: radial-gradient(transparent 50%,#000 50%); +} +.mask { + border-radius: 100% 0 0 100% / 50% 0 0 50%; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 50%; + -webkit-animation: mask calc(var(--duration) * 1s) steps(500, start) infinite; + -webkit-transform-origin: 100% 50%; +} +@-webkit-keyframes time { + 100% { + -webkit-transform: rotate(360deg); + } +} +@-webkit-keyframes mask { + 0% { + background: #eee; + -webkit-transform: rotate(0deg); + } + 50% { + background: #eee; + -webkit-transform: rotate(-180deg); + } + 50.01% { + background: skyBlue; + -webkit-transform: rotate(0deg); + } + 100% { + background: skyBlue; + -webkit-transform: rotate(-180deg); + } +} diff --git a/projects/tentacles/src/python/tentacles/static/css/style.scss b/projects/tentacles/src/python/tentacles/static/css/style.scss index 23db6f5..7d128da 100644 --- a/projects/tentacles/src/python/tentacles/static/css/style.scss +++ b/projects/tentacles/src/python/tentacles/static/css/style.scss @@ -1,47 +1,4 @@ -// @use 'tirefire' as tfi; - -// FIXME: Why isn't this happy in the other file? -$black: #171426; -$beige: #F4F8EE; -$red: #BB2D2E; -$orange: #CA4F1F; -$yellow: #EDB822; -$secondary_blue: #288BC2; -$secondary_green: #A5C426; -$secondary_light_grey: #CACBCA; -$secondary_dark_grey: #9A9A9A; -$secondary_red: red; -$clear: rgba(255, 255, 255, 255); - -@font-face { - font-family: 'Aaux Next'; - font-style: normal; - font-weight: 400; - src: local('Aaux Next'), url('/static/font/AauxNextBlk.otf') format('otf'); -} - -@font-face { - font-family: 'Aaux Next'; - font-style: normal; - font-weight: 400; - src: local('Aaux Next'), url('/static/font/aauxnextbdwebfont.otf') format('otf'); -} - -@font-face { - font-family: 'Aaux Next'; - font-style: normal; - font-weight: 400; - src: local('Aaux Next'), url('/static/font/aauxnextltwebfont.otf') format('otf'); -} - -@font-face { - font-family: 'Aaux Next'; - font-style: normal; - font-weight: 400; - src: local('Aaux Next'), url('/static/font/aauxnextmdwebfont.otf') format('otf'); -} - -@import url(https://fonts.googleapis.com/css?family=Raleway); +@import "tirefire"; .color-yellow { color: $yellow; @@ -51,7 +8,6 @@ html { font-family: 'Aaux Next', sans-serif; background-color: $beige; color: $black; - display: flex; } html, body { @@ -59,9 +15,11 @@ html, body { height: 100%; width: 100%; min-width: 400px; + display: flex; flex-grow: 1; flex-direction: column; + justify-content: center; } .content, .footer { @@ -83,6 +41,16 @@ a { *, *::before, *::after { margin: 0; padding: 0; + align-content: center; +} + +h1, h2, h3, h4, h5, h6 { + width: 100%; +} + +span { + display: flex; + align-self: center; } ul { @@ -92,6 +60,10 @@ ul { list-style: auto; padding: 1em; } + + li { + padding-top: 0.1em; + } } nav { @@ -154,7 +126,7 @@ $navbar_padding: 10px; .menu > li { margin: 0 1rem; - overflow: hidden; + // overflow: hidden; } .menu-button-container { @@ -261,25 +233,36 @@ $navbar_padding: 10px; width: 100%; } +.row { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +.container { + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: 100%; +} + +.mr-auto { + margin-right: auto; +} + +.ml-auto { + margin-left: auto; +} + .flashes { - .flash { - border: 10px solid $secondary_blue; - border-radius: 20px; - min-height: 40px; + .alert { p { font-size: 20px; margin-top: 10px; margin-left: 10px; } } - - .success { - border-color: $secondary_green; - } - - .error { - border-color: $secondary_red; - } } form { @@ -295,17 +278,25 @@ form { margin-right: 10px; } - input { + input[type=text] { margin-right: auto; width: 400px; } } } -.button { - border: 1px solid; - border-radius: 1em; +.button, +input[type=submit], +input[type=button], +button[type=submit] { + border: 1px solid $secondary_blue; + border-radius: 0.25em; padding: 0.5em; + background-color: $secondary_blue; + color: $beige; + cursor: pointer; + text-transform: uppercase; + font-weight: bold; } .keys ul li { @@ -321,3 +312,17 @@ form { overflow: clip; } } + +.printer-name, +.printer-status, +.printer-url, +.printer-date, +.printer-controls { + margin-top: 0.1em; + margin-right: 1em; + min-width: 10em; +} + +.printer-controls * { + margin-right: 0.1em; +} diff --git a/projects/tentacles/src/python/tentacles/templates/add_printer.html.j2 b/projects/tentacles/src/python/tentacles/templates/add_printer.html.j2 index 56b094a..49545b4 100644 --- a/projects/tentacles/src/python/tentacles/templates/add_printer.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/add_printer.html.j2 @@ -1,14 +1,18 @@ {% extends "base.html.j2" %} {% block content %} -<h1>Add printer</h1> -<form method="post" id="form"> - <span class="form-input"><span class="form-label">Printer name</span><input type="text" name="name" /></span> - <span class="form-input"><span class="form-label">Printer API URL</span><input type="text" name="url" /></span> - <span class="form-input"><span class="form-label">API key</span><input type="text" name="api_key" /></span> - <span><input id="test" type="button" value="Test" enabled="false" /></span> - <span><input id="submit" type="submit" value="Add" onclick="maybeSubmit();" /></span> - <input type="hidden" name="tested" value="false" /> -</form> +<div class="container"> + <h1>Add printer</h1> + <form class="row" method="post"> + <span class="form-input row"><span class="form-label">Printer name</span><input type="text" name="name" /></span> + <span class="form-input row"><span class="form-label">Printer API URL</span><input type="text" name="url" /></span> + <span class="form-input row"><span class="form-label">API key</span><input type="text" name="api_key" /></span> + <input type="hidden" name="tested" value="false" /> + <span class="row"> + <span><input id="test" type="button" value="Test" enabled="false" /></span> + <span><input id="submit" type="submit" value="Add" onclick="maybeSubmit();" /></span> + </span> + </form> +</div> <script type="text/javascript"> document.getElementById("input").disabled = True; diff --git a/projects/tentacles/src/python/tentacles/templates/base.html.j2 b/projects/tentacles/src/python/tentacles/templates/base.html.j2 index ac13462..70962ee 100644 --- a/projects/tentacles/src/python/tentacles/templates/base.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/base.html.j2 @@ -12,7 +12,7 @@ <body> <nav class="navbar"> <span class="logo"> - <a href="/"> + <a class="row"href="/"> <img src="/static/tentacles.svg" alt="Tentacles"> <span class="color-yellow">Tentacles</span> </a> @@ -26,14 +26,14 @@ <ul class="menu"> {% if not ctx.uid %} - <li><a href="/user/login">Log in</a></li> - <li><a href="/user/register">Register</a></li> + <li><a href="/user/login"><span class="button slide">Log in</span></a></li> + <li><a href="/user/register"><span class="button slide">Register<span></a></li> {% else %} {% if ctx.is_admin %} - <li><a href="/printers">Printers</a></li> + <li><a href="/printers"><span class="button slide">Printers</span></a></li> {% endif %} - <li><a href="/user">Settings</a></li> - <li><a href="/user/logout">Log out</a></li> + <li><a href="/user"><span class="button slide">Settings</span></a></li> + <li><a href="/user/logout"><span class="button slide">Log out</span></a></li> {% endif %} </ul> </nav> @@ -43,8 +43,15 @@ {% if messages %} <div class="flashes"> {% for category, message in messages %} - <div class="flash {{ category }}"> - <center><p>{{ message }}</p></center> + <div class="alert {{ category }}"> + <input type="checkbox" id="alert{{ loop.index }}"/> + <label class="close" title="close" for="alert{{loop.index}}"> + <div class="paused-timer" style="--duration: 2; --size: 10;"> + <div class="mask"> + </div> + </div> + </label> + <center><p class="inner">{{ message }}</p></center> </div> {% endfor %} </div> @@ -59,4 +66,18 @@ {% endblock %} </div> </body> +<footer> + <script type="text/javascript"> + document.querySelectorAll('.alert input[type=checkbox]').forEach(function($el) { + console.log("Setting timeout on", $el); + document.querySelectorAll(`.alert label[for=${$el.id}] .paused-timer`).forEach(function($it) { + $it.setAttribute("class", "timer"); + }); + setTimeout(() => { + console.log("Timeout firing...") + $el.click(); + }, 2000); + }); + </script> +</footer> </html> diff --git a/projects/tentacles/src/python/tentacles/templates/printers.html.j2 b/projects/tentacles/src/python/tentacles/templates/printers.html.j2 index cb6f57d..e4443d3 100644 --- a/projects/tentacles/src/python/tentacles/templates/printers.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/printers.html.j2 @@ -1,25 +1,28 @@ {% extends "base.html.j2" %} {% block content %} - <div class="panel printers"> + <div class="printers"> <h2>Printers</h2> {% with printers = ctx.db.list_printers() %} {% if printers %} <ul> {% for printer in printers %} {% with id, name, url, last_poll, status = printer %} - <li class="printer"> + <li class="printer row"> <span class="printer-name">{{name}}</span> - <span class="printer-url">{{url}}</span> + <span class="printer-url"><code>{{url}}</code></span> <span class="printer-status">{{status}}</span> <span class="printer-date">{{last_poll}}</span> {# FIXME: How should these action buttons work? #} - <a class="button" href="/printers/action?id={{id}}">Test</a> - <a class="button" href="/printers/edit?id={{id}}">Edit</a> - <a class="button" href="/printers/delete?id={{id}}">Remove</a> + <span class="printer-controls ml-auto"> + <a class="button" href="/printers/test?id={{id}}">Test</a> + <a class="button" href="/printers/edit?id={{id}}">Edit</a> + <a class="button" href="/printers/delete?id={{id}}">Remove</a> + </span> </li> {% endwith %} {% endfor %} </ul> + {% if ctx.is_admin %}<a class="button" href="/printers/add">Add a printer</a>{% endif %} {% else %} No printers available. {% if ctx.is_admin %}<a href="/printers/add">Configure one!</a>{% else %}Ask the admin to configure one!{% endif %} {% endif %} diff --git a/projects/tentacles/src/python/tentacles/templates/user.html.j2 b/projects/tentacles/src/python/tentacles/templates/user.html.j2 index fdd9ca0..c209456 100644 --- a/projects/tentacles/src/python/tentacles/templates/user.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/user.html.j2 @@ -1,30 +1,37 @@ {% extends "base.html.j2" %} {% block content %} <h1>User settings</h1> -<div class="keys"> +<div class="keys container"> <h2>API keys</h2> {% with keys = ctx.db.list_keys(ctx.uid) %} <ul> {% for id, name, exp in keys if name != 'web session' %} - <li> - <span class="key-name">{{ name or 'anonymous' }}</span> - <span class="key-key">{{ id[:10] }}...</span> - <span class="key-expiration">{{ 'Expires in' if exp else ''}} {{ exp - datetime.now() if exp else 'Never' }}</span> - <form method="post"> - <input type="hidden" name="action" value="revoke"> - <input type="hidden" name="id" value="{{ id }}"> - <span><input id="submit" type="submit" value="Revoke"/></span> - </form> + <li class="row"> + <div class="row key"> + <div class="row"> + <span class="key-name">{{ name or 'anonymous' }}</span> + <span class="key-expiration ml-auto">{{ 'Expires in ' if exp else ''}}{{ exp - datetime.now() if exp else 'Never expires' }}</span> + </div> + <div class="row"> + <span class="key-key">{{ id[:10] }}...</span> + <form method="post" class="ml-auto"> + <input type="hidden" name="action" value="revoke"> + <input type="hidden" name="id" value="{{ id }}"> + <span><input id="submit" type="submit" value="Revoke"/></span> + </form> + </div> + </div> </li> {% endfor %} </ul> {% endwith %} - +</div> +<div class="keys-form container"> <h2>Add a key</h2> <form method="post"> <input type="hidden" name="action" value="add"> - <span class="form-input"><span class="form-label">API key name</span><input type="text" name="name" /></span> - <span class="form-input"><span class="form-label">Key lifetime</span> + <span class="form-input mr-auto row"><span class="form-label">API key name</span><input type="text" name="name" /></span> + <span class="form-input mr-auto row"><span class="form-label">Key lifetime</span> <select name="ttl"> <option value="30d">30 days</option> <option value="90d">90 days</option> @@ -32,7 +39,7 @@ <option value="forever">Forever (not recommended)</option> </select> </span> - <span><input id="submit" type="submit" value="Add" onclick="maybeSubmit();" /></span> + <span class="form-input mr-auto row"><input id="submit" type="submit" value="Add" onclick="maybeSubmit();" /></span> </form> </div> {% endblock %}