Printio/templates/about.html
2025-10-27 22:24:25 -07:00

358 lines
11 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>About Printio</title>
<link rel="icon" type="image/x-icon" href="/static/images/favicon.ico" />
<link rel="stylesheet" href="/static/css/styles.css" />
<link rel="stylesheet" href="/static/fontawesome/css/all.min.css" />
<style>
:root{
--brand: #1e40af; /* blue-800 */
--brand-hover: #15327f;
--ok: #16a34a; /* green-600 */
--ok-hover: #118039;
--border: #d1d5db; /* gray-300 */
--text: #111827; /* gray-900 */
--muted: #4b5563; /* gray-600 */
--bg: #ffffff;
--bg-alt: #f9fafb; /* gray-50 */
--focus: #2563eb; /* blue-600 */
}
@media (prefers-color-scheme: dark){
:root{
--brand: #60a5fa;
--brand-hover: #3b82f6;
--ok: #34d399;
--ok-hover: #10b981;
--border: #374151;
--text: #e5e7eb;
--muted: #9ca3af;
--bg: #0b0f14;
--bg-alt: #111827;
--focus: #60a5fa;
}
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body{
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color: var(--text);
background: var(--bg);
line-height: 1.55;
}
#navbar { position: sticky; top: 0; z-index: 20; }
h1 { margin: 0 0 16px; font-size: clamp(1.5rem, 2vw, 2rem); }
h3 { margin: 0 0 12px; font-size: 1.125rem; }
p { margin: 0 0 12px; }
a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--brand-hover); }
.content-wrapper{
display: grid;
grid-template-columns: 1fr;
gap: 20px;
max-width: 1100px;
margin: 0 auto;
padding: 16px;
}
@media (min-width: 840px){
.content-wrapper{
grid-template-columns: 300px 1fr;
gap: 24px;
padding: 24px;
}
.left-column{
position: sticky;
top: 72px; /* keep below navbar */
align-self: start;
}
}
.left-column, .right-column { width: 100%; }
.card{
padding: 16px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--bg-alt);
}
.info-box{ margin-bottom: 16px; }
.kv p{
display: flex;
justify-content: space-between;
gap: 12px;
border-bottom: 1px dashed var(--border);
padding: 6px 0;
margin: 0;
font-size: 0.95rem;
}
.kv p span:first-child{ color: var(--muted); }
.kv p span:last-child{ font-variant-numeric: tabular-nums; }
.license-section{
margin-top: 20px;
text-align: left;
}
.license-status{ font-size: 1rem; margin: 0 0 10px; }
.field{
display: grid;
grid-template-columns: 140px 1fr auto;
gap: 10px;
align-items: center;
margin: 10px 0;
}
@media (max-width: 520px){
.field{ grid-template-columns: 1fr; }
.field label{ margin-bottom: -4px; }
}
input[type="text"], input[type="password"]{
padding: 10px 12px;
font-size: 1rem;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--bg);
color: var(--text);
}
input[readonly]{
background: rgba(0,0,0,0.03);
}
button{
appearance: none;
border: none;
border-radius: 8px;
padding: 10px 16px;
font-size: 1rem;
cursor: pointer;
color: #fff;
background: var(--brand);
}
button:hover{ background: var(--brand-hover); }
button:focus-visible{
outline: 3px solid var(--focus);
outline-offset: 2px;
}
.btn-ok{
background: var(--ok);
}
.btn-ok:hover{
background: var(--ok-hover);
}
.actions{
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.qr{
width: 140px;
max-width: 40vw;
height: auto;
border-radius: 8px;
border: 1px solid var(--border);
background: #fff;
}
.muted{ color: var(--muted); font-size: 0.95rem; }
</style>
<script src="/static/js/crypto-js.min.js" defer></script>
</head>
<body>
<div id="navbar" role="navigation" aria-label="Primary"></div>
<script>
// Load navbar with basic error handling
fetch('/static/html/nav.html')
.then(r => r.ok ? r.text() : Promise.reject(r.status))
.then(html => { document.getElementById('navbar').innerHTML = html; })
.catch(() => { document.getElementById('navbar').innerHTML = '<div class="card" role="alert">Navigation failed to load.</div>'; });
</script>
<main class="content-wrapper">
<!-- Sidebar -->
<aside class="left-column" aria-label="System and License Information">
<section class="card info-box" aria-labelledby="sysinfo-heading">
<h3 id="sysinfo-heading">System Info</h3>
<div class="kv">
<p><span>Version</span><span>{{info.software_version}}</span></p>
<p><span>CPU %</span><span>{{info.cpu}}</span></p>
<p><span>CPU Temp</span><span>{{info.cpu_t}}</span></p>
<p><span>Disk Size</span><span>{{info.disk_size}}</span></p>
<p><span>Disk Used</span><span>{{info.disk_used}}</span></p>
<p><span>RAM Size</span><span>{{info.ram_size}}</span></p>
<p><span>RAM Used</span><span>{{info.ram_used}}</span></p>
<p><span>Up Time</span><span>{{info.uptime}}</span></p>
</div>
</section>
<section class="card" aria-labelledby="licenseinfo-heading">
<h3 id="licenseinfo-heading">License Info</h3>
<p class="muted">Status: <strong>{{info.license}}</strong></p>
<!-- Optional fields kept commented
<p>ImageMagick: {{info.image_magic}}</p>
<p>Drop Folder: {{info.drop_folder}}</p>
-->
</section>
</aside>
<!-- Main -->
<section class="right-column">
<h1>About Printio</h1>
<p>
Printio is a professional printing solution designed for seamless printing from iPads. Its widely used in the photobooth industry and supports a range of dye-sublimation printers. Printio works with well-known brands such as DNP, Sinfonia, HiTi, and Mitsubishi, as well as many other printers, including Zebra label printers and most HP and Epson models—making it a versatile choice for diverse printing needs.
</p>
<p class="muted">
Warning: Unauthorized reproduction or distribution of this product is strictly prohibited and may result in civil and criminal penalties.
</p>
<p>
<a href="https://www.ataphotobooths.com/knowledge-base/category/printio-setup/" target="_blank" rel="noopener noreferrer">
Printio Help: Click me!
</a>
</p>
<figure aria-label="Printio Help QR Code" style="margin: 16px 0;">
<img class="qr" src="/static/images/printio_help_qr.jpg" alt="Scan for Printio setup help" />
</figure>
<!-- License Section (conditionally hidden) -->
<section class="license-section card" {{info.hidden}} aria-labelledby="license-activation-heading">
<h3 id="license-activation-heading">License Activation</h3>
<p class="license-status muted" id="license-status" aria-live="polite"></p>
<div class="field">
<label for="idcode">ID Code</label>
<input type="text" id="idcode" value="{{info.idcode}}" readonly aria-readonly="true" />
<div class="actions">
<button type="button" id="copy-btn" class="btn-ok" aria-describedby="copy-hint">
<i class="fa fa-copy" aria-hidden="true"></i> Copy
</button>
</div>
</div>
<p class="muted" id="copy-hint">Copies your ID Code to the clipboard.</p>
<div class="field">
<label for="license-password">Activation Key</label>
<input type="password" id="license-password" placeholder="Enter Activation Key" autocomplete="one-time-code" />
<div class="actions">
<button type="button" id="activate-btn">
<i class="fa fa-bolt" aria-hidden="true"></i> Activate
</button>
</div>
</div>
</section>
</section>
</main>
<script>
// Optional: simple (placeholder) hashing before sending; replace with real logic if needed.
function hashPass(plain) {
try {
// Example using CryptoJS if you choose to enable hashing later:
// return CryptoJS.SHA256(plain).toString();
return plain; // currently pass-through; replace when backend expects a hash
} catch {
return plain;
}
}
function setStatus(msg, ok = true){
const el = document.getElementById('license-status');
if (!el) return;
el.textContent = msg || '';
el.style.color = ok ? 'inherit' : '#dc2626'; // red-600 in light; acceptable in dark
}
async function activateLicense() {
const input = document.getElementById('license-password');
const pass = (input?.value || '').trim();
if (!pass){
setStatus('Please enter your activation key.', false);
input?.focus();
return;
}
setStatus('Activating…');
try {
const res = await fetch('/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pass: hashPass(pass) })
});
if (!res.ok) throw new Error('Network error');
const data = await res.json();
if (data.reply === true) {
alert('Activation successful!');
window.location.href = '/about';
} else {
setStatus('Activation failed. Please verify your key and try again.', false);
alert('Activation failed. Try again.');
}
} catch (err) {
console.error('Activation error:', err);
setStatus('Error during activation attempt.', false);
alert('Error during activation attempt.');
}
}
async function copyToClipboard() {
const el = document.getElementById('idcode');
const btn = document.getElementById('copy-btn');
if (!el) return alert('No ID Code field found.');
const text = el.value || el.textContent || '';
if (!text) return alert('Nothing to copy.');
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
} else {
// Fallback
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
btn?.classList.add('copied');
btn?.setAttribute('aria-label', 'Copied!');
setStatus('ID Code copied to clipboard.');
setTimeout(() => {
btn?.removeAttribute('aria-label');
setStatus('');
}, 1500);
} catch (e) {
console.error('Clipboard error:', e);
alert('Failed to copy to clipboard.');
}
}
// Wire up buttons
document.getElementById('copy-btn')?.addEventListener('click', copyToClipboard);
document.getElementById('activate-btn')?.addEventListener('click', activateLicense);
// Allow Enter key to activate from password field
document.getElementById('license-password')?.addEventListener('keydown', (e) => {
if (e.key === 'Enter') activateLicense();
});
</script>
</body>
</html>