addition of new dashboard
This commit is contained in:
26
backend/http/views/include/agreements.ejs
Normal file
26
backend/http/views/include/agreements.ejs
Normal file
@@ -0,0 +1,26 @@
|
||||
<% const cardStyle=`"background: linear-gradient(333deg, ${app.background.color1} 0%, ${app.background.color2} 100%);
|
||||
margin-bottom: 30px;"`; const userApp=user.apps.find(ua=> app.type === 'core');
|
||||
const buttonStyle = `"padding: 8px 10px 8px 10px; border-radius: 16px !important; background-color:
|
||||
${app.button.color} !important; border: none !important;"`
|
||||
%>
|
||||
|
||||
<div class="mdc-card mdc-theme--dark" style=<%- cardStyle %>>
|
||||
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="width: 30px">
|
||||
<%- app.logo %>
|
||||
</div>
|
||||
<h2 style="margin-left: 10px; padding-bottom: 10px;">
|
||||
Available Agreements
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center;">
|
||||
<ul style="margin-top: 0px">
|
||||
<% for (const agreement of agreements) { %>
|
||||
<li style="padding-bottom: 1em"><a target="_new" class="agreement-link" href="/agreements/<%- agreement.hash %>"><%- agreement.title %></a></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
163
backend/http/views/include/app.ejs
Normal file
163
backend/http/views/include/app.ejs
Normal file
@@ -0,0 +1,163 @@
|
||||
<% const cardStyle=`"background: linear-gradient(333deg, ${app.background.color1} 0%, ${app.background.color2} 100%);
|
||||
margin-bottom: 30px;"`; const userApp=user.apps.find(ua=> app.type === ua.type);
|
||||
const buttonStyle = `"padding: 8px 10px 8px 10px; border-radius: 16px !important; background-color:
|
||||
${app.button.color} !important; border: none !important;"`
|
||||
%>
|
||||
|
||||
<script>
|
||||
function copyToClipboard(label, text) {
|
||||
if (window.clipboardData && window.clipboardData.setData) {
|
||||
// Internet Explorer specific code path to prevent textarea being shown while dialog is visible.
|
||||
return clipboardData.setData('Text', text);
|
||||
} else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.textContent = text;
|
||||
// Prevent scrolling to bottom of page in Microsoft Edge.
|
||||
textarea.style.position = 'fixed';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
const flash = document.createElement('div');
|
||||
flash.style.position = 'fixed';
|
||||
flash.style.top = '0';
|
||||
flash.style.left = '0';
|
||||
flash.style.width = '100%';
|
||||
flash.style.backgroundColor = '#000';
|
||||
flash.style.color = '#ggg';
|
||||
flash.style.opacity = '0.7';
|
||||
flash.style.textAlign = 'center';
|
||||
flash.style.fontSize = '12px';
|
||||
flash.style.padding = '10px';
|
||||
try {
|
||||
const cmd = document.execCommand('copy'); // Security exception may be thrown by some browsers.
|
||||
flash.innerHTML = `${label} copied to clipboard`;
|
||||
document.body.appendChild(flash);
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(flash);
|
||||
}, 1500);
|
||||
return cmd;
|
||||
} catch (ex) {
|
||||
flash.style.backgroundColor = '#f00';
|
||||
flash.innerHTML = 'API key copy failed';
|
||||
document.body.appendChild(flash);
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(flash);
|
||||
}, 1500);
|
||||
return false;
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mdc-card mdc-theme--dark" style=<%- cardStyle %>>
|
||||
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="width: 30px">
|
||||
<%- app.logo %>
|
||||
</div>
|
||||
<h2 style="margin-left: 10px; padding-bottom: 10px;">
|
||||
<%= app.title %>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<label class="mdc-text-field mdc-text-field--outlined mdc-text-field--focused">
|
||||
<span class="mdc-notched-outline" style="--mdc-theme-primary: rgba(255, 255, 255, 0.3)">
|
||||
<span class="mdc-notched-outline__leading"></span>
|
||||
<span class="mdc-notched-outline__trailing"></span>
|
||||
</span>
|
||||
<input style="color: #fff; text-overflow: ellipsis;" type="text" id="endpoint-input"
|
||||
aria-describedby="api-key-helper" class="mdc-text-field__input" disabled type="text"
|
||||
value="<%= app.endpoint %>">
|
||||
<i style="color: #fff" class="material-icons mdc-text-field__icon mdc-text-field__icon--trailing"
|
||||
tabindex="0" role="button" onclick="copyToClipboard('API Endpoint', '<%= app.endpoint %>')">
|
||||
content_copy
|
||||
</i>
|
||||
</label>
|
||||
<div class="mdc-text-field-helper-line" style="padding-bottom: 20px">
|
||||
<div style="color: #fff" class="mdc-text-field-helper-text" id="endpoint-helper" aria-hidden="false">API
|
||||
Endpoint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="mdc-text-field mdc-text-field--outlined mdc-text-field--focused">
|
||||
<span class="mdc-notched-outline" style="--mdc-theme-primary: rgba(255, 255, 255, 0.3)">
|
||||
<span class="mdc-notched-outline__leading"></span>
|
||||
<span class="mdc-notched-outline__trailing"></span>
|
||||
</span>
|
||||
<input style="color: #fff; text-overflow: ellipsis;" type="text" id="api-key-input"
|
||||
aria-describedby="api-key-helper" class="mdc-text-field__input" disabled type="text"
|
||||
value="<%= userApp.apiKey %>">
|
||||
<i style="color: #fff" class="material-icons mdc-text-field__icon mdc-text-field__icon--trailing"
|
||||
tabindex="0" role="button" onclick="copyToClipboard('API Key', '<%= userApp.apiKey %>')">
|
||||
content_copy
|
||||
</i>
|
||||
</label>
|
||||
<div class="mdc-text-field-helper-line">
|
||||
<div style="color: #fff" class="mdc-text-field-helper-text" id="api-key-helper" aria-hidden="false">API Key
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end;">
|
||||
<button style="width: 140px; --mdc-theme-primary: <%= app.button.color %>"
|
||||
class="mdc-button mdc-button--raised mdc-button--leading"
|
||||
onclick="window.location.href='/refresh?app=<%= app.type %>'">
|
||||
<span class="mdc-button__ripple"></span>
|
||||
<i class="material-icons mdc-button__icon" aria-hidden="true">refresh</i>
|
||||
<span class="mdc-button__label">Refresh</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<% if (usage) { %>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<h3 style="margin: 0; display: flex; align-items: center; margin-right: 16px;">Hits this month:</h3>
|
||||
<span class="mdc-evolution-chip-set" role="grid">
|
||||
<span class="mdc-evolution-chip-set__chips" role="presentation">
|
||||
<span class="mdc-evolution-chip" role="row">
|
||||
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary" role="gridcell">
|
||||
<button style=<%- buttonStyle %>
|
||||
class="mdc-evolution-chip__action mdc-evolution-chip__action--primary" type="button"
|
||||
tabindex="0">
|
||||
<span class="mdc-evolution-chip__ripple mdc-evolution-chip__ripple--primary"></span>
|
||||
<span class="mdc-evolution-chip__text-label">
|
||||
<%= usage %>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
|
||||
<% if (userApp.devices?.length> 0) { %>
|
||||
<h3 style="margin-bottom: 0px">Synced devices</h3>
|
||||
<span class="mdc-evolution-chip-set" role="grid" style="padding-top: 16px">
|
||||
<span class="mdc-evolution-chip-set__chips" role="presentation">
|
||||
|
||||
<% for (const device of userApp.devices.sort((a, b)=>
|
||||
a.identifier.localeCompare(b.identifier))) {
|
||||
%>
|
||||
<span style="padding-right: 10px" class="mdc-evolution-chip" role="row"
|
||||
id="device-<%- device.id %>">
|
||||
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary"
|
||||
role="gridcell">
|
||||
<button style=<%- buttonStyle %> class="mdc-evolution-chip__action
|
||||
mdc-evolution-chip__action--primary"
|
||||
type="button" tabindex="0">
|
||||
<span
|
||||
class="mdc-evolution-chip__ripple mdc-evolution-chip__ripple--primary"></span>
|
||||
<span class="mdc-evolution-chip__text-label">
|
||||
<%= device.identifier %>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
6
backend/http/views/include/footer.ejs
Normal file
6
backend/http/views/include/footer.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
125
backend/http/views/include/header.ejs
Normal file
125
backend/http/views/include/header.ejs
Normal file
@@ -0,0 +1,125 @@
|
||||
<head>
|
||||
<title>
|
||||
<%= title %>
|
||||
</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="/images/icon.svg" type="image/x-icon">
|
||||
<link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--md-ref-typeface-brand: 'Open Sans', sans-serif;
|
||||
--md-ref-typeface-plain: system-ui, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--md-ref-typeface-plain);
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
background-image: linear-gradient(135deg, #31A9BA 10%, #0E0618 10%, #231641 90%, #31A9BA 90%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
font-family: var(--md-ref-typeface-plain);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--md-ref-typeface-brand);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mdc-card {
|
||||
padding-bottom: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
background-color: rgb(68, 62, 77);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.mdc-button {
|
||||
--mdc-theme-primary: rgb(255, 255, 255, .85);
|
||||
--mdc-theme-on-primary: rgb(68, 62, 77);
|
||||
|
||||
/* @include button.ink-color(#84565E); */
|
||||
}
|
||||
|
||||
.agreement-link {
|
||||
font-family: var(--md-ref-typeface-plain);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="profile-icon-container" style="position: absolute; top: 16px; right: 16px;">
|
||||
<% if (typeof user !=='undefined' && user) { %>
|
||||
<div style="position: relative;">
|
||||
<button id="profileButton" class="profile-icon"
|
||||
style="border: none; border-radius: 50%; width: 48px; height: 48px; display: flex; justify-content: center; align-items: center; background-color: #31A9BA; color: #fff; cursor: pointer;">
|
||||
<span style="font-size: 30px">
|
||||
<%= user.username.charAt(0).toUpperCase() %>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div id="profileMenu"
|
||||
style="display: none; position: absolute; top: 60px; right: 0; background-color: #4F378B; box-shadow: 0 2px 8px rgba(0,0,0,0.15); border-radius: 8px; padding: 10px; min-width: 160px; z-index: 100;">
|
||||
<div style="padding: 8px; font-weight: bold;">
|
||||
<span style="display: block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<%= user.username %>
|
||||
</span>
|
||||
</div>
|
||||
<form method="POST" action="/logout">
|
||||
<button type="submit"
|
||||
style="width: 100%; padding: 8px; background-color: #31A9BA; color: white; border: none; border-radius: 4px; cursor: pointer;">Logout</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const profileButton = document.getElementById('profileButton');
|
||||
const profileMenu = document.getElementById('profileMenu');
|
||||
|
||||
profileButton.addEventListener('click', () => {
|
||||
profileMenu.style.display = profileMenu.style.display === 'block' ? 'none' : 'block';
|
||||
});
|
||||
|
||||
// Optional: Hide menu if clicking outside
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!profileButton.contains(event.target) && !profileMenu.contains(event.target)) {
|
||||
profileMenu.style.display = 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<% } %>
|
||||
</div>
|
||||
<div style="width: 100%; overflow-y: auto; max-height: 100%; word-wrap: break-word;">
|
||||
<center>
|
||||
<div style="width: 90%; max-width: 600px; text-align: left">
|
||||
<div
|
||||
style="padding-top: 20px; display: flex; align-items: center; justify-content: center; gap: 0px; text-align: center; transform: translateX(-0px);">
|
||||
<div style="width: 200px; padding-bottom: 26px">
|
||||
<%- include('./logo-white.svg') %>
|
||||
</div>
|
||||
</div>
|
||||
1
backend/http/views/include/logo-white.svg
Normal file
1
backend/http/views/include/logo-white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 362.08 100.47"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#31a9ba;}</style></defs><title>JLINC Logo H White</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M157.32,29.36h8.35V57.2q0,13.9-13.92,13.91H137.84q-14.22,0-14-13.91l0-1.4h8.35v.25q0,6.72,6.68,6.71h11.66q6.7,0,6.71-6.73Z"/><path class="cls-1" d="M223.12,62.76v8.35H181.37V29.36h8.35v33.4Z"/><path class="cls-1" d="M247.18,71.11h-8.35V29.36h8.35Z"/><path class="cls-1" d="M271.23,42V71.11h-8.35V29.36h8.51l24.89,29.09V29.36h8.35V71.11h-8.49Z"/><path class="cls-1" d="M362.08,62.76v8.35H334.25q-13.92,0-13.92-13.91V43.28q0-13.92,13.92-13.92h27.83v8.35H335.39q-6.7,0-6.71,6.68V56.05q0,6.72,6.74,6.71Z"/><path class="cls-2" d="M8.52,41.22l9,9L36.46,69.16l7.76-7.76L25.3,42.48l-9.42-9.42q-4.5-4.52-5.14-9.26c-.48-3.11,1-6.36,4.35-9.73Q19.22,9.95,23.64,10t9.26,4.83l4,4,7.2-7.21L38.52,6a20.93,20.93,0,0,0-7.91-4.9A17.1,17.1,0,0,0,20,.61Q14,2,8.05,8a28.56,28.56,0,0,0-6.33,9.5,18.2,18.2,0,0,0-.79,11.4Q2.34,35,8.52,41.22Z"/><path class="cls-2" d="M61.4,56.25,42.48,75.17l-9.42,9.42q-4.52,4.51-9.26,5.14t-9.73-4.35Q9.95,81.27,10,76.83t4.83-9.26l4-3.95-7.21-7.21L6,62a21,21,0,0,0-4.9,7.92A17.09,17.09,0,0,0,.61,80.48q1.43,6,7.36,12a28.87,28.87,0,0,0,9.5,6.33,18.2,18.2,0,0,0,11.4.79Q35,98.13,41.22,92l9-9L69.16,64Z"/><path class="cls-2" d="M92,59.26l-9-9L64,31.31l-7.76,7.76L75.17,58l9.42,9.42q4.51,4.52,5.14,9.26t-4.35,9.74c-2.74,2.74-5.59,4.12-8.55,4.11s-6-1.55-9.26-4.83l-3.95-4-7.21,7.2L62,94.48a21.11,21.11,0,0,0,7.92,4.91,17.06,17.06,0,0,0,10.6.47q6-1.42,12-7.36A28.87,28.87,0,0,0,98.76,83a18.16,18.16,0,0,0,.79-11.39Q98.13,65.43,92,59.26Z"/><path class="cls-2" d="M39.07,44.22,58,25.3l9.42-9.42q4.52-4.5,9.26-5.14c3.12-.48,6.36,1,9.74,4.35,2.74,2.75,4.11,5.59,4.11,8.55s-1.55,6-4.83,9.26l-4,4,7.2,7.2,5.54-5.54a21.07,21.07,0,0,0,4.91-7.91A17.17,17.17,0,0,0,99.86,20Q98.44,14,92.5,8.05A28.56,28.56,0,0,0,83,1.72,18.19,18.19,0,0,0,71.6.93Q65.44,2.34,59.26,8.52l-9,9L31.31,36.46Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Reference in New Issue
Block a user