Browse Source

add styles

marek 1 tháng trước cách đây
mục cha
commit
2655d330a5
36 tập tin đã thay đổi với 1276 bổ sung453 xóa
  1. 179 40
      assets/css/style.css
  2. 1 1
      routes/include/auth.js
  3. 13 0
      routes/include/panel.js
  4. 35 11
      routes/include/panel/stations.js
  5. 30 4
      templates/en/auth/index.eta
  6. 35 7
      templates/en/auth/verify.eta
  7. 60 6
      templates/en/email/auth.eta
  8. 3 3
      templates/en/index.eta
  9. 18 0
      templates/en/panel/deleteAccount.eta
  10. 17 17
      templates/en/panel/index.eta
  11. 65 8
      templates/en/panel/partials/navbar.eta
  12. 50 25
      templates/en/panel/settings.eta
  13. 40 26
      templates/en/panel/stations/add.eta
  14. 18 0
      templates/en/panel/stations/delete.eta
  15. 53 29
      templates/en/panel/stations/edit.eta
  16. 37 12
      templates/en/panel/stations/index.eta
  17. 9 2
      templates/en/panel/stations/notFound.eta
  18. 48 53
      templates/en/panel/stations/station.eta
  19. 45 8
      templates/en/partials/navbar.eta
  20. 29 3
      templates/sk/auth/index.eta
  21. 32 4
      templates/sk/auth/verify.eta
  22. 60 6
      templates/sk/email/auth.eta
  23. 3 4
      templates/sk/index.eta
  24. 18 0
      templates/sk/panel/deleteAccount.eta
  25. 17 17
      templates/sk/panel/index.eta
  26. 62 8
      templates/sk/panel/partials/navbar.eta
  27. 50 25
      templates/sk/panel/settings.eta
  28. 40 26
      templates/sk/panel/stations/add.eta
  29. 18 0
      templates/sk/panel/stations/delete.eta
  30. 53 29
      templates/sk/panel/stations/edit.eta
  31. 36 13
      templates/sk/panel/stations/index.eta
  32. 9 2
      templates/sk/panel/stations/notFound.eta
  33. 46 51
      templates/sk/panel/stations/station.eta
  34. 42 8
      templates/sk/partials/navbar.eta
  35. 2 2
      utils/auth.js
  36. 3 3
      utils/meteostanice.js

+ 179 - 40
assets/css/style.css

@@ -1,85 +1,224 @@
+img {
+    object-fit: cover;
+    width: 100%;
+}
+
 body {
+    min-height: 100dvh;
+    margin: 0;
+    display: flex;
+    flex-direction: column;
     padding-inline: 10%;
+    font-family: sans-serif;
+    line-height: 1.3;
+    background-color: #eee;
+    gap: 0.5em;
+}
+
+#mainStats {
+    display: flex;
+    flex-direction: column;
+    gap: 1em;
+    margin: auto;
+    justify-content: center;
+    align-items: center;
+    padding: 1em;
+}
+
+.message {
+    max-width: max-content;
+    margin-block: 0.5em;
+    padding: 0.25em 1em;
+    border-radius: 1em;
+    background-color: rgba(50, 50, 50, 0.3);
+    border: 2px solid rgb(50, 50, 50);
+    color: rgb(50, 50, 50);
+}
+
+.message.error {
+    background-color: rgba(255, 50, 50, 0.3);
+    border: 2px solid rgb(255, 50, 50);
+    color: rgb(255, 50, 50);
 }
 
-nav {
+#mainStats .stats {
     display: flex;
-    gap: 1rem 3rem;
     flex-wrap: wrap;
+    justify-content: center;
+    align-items: stretch;
+    gap: 1em 5em;
+}
+
+#mainStats .stats .wrapper {
+    display: flex;
+    gap: 1em;
+    flex-direction: column;
+}
+
+#mainStats .stats .values {
+    background-color: rgba(0, 0, 0, 0.1);
+    padding: 0.5em 1em;
+    border-radius: 1em;
+    width: 10em;
+    flex: 1;
+    font-size: 1.2em;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-end;
+}
+
+#mainStats .stats .values p {
+    margin: 0.5em 0;
+}
+
+p.messageText, a, button, h1, h2, h3 {
+    display: flex;
     align-items: center;
+    gap: 0.5em;
+}
+
+header {
+    display: flex;
     justify-content: space-between;
-    margin-inline: 5%;
-    padding: 0.5rem 3rem;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: 0 3em;
+    padding-block: 1em;
 }
 
-nav h2 {
-    margin: 0 auto;
+header > * {
+    margin-inline: auto;
 }
 
-nav ul {
-    list-style: none;
+.icon {
+    display: inline-block;
+    width: 1em;
+    height: 1em;
+}
+
+header .languageSwitcher {
+    font-size: 1.3em;
+}
+
+header .links {
+    font-size: 1.1em;
     display: flex;
-    gap: 1rem;
-    flex-wrap: wrap;
+    gap: 1em;
     align-items: center;
     justify-content: center;
-    padding: 0;
-    margin: 0 auto;
+    flex-wrap: wrap;
 }
 
-nav li {
-    margin-inline: auto;
+[role="button"], button, select, input, textarea {
+    background-color: rgba(0, 0, 0, 0.1);
+    border-radius: 0.5em;
+    padding: 0.5em 1em;
+    text-decoration: none;
+    color: initial;
+    border: none;
+    width: max-content;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    gap: 0.5em;
+    font-size: 1em;
+    height: max-content;
+    font-family: inherit;
 }
 
-/* nav a {
+[role="button"].danger, button.danger {
     color: #eee;
-    text-decoration: none;
-    font-size: 1.2rem;
-} */
+    background-color: rgb(255, 50, 50);
+}
 
-header {
+[role="button"].primary, button.primary {
+    color: #eee;
+    background-color: rgb(50, 50, 255);
+}
+
+input, select, textarea {
+    border: 2px solid rgba(0, 0, 0);
+}
+
+form {
+    display: flex;
+    flex-direction: column;
+    gap: 1em;
+}
+
+form > div {
     display: flex;
+    flex-direction: column;
+    gap: 0.5em;
+}
+
+.historyLinks {
+    display: flex;
+    gap: 1em;
     flex-wrap: wrap;
-    justify-content: space-between;
-    padding-block: 2rem;
-    gap: 2rem;
+    margin-block: 1em;
+    align-items: center;
+    justify-content: center;
 }
 
-img {
-    object-fit: cover;
-    width: 100%;
+#historyForm {
+    flex-direction: row;
+    flex-wrap: wrap;
+    align-items: flex-end;
 }
 
-.stats {
+.container-row {
     display: flex;
     flex-wrap: wrap;
-    gap: 2rem;
+    gap: 0 1em;
+    align-items: center;
+    margin-block: 0.5em;
 }
 
-.stats > div {
-    padding: 0.5rem 2rem;
-    background-color: lightgray;
+hr {
+    width: 100%;
+    margin-block: 1em;
+    margin-inline: 0 auto;
 }
 
-div:has(> table) {
-  overflow-x: auto;
+.table-wrapper {
+    overflow: auto;
 }
 
 table {
-  border-collapse: collapse;
-  width: 100%;
+    background-color: rgba(0, 0, 0, 0.1);
+    border-collapse: collapse;
+    overflow: hidden;
+    border-radius: 0.5em;
+    width: 100%;
 }
 
-td, th {
-  border: 1px solid #dddddd;
+th, td {
   text-align: left;
-  padding: 8px;
+  padding: 0.5em 1em;
+  border-right: 1px solid currentColor;
+}
+
+th:last-of-type, td:last-of-type {
+    border-right: none;
 }
 
 tr:nth-child(even) {
-  background-color: #dddddd;
+  background-color: rgba(255, 255, 255, 0.2);
+}
+
+section {
+    margin-block: 1em;
 }
 
-span.error {
-    color: red;
+.indexHero {
+    display: flex;
+    flex-wrap: wrap-reverse;
+    gap: 1em 5em;
+    align-items: center;
+    justify-content: space-between;
+}
+
+.indexHero > * {
+    margin-inline: auto;
 }

+ 1 - 1
routes/include/auth.js

@@ -126,7 +126,7 @@ export default (langName, lang) => new Elysia({ prefix: "/auth" })
 
     delete cookie.session
 
-    Auth.removeUser(session.email)
+    Auth.deleteUser(session.email)
     
     return redirect(`/${langName === "sk" ? `` : langName}`)
   })

+ 13 - 0
routes/include/panel.js

@@ -97,4 +97,17 @@ export default (langName, lang) => new Elysia({ prefix: "/panel" })
 
     set.headers['content-type'] = 'text/html; charset=utf8'
     return redirect(`/${langName === "sk" ? `` : `${langName}/`}panel`)
+  })
+  .get("/deleteAccount", async ({ cookie, redirect, set }) => {
+    const token = cookie.session.value
+    const session = await Auth.getSession(token)
+
+    if (!session) {
+      return redirect(`/${langName === "sk" ? `` : `${langName}/`}auth?error=loginNeeded`)
+    }
+
+    const user = Auth.getUser(session.email)
+
+    set.headers['content-type'] = 'text/html; charset=utf8'
+    return eta.render(`${langName}/panel/deleteAccount`, { user })
   })

+ 35 - 11
routes/include/panel/stations.js

@@ -91,14 +91,14 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
     
     if (!station) {
         set.headers['content-type'] = 'text/html; charset=utf8'
-        return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+        return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
     
     const meteostanica = Meteostanice.get(session.email, station)
     
     if (!meteostanica) {
       set.headers['content-type'] = 'text/html; charset=utf8'
-      return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+      return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
 
     const data = Meteostanice.getData(meteostanica.id)
@@ -106,7 +106,6 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
     set.headers['content-type'] = 'text/html; charset=utf8'
     return eta.render(`${langName}/panel/stations/station`, { user, meteostanica, data })
   })
-
   .get("/:station/edit",  async ({ cookie, redirect, set, params: { station } }) => {
     const token = cookie.session.value
     const session = await Auth.getSession(token)
@@ -119,14 +118,14 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
     
     if (!station) {
         set.headers['content-type'] = 'text/html; charset=utf8'
-        return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+        return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
     
     const meteostanica = Meteostanice.get(session.email, station)
     
     if (!meteostanica) {
       set.headers['content-type'] = 'text/html; charset=utf8'
-      return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+      return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
 
     set.headers['content-type'] = 'text/html; charset=utf8'
@@ -146,14 +145,14 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
 
     if (!station) {
         set.headers['content-type'] = 'text/html; charset=utf8'
-        return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+        return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
     
     const meteostanica = Meteostanice.get(session.email, station)
     
     if (!meteostanica) {
       set.headers['content-type'] = 'text/html; charset=utf8'
-      return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+      return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
 
     const turnstileResponse = body?.["cf-turnstile-response"]
@@ -217,21 +216,46 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
     
     if (!station) {
         set.headers['content-type'] = 'text/html; charset=utf8'
-        return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+        return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
     
     const meteostanica = Meteostanice.get(session.email, station)
     
     if (!meteostanica) {
       set.headers['content-type'] = 'text/html; charset=utf8'
-      return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
+      return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
     }
 
     Meteostanice.resetWebsocketKey(meteostanica.id)
 
     return redirect(`/${langName === "sk" ? `` : `${langName}/`}panel/stations/${meteostanica.id}`)
   })
-  .get("/:station/remove",  async ({ cookie, redirect, set, params: { station } }) => {
+  .get("/:station/delete",  async ({ cookie, redirect, set, params: { station } }) => {
+    const token = cookie.session.value
+    const session = await Auth.getSession(token)
+
+    if (!session) {
+      return redirect(`/${langName === "sk" ? `` : `${langName}/`}auth?error=loginNeeded`)
+    }
+
+    const user = Auth.getUser(session.email)
+    
+    if (!station) {
+        set.headers['content-type'] = 'text/html; charset=utf8'
+        return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
+    }
+    
+    const meteostanica = Meteostanice.get(session.email, station)
+    
+    if (!meteostanica) {
+      set.headers['content-type'] = 'text/html; charset=utf8'
+      return eta.render(`${langName}/panel/stations/notFound`, { lang, user })
+    }
+
+    set.headers['content-type'] = 'text/html; charset=utf8'
+    return eta.render(`${langName}/panel/stations/delete`, { user, meteostanica })
+  })
+  .get("/:station/deleteConfirm",  async ({ cookie, redirect, set, params: { station } }) => {
     const token = cookie.session.value
     const session = await Auth.getSession(token)
 
@@ -253,7 +277,7 @@ export default (langName, lang) => new Elysia({ prefix: "/stations" })
       return eta.render(`${langName}/panel/stations/notFound`, { siteKey: process.env.TURNSTILE_SITE_KEY, lang, user })
     }
 
-    Meteostanice.remove(meteostanica.id)
+    Meteostanice.delete(meteostanica.id)
 
     return redirect(`/${langName === "sk" ? `` : `${langName}/`}panel/stations`)
   })

+ 30 - 4
templates/en/auth/index.eta

@@ -1,19 +1,45 @@
 <% layout("/en/layout", { title: "auth" }) %>
 
-<h2>auth</h2>
-<p>enter your email and we will send you a code.</p>
+<%~ include("/en/partials/navbar") %>
+
+<% const authIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" />
+      <circle cx="16.5" cy="7.5" r=".5" fill="currentColor" />
+    </g>
+  </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<% const sendIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11zm7.318-19.539l-10.94 10.939" />
+  </svg>
+` %>
+
+
+<h1><%~ authIcon %> auth</h1>
+<p>enter your email and click on the link you will get.</p>
 
 <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.auth?.errors)?.(it?.errorDetails) %>
 
 <% if (typeof errorValue === "string") { %>
-  <p class="error" style="color: red;"><%= errorValue %></p>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
 <% } %>
 
 <form action="/en/auth" method="post">
     <input type="email" name="email" placeholder="your@email.com" required />
 
     <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
-    <button type="submit">send</button>
+    <button type="submit"><%~ sendIcon %> send</button>
 </form>
 
 <script

+ 35 - 7
templates/en/auth/verify.eta

@@ -1,17 +1,45 @@
-<% layout("/en/layout", { title: "verify" }) %>
+<% layout("/en/layout", { title: "verification" }) %>
 
-<h2>verification</h2>
-<p>enter the code from your email or click on the link</p>
+<%~ include("/en/partials/navbar") %>
+
+<% const verificationIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
+      <path d="M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3m.08 4h.01" />
+    </g>
+  </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<% const verifyIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
+      <path d="m9 12l2 2l4-4" />
+    </g>
+  </svg>
+` %>
+
+<h1><%~ verificationIcon %> verification</h1>
+<p>enter the code you got in your email or click on the link.</p>
 
 <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.auth?.errors)?.(it?.errorDetails) %>
 
-<% if (errorValue) { %>
-  <p class="error" style="color: red;"><%= errorValue %></p>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
 <% } %>
 
 <form action="/en/auth/verify">
     <input type="text" name="token" value="<%= it.token %>" style="display: none;" />
-    <input type="number" length="6" name="code" placeholder="676767" />
+    <input type="text" pattern="\d+" minlength="6" maxlength="6" name="code" placeholder="676767" />
 
-    <button type="submit">send</button>
+    <button type="submit"><%~ verifyIcon %> verify</button>
 </form>

+ 60 - 6
templates/en/email/auth.eta

@@ -1,10 +1,64 @@
-<p>hi!</p>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
-<p>you can login using the following code: <strong><%= it.code %></strong></p>
-<p>or <a href="<%= it.link %>">click on this link</a></p>
+    <style>
+        body {
+            margin: 0;
+            display: flex;
+            flex-direction: column;
+            padding-inline: 10%;
+            font-family: sans-serif;
+            line-height: 1.3;
+            background-color: #eee;
+            gap: 0.5em;
+        }
 
-<p><%= it.link %></p>
+        h2, p {
+            margin-block: 0.5em;
+        }
 
-<p><small>if you did not request this email, feel free to ignore it.</small></p>
+        [role="button"] {
+            background-color: rgba(0, 0, 0, 0.1);
+            border-radius: 0.5em;
+            padding: 0.5em 1em;
+            text-decoration: none;
+            color: initial;
+            border: none;
+            width: max-content;
+            cursor: pointer;
+            display: flex;
+            align-items: center;
+            gap: 0.5em;
+            font-size: 1em;
+            height: max-content;
+        }
 
-<p>meteostanica</p>
+        [role="button"].primary {
+            color: #eee;
+            background-color: rgb(50, 50, 255);
+        }
+    </style>
+</head>
+<body>
+    <h2>meteostanica</h2>
+
+    <p>hi,</p>
+
+    <p>to login click on the button below.</p>
+
+    <a role="button" class="primary" href="<%= it.link %>">click here</a>
+
+    <p>or enter this code: <strong><%= it.code %></strong></p>
+
+
+    <p>cannot click on the link? copy it: <%= it.link %></p>
+
+    <footer>
+        <p><small>if you did not request this email, you can safely ignore it.</small></p>
+        <p><small>meteostanica <%= new Date().getFullYear() %></small></p>
+    </footer>
+</body>
+</html>

+ 3 - 3
templates/en/index.eta

@@ -2,14 +2,14 @@
 
 <%~ include("/en/partials/navbar") %>
 
-<header>
+<section class="indexHero">
     <div>
         <h1>it looks like this >>>>></h1>
         <p>the best weather station you have ever seen</p>
 
-        <a href="/en/auth" class="button">login</a>
+        <a role="button" href="/en/auth" class="button">login</a>
     </div>
     <div>
         <img alt="a picture of the best weather station" src="https://placehold.net/600x400.png">
     </div>
-</header>
+</section>

+ 18 - 0
templates/en/panel/deleteAccount.eta

@@ -0,0 +1,18 @@
+<% layout("/en/layout", { title: "delete account" }) %>
+
+<%~ include("/en/panel/partials/navbar") %>
+
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/en/panel"><%~ backIcon %></a>
+    <h2>delete account</h2>
+</div>
+
+<p>are you sure you want to delete your account <strong><%= it?.user?.name ? `${it?.user?.name} (${it?.user.email})` : it?.user.email %></strong>?</p>
+
+<a role="button" class="danger" href="/en/auth/delete">delete account</a>

+ 17 - 17
templates/en/panel/index.eta

@@ -2,24 +2,24 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<h2>welcome back <%= it.user.name?.length ? it.user.name : it.user.email %> !!</h2>
+<% const stationsIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
 
-<div>
-  <h3>your stations</h3>
+<h2>welcome <%= it.user.name?.length ? it.user.name : it.user.email %>!</h2>
 
-    <% if (it.meteostanice?.length) { %>
-        <ul>
-            <% for (const meteostanica of it.meteostanice) { %>
-                <li><a href="/en/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></li>
-            <% } %>
-        </ul>
-    <% } else { %>
-        <p>no stations. :(</p>
-    <% } %>
+<section class="container-row">
+    <a role="button" href="/en/panel/stations"><%~ stationsIcon %> stations</a>
+</section>
 
-  <a href="/en/panel/stations">see all</a>
-</div>
+<section>
+    <h3>recent warnings</h3>
+    <p>no recent warnings yet.</p>
+</section>
 
-<br>
-
-<a href="/en/auth/logout">logout</a>
+<a role="button" class="danger" href="/en/auth/logout">logout</a>

+ 65 - 8
templates/en/panel/partials/navbar.eta

@@ -1,10 +1,67 @@
-<nav>
+<% const skFlag = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
+        <mask id="SVGuywqVbel">
+            <circle cx="256" cy="256" r="256" fill="#fff" />
+        </mask>
+        <g mask="url(#SVGuywqVbel)">
+            <path fill="#0052b4" d="m0 160l256-32l256 32v192l-256 32L0 352z" />
+            <path fill="#eee" d="M0 0h512v160H0z" />
+            <path fill="#d80027" d="M0 352h512v160H0z" />
+            <path fill="#eee" d="M64 63v217c0 104 144 137 144 137s144-33 144-137V63z" />
+            <path fill="#d80027" d="M96 95v185a83 78 0 0 0 9 34h206a83 77 0 0 0 9-34V95z" />
+            <path fill="#eee" d="M288 224h-64v-32h32v-32h-32v-32h-32v32h-32v32h32v32h-64v32h64v32h32v-32h64z" />
+            <path fill="#0052b4" d="M152 359a247 231 0 0 0 56 24c12-3 34-11 56-24a123 115 0 0 0 47-45a60 56 0 0 0-34-10l-14 2a60 56 0 0 0-110 0a60 56 0 0 0-14-2c-12 0-24 4-34 10a123 115 0 0 0 47 45" />
+        </g>
+    </svg>
+` %>
+
+<% const homeIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
+            <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
+        </g>
+    </svg>
+` %>
+
+<% const dashboardIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <rect width="20" height="14" x="2" y="3" rx="2" />
+            <path d="M8 21h8m-4-4v4" />
+        </g>
+    </svg>
+` %>
+
+<% const stationsIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
+
+<% const settingsIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0a2.34 2.34 0 0 0 3.319 1.915a2.34 2.34 0 0 1 2.33 4.033a2.34 2.34 0 0 0 0 3.831a2.34 2.34 0 0 1-2.33 4.033a2.34 2.34 0 0 0-3.319 1.915a2.34 2.34 0 0 1-4.659 0a2.34 2.34 0 0 0-3.32-1.915a2.34 2.34 0 0 1-2.33-4.033a2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
+      <circle cx="12" cy="12" r="3" />
+    </g>
+  </svg>
+` %>
+
+<header>
+    <div class="links languageSwitcher">
+        <a role="button" href="/panel"><%~ skFlag %></a>
+        <a role="button" href="/en"><%~ homeIcon %></a>
+    </div>
+
     <h2>meteostanica panel</h2>
 
-    <ul>
-        <li><a href="/en/panel">dashboard</a></li>
-        <li><a href="/en/panel/stations">stations</a></li>
-        <li><a href="/en/panel/settings">settings</a></li>
-        <li><a href="/panel">⛰️⛰️⛰️🇸🇰🇸🇰🇸🇰</a></li>
-    </ul>
-</nav>
+    <div class="links">
+        <a role="button" href="/en/panel"><%~ dashboardIcon %> dashboard</a>
+        <a role="button" href="/en/panel/stations"><%~ stationsIcon %> stations</a>
+        <a role="button" href="/en/panel/settings"><%~ settingsIcon %> settings</a>
+    </div>
+</header>

+ 50 - 25
templates/en/panel/settings.eta

@@ -2,42 +2,67 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<div>
-  <h2>settings</h2>
+<% const settingsIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0a2.34 2.34 0 0 0 3.319 1.915a2.34 2.34 0 0 1 2.33 4.033a2.34 2.34 0 0 0 0 3.831a2.34 2.34 0 0 1-2.33 4.033a2.34 2.34 0 0 0-3.319 1.915a2.34 2.34 0 0 1-4.659 0a2.34 2.34 0 0 0-3.32-1.915a2.34 2.34 0 0 1-2.33-4.033a2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
+      <circle cx="12" cy="12" r="3" />
+    </g>
+  </svg>
+` %>
 
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.settings?.errors)?.(it?.errorDetails) %>
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
 
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/en/panel/settings" method="post">
-    <div>
-      <label for="name">name</label>
-      <input type="text" id="name" name="name" placeholder="ferris" value="<%= it.user.name ?? "" %>">
-    </div>
+<% const saveIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" />
+      <path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7" />
+    </g>
+  </svg>
+` %>
 
-    <br>
+<% const deleteIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
+  </svg>
+` %>
 
-    <div>
-      <label for="email">email</label>
-      <input type="text" id="email" name="email" placeholder="your@email.com" value="<%= it.user.email %>">
-    </div>
+<h2><%~ settingsIcon %> settings</h2>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.settings?.errors)?.(it?.errorDetails) %>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/en/panel/settings" method="post">
+  <div>
+    <label for="name">name</label>
+    <input type="text" id="name" name="name" placeholder="ferris" value="<%= it.user.name ?? "" %>">
+  </div>
 
-    <button type="submit">save</button>
-  </form>
+  <div>
+    <label for="email">email</label>
+    <input type="text" id="email" name="email" placeholder="your@email.com" value="<%= it.user.email %>">
+  </div>
 
-  <br>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
 
-  <a href="/en/auth/delete">delete account</a>
-</div>
+  <button type="submit" class="primary"><%~ saveIcon %> save</button>
+</form>
+
+<hr>
 
+<div class="container-row">
+  <a role="button" class="danger" href="/en/panel/deleteAccount"><%~ deleteIcon %> delete account</a>
+</div>
 
 <script
   src="https://challenges.cloudflare.com/turnstile/v0/api.js"

+ 40 - 26
templates/en/panel/stations/add.eta

@@ -2,38 +2,52 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<div>
-  <h2>add a station</h2>
-
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
-
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/en/panel/stations/add" method="post">
-    <div>
-      <label for="name">name</label>
-      <input type="text" id="name" name="name" placeholder="cool station">
-    </div>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<% const addIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14" />
+    </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/en/panel/stations"><%~ backIcon %></a>
+    <h2><%~ addIcon %> add station</h2>
+</div>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
 
-    <div>
-      <label for="description">description</label>
-      <textarea id="description" name="description" placeholder="cool station"></textarea>
-    </div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/en/panel/stations/add" method="post">
+  <div>
+    <label for="name">name</label>
+    <input type="text" id="name" name="name" placeholder="cool station">
+  </div>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+  <div>
+    <label for="description">description</label>
+    <textarea id="description" name="description" placeholder="the coolest one"></textarea>
+  </div>
 
-    <br>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
 
-    <button type="submit">add</button>
-    <a href="/en/panel/stations">&lt;&lt; go back</a>
-  </form>
-</div>
+  <button type="submit" class="primary"><%~ addIcon %> add</button>
+</form>
 
 
 <script

+ 18 - 0
templates/en/panel/stations/delete.eta

@@ -0,0 +1,18 @@
+<% layout("/en/layout", { title: "delete station" }) %>
+
+<%~ include("/en/panel/partials/navbar") %>
+
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/en/panel/stations/<%= it?.meteostanica.id %>"><%~ backIcon %></a>
+    <h2>delete station</h2>
+</div>
+
+<p>are you sure you want to delete your station <strong><%= it?.meteostanica.name %></strong>?</p>
+
+<a role="button" class="danger" href="/en/panel/stations/<%= it?.meteostanica.id %>/deleteConfirm">delete station</a>

+ 53 - 29
templates/en/panel/stations/edit.eta

@@ -2,45 +2,69 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<div>
-  <h2><%= it.meteostanica.name %> - edit</h2>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
 
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
+<% const editIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
+            <path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />
+        </g>
+    </svg>
+` %>
 
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/en/panel/stations/<%= it.meteostanica.id %>/edit" method="post">
-    <div>
-      <label for="name">name</label>
-      <input type="text" id="name" name="name" placeholder="cool station" value="<%= it.meteostanica.name %>">
-    </div>
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
 
-    <br>
+<% const saveIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" />
+      <path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7" />
+    </g>
+  </svg>
+` %>
 
-    <div>
-      <label for="description">description</label>
-      <textarea id="description" name="description" placeholder="cool station"><%= it.meteostanica.description %></textarea>
-    </div>
+<div class="container-row">
+    <a role="button" href="/en/panel/stations/<%= it.meteostanica.id %>"><%~ backIcon %></a>
+    <h2><%~ editIcon %> edit <%= it.meteostanica.name %></h2>
+</div>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
 
-    <div>
-      <label for="owner">owner</label>
-      <input type="text" id="owner" name="owner" placeholder="your@email.com" value="<%= it.meteostanica.owner %>">
-    </div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/en/panel/stations/<%= it.meteostanica.id %>/edit" method="post">
+  <div>
+    <label for="name">name</label>
+    <input type="text" id="name" name="name" placeholder="cool station" value="<%= it.meteostanica.name %>">
+  </div>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+  <div>
+    <label for="description">description</label>
+    <textarea id="description" name="description" placeholder="the coolest one"><%= it.meteostanica.description %></textarea>
+  </div>
 
-    <br>
+  <div>
+    <label for="owner">owner</label>
+    <input type="text" id="owner" name="owner" placeholder="your@email.com" value="<%= it.meteostanica.owner %>">
+  </div>
 
-    <button type="submit">save</button>
-    <a href="/en/panel/stations/<%= it.meteostanica.id %>">&lt;&lt; go back</a>
-  </form>
-</div>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+
+  <button type="submit" class="primary"><%~ saveIcon %> save</button>
+</form>
 
 <script
   src="https://challenges.cloudflare.com/turnstile/v0/api.js"

+ 37 - 12
templates/en/panel/stations/index.eta

@@ -2,18 +2,43 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<div>
-  <h2>your stations</h2>
+<% const staniceIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
 
-    <% if (it.meteostanice?.length) { %>
-        <ul>
+<% const addIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14" />
+    </svg>
+` %>
+
+<div class="container-row">
+    <h2><%~ staniceIcon %> stations</h2>
+    <a role="button" class="primary" href="/en/panel/stations/add"><%~ addIcon %> add</a>
+</div>
+
+<% if (it.meteostanice?.length) { %>
+    <div class="table-wrapper">
+        <table>
+            <tr>
+                <th>name</th>
+                <th>description</th>
+                <th>created</th>
+            </tr>
             <% for (const meteostanica of it.meteostanice) { %>
-                <li><a href="/en/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></li>
+                <tr>
+                    <td><a href="/en/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></td>
+                    <td><%= meteostanica?.description ?? `` %></td>
+                    <td><%= meteostanica.timestamp %></td>
+                </tr>
             <% } %>
-        </ul>
-    <% } else { %>
-        <p>no stations. :(</p>
-    <% } %>
-
-    <a href="/en/panel/stations/add">add</a>
-</div>
+        </table>
+    </div>
+<% } else { %>
+    <p>no stations. :(</p>
+<% } %>

+ 9 - 2
templates/en/panel/stations/notFound.eta

@@ -2,6 +2,13 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<a href="/en/panel/stations">&lt;&lt; go back</a>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
 
-<h2>station not found</h2>
+<div class="container-row">
+    <a role="button" href="/en/panel/stations"><%~ backIcon %></a>
+    <h2>station not found</h2>
+</div>

+ 48 - 53
templates/en/panel/stations/station.eta

@@ -2,62 +2,57 @@
 
 <%~ include("/en/panel/partials/navbar") %>
 
-<a href="/en/panel/stations">&lt;&lt; go back</a>
-
-<h2><%= it.meteostanica.name %></h2>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<% const resetIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M3 12a9 9 0 1 0 9-9a9.75 9.75 0 0 0-6.74 2.74L3 8" />
+            <path d="M3 3v5h5" />
+        </g>
+    </svg>
+` %>
+
+<% const editIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
+            <path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />
+        </g>
+    </svg>
+` %>
+
+<% const deleteIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
+  </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/en/panel/stations"><%~ backIcon %></a>
+    <h2><%= it.meteostanica.name %></h2>
+</div>
 
 <% if (it.meteostanica.description) { %>
-    <p><%= it.meteostanica.description %></p>
+    <p><strong>description:</strong> <%= it.meteostanica.description %></p>
 <% } %>
 
-<p>
-    <a href="/en/panel/stations/<%= it.meteostanica.id %>/edit">edit</a>
-    <a href="/en/panel/stations/<%= it.meteostanica.id %>/remove">remove</a>
-</p>
-
-<p><strong>created at:</strong> <%= it.meteostanica.timestamp %></p>
-
-<p><strong>websocket key:</strong> <%= it.meteostanica.websocketKey %> <a href="/en/panel/stations/<%= it.meteostanica.id %>/resetWebsocketKey">reset</a></p>
-
-<% if (it.data?.[0]) { %>
-<div class="stats">
-    <div class="indoor">
-        <h3>indoor</h3>
-        <p>temp: <%= it.data?.[0].indoorTemp / 100 %> °C</p>
-        <p>pressure: <%= it.data?.[0].indoorPressure / 100 %> hPa</p>
-        <p>humidity: <%= it.data?.[0].indoorHumidity / 100 %>%</p>
-        <p>altitude: <%= it.data?.[0].indoorAltitude / 100 %>m</p>
-    </div>
-    <div class="outdoor">
-        <h3>outdoor</h3>
-        <p>connected: <%= it.data?.[0].outdoorConnected %></p>
-        <p>temp: <%= it.data?.[0].outdoorTemp / 100 %> °C</p>
-        <p>pressure: <%= it.data?.[0].outdoorPressure / 100 %> hPa</p>
-        <p>humidity: <%= it.data?.[0].outdoorHumidity / 100 %>%</p>
-        <p>altitude: <%= it.data?.[0].outdoorAltitude / 100 %>m</p>
-    </div>
+<p><strong>created:</strong> <%= it.meteostanica.timestamp %></p>
+
+<div class="container-row">
+    <p><strong>websocket key:</strong> <%= it.meteostanica.websocketKey %></p>
+    <a role="button" href="/en/panel/stations/<%= it.meteostanica.id %>/resetWebsocketKey"><%~ resetIcon %> reset</a>
+</div>
+
+<hr>
+
+<div class="container-row">
+    <a role="button" class="primary" href="/en/panel/stations/<%= it.meteostanica.id %>/edit"><%~ editIcon %> edit</a>
+    <a role="button" class="danger" href="/en/panel/stations/<%= it.meteostanica.id %>/delete"><%~ deleteIcon %> delete</a>
 </div>
-<% } %>
 
-<h3>data</h3>
-
-<% if (it.data?.length) { %>
-    <div>
-        <table>
-            <tr>
-                <th>time</th>
-                <th>indoor</th>
-                <th>outdoor</th>
-            </tr>
-            <% for (const item of it.data) { %>
-                <tr>
-                    <td><%= item.timestamp %></td>
-                    <td><strong>temp:</strong> <%= item.indoorTemp / 100 %> °C, <strong>pressure:</strong> <%= item.indoorPressure / 100 %> hPa, <strong>humidity:</strong> <%= item.indoorHumidity / 100 %>%, <strong>altitude:</strong> <%= item.indoorAltitude / 100 %>m</td>
-                    <td><strong>connected:</strong> <%= item.outdoorConnected %>, <strong>temp:</strong> <%= item.outdoorTemp / 100 %> °C, <strong>pressure:</strong> <%= item.outdoorPressure  / 100%>hPa, <strong>humidity:</strong> <%= item.outdoorHumidity / 100 %>%, <strong>altitude:</strong> <%= item.outdoorAltitude / 100 %>m</td>
-                </tr>
-            <% } %>
-        </table>
-    </div>
-<% } else { %>
-    <p>no data yet. :(</p>
-<% } %>
+<p>(data with history (graphs) will go here)</p>

+ 45 - 8
templates/en/partials/navbar.eta

@@ -1,10 +1,47 @@
-<nav>
+<% const skFlag = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
+        <mask id="SVGuywqVbel">
+            <circle cx="256" cy="256" r="256" fill="#fff" />
+        </mask>
+        <g mask="url(#SVGuywqVbel)">
+            <path fill="#0052b4" d="m0 160l256-32l256 32v192l-256 32L0 352z" />
+            <path fill="#eee" d="M0 0h512v160H0z" />
+            <path fill="#d80027" d="M0 352h512v160H0z" />
+            <path fill="#eee" d="M64 63v217c0 104 144 137 144 137s144-33 144-137V63z" />
+            <path fill="#d80027" d="M96 95v185a83 78 0 0 0 9 34h206a83 77 0 0 0 9-34V95z" />
+            <path fill="#eee" d="M288 224h-64v-32h32v-32h-32v-32h-32v32h-32v32h32v32h-64v32h64v32h32v-32h64z" />
+            <path fill="#0052b4" d="M152 359a247 231 0 0 0 56 24c12-3 34-11 56-24a123 115 0 0 0 47-45a60 56 0 0 0-34-10l-14 2a60 56 0 0 0-110 0a60 56 0 0 0-14-2c-12 0-24 4-34 10a123 115 0 0 0 47 45" />
+        </g>
+    </svg>
+` %>
+
+<% const homeIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
+            <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
+        </g>
+    </svg>
+` %>
+
+<% const panelIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 17v4m2.305-13.47l.923-.382m0-2.296l-.923-.383m2.547-1.241l-.383-.924m.383 6.468l-.383.923m2.679-6.467l.383-.924m-.001 7.392l-.382-.924m1.624-3.92l.924-.383m-.924 2.679l.924.383M22 13v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7M8 21h8" />
+            <circle cx="18" cy="6" r="3" />
+        </g>
+    </svg>
+` %>
+
+<header>
+    <div class="links languageSwitcher">
+        <a role="button" href="/"><%~ skFlag %></a>
+    </div>
+
     <h2>meteostanica</h2>
 
-    <ul>
-        <li><a href="#">home</a></li>
-        <li><a href="#">home</a></li>
-        <li><a href="/en/auth" class="button">panel</a></li>
-        <li><a href="/">⛰️⛰️⛰️🇸🇰🇸🇰🇸🇰</a></li>
-    </ul>
-</nav>
+    <div class="links">
+        <a role="button" href="/en"><%~ homeIcon %> home</a>
+        <a role="button" href="/en/panel"><%~ panelIcon %> panel</a>
+    </div>
+</header>

+ 29 - 3
templates/sk/auth/index.eta

@@ -1,19 +1,45 @@
 <% layout("/sk/layout", { title: "prihlásenie" }) %>
 
-<h1>prihlásenie</h1>
+<%~ include("/sk/partials/navbar") %>
+
+<% const authIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" />
+      <circle cx="16.5" cy="7.5" r=".5" fill="currentColor" />
+    </g>
+  </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<% const sendIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11zm7.318-19.539l-10.94 10.939" />
+  </svg>
+` %>
+
+
+<h1><%~ authIcon %> prihlásenie</h1>
 <p>zadajte svoj email a kliknite na link, ktorý vám príde.</p>
 
 <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.auth?.errors)?.(it?.errorDetails) %>
 
 <% if (typeof errorValue === "string") { %>
-  <p class="error" style="color: red;"><%= errorValue %></p>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
 <% } %>
 
 <form action="/auth" method="post">
     <input type="email" name="email" placeholder="vas@email.sk" required />
 
     <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
-    <button type="submit">odoslať</button>
+    <button type="submit"><%~ sendIcon %> odoslať</button>
 </form>
 
 <script

+ 32 - 4
templates/sk/auth/verify.eta

@@ -1,17 +1,45 @@
 <% layout("/sk/layout", { title: "overenie" }) %>
 
-<h1>overenie</h1>
+<%~ include("/sk/partials/navbar") %>
+
+<% const verificationIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
+      <path d="M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3m.08 4h.01" />
+    </g>
+  </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<% const verifyIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
+      <path d="m9 12l2 2l4-4" />
+    </g>
+  </svg>
+` %>
+
+<h1><%~ verificationIcon %> overenie</h1>
 <p>zadajte kód z emailu alebo kliknite na link v emaili</p>
 
 <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.auth?.errors)?.(it?.errorDetails) %>
 
 <% if (typeof errorValue === "string") { %>
-  <p class="error" style="color: red;"><%= errorValue %></p>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
 <% } %>
 
 <form action="/auth/verify">
     <input type="text" name="token" value="<%= it.token %>" style="display: none;" />
-    <input type="number" length="6" name="code" placeholder="676767" />
+    <input type="text" pattern="\d+" minlength="6" maxlength="6" name="code" placeholder="676767" />
 
-    <button type="submit">odoslať</button>
+    <button type="submit"><%~ verifyIcon %> overiť</button>
 </form>

+ 60 - 6
templates/sk/email/auth.eta

@@ -1,10 +1,64 @@
-<p>dobrý deň,</p>
+<!DOCTYPE html>
+<html lang="sk">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
-<p>môžete sa prihlásiť nasledujúcim kódom: <strong><%= it.code %></strong></p>
-<p>alebo <a href="<%= it.link %>">kliknite na tento link</a></p>
+    <style>
+        body {
+            margin: 0;
+            display: flex;
+            flex-direction: column;
+            padding-inline: 10%;
+            font-family: sans-serif;
+            line-height: 1.3;
+            background-color: #eee;
+            gap: 0.5em;
+        }
 
-<p><%= it.link %></p>
+        h2, p {
+            margin-block: 0.5em;
+        }
 
-<p><small>ak ste tento email nevyžiadali, môžete ho kľudne ignorovať.</small></p>
+        [role="button"] {
+            background-color: rgba(0, 0, 0, 0.1);
+            border-radius: 0.5em;
+            padding: 0.5em 1em;
+            text-decoration: none;
+            color: initial;
+            border: none;
+            width: max-content;
+            cursor: pointer;
+            display: flex;
+            align-items: center;
+            gap: 0.5em;
+            font-size: 1em;
+            height: max-content;
+        }
 
-<p>meteostanica</p>
+        [role="button"].primary {
+            color: #eee;
+            background-color: rgb(50, 50, 255);
+        }
+    </style>
+</head>
+<body>
+    <h2>meteostanica</h2>
+
+    <p>dobrý deň,</p>
+
+    <p>pre prihlásenie kliknite na tlačidlo nižšie.</p>
+
+    <a role="button" class="primary" href="<%= it.link %>">kliknite sem</a>
+
+    <p>alebo zadajte tento kód: <strong><%= it.code %></strong></p>
+
+
+    <p>nemôžete kliknúť na link? skopírujte si ho: <%= it.link %></p>
+
+    <footer>
+        <p><small>ak ste si tento email nevyžiadali, môžete ho kľudne ignorovať.</small></p>
+        <p><small>meteostanica <%= new Date().getFullYear() %></small></p>
+    </footer>
+</body>
+</html>

+ 3 - 4
templates/sk/index.eta

@@ -2,15 +2,14 @@
 
 <%~ include("/sk/partials/navbar") %>
 
-
-<header>
+<section class="indexHero">
     <div>
         <h1>takto vyzerá >>>>></h1>
         <p>najlepšia meteostanica akú ste kedy videli</p>
 
-        <a href="/auth" class="button">prihlásenie</a>
+        <a role="button" href="/auth" class="button">prihlásenie</a>
     </div>
     <div>
         <img alt="obrázok najlepšej meteostanice" src="https://placehold.net/600x400.png">
     </div>
-</header>
+</section>

+ 18 - 0
templates/sk/panel/deleteAccount.eta

@@ -0,0 +1,18 @@
+<% layout("/sk/layout", { title: "zmazať účet" }) %>
+
+<%~ include("/sk/panel/partials/navbar") %>
+
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/panel"><%~ backIcon %></a>
+    <h2>zmazať účet</h2>
+</div>
+
+<p>ste si istý, že chcete zmazať svoj účet <strong><%= it?.user?.name ? `${it?.user?.name} (${it?.user.email})` : it?.user.email %></strong>?</p>
+
+<a role="button" class="danger" href="/auth/delete">zmazať účet</a>

+ 17 - 17
templates/sk/panel/index.eta

@@ -2,24 +2,24 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<h2>vitajte späť <%= it.user.name?.length ? it.user.name : it.user.email %> !!</h2>
+<% const staniceIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
 
-<div>
-  <h2>vaše stanice</h2>
+<h2>vitajte <%= it.user.name?.length ? it.user.name : it.user.email %>!</h2>
 
-    <% if (it.meteostanice?.length) { %>
-        <ul>
-            <% for (const meteostanica of it.meteostanice) { %>
-                <li><a href="/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></li>
-            <% } %>
-        </ul>
-    <% } else { %>
-        <p>žiadne stanice. :(</p>
-    <% } %>
+<section class="container-row">
+    <a role="button" href="/panel/stations"><%~ staniceIcon %> stanice</a>
+</section>
 
-    <a href="/panel/stations">zobraziť všetky</a>
-</div>
+<section>
+    <h3>posledné upozornenia</h3>
+    <p>zatiaľ žiadne upozornenia.</p>
+</section>
 
-<br>
-
-<a href="/auth/logout">odhlásiť sa</a>
+<a role="button" class="danger" href="/auth/logout">odhlásiť sa</a>

+ 62 - 8
templates/sk/panel/partials/navbar.eta

@@ -1,10 +1,64 @@
-<nav>
+<% const usFlag = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
+        <mask id="SVGuywqVbel">
+            <circle cx="256" cy="256" r="256" fill="#fff" />
+        </mask>
+        <g mask="url(#SVGuywqVbel)">
+            <path fill="#eee" d="M256 0h256v64l-32 32l32 32v64l-32 32l32 32v64l-32 32l32 32v64l-256 32L0 448v-64l32-32l-32-32v-64z" />
+            <path fill="#d80027" d="M224 64h288v64H224Zm0 128h288v64H256ZM0 320h512v64H0Zm0 128h512v64H0Z" />
+            <path fill="#0052b4" d="M0 0h256v256H0Z" />
+            <path fill="#eee" d="m187 243l57-41h-70l57 41l-22-67zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67zm162-81l57-41h-70l57 41l-22-67zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67Zm162-82l57-41h-70l57 41l-22-67Zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67Z" />
+        </g>
+    </svg>
+` %>
+
+<% const homeIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
+            <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
+        </g>
+    </svg>
+` %>
+
+<% const dashboardIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <rect width="20" height="14" x="2" y="3" rx="2" />
+            <path d="M8 21h8m-4-4v4" />
+        </g>
+    </svg>
+` %>
+
+<% const stationsIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
+
+<% const settingsIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0a2.34 2.34 0 0 0 3.319 1.915a2.34 2.34 0 0 1 2.33 4.033a2.34 2.34 0 0 0 0 3.831a2.34 2.34 0 0 1-2.33 4.033a2.34 2.34 0 0 0-3.319 1.915a2.34 2.34 0 0 1-4.659 0a2.34 2.34 0 0 0-3.32-1.915a2.34 2.34 0 0 1-2.33-4.033a2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
+      <circle cx="12" cy="12" r="3" />
+    </g>
+  </svg>
+` %>
+
+<header>
+    <div class="links languageSwitcher">
+        <a role="button" href="/en/panel"><%~ usFlag %></a>
+        <a role="button" href="/"><%~ homeIcon %></a>
+    </div>
+
     <h2>meteostanica panel</h2>
 
-    <ul>
-        <li><a href="/panel">dashboard</a></li>
-        <li><a href="/panel/stations">stanice</a></li>
-        <li><a href="/panel/settings">nastavenia</a></li>
-        <li><a href="/en/panel">🦅🦅🦅🇺🇸🇺🇸🇺🇸</a></li>
-    </ul>
-</nav>
+    <div class="links">
+        <a role="button" href="/panel"><%~ dashboardIcon %> dashboard</a>
+        <a role="button" href="/panel/stations"><%~ stationsIcon %> stanice</a>
+        <a role="button" href="/panel/settings"><%~ settingsIcon %> nastavenia</a>
+    </div>
+</header>

+ 50 - 25
templates/sk/panel/settings.eta

@@ -2,42 +2,67 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<div>
-  <h2>nastavenia</h2>
+<% const settingsIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0a2.34 2.34 0 0 0 3.319 1.915a2.34 2.34 0 0 1 2.33 4.033a2.34 2.34 0 0 0 0 3.831a2.34 2.34 0 0 1-2.33 4.033a2.34 2.34 0 0 0-3.319 1.915a2.34 2.34 0 0 1-4.659 0a2.34 2.34 0 0 0-3.32-1.915a2.34 2.34 0 0 1-2.33-4.033a2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
+      <circle cx="12" cy="12" r="3" />
+    </g>
+  </svg>
+` %>
 
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.settings?.errors)?.(it?.errorDetails) %>
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
 
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/panel/settings" method="post">
-    <div>
-      <label for="name">meno</label>
-      <input type="text" id="name" name="name" placeholder="vaše meno" value="<%= it.user.name ?? "" %>">
-    </div>
+<% const saveIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" />
+      <path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7" />
+    </g>
+  </svg>
+` %>
 
-    <br>
+<% const deleteIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
+  </svg>
+` %>
 
-    <div>
-      <label for="email">email</label>
-      <input type="text" id="email" name="email" placeholder="váš@email.sk" value="<%= it.user.email %>">
-    </div>
+<h2><%~ settingsIcon %> nastavenia</h2>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.settings?.errors)?.(it?.errorDetails) %>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/panel/settings" method="post">
+  <div>
+    <label for="name">meno</label>
+    <input type="text" id="name" name="name" placeholder="vaše meno" value="<%= it.user.name ?? "" %>">
+  </div>
 
-    <button type="submit">uložiť</button>
-  </form>
+  <div>
+    <label for="email">email</label>
+    <input type="text" id="email" name="email" placeholder="váš@email.sk" value="<%= it.user.email %>">
+  </div>
 
-  <br>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
 
-  <a href="/auth/delete">zmazať účet</a>
-</div>
+  <button type="submit" class="primary"><%~ saveIcon %> uložiť</button>
+</form>
+
+<hr>
 
+<div class="container-row">
+  <a role="button" class="danger" href="/panel/deleteAccount"><%~ deleteIcon %> zmazať účet</a>
+</div>
 
 <script
   src="https://challenges.cloudflare.com/turnstile/v0/api.js"

+ 40 - 26
templates/sk/panel/stations/add.eta

@@ -2,38 +2,52 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<div>
-  <h2>pridať stanicu</h2>
-
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
-
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/panel/stations/add" method="post">
-    <div>
-      <label for="name">meno</label>
-      <input type="text" id="name" name="name" placeholder="cool stanica">
-    </div>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<% const addIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14" />
+    </svg>
+` %>
+
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/panel/stations"><%~ backIcon %></a>
+    <h2><%~ addIcon %> pridať stanicu</h2>
+</div>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
 
-    <div>
-      <label for="description">popis</label>
-      <textarea id="description" name="description" placeholder="tá najviac cool"></textarea>
-    </div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/panel/stations/add" method="post">
+  <div>
+    <label for="name">meno</label>
+    <input type="text" id="name" name="name" placeholder="cool stanica">
+  </div>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+  <div>
+    <label for="description">popis</label>
+    <textarea id="description" name="description" placeholder="tá najviac cool"></textarea>
+  </div>
 
-    <br>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
 
-    <button type="submit">pridať</button>
-    <a href="/panel/stations">&lt;&lt; späť</a>
-  </form>
-</div>
+  <button type="submit" class="primary"><%~ addIcon %> pridať</button>
+</form>
 
 
 <script

+ 18 - 0
templates/sk/panel/stations/delete.eta

@@ -0,0 +1,18 @@
+<% layout("/sk/layout", { title: "zmazať stanicu" }) %>
+
+<%~ include("/sk/panel/partials/navbar") %>
+
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/panel/stations/<%= it?.meteostanica.id %>"><%~ backIcon %></a>
+    <h2>zmazať stanicu</h2>
+</div>
+
+<p>ste si istý, že chcete zmazať svoju stanicu <strong><%= it?.meteostanica.name %></strong>?</p>
+
+<a role="button" class="danger" href="/panel/stations/<%= it?.meteostanica.id %>/deleteConfirm">zmazať stanicu</a>

+ 53 - 29
templates/sk/panel/stations/edit.eta

@@ -2,45 +2,69 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<div>
-  <h2><%= it.meteostanica.name %> - upraviť</h2>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
 
-  <% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
+<% const editIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
+            <path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />
+        </g>
+    </svg>
+` %>
 
-  <% if (typeof errorValue === "string") { %>
-    <span class="error"><%= errorValue %></span>
-  <% } %>
-  
-  <form action="/panel/stations/<%= it.meteostanica.id %>/edit" method="post">
-    <div>
-      <label for="name">meno</label>
-      <input type="text" id="name" name="name" placeholder="cool stanica" value="<%= it.meteostanica.name %>">
-    </div>
+<% const errorIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01" />
+  </svg>
+` %>
 
-    <br>
+<% const saveIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+      <path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" />
+      <path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7" />
+    </g>
+  </svg>
+` %>
 
-    <div>
-      <label for="description">popis</label>
-      <textarea id="description" name="description" placeholder="tá najviac cool"><%= it.meteostanica.description %></textarea>
-    </div>
+<div class="container-row">
+    <a role="button" href="/panel/stations/<%= it.meteostanica.id %>"><%~ backIcon %></a>
+    <h2><%~ editIcon %> upraviť <%= it.meteostanica.name %></h2>
+</div>
 
-    <br>
+<% const errorValue = it.error?.split('.').reduce((a, b) => a[b], it.lang.stations?.errors)?.(it?.errorDetails) %>
 
-    <div>
-      <label for="owner">vlastník</label>
-      <input type="text" id="owner" name="owner" placeholder="váš@email.sk" value="<%= it.meteostanica.owner %>">
-    </div>
+<% if (typeof errorValue === "string") { %>
+  <div class="message error">
+    <p class="messageText"><%~ errorIcon %> <%= errorValue %></p>
+  </div>
+<% } %>
 
-    <br>
+<form action="/panel/stations/<%= it.meteostanica.id %>/edit" method="post">
+  <div>
+    <label for="name">meno</label>
+    <input type="text" id="name" name="name" placeholder="cool stanica" value="<%= it.meteostanica.name %>">
+  </div>
 
-    <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+  <div>
+    <label for="description">popis</label>
+    <textarea id="description" name="description" placeholder="tá najviac cool"><%= it.meteostanica.description %></textarea>
+  </div>
 
-    <br>
+  <div>
+    <label for="owner">vlastník</label>
+    <input type="text" id="owner" name="owner" placeholder="váš@email.sk" value="<%= it.meteostanica.owner %>">
+  </div>
 
-    <button type="submit">uložiť</button>
-    <a href="/panel/stations/<%= it.meteostanica.id %>">&lt;&lt; späť</a>
-  </form>
-</div>
+  <div class="cf-turnstile" data-sitekey="<%= it.siteKey %>"></div>
+
+  <button type="submit" class="primary"><%~ saveIcon %> uložiť</button>
+</form>
 
 <script
   src="https://challenges.cloudflare.com/turnstile/v0/api.js"

+ 36 - 13
templates/sk/panel/stations/index.eta

@@ -2,18 +2,41 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<div>
-  <h2>vaše stanice</h2>
+<% const staniceIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M16.247 7.761a6 6 0 0 1 0 8.478m2.828-11.306a10 10 0 0 1 0 14.134m-14.15 0a10 10 0 0 1 0-14.134m2.828 11.306a6 6 0 0 1 0-8.478" />
+            <circle cx="12" cy="12" r="2" />
+        </g>
+    </svg>
+` %>
 
-    <% if (it.meteostanice?.length) { %>
-        <ul>
-            <% for (const meteostanica of it.meteostanice) { %>
-                <li><a href="/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></li>
-            <% } %>
-        </ul>
-    <% } else { %>
-        <p>žiadne stanice. :(</p>
-    <% } %>
+<% const addIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14" />
+    </svg>
+` %>
 
-    <a href="/panel/stations/add">pridať</a>
-</div>
+<div class="container-row">
+    <h2><%~ staniceIcon %> stanice</h2>
+    <a role="button" class="primary" href="/panel/stations/add"><%~ addIcon %> pridať</a>
+</div>
+
+<% if (it.meteostanice?.length) { %>
+    <table>
+        <tr>
+            <th>meno</th>
+            <th>popis</th>
+            <th>vytvorená</th>
+        </tr>
+        <% for (const meteostanica of it.meteostanice) { %>
+            <tr>
+                <td><a href="/panel/stations/<%= meteostanica.id %>"><%= meteostanica.name %></a></td>
+                <td><%= meteostanica?.description ?? `` %></td>
+                <td><%= meteostanica.timestamp %></td>
+            </tr>
+        <% } %>
+    </table>
+<% } else { %>
+    <p>žiadne stanice. :(</p>
+<% } %>

+ 9 - 2
templates/sk/panel/stations/notFound.eta

@@ -2,6 +2,13 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<a href="/panel/stations">&lt;&lt; späť</a>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
 
-<h2>stanica nenájdená</h2>
+<div class="container-row">
+    <a role="button" href="/panel/stations"><%~ backIcon %></a>
+    <h2>stanica nenájdená</h2>
+</div>

+ 46 - 51
templates/sk/panel/stations/station.eta

@@ -2,62 +2,57 @@
 
 <%~ include("/sk/panel/partials/navbar") %>
 
-<a href="/panel/stations">&lt;&lt; späť</a>
-
-<h2><%= it.meteostanica.name %></h2>
+<% const backIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5" />
+    </svg>
+` %>
+
+<% const resetIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M3 12a9 9 0 1 0 9-9a9.75 9.75 0 0 0-6.74 2.74L3 8" />
+            <path d="M3 3v5h5" />
+        </g>
+    </svg>
+` %>
+
+<% const editIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
+            <path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />
+        </g>
+    </svg>
+` %>
+
+<% const deleteIcon = `
+  <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11v6m4-6v6m5-11v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
+  </svg>
+` %>
+
+<div class="container-row">
+    <a role="button" href="/panel/stations"><%~ backIcon %></a>
+    <h2><%= it.meteostanica.name %></h2>
+</div>
 
 <% if (it.meteostanica.description) { %>
-    <p><%= it.meteostanica.description %></p>
+    <p><strong>popis:</strong> <%= it.meteostanica.description %></p>
 <% } %>
 
-<p>
-    <a href="/panel/stations/<%= it.meteostanica.id %>/edit">upraviť</a>
-    <a href="/panel/stations/<%= it.meteostanica.id %>/remove">odstrániť</a>
-</p>
-
 <p><strong>vytvorená:</strong> <%= it.meteostanica.timestamp %></p>
 
-<p><strong>websocket kľúč:</strong> <%= it.meteostanica.websocketKey %> <a href="/panel/stations/<%= it.meteostanica.id %>/resetWebsocketKey">reset</a></p>
-
-<% if (it.data?.[0]) { %>
-<div class="stats">
-    <div class="indoor">
-        <h3>vnútorné</h3>
-        <p>teplota: <%= it.data?.[0].indoorTemp / 100 %> °C</p>
-        <p>tlak: <%= it.data?.[0].indoorPressure / 100 %> hPa</p>
-        <p>vlhkosť: <%= it.data?.[0].indoorHumidity / 100 %>%</p>
-        <p>nadmorská výška: <%= it.data?.[0].indoorAltitude / 100 %>m</p>
-    </div>
-    <div class="outdoor">
-        <h3>vonkajšie</h3>
-        <p>pripojené: <%= it.data?.[0].outdoorConnected %></p>
-        <p>teplota: <%= it.data?.[0].outdoorTemp / 100 %> °C</p>
-        <p>tlak: <%= it.data?.[0].outdoorPressure / 100 %> hPa</p>
-        <p>vlhkosť: <%= it.data?.[0].outdoorHumidity / 100 %>%</p>
-        <p>nadmorská výška: <%= it.data?.[0].outdoorAltitude / 100 %>m</p>
-    </div>
+<div class="container-row">
+    <p><strong>websocket kľúč:</strong> <%= it.meteostanica.websocketKey %></p>
+    <a role="button" href="/panel/stations/<%= it.meteostanica.id %>/resetWebsocketKey"><%~ resetIcon %> reset</a>
+</div>
+
+<hr>
+
+<div class="container-row">
+    <a role="button" class="primary" href="/panel/stations/<%= it.meteostanica.id %>/edit"><%~ editIcon %> upraviť</a>
+    <a role="button" class="danger" href="/panel/stations/<%= it.meteostanica.id %>/delete"><%~ deleteIcon %> odstrániť</a>
 </div>
-<% } %>
 
-<h3>dáta</h3>
-
-<% if (it.data?.length) { %>
-    <div>
-        <table>
-            <tr>
-                <th>čas</th>
-                <th>vnútorné</th>
-                <th>vonkajšie</th>
-            </tr>
-            <% for (const item of it.data) { %>
-                <tr>
-                    <td><%= item.timestamp %></td>
-                    <td><strong>teplota:</strong> <%= item.indoorTemp / 100 %> °C, <strong>tlak:</strong> <%= item.indoorPressure / 100 %> hPa, <strong>vlhkosť:</strong> <%= item.indoorHumidity / 100 %>%, <strong>nadmorská výška:</strong> <%= item.indoorAltitude / 100 %>m</td>
-                    <td><strong>pripojené:</strong> <%= item.outdoorConnected %>, <strong>teplota:</strong> <%= item.outdoorTemp / 100 %> °C, <strong>tlak:</strong> <%= item.outdoorPressure  / 100%>hPa, <strong>vlhkosť:</strong> <%= item.outdoorHumidity / 100 %>%, <strong>nadmorská výška:</strong> <%= item.outdoorAltitude / 100 %>m</td>
-                </tr>
-            <% } %>
-        </table>
-    </div>
-<% } else { %>
-    <p>zatiaľ žiadne dáta. :(</p>
-<% } %>
+<p>(dáta s histórou (grafmi) bude tu)</p>

+ 42 - 8
templates/sk/partials/navbar.eta

@@ -1,10 +1,44 @@
-<nav>
+<% const usFlag = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
+        <mask id="SVGuywqVbel">
+            <circle cx="256" cy="256" r="256" fill="#fff" />
+        </mask>
+        <g mask="url(#SVGuywqVbel)">
+            <path fill="#eee" d="M256 0h256v64l-32 32l32 32v64l-32 32l32 32v64l-32 32l32 32v64l-256 32L0 448v-64l32-32l-32-32v-64z" />
+            <path fill="#d80027" d="M224 64h288v64H224Zm0 128h288v64H256ZM0 320h512v64H0Zm0 128h512v64H0Z" />
+            <path fill="#0052b4" d="M0 0h256v256H0Z" />
+            <path fill="#eee" d="m187 243l57-41h-70l57 41l-22-67zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67zm162-81l57-41h-70l57 41l-22-67zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67Zm162-82l57-41h-70l57 41l-22-67Zm-81 0l57-41H93l57 41l-22-67zm-81 0l57-41H12l57 41l-22-67Z" />
+        </g>
+    </svg>
+` %>
+
+<% const homeIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
+            <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
+        </g>
+    </svg>
+` %>
+
+<% const panelIcon = `
+    <svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+        <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+            <path d="M12 17v4m2.305-13.47l.923-.382m0-2.296l-.923-.383m2.547-1.241l-.383-.924m.383 6.468l-.383.923m2.679-6.467l.383-.924m-.001 7.392l-.382-.924m1.624-3.92l.924-.383m-.924 2.679l.924.383M22 13v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7M8 21h8" />
+            <circle cx="18" cy="6" r="3" />
+        </g>
+    </svg>
+` %>
+
+<header>
+    <div class="links languageSwitcher">
+        <a role="button" href="/en"><%~ usFlag %></a>
+    </div>
+
     <h2>meteostanica</h2>
 
-    <ul>
-        <li><a href="#">domov</a></li>
-        <li><a href="#">domov</a></li>
-        <li><a href="/auth" class="button">panel</a></li>
-        <li><a href="/en">🦅🦅🦅🇺🇸🇺🇸🇺🇸</a></li>
-    </ul>
-</nav>
+    <div class="links">
+        <a role="button" href="/"><%~ homeIcon %> domov</a>
+        <a role="button" href="/panel"><%~ panelIcon %> panel</a>
+    </div>
+</header>

+ 2 - 2
utils/auth.js

@@ -140,8 +140,8 @@ export default class Auth {
         return result
     }
 
-    static removeUser(email) {
-        Meteostanice.removeOwned(email)
+    static deleteUser(email) {
+        Meteostanice.deleteOwned(email)
 
         this.removeUserSessions(email)
         this.removeUserVerifications(email)

+ 3 - 3
utils/meteostanice.js

@@ -123,7 +123,7 @@ export default class Meteostanice {
         `).run(newOwner, owner)
     }
 
-    static remove(id) {
+    static delete(id) {
         meteostaniceDB.prepare(`
             DELETE
             FROM data 
@@ -149,11 +149,11 @@ export default class Meteostanice {
         });
     }
 
-    static removeOwned(owner) {
+    static deleteOwned(owner) {
         const meteostanice = this.getOwned(owner)
 
         for (const meteostanica of meteostanice) {
-            this.remove(meteostanica.owner, meteostanica.id)
+            this.delete(meteostanica.owner, meteostanica.id)
         }
     }