Illustrations/web/templates/settings/home.html

235 lines
8.6 KiB
HTML
Raw 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.

{% extends "base.html" %}
{% load static %}
{# Leave body_class empty so Classic gradient is controlled by base.html logic #}
{% block body_class %}{% endblock %}
{% block content %}
<div class="container">
<h1 class="page-title">Settings</h1>
<!-- Intro / placeholder -->
<div class="card" style="padding:20px; margin-bottom:20px;">
<p class="muted small">Add your settings here when youre ready.</p>
</div>
<!-- Privacy: Clear History -->
<div class="card" style="margin-top:18px; padding:20px;">
<h2 class="page-title">Privacy · Clear My History</h2>
<p class="muted" style="margin:-6px 0 12px;">
Remove your Recent Searches and Recently Viewed illustrations from the Search page.
</p>
<button id="clear-history-btn" class="btn btn-danger">Clear History</button>
</div>
<!-- Theme chooser -->
<div class="card" style="padding:20px; margin-top:18px;">
<h2 class="page-title">Appearance · Theme</h2>
<form method="post" action="{% url 'set_theme' %}" style="margin-top:10px;">
{% csrf_token %}
<label for="theme" class="small muted" style="display:block; margin-bottom:6px;">Choose site theme</label>
<select id="theme" name="theme" class="form-control" style="max-width: 320px;">
{% for t in available_themes %}
<option value="{{ t }}" {% if request.session.theme|default:'classic' == t %}selected{% endif %}>
{{ t|capfirst }}
</option>
{% endfor %}
</select>
<button class="btn btn-primary" style="margin-top:10px;">Save</button>
<small class="text-muted" style="display:block;margin-top:6px;">Changes apply immediately across the site.</small>
</form>
</div>
<!-- Highlight search hits -->
<div class="card" style="padding:20px; margin-top:18px;">
<h2 class="page-title">Reading · Highlight Search Hits</h2>
<label style="display:flex;gap:10px;align-items:center; margin-top:8px;">
<input id="highlightHitsToggle" type="checkbox">
<span>Highlight search hits on entry page</span>
</label>
<p class="small muted" style="margin-top:6px;">When on, your last search terms are lightly highlighted on result pages—but only in the fields you searched.</p>
</div>
{% if user.is_authenticated and user.is_superuser %}
<h1 class="page-title" style="margin-top:22px;">Tools</h1>
<!-- Release Announcement -->
<div class="card" style="margin-top:18px; padding:20px;">
<h2 class="page-title">Release Announcement</h2>
<p class="muted" style="margin:-6px 0 12px;">
Publish a message to show once to each user on their next search page load.
</p>
<form method="post" action="{% url 'announcement_tools' %}" class="vertical-stack" style="gap:10px;">
{% csrf_token %}
{% if announcement_form %}
{{ announcement_form.as_p }}
{% else %}
<label class="small muted" style="display:block; margin-bottom:4px;">Title (optional)</label>
<input type="text" name="title" class="tool-input">
<label class="small muted" style="display:block; margin:10px 0 4px;">Message</label>
<textarea name="message" rows="6" class="tool-input" placeholder="Whats new in this release…"></textarea>
<label style="display:flex; gap:6px; align-items:center; margin-top:8px;">
<input type="checkbox" name="is_active" checked> Active
</label>
{% endif %}
<button class="btn btn-primary">Publish</button>
</form>
{% if announcements_recent %}
<h3 style="margin-top:16px;">Recent</h3>
<table class="table">
<thead>
<tr><th>ID</th><th>Title</th><th>Active?</th><th>Window</th><th>Created</th></tr>
</thead>
<tbody>
{% for a in announcements_recent %}
<tr>
<td>#{{ a.id }}</td>
<td>{{ a.title|default:"(untitled)" }}</td>
<td>{{ a.is_current|yesno:"✅,—" }}</td>
<td>
{{ a.start_at|date:"Y-m-d H:i" }}
{% if a.end_at %} → {{ a.end_at|date:"Y-m-d H:i" }}{% endif %}
</td>
<td>{{ a.created_at|date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<!-- Security tools -->
<div class="card" style="margin-top:18px; padding:20px;">
<h2 class="page-title">Security · Login Attempts (7 days)</h2>
<p class="muted" style="margin:-6px 0 12px;">
View successful and failed login attempts from the last week.
</p>
<a class="btn btn-secondary" href="{% url 'login_attempts' %}">Open Login Attempts</a>
</div>
<div class="card" style="margin-top:18px; padding:20px;">
<h2 class="page-title">Security · Audit Log</h2>
<p class="muted" style="margin:-6px 0 12px;">Track adds, edits (with per-field changes), and deletes. Newest first, latest 100 entries.</p>
<a class="btn btn-secondary" href="{% url 'audit_log' %}">Open Audit Log</a>
</div>
{% endif %}
</div>
<!-- Toast for Clear History -->
<div id="toast-clear-history"
style="position:fixed; right:16px; bottom:16px; padding:10px 14px; border-radius:10px;
background:#111827; color:#fff; box-shadow:0 6px 20px rgba(0,0,0,.25);
opacity:0; pointer-events:none; transition:opacity .25s;">
History cleared.
</div>
<style>
.tool-input {
border:1px solid var(--border, #d1d5db);
border-radius:8px;
padding:6px 8px;
font-size:14px;
width:100%;
max-width:640px;
}
</style>
<script>
(function(){
// Simple CSRF helper
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length === 2) return parts.pop().split(";").shift();
return "";
}
// Clear history
var btn = document.getElementById("clear-history-btn");
var toast = document.getElementById("toast-clear-history");
if (btn) {
btn.addEventListener("click", function(){
fetch("{% url 'clear_history' %}", {
method: "POST",
headers: { "X-CSRFToken": getCookie("csrftoken") }
})
.then(function(r){ return r.ok ? r.json() : Promise.reject(); })
.then(function(data){
// Clear likely client-side copies too (harmless if not present)
var keys = ["recent_searches","recent_entries","recent_viewed","recently_viewed","recent_results","recentSearches","recentEntries"];
try {
keys.forEach(function(k){
localStorage.removeItem(k);
sessionStorage.removeItem(k);
document.cookie = k + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
});
} catch(e) {}
if (toast){
toast.textContent = "History cleared.";
toast.style.opacity = "1";
setTimeout(function(){ toast.style.opacity = "0"; }, 1500);
}
if (data && data.cleared) { console.log("Cleared server keys:", data.cleared); }
})
.catch(function(){ /* silent */ });
});
}
// Theme live preview (without saving)
var themeSel = document.getElementById('theme');
if (themeSel) {
themeSel.addEventListener('change', function(e){
var name = e.target.value;
var link = document.getElementById('theme-css');
if (link) link.href = '{% static "themes/" %}' + name + '.css';
try {
localStorage.setItem('theme', name);
// Toggle the classic gradient body class in case user previews Classic
if (name === 'classic') {
document.body.classList.add('themed-bg');
} else {
document.body.classList.remove('themed-bg');
}
document.documentElement.setAttribute('data-theme', name);
} catch(e){}
});
}
// Highlight search hits toggle
(async function(){
const toggle = document.getElementById("highlightHitsToggle");
if (!toggle) return;
// Load current prefs
try {
const res = await fetch("{% url 'api_get_prefs' %}");
const data = await res.json();
if (data && typeof data.highlight_search_hits !== "undefined") {
toggle.checked = !!data.highlight_search_hits;
}
} catch (e) { /* ignore */ }
// Save on change
toggle.addEventListener("change", async () => {
const form = new FormData();
form.append("enabled", toggle.checked ? "true" : "false");
try {
await fetch("{% url 'api_set_highlight_hits' %}", {
method: "POST",
body: form,
headers: { "X-CSRFToken": getCookie("csrftoken") }
});
} catch (e) {
alert("Could not save the setting. Please try again.");
}
});
})();
})();
</script>
{% endblock %}