125 lines
6.3 KiB
Plaintext
125 lines
6.3 KiB
Plaintext
<%- include('../partials/head', { title: 'Settings' }) %>
|
||
<%- include('../partials/adminNav', { currentPath: '/admin/settings' }) %>
|
||
<%- include('../partials/adminTopBar') %>
|
||
|
||
<div class="lg:pl-56 pt-14 lg:pt-0 min-h-screen">
|
||
<%- include('../partials/adminBanner') %>
|
||
<div class="max-w-2xl mx-auto px-4 sm:px-8 py-6 sm:py-8">
|
||
<div class="mb-8">
|
||
<h1 class="text-xl font-semibold text-white">Settings</h1>
|
||
<p class="text-sm text-gray-500 mt-0.5">Branding, appearance, and app configuration</p>
|
||
</div>
|
||
|
||
<% if (saved) { %>
|
||
<div class="mb-6 flex items-start gap-3 bg-green-500/10 border border-green-500/30 rounded-lg px-4 py-3">
|
||
<svg class="w-4 h-4 text-green-400 mt-0.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
</svg>
|
||
<p class="text-sm text-green-400">Settings saved.</p>
|
||
</div>
|
||
<% } %>
|
||
|
||
<!-- Branding -->
|
||
<form method="POST" action="/admin/settings" class="bg-surface-900 border border-gray-800 rounded-2xl p-6 mb-6 space-y-5">
|
||
<h2 class="text-sm font-semibold text-white">Branding</h2>
|
||
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-400 mb-1.5">App Name</label>
|
||
<input name="brand_name" type="text" value="<%= settings.brand_name %>"
|
||
class="w-full bg-surface-800 border border-gray-700 rounded-lg px-3.5 py-2.5 text-sm text-white focus:outline-none focus:border-accent transition-colors"
|
||
placeholder="StepView" />
|
||
<p class="text-xs text-gray-600 mt-1.5">Shown in the browser tab, viewer overlay, and admin sidebar.</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-400 mb-1.5">Tagline</label>
|
||
<input name="brand_tagline" type="text" value="<%= settings.brand_tagline %>"
|
||
class="w-full bg-surface-800 border border-gray-700 rounded-lg px-3.5 py-2.5 text-sm text-white focus:outline-none focus:border-accent transition-colors"
|
||
placeholder="3D Model Viewer" />
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-400 mb-1.5">Accent Color</label>
|
||
<div class="flex items-center gap-3">
|
||
<input type="color" name="brand_accent" value="<%= settings.brand_accent %>"
|
||
class="h-10 w-16 rounded-lg border border-gray-700 bg-surface-800 cursor-pointer p-1" />
|
||
<input type="text" id="accent-hex" value="<%= settings.brand_accent %>"
|
||
class="bg-surface-800 border border-gray-700 rounded-lg px-3.5 py-2.5 text-sm text-white font-mono w-32 focus:outline-none focus:border-accent transition-colors"
|
||
placeholder="#3b82f6" />
|
||
</div>
|
||
<p class="text-xs text-gray-600 mt-1.5">Used for buttons, highlights, and active nav items.</p>
|
||
</div>
|
||
|
||
<div class="pt-2">
|
||
<button type="submit" class="btn-accent text-white rounded-lg px-5 py-2 text-sm font-medium transition-all">
|
||
Save Branding
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- Logo -->
|
||
<div class="bg-surface-900 border border-gray-800 rounded-2xl p-6 mb-6">
|
||
<h2 class="text-sm font-semibold text-white mb-4">Logo</h2>
|
||
|
||
<% if (settings.brand_logo_path) { %>
|
||
<div class="flex items-center gap-4 mb-5 p-4 bg-surface-800 rounded-xl border border-gray-700">
|
||
<img src="/brand/logo" alt="Current logo" class="h-10 w-auto object-contain" />
|
||
<div class="flex-1">
|
||
<p class="text-sm text-gray-300">Logo uploaded</p>
|
||
<p class="text-xs text-gray-500 mt-0.5">PNG, SVG, or JPEG recommended</p>
|
||
</div>
|
||
<form method="POST" action="/admin/settings/logo/delete">
|
||
<button type="submit" class="text-xs text-gray-500 hover:text-red-400 transition-colors">Remove</button>
|
||
</form>
|
||
</div>
|
||
<% } else { %>
|
||
<p class="text-sm text-gray-500 mb-4">No logo uploaded. A default icon is shown.</p>
|
||
<% } %>
|
||
|
||
<form method="POST" action="/admin/settings/logo" enctype="multipart/form-data" class="flex items-end gap-3">
|
||
<div class="flex-1">
|
||
<label class="block text-xs font-medium text-gray-400 mb-1.5">Upload Logo</label>
|
||
<input type="file" name="logo" accept=".png,.jpg,.jpeg,.svg,.webp" required
|
||
class="w-full text-sm text-gray-400 file:mr-3 file:py-1.5 file:px-3 file:rounded-lg file:border-0 file:text-xs file:font-medium file:bg-surface-700 file:text-gray-300 hover:file:bg-surface-600 transition-colors" />
|
||
<p class="text-xs text-gray-600 mt-1">Max 2 MB. PNG or SVG recommended for best quality.</p>
|
||
</div>
|
||
<button type="submit" class="bg-surface-700 hover:bg-surface-600 border border-gray-700 text-sm text-gray-300 rounded-lg px-4 py-2 transition-colors shrink-0">
|
||
Upload
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Favicon -->
|
||
<div class="bg-surface-900 border border-gray-800 rounded-2xl p-6">
|
||
<h2 class="text-sm font-semibold text-white mb-4">Favicon</h2>
|
||
<form method="POST" action="/admin/settings/favicon" enctype="multipart/form-data" class="flex items-end gap-3">
|
||
<div class="flex-1">
|
||
<label class="block text-xs font-medium text-gray-400 mb-1.5">Upload Favicon</label>
|
||
<input type="file" name="favicon" accept=".ico,.png,.svg" required
|
||
class="w-full text-sm text-gray-400 file:mr-3 file:py-1.5 file:px-3 file:rounded-lg file:border-0 file:text-xs file:font-medium file:bg-surface-700 file:text-gray-300 hover:file:bg-surface-600 transition-colors" />
|
||
<p class="text-xs text-gray-600 mt-1">ICO or PNG, max 512 KB. Ideal size: 32×32 px.</p>
|
||
</div>
|
||
<button type="submit" class="bg-surface-700 hover:bg-surface-600 border border-gray-700 text-sm text-gray-300 rounded-lg px-4 py-2 transition-colors shrink-0">
|
||
Upload
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Keep color picker and hex input in sync
|
||
const colorInput = document.querySelector('input[type="color"]')
|
||
const hexInput = document.getElementById('accent-hex')
|
||
if (colorInput && hexInput) {
|
||
colorInput.addEventListener('input', () => { hexInput.value = colorInput.value })
|
||
hexInput.addEventListener('input', () => {
|
||
if (/^#[0-9a-fA-F]{6}$/.test(hexInput.value)) colorInput.value = hexInput.value
|
||
})
|
||
}
|
||
</script>
|
||
<script src="/admin.js"></script>
|
||
</body>
|
||
</html>
|