nymea-experience-plugin-evdash/dashboard/index.html

424 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>nymea EV Dash</title>
<style>
:root {
--primary-color: #0050a0;
--secondary-color: #00a0e0;
--accent-color: #f4b400;
--surface-color: #ffffff;
--background-color: #f5f7fa;
--text-color: #1f2d3d;
--muted-text-color: #566b84;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Segoe UI", Roboto, sans-serif;
background: var(--background-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: #ffffff;
padding: 2.5rem 1.5rem 2rem;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.header-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
}
.brand h1 {
margin: 0 0 0.4rem;
font-weight: 600;
letter-spacing: 0.02em;
}
.brand p {
margin: 0;
opacity: 0.9;
}
.session-panel {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem 1.5rem;
}
.tool-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.65);
color: #ffffff;
background: transparent;
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
transition: background 0.15s ease, border-color 0.15s ease;
}
.tool-button:hover {
background: rgba(255, 255, 255, 0.15);
border-color: #ffffff;
}
main {
flex: 1;
width: min(960px, 92vw);
margin: 2rem auto 3rem;
display: grid;
gap: 1.5rem;
}
.card {
background: var(--surface-color);
border-radius: 14px;
padding: 1.5rem;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.06);
}
.card h2 {
margin-top: 0;
font-size: 1.25rem;
}
.status-indicator {
display: inline-flex;
align-items: center;
gap: 0.75rem;
font-weight: 600;
margin: 0;
}
.status-dot {
width: 14px;
height: 14px;
border-radius: 50%;
background-color: #d3dce6;
transition: background-color 0.3s ease;
}
.status-dot.connecting {
background-color: #f4b400;
}
.status-dot.connected {
background-color: #2ecc71;
}
.status-dot.authenticating {
background-color: #8e44ad;
}
.status-dot.error {
background-color: #e74c3c;
}
.grid-two-column {
display: grid;
gap: 1.5rem;
}
@media (min-width: 900px) {
.grid-two-column {
grid-template-columns: 1fr 1fr;
}
}
.table-wrapper {
overflow-x: auto;
}
table.chargers-table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
}
.chargers-table th,
.chargers-table td {
padding: 0.5rem 0.75rem;
border-bottom: 1px solid #e4e9f2;
text-align: left;
}
.chargers-table th {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted-text-color);
}
.chargers-table tbody tr:nth-child(even) {
background: #f9fbfd;
}
.chargers-table td[data-column="id"] {
font-family: "Fira Code", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.85rem;
}
.chargers-table .empty-row td {
text-align: center;
font-style: italic;
color: var(--muted-text-color);
}
pre {
margin: 0;
background: #f8fafc;
border-radius: 10px;
padding: 1rem;
overflow-x: auto;
font-size: 0.95rem;
}
code {
font-family: "Fira Code", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}
footer {
text-align: center;
padding: 1.25rem 1rem;
background: #ffffff;
border-top: 1px solid #d3dce6;
font-size: 0.9rem;
color: var(--muted-text-color);
}
/* Authentication view */
body.needs-auth {
background: var(--surface-color);
}
body.needs-auth header,
body.needs-auth main,
body.needs-auth footer {
display: none;
}
.login-view {
width: 100%;
padding: 3rem 1.5rem;
display: none;
}
body.needs-auth .login-view {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
}
.login-panel {
background: var(--surface-color);
width: min(420px, 100%);
border-radius: 16px;
padding: 2.25rem;
border: 1px solid rgba(0, 0, 0, 0.06);
box-shadow: 0 18px 28px rgba(0, 0, 0, 0.08);
}
.login-panel h2 {
margin: 0 0 0.5rem;
font-size: 1.5rem;
}
.login-panel p {
margin: 0;
color: var(--muted-text-color);
}
form {
margin-top: 1.5rem;
display: grid;
gap: 1rem;
}
label {
display: grid;
gap: 0.35rem;
font-weight: 600;
}
input[type="text"],
input[type="password"] {
border-radius: 10px;
border: 1px solid #d3dce6;
padding: 0.75rem 1rem;
font-size: 1rem;
background: #f9fbfd;
color: var(--text-color);
}
input[type="text"]:focus,
input[type="password"]:focus {
outline: none;
border-color: var(--secondary-color);
box-shadow: 0 0 0 3px rgba(0, 160, 224, 0.2);
}
button {
border: none;
border-radius: 999px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
justify-self: start;
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
button.primary {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: #ffffff;
box-shadow: 0 10px 24px rgba(0, 80, 160, 0.26);
}
button.primary:disabled {
opacity: 0.65;
cursor: progress;
box-shadow: none;
}
button.primary:not(:disabled):hover {
transform: translateY(-1px);
box-shadow: 0 16px 32px rgba(0, 80, 160, 0.28);
}
.error-message {
background: rgba(231, 76, 60, 0.08);
border: 1px solid rgba(231, 76, 60, 0.18);
color: #c0392b;
border-radius: 10px;
padding: 0.75rem 1rem;
}
.helper-text {
font-size: 0.9rem;
color: var(--muted-text-color);
}
.hidden {
display: none !important;
}
.session-details {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.95rem;
}
button.ghost {
border: 1px solid rgba(255, 255, 255, 0.55);
color: #ffffff;
background: transparent;
padding: 0.35rem 0.85rem;
border-radius: 999px;
font-size: 0.9rem;
}
button.ghost:hover {
border-color: #ffffff;
background: rgba(255, 255, 255, 0.12);
}
</style>
</head>
<body class="needs-auth">
<header>
<div class="header-bar">
<div class="brand">
<h1>EV Dash</h1>
<p>Monitor EV chargers in real time.</p>
</div>
<div class="session-panel">
<p class="status-indicator">
<span id="statusDot" class="status-dot connecting" aria-hidden="true"></span>
<span id="connectionStatus">Awaiting login…</span>
</p>
<div class="session-details">
<span id="sessionSummary">Please sign in.</span>
<button type="button" id="logoutButton" class="ghost hidden">Logout</button>
</div>
<a class="tool-button" href="help.html" aria-label="Open help">?</a>
</div>
</div>
</header>
<main>
<section class="card">
<h2>Chargers</h2>
<div class="table-wrapper">
<table class="chargers-table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Connected</th>
<th scope="col">Charging current</th>
<th scope="col">Charging allowed</th>
<th scope="col">Current power</th>
<th scope="col">Plugged in</th>
<th scope="col">Version</th>
<th scope="col">Session energy</th>
<th scope="col">Temperature</th>
<th scope="col">Charging phases</th>
</tr>
</thead>
<tbody id="chargerTableBody">
<tr id="chargerEmptyRow" class="empty-row">
<td colspan="11">No chargers loaded yet.</td>
</tr>
</tbody>
</table>
</div>
</section>
</main>
<section id="loginOverlay" class="login-view" aria-labelledby="loginTitle">
<div class="login-panel">
<h2 id="loginTitle">Sign in</h2>
<p>Authenticate with your nymea credentials to obtain a session token for the dashboard.</p>
<div id="loginError" class="error-message hidden" role="alert"></div>
<form id="loginForm" autocomplete="on">
<label for="username">Username
<input type="text" id="username" name="username" autocomplete="username" required placeholder="nymea user">
</label>
<label for="password">Password
<input type="password" id="password" name="password" autocomplete="current-password" required placeholder="••••••••">
</label>
<p class="helper-text">Tokens remain valid for one hour. Reloading the dashboard reuses an active session automatically.</p>
<button type="submit" id="loginButton" class="primary">Sign in</button>
</form>
</div>
</section>
<footer>
<span id="appVersion">nymea EV Dash</span> · &copy; 20132025 chargebyte GmbH. All rights reserved.
</footer>
<script src="app.js"></script>
</body>
</html>