Skip to content

Commit 5c15cf3

Browse files
authored
Merge pull request #75 from SenaxInc/copilot/update-config-generator-styling
Modernize Config Generator UI to match Dashboard/Viewer styling
2 parents ca9de93 + f9c1805 commit 5c15cf3

File tree

1 file changed

+238
-24
lines changed

1 file changed

+238
-24
lines changed

TankAlarm-112025-Server-BluesOpta/TankAlarm-112025-Server-BluesOpta.ino

Lines changed: 238 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -241,33 +241,229 @@ static const char CONFIG_GENERATOR_HTML[] PROGMEM = R"HTML(
241241
<meta name="viewport" content="width=device-width, initial-scale=1">
242242
<title>Config Generator</title>
243243
<style>
244-
:root { color-scheme: light dark; font-family: "Segoe UI", Arial, sans-serif; }
245-
body { margin: 0; background: #f4f6f8; color: #1f2933; }
246-
header { padding: 16px 24px; background: #1d3557; color: #fff; box-shadow: 0 2px 6px rgba(0,0,0,0.2); display: flex; justify-content: space-between; align-items: center; }
247-
header h1 { margin: 0; font-size: 1.6rem; }
248-
header a { color: #fff; text-decoration: none; font-size: 0.95rem; }
249-
main { padding: 20px; max-width: 800px; margin: 0 auto; }
250-
.card { background: #fff; border-radius: 12px; box-shadow: 0 10px 30px rgba(15,23,42,0.08); padding: 20px; }
251-
h2 { margin-top: 0; font-size: 1.3rem; }
252-
h3 { margin: 20px 0 10px; font-size: 1.1rem; border-bottom: 1px solid #e2e8f0; padding-bottom: 6px; }
253-
.field { display: flex; flex-direction: column; margin-bottom: 12px; }
254-
.field span { font-size: 0.9rem; color: #475569; margin-bottom: 4px; }
255-
.field input, .field select { padding: 8px 10px; border-radius: 6px; border: 1px solid #cbd5f5; font-size: 0.95rem; }
256-
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
257-
.sensor-card { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; margin-bottom: 16px; position: relative; }
258-
.sensor-header { display: flex; justify-content: space-between; margin-bottom: 12px; }
259-
.sensor-title { font-weight: 600; color: #334155; }
260-
.remove-btn { color: #ef4444; cursor: pointer; font-size: 0.9rem; border: none; background: none; padding: 0; }
261-
.actions { margin-top: 24px; display: flex; gap: 12px; }
262-
button { border: none; border-radius: 6px; padding: 10px 16px; font-size: 0.95rem; cursor: pointer; background: #1d4ed8; color: #fff; }
263-
button.secondary { background: #64748b; }
264-
button:hover { opacity: 0.9; }
244+
:root {
245+
font-family: "Segoe UI", Arial, sans-serif;
246+
color-scheme: light dark;
247+
}
248+
* {
249+
box-sizing: border-box;
250+
}
251+
body {
252+
margin: 0;
253+
min-height: 100vh;
254+
background: var(--bg);
255+
color: var(--text);
256+
transition: background 0.2s ease, color 0.2s ease;
257+
}
258+
body[data-theme="light"] {
259+
--bg: #f8fafc;
260+
--surface: #ffffff;
261+
--text: #1f2933;
262+
--muted: #475569;
263+
--header-bg: #e2e8f0;
264+
--card-border: rgba(15,23,42,0.08);
265+
--card-shadow: rgba(15,23,42,0.08);
266+
--accent: #2563eb;
267+
--accent-strong: #1d4ed8;
268+
--accent-contrast: #f8fafc;
269+
--chip: #f8fafc;
270+
--input-border: #cbd5e1;
271+
--danger: #ef4444;
272+
--pill-bg: rgba(37,99,235,0.12);
273+
}
274+
body[data-theme="dark"] {
275+
--bg: #0f172a;
276+
--surface: #1e293b;
277+
--text: #e2e8f0;
278+
--muted: #94a3b8;
279+
--header-bg: #16213d;
280+
--card-border: rgba(15,23,42,0.55);
281+
--card-shadow: rgba(0,0,0,0.55);
282+
--accent: #38bdf8;
283+
--accent-strong: #22d3ee;
284+
--accent-contrast: #0f172a;
285+
--chip: rgba(148,163,184,0.15);
286+
--input-border: rgba(148,163,184,0.4);
287+
--danger: #f87171;
288+
--pill-bg: rgba(56,189,248,0.18);
289+
}
290+
header {
291+
background: var(--header-bg);
292+
padding: 28px 24px;
293+
box-shadow: 0 20px 45px var(--card-shadow);
294+
}
295+
header .bar {
296+
display: flex;
297+
justify-content: space-between;
298+
gap: 16px;
299+
flex-wrap: wrap;
300+
align-items: flex-start;
301+
}
302+
header h1 {
303+
margin: 0;
304+
font-size: 1.9rem;
305+
}
306+
header p {
307+
margin: 8px 0 0;
308+
color: var(--muted);
309+
max-width: 640px;
310+
line-height: 1.4;
311+
}
312+
.header-actions {
313+
display: flex;
314+
gap: 12px;
315+
flex-wrap: wrap;
316+
align-items: center;
317+
}
318+
.pill {
319+
border-radius: 999px;
320+
padding: 10px 20px;
321+
text-decoration: none;
322+
font-weight: 600;
323+
background: var(--pill-bg);
324+
color: var(--accent);
325+
border: 1px solid transparent;
326+
transition: transform 0.15s ease;
327+
}
328+
.pill:hover {
329+
transform: translateY(-1px);
330+
}
331+
.icon-button {
332+
width: 42px;
333+
height: 42px;
334+
border-radius: 50%;
335+
border: 1px solid var(--card-border);
336+
background: var(--surface);
337+
color: var(--text);
338+
font-size: 1.2rem;
339+
cursor: pointer;
340+
transition: transform 0.15s ease;
341+
}
342+
.icon-button:hover {
343+
transform: translateY(-1px);
344+
}
345+
main {
346+
padding: 24px;
347+
max-width: 1000px;
348+
margin: 0 auto;
349+
width: 100%;
350+
}
351+
.card {
352+
background: var(--surface);
353+
border-radius: 24px;
354+
border: 1px solid var(--card-border);
355+
padding: 20px;
356+
box-shadow: 0 25px 55px var(--card-shadow);
357+
}
358+
h2 {
359+
margin-top: 0;
360+
font-size: 1.3rem;
361+
}
362+
h3 {
363+
margin: 20px 0 10px;
364+
font-size: 1.1rem;
365+
border-bottom: 1px solid var(--card-border);
366+
padding-bottom: 6px;
367+
color: var(--text);
368+
}
369+
.field {
370+
display: flex;
371+
flex-direction: column;
372+
margin-bottom: 12px;
373+
}
374+
.field span {
375+
font-size: 0.9rem;
376+
color: var(--muted);
377+
margin-bottom: 4px;
378+
}
379+
.field input, .field select {
380+
padding: 10px 12px;
381+
border-radius: 8px;
382+
border: 1px solid var(--input-border);
383+
font-size: 0.95rem;
384+
background: var(--bg);
385+
color: var(--text);
386+
}
387+
.form-grid {
388+
display: grid;
389+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
390+
gap: 12px;
391+
}
392+
.sensor-card {
393+
background: var(--chip);
394+
border: 1px solid var(--card-border);
395+
border-radius: 12px;
396+
padding: 16px;
397+
margin-bottom: 16px;
398+
position: relative;
399+
}
400+
.sensor-header {
401+
display: flex;
402+
justify-content: space-between;
403+
margin-bottom: 12px;
404+
}
405+
.sensor-title {
406+
font-weight: 600;
407+
color: var(--text);
408+
}
409+
.remove-btn {
410+
color: var(--danger);
411+
cursor: pointer;
412+
font-size: 0.9rem;
413+
border: none;
414+
background: none;
415+
padding: 0;
416+
font-weight: 600;
417+
}
418+
.remove-btn:hover {
419+
opacity: 0.8;
420+
}
421+
.actions {
422+
margin-top: 24px;
423+
display: flex;
424+
gap: 12px;
425+
flex-wrap: wrap;
426+
}
427+
button {
428+
border: none;
429+
border-radius: 10px;
430+
padding: 10px 16px;
431+
font-size: 0.95rem;
432+
font-weight: 600;
433+
cursor: pointer;
434+
background: var(--accent);
435+
color: var(--accent-contrast);
436+
transition: transform 0.15s ease;
437+
}
438+
button.secondary {
439+
background: transparent;
440+
border: 1px solid var(--card-border);
441+
color: var(--text);
442+
}
443+
button:hover {
444+
transform: translateY(-1px);
445+
}
446+
button:disabled {
447+
opacity: 0.5;
448+
cursor: not-allowed;
449+
transform: none;
450+
}
265451
</style>
266452
</head>
267-
<body>
453+
<body data-theme="light">
268454
<header>
269-
<h1>Config Generator</h1>
270-
<a href="/">&larr; Back to Dashboard</a>
455+
<div class="bar">
456+
<div>
457+
<h1>Config Generator</h1>
458+
<p>
459+
Create new client configurations with sensor definitions and upload settings for Tank Alarm field units.
460+
</p>
461+
</div>
462+
<div class="header-actions">
463+
<button class="icon-button" id="themeToggle" aria-label="Switch to dark mode">&#9789;</button>
464+
<a class="pill" href="/">&larr; Back to Dashboard</a>
465+
</div>
466+
</div>
271467
</header>
272468
<main>
273469
<div class="card">
@@ -294,6 +490,24 @@ static const char CONFIG_GENERATOR_HTML[] PROGMEM = R"HTML(
294490
</div>
295491
</main>
296492
<script>
493+
// Theme support
494+
const THEME_KEY = 'tankalarmTheme';
495+
const themeToggle = document.getElementById('themeToggle');
496+
497+
function applyTheme(next) {
498+
const theme = next === 'dark' ? 'dark' : 'light';
499+
document.body.dataset.theme = theme;
500+
themeToggle.textContent = theme === 'dark' ? '☀' : '☾';
501+
themeToggle.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
502+
localStorage.setItem(THEME_KEY, theme);
503+
}
504+
505+
applyTheme(localStorage.getItem(THEME_KEY) || 'light');
506+
themeToggle.addEventListener('click', () => {
507+
const next = document.body.dataset.theme === 'dark' ? 'light' : 'dark';
508+
applyTheme(next);
509+
});
510+
297511
const sensorTypes = [
298512
{ value: 0, label: 'Digital Input' },
299513
{ value: 1, label: 'Analog Input (0-10V)' },

0 commit comments

Comments
 (0)