mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-03-26 19:03:08 +00:00
299 lines
8.4 KiB
Text
299 lines
8.4 KiB
Text
---
|
|
import Layout from '../layouts/Layout.astro';
|
|
import { API_BASE as apiBase } from '../lib/api';
|
|
|
|
function isEnabled(v: string | undefined): boolean {
|
|
if (!v) return false;
|
|
const n = v.trim().toLowerCase();
|
|
return n === '1' || n === 'true' || n === 'yes' || n === 'on';
|
|
}
|
|
|
|
const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_SITE);
|
|
---
|
|
|
|
<Layout title="Settings">
|
|
<section class="settings-page">
|
|
<h1>Settings</h1>
|
|
|
|
<div id="settings-content">
|
|
<p class="loading">Loading...</p>
|
|
</div>
|
|
</section>
|
|
</Layout>
|
|
|
|
<script define:vars={{ apiBase, publicDemoSite }}>
|
|
import { getAllThemes, loadSavedTheme, saveTheme } from '../lib/themes';
|
|
|
|
const token = localStorage.getItem('token');
|
|
|
|
if (!token) {
|
|
window.location.href = '/login';
|
|
}
|
|
|
|
async function loadSettings() {
|
|
const container = document.getElementById('settings-content');
|
|
if (!container) return;
|
|
|
|
try {
|
|
const res = await fetch(`${apiBase}/api/auth/me`, {
|
|
headers: { 'Authorization': `Bearer ${token}` },
|
|
});
|
|
|
|
if (res.status === 401) {
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
|
|
if (!res.ok) {
|
|
throw new Error('Failed to load settings');
|
|
}
|
|
|
|
const user = await res.json();
|
|
|
|
let appearanceHtml = '';
|
|
if (publicDemoSite) {
|
|
appearanceHtml = `
|
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
|
<h2>Appearance</h2>
|
|
<p class="hint">On the public demo, appearance is managed by the instance administrator.</p>
|
|
</div>
|
|
`;
|
|
} else {
|
|
const currentTheme = loadSavedTheme();
|
|
const themeOptions = getAllThemes()
|
|
.map((t) => {
|
|
const selected = t.id === currentTheme ? 'selected' : '';
|
|
return `<option value="${t.id}" ${selected}>${t.name}</option>`;
|
|
})
|
|
.join('');
|
|
|
|
appearanceHtml = `
|
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
|
<h2>Appearance</h2>
|
|
|
|
<div class="form-group">
|
|
<label for="theme-select">Theme</label>
|
|
<select id="theme-select" class="theme-select">
|
|
${themeOptions}
|
|
</select>
|
|
<p class="hint">Choose a visual theme for the interface</p>
|
|
</div>
|
|
|
|
<div id="theme-preview" class="theme-preview">
|
|
<div class="preview-colors">
|
|
<span class="preview-swatch preview-bg" title="Background"></span>
|
|
<span class="preview-swatch preview-surface" title="Surface"></span>
|
|
<span class="preview-swatch preview-primary" title="Primary"></span>
|
|
<span class="preview-swatch preview-success" title="Success"></span>
|
|
<span class="preview-swatch preview-error" title="Error"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
container.innerHTML = `
|
|
${appearanceHtml}
|
|
|
|
<form id="profile-form" class="settings-form">
|
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
|
<h2>Profile</h2>
|
|
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" value="${user.username}" disabled />
|
|
<p class="hint">Username cannot be changed</p>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="display_name">Display Name</label>
|
|
<input type="text" id="display_name" value="${user.display_name || ''}" placeholder="Enter display name" />
|
|
</div>
|
|
|
|
<button type="submit" class="ui-btn ui-btn-primary settings-save-btn">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="form-section ui-card ui-card-pad-lg danger-zone">
|
|
<h2>Account</h2>
|
|
<p class="hint">Logged in as @${user.username}</p>
|
|
<button id="logout-btn" class="ui-btn ui-btn-danger settings-logout-btn" type="button">Logout</button>
|
|
</div>
|
|
`;
|
|
|
|
if (!publicDemoSite) {
|
|
setupThemeSwitcher();
|
|
}
|
|
|
|
document.getElementById('profile-form')?.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const displayName = (document.getElementById('display_name')).value;
|
|
|
|
const btn = e.target.querySelector('button[type="submit"]');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Saving...';
|
|
|
|
try {
|
|
const res = await fetch(`${apiBase}/api/users/me/profile`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${token}`,
|
|
},
|
|
body: JSON.stringify({ display_name: displayName || null }),
|
|
});
|
|
|
|
if (res.status === 401) {
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
|
|
if (res.ok) {
|
|
btn.textContent = 'Saved!';
|
|
setTimeout(() => { btn.textContent = 'Save Changes'; btn.disabled = false; }, 2000);
|
|
} else {
|
|
throw new Error('Failed to save');
|
|
}
|
|
} catch (e) {
|
|
btn.textContent = 'Error';
|
|
btn.disabled = false;
|
|
}
|
|
});
|
|
|
|
document.getElementById('logout-btn')?.addEventListener('click', () => {
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('user');
|
|
window.location.href = '/';
|
|
});
|
|
|
|
} catch (error) {
|
|
container.innerHTML = '<div class="error">Failed to load settings</div>';
|
|
}
|
|
}
|
|
|
|
function setupThemeSwitcher() {
|
|
if (publicDemoSite) return;
|
|
|
|
function updatePreview() {
|
|
const previewBg = document.querySelector('.preview-bg');
|
|
const previewSurface = document.querySelector('.preview-surface');
|
|
const previewPrimary = document.querySelector('.preview-primary');
|
|
const previewSuccess = document.querySelector('.preview-success');
|
|
const previewError = document.querySelector('.preview-error');
|
|
|
|
if (previewBg) previewBg.style.background = getComputedStyle(document.documentElement).getPropertyValue('--color-bg');
|
|
if (previewSurface) previewSurface.style.background = getComputedStyle(document.documentElement).getPropertyValue('--color-surface');
|
|
if (previewPrimary) previewPrimary.style.background = getComputedStyle(document.documentElement).getPropertyValue('--color-primary');
|
|
if (previewSuccess) previewSuccess.style.background = getComputedStyle(document.documentElement).getPropertyValue('--color-success');
|
|
if (previewError) previewError.style.background = getComputedStyle(document.documentElement).getPropertyValue('--color-error');
|
|
}
|
|
|
|
const select = document.getElementById('theme-select');
|
|
if (select) {
|
|
select.addEventListener('change', (e) => {
|
|
const themeId = (e.target).value;
|
|
saveTheme(themeId);
|
|
window.location.reload();
|
|
});
|
|
}
|
|
|
|
updatePreview();
|
|
}
|
|
|
|
loadSettings();
|
|
</script>
|
|
|
|
<style>
|
|
.settings-page {
|
|
padding: 2rem 0;
|
|
max-width: 500px;
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
--ui-form-group-mb: 1.25rem;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.settings-page {
|
|
padding: 1.5rem 0;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 1.75rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
}
|
|
|
|
.settings-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2rem;
|
|
}
|
|
|
|
.form-section {
|
|
border-radius: 12px;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.form-section h2 {
|
|
font-size: 1.125rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.hint {
|
|
font-size: 0.75rem;
|
|
color: var(--color-text-muted);
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.settings-save-btn {
|
|
width: 100%;
|
|
}
|
|
|
|
.danger-zone {
|
|
border-color: var(--color-error);
|
|
}
|
|
|
|
.settings-logout-btn {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.loading, .error {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.theme-select {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.theme-preview {
|
|
margin-top: 1rem;
|
|
padding: 0.75rem;
|
|
background: var(--color-bg);
|
|
border-radius: 8px;
|
|
border: 1px solid var(--color-border);
|
|
}
|
|
|
|
.preview-colors {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.preview-swatch {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 6px;
|
|
border: 1px solid var(--color-border);
|
|
transition: transform 0.15s ease;
|
|
}
|
|
|
|
.preview-swatch:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
</style>
|