298 lines
11 KiB
HTML
298 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Wild Dragon - Settings</title>
|
|
<link rel="stylesheet" href="/css/common.css">
|
|
<style>
|
|
.settings-container {
|
|
max-width: 720px;
|
|
margin: 0 auto;
|
|
padding: var(--spacing-lg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-xl);
|
|
}
|
|
|
|
.settings-section {
|
|
background-color: var(--color-bg-tertiary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.settings-section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
padding: var(--spacing-lg);
|
|
border-bottom: 1px solid var(--color-border);
|
|
background-color: var(--color-bg-secondary);
|
|
}
|
|
|
|
.settings-section-icon {
|
|
font-size: 1.4rem;
|
|
}
|
|
|
|
.settings-section-title {
|
|
font-size: 1.1rem;
|
|
font-weight: 700;
|
|
color: var(--color-text-primary);
|
|
margin: 0;
|
|
}
|
|
|
|
.settings-section-subtitle {
|
|
font-size: 0.85rem;
|
|
color: var(--color-text-tertiary);
|
|
margin: 0;
|
|
}
|
|
|
|
.settings-body {
|
|
padding: var(--spacing-lg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.settings-row {
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.settings-row .form-group {
|
|
flex: 1;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.settings-actions {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
align-items: center;
|
|
padding-top: var(--spacing-md);
|
|
border-top: 1px solid var(--color-border);
|
|
}
|
|
|
|
.status-msg {
|
|
font-size: 0.9rem;
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-radius: var(--radius-md);
|
|
display: none;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.status-msg.success {
|
|
display: flex;
|
|
background-color: rgba(16, 185, 129, 0.15);
|
|
color: var(--color-success);
|
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
|
}
|
|
|
|
.status-msg.error {
|
|
display: flex;
|
|
background-color: rgba(239, 68, 68, 0.15);
|
|
color: var(--color-danger);
|
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
}
|
|
|
|
.status-msg.loading {
|
|
display: flex;
|
|
background-color: rgba(59, 130, 246, 0.15);
|
|
color: var(--color-info);
|
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
}
|
|
|
|
.token-hint {
|
|
font-size: 0.82rem;
|
|
color: var(--color-text-tertiary);
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
|
|
.token-set-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 0.8rem;
|
|
color: var(--color-success);
|
|
background-color: rgba(16, 185, 129, 0.12);
|
|
border: 1px solid rgba(16, 185, 129, 0.25);
|
|
border-radius: 12px;
|
|
padding: 2px 8px;
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-container">
|
|
<!-- Header -->
|
|
<header class="header">
|
|
<div class="header-logo">
|
|
<div class="header-logo-icon">D</div>
|
|
<span>WILD DRAGON</span>
|
|
</div>
|
|
<nav class="header-nav">
|
|
<div class="nav-item" data-page="assets">Assets</div>
|
|
<div class="nav-item" data-page="capture">Capture</div>
|
|
<div class="nav-item" data-page="upload">Upload</div>
|
|
<div class="nav-item" data-page="recorders">Recorders</div>
|
|
<div class="nav-item active" data-page="settings">Settings</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-content">
|
|
<div class="content-area">
|
|
<div class="content-main">
|
|
<div class="settings-container">
|
|
|
|
<h1 style="margin-bottom: 0;">Settings</h1>
|
|
|
|
<!-- AMPP Integration Section -->
|
|
<div class="settings-section">
|
|
<div class="settings-section-header">
|
|
<div class="settings-section-icon">📡</div>
|
|
<div>
|
|
<h3 class="settings-section-title">AMPP FramelightX</h3>
|
|
<p class="settings-section-subtitle">Dragon-Wind will pre-create folder paths in AMPP automatically on each upload.</p>
|
|
</div>
|
|
</div>
|
|
<div class="settings-body">
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="amppBaseUrl">AMPP Base URL</label>
|
|
<input
|
|
type="url"
|
|
id="amppBaseUrl"
|
|
class="form-input"
|
|
placeholder="https://ampp.yourdomain.net"
|
|
>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="amppToken">API Token</label>
|
|
<input
|
|
type="password"
|
|
id="amppToken"
|
|
class="form-input"
|
|
placeholder="Paste token — leave blank to keep existing"
|
|
>
|
|
<div id="tokenSetBadge" class="token-set-badge" style="display: none;">✓ Token saved</div>
|
|
<div class="token-hint">Token is stored securely and never returned to the UI.</div>
|
|
</div>
|
|
|
|
<div id="amppStatus" class="status-msg"></div>
|
|
|
|
<div class="settings-actions">
|
|
<button class="btn btn-primary" onclick="saveAmpp()">Save</button>
|
|
<button class="btn btn-secondary" onclick="testAmpp()">Test Connection</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Bar -->
|
|
<footer class="status-bar">
|
|
<div class="status-item">
|
|
<span class="status-indicator" id="statusIndicator"></span>
|
|
<span id="statusText">Settings</span>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
const API = '/api/v1';
|
|
|
|
// ── Navigation ──────────────────────────────────────────────
|
|
document.querySelectorAll('[data-page]').forEach(el => {
|
|
el.addEventListener('click', () => {
|
|
const page = el.dataset.page;
|
|
if (page === 'assets') window.location.href = '/index.html';
|
|
if (page === 'capture') window.location.href = '/capture.html';
|
|
if (page === 'upload') window.location.href = '/upload.html';
|
|
if (page === 'recorders') window.location.href = '/recorders.html';
|
|
});
|
|
});
|
|
|
|
// ── Init ─────────────────────────────────────────────────────
|
|
document.addEventListener('DOMContentLoaded', loadAmppSettings);
|
|
|
|
async function loadAmppSettings() {
|
|
try {
|
|
const res = await fetch(`${API}/settings/ampp`);
|
|
if (!res.ok) return;
|
|
const data = await res.json();
|
|
if (data.ampp_base_url) {
|
|
document.getElementById('amppBaseUrl').value = data.ampp_base_url;
|
|
}
|
|
if (data.ampp_token_exists) {
|
|
document.getElementById('tokenSetBadge').style.display = 'inline-flex';
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to load AMPP settings:', err);
|
|
}
|
|
}
|
|
|
|
// ── Save ──────────────────────────────────────────────────────
|
|
async function saveAmpp() {
|
|
const baseUrl = document.getElementById('amppBaseUrl').value.trim();
|
|
const token = document.getElementById('amppToken').value.trim();
|
|
|
|
if (!baseUrl) {
|
|
showStatus('amppStatus', 'error', 'Base URL is required.');
|
|
return;
|
|
}
|
|
|
|
showStatus('amppStatus', 'loading', 'Saving…');
|
|
|
|
try {
|
|
const body = { ampp_base_url: baseUrl };
|
|
if (token) body.ampp_token = token;
|
|
|
|
const res = await fetch(`${API}/settings/ampp`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.error || 'Save failed');
|
|
|
|
if (token) {
|
|
document.getElementById('amppToken').value = '';
|
|
document.getElementById('tokenSetBadge').style.display = 'inline-flex';
|
|
}
|
|
|
|
showStatus('amppStatus', 'success', '✓ Settings saved.');
|
|
} catch (err) {
|
|
showStatus('amppStatus', 'error', err.message);
|
|
}
|
|
}
|
|
|
|
// ── Test ──────────────────────────────────────────────────────
|
|
async function testAmpp() {
|
|
showStatus('amppStatus', 'loading', 'Testing connection…');
|
|
try {
|
|
const res = await fetch(`${API}/settings/ampp/test`, { method: 'POST' });
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.error || 'Test failed');
|
|
showStatus('amppStatus', 'success', '✓ ' + data.message);
|
|
} catch (err) {
|
|
showStatus('amppStatus', 'error', err.message);
|
|
}
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────
|
|
function showStatus(id, type, msg) {
|
|
const el = document.getElementById(id);
|
|
el.className = 'status-msg ' + type;
|
|
el.textContent = msg;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|