Illustrations/web/templates/settings/home.html

331 lines
13 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 %}
{% block body_class %}{% endblock %}
{% block content %}
<div class="container settings-console">
<!-- Header -->
<div class="cc-header">
<h1 class="cc-title">Command Center</h1>
<div class="cc-subtitle">Personalization · Privacy · Security</div>
</div>
<!-- 3-Column Grid -->
<div class="cc-grid">
<!-- Appearance -->
<section class="card cc-panel">
<div class="cc-panel-head">
<div class="cc-kicker">Appearance</div>
<h2 class="cc-panel-title">Theme & Surface</h2>
</div>
<div class="cc-panel-body">
<p class="muted small">Pick a theme; preview applies instantly.</p>
<form method="post" action="{% url 'set_theme' %}" class="cc-form" onsubmit="return saveThemeFromSelect()">
{% csrf_token %}
<label for="theme-select" class="cc-label">Theme</label>
<select id="theme-select" 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>
<div class="cc-actions">
<button class="btn btn-primary">Save</button>
<small class="text-muted">Applies across the site</small>
</div>
</form>
<div class="swatch-grid">
{% for t in available_themes %}
<button class="swatch" data-theme="{{ t }}" aria-label="Preview {{ t|capfirst }}">
<span class="swatch-name">{{ t|capfirst }}</span>
</button>
{% endfor %}
</div>
</div>
</section>
<!-- Reading -->
<section class="card cc-panel">
<div class="cc-panel-head">
<div class="cc-kicker">Reading</div>
<h2 class="cc-panel-title">Search Highlights</h2>
</div>
<div class="cc-panel-body">
<div class="cc-row">
<label class="cc-label">Highlight search hits</label>
<label class="switch">
<input id="highlightHitsToggle" type="checkbox">
<span class="slider"></span>
</label>
</div>
<p class="small muted">Lightly marks terms you searched for on entry pages.</p>
</div>
</section>
<!-- Privacy -->
<section class="card cc-panel">
<div class="cc-panel-head">
<div class="cc-kicker">Privacy</div>
<h2 class="cc-panel-title">Your Footprint</h2>
</div>
<div class="cc-panel-body">
<p class="small muted">Remove Recent Searches and Recently Viewed.</p>
<button id="clear-history-btn" class="btn btn-danger">Clear My History</button>
</div>
</section>
{% if user.is_authenticated and user.is_superuser %}
<!-- Security (accented) -->
<section class="card cc-panel cc-sec">
<div class="cc-sec-bg"></div>
<div class="cc-panel-head">
<div class="cc-kicker cc-kicker-sec">Security</div>
<h2 class="cc-panel-title">Monitoring</h2>
</div>
<div class="cc-panel-body">
<div class="sec-grid">
<a class="sec-tile" href="{% url 'login_attempts' %}">
<div class="sec-icon">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path fill="currentColor" d="M12 2l7 3v6c0 5-3.8 9.7-7 11-3.2-1.3-7-6-7-11V5l7-3z"/></svg>
</div>
<div class="sec-meta">
<div class="sec-title">Login Attempts</div>
<div class="sec-sub">Last 7 days</div>
</div>
<div class="sec-cta">Open →</div>
</a>
<a class="sec-tile" href="{% url 'audit_log' %}">
<div class="sec-icon">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path fill="currentColor" d="M3 12h3l2 7 4-14 3 10h6"/></svg>
</div>
<div class="sec-meta">
<div class="sec-title">Audit Log</div>
<div class="sec-sub">Latest 100</div>
</div>
<div class="sec-cta">Open →</div>
</a>
</div>
<p class="tiny muted" style="margin-top:10px;">Superuser tools</p>
</div>
</section>
<!-- Release Announcement -->
<section class="card cc-panel">
<div class="cc-panel-head">
<div class="cc-kicker">Comms</div>
<h2 class="cc-panel-title">Release Announcement</h2>
</div>
<div class="cc-panel-body">
<form method="post" action="{% url 'announcement_tools' %}" class="cc-form">
{% csrf_token %}
{% if announcement_form %}
{{ announcement_form.as_p }}
{% else %}
<label class="cc-label">Title</label>
<input type="text" name="title" class="tool-input">
<label class="cc-label">Message</label>
<textarea name="message" rows="5" class="tool-input" placeholder="Whats new…"></textarea>
<label class="cc-check"><input type="checkbox" name="is_active" checked> Active</label>
{% endif %}
<div class="cc-actions">
<button class="btn btn-primary">Publish</button>
</div>
</form>
{% if announcements_recent %}
<div class="cc-table-wrap">
<table class="table small">
<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>
</div>
{% endif %}
</div>
</section>
{% endif %}
</div>
</div>
<!-- Toast -->
<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>
/* ---------- Console Layout (same vibe, sans quick strip) ---------- */
.settings-console .cc-title{margin:0;font-size:24px;font-weight:700;color:#0f172a}
.settings-console .cc-subtitle{color:#64748b;margin-top:6px}
.cc-header{margin:6px 0 14px}
.cc-grid{
display:grid;
grid-template-columns: repeat(3, minmax(0,1fr));
gap:16px;
}
@media (max-width: 1020px){ .cc-grid{ grid-template-columns: 1fr 1fr; } }
@media (max-width: 720px){ .cc-grid{ grid-template-columns: 1fr; } }
.cc-panel{padding:16px 16px 14px}
.cc-panel-head{margin-bottom:8px}
.cc-kicker{font-size:12px; text-transform:uppercase; letter-spacing:.08em; color:#64748b}
.cc-kicker-sec{color:#22c55e}
.cc-panel-title{margin:2px 0 0; font-size:18px; font-weight:700; color:#0f172a}
.cc-panel-body{margin-top:8px}
.cc-label{display:block; font-weight:600; margin:8px 0 6px; color:#0f172a}
.cc-actions{display:flex; align-items:center; gap:10px; margin-top:10px}
.cc-form .tool-input{border:1px solid var(--border,#d1d5db); border-radius:10px; padding:8px 10px; font-size:14px}
/* Switch */
.switch{position:relative; display:inline-block; width:46px; height:26px}
.switch input{display:none}
.slider{position:absolute; inset:0; background:#e5e7eb; border-radius:999px; transition:all .15s;}
.slider:before{content:""; position:absolute; height:20px; width:20px; left:3px; top:3px; background:white; border-radius:50%; transition:all .15s; box-shadow:0 2px 6px rgba(0,0,0,.15);}
.switch input:checked + .slider{ background:#22c55e; }
.switch input:checked + .slider:before{ transform:translateX(20px); }
/* Swatches */
.swatch-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; margin-top:12px; }
@media (max-width:720px){ .swatch-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
.swatch{
position:relative; display:flex; align-items:flex-end; justify-content:flex-start;
height:64px; border:1px solid var(--border,#e5e7eb); border-radius:12px; padding:8px;
background:linear-gradient(135deg,#eef2f7,#dfe7f2); cursor:pointer;
}
.swatch[data-theme="classic"]{ background:linear-gradient(110deg,#d7b592 0%,#e7e3db 35%,#8fbfe0 100%); }
.swatch[data-theme="dawn"]{ background:linear-gradient(135deg,#ffd9a0 0%,#ffb6b9 40%,#a3d5ff 100%); }
.swatch[data-theme="midnight"]{ background:linear-gradient(135deg,#0b1220,#1c2741); }
.swatch[data-theme="forest"]{ background:linear-gradient(135deg,#d7f3e2,#92c7a3); }
.swatch[data-theme="sandstone"]{ background:linear-gradient(135deg,#f7efe4,#e4d2b6); }
.swatch-name{ background:rgba(255,255,255,.8); padding:2px 6px; border-radius:8px; font-size:12px; color:#0f172a; }
/* Security accent */
.cc-sec{ position:relative; overflow:hidden; color:#0f172a; }
.cc-sec .cc-panel-title{ color:#0f172a; }
.cc-sec .cc-kicker{ color:#16a34a; }
.cc-sec-bg{
position:absolute; inset:-1px; z-index:0;
background: radial-gradient(800px 300px at 90% -20%, rgba(34,197,94,.25), transparent 55%),
linear-gradient(135deg, rgba(34,197,94,.10), rgba(59,130,246,.06));
pointer-events:none;
}
.cc-sec > * { position:relative; z-index:1; }
.sec-grid{ display:grid; grid-template-columns:1fr; gap:10px; }
.sec-tile{
display:flex; align-items:center; gap:12px; padding:12px; border:1px solid rgba(34,197,94,.25);
border-radius:12px; background:rgba(255,255,255,.8); text-decoration:none; color:inherit;
transition:transform .12s, box-shadow .12s, border-color .12s;
}
.sec-tile:hover{ transform: translateY(-2px); box-shadow:0 10px 24px rgba(16,24,40,.08); border-color: rgba(34,197,94,.45); }
.sec-icon{ display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px; border-radius:10px; background:rgba(34,197,94,.18); color:#065f46; }
.sec-title{ font-weight:700; }
.sec-sub{ font-size:12px; color:#64748b; }
.sec-meta{ flex:1; }
.sec-cta{ font-size:13px; color:#0f172a; opacity:.7 }
/* Tiny helper */
.tiny{ font-size:12px }
</style>
<script>
(function(){
// Helpers
function getCookie(name){ const m=document.cookie.match("(^|;)\\s*"+name+"\\s*=\\s*([^;]+)"); return m?m.pop():""; }
function setTheme(name){
var link=document.getElementById('theme-css');
if(link) link.href='{% static "themes/" %}'+name+'.css';
document.documentElement.setAttribute('data-theme',name);
try{ localStorage.setItem('theme',name); }catch(e){}
if(name==='classic'){ document.body.classList.add('themed-bg'); } else { document.body.classList.remove('themed-bg'); }
}
// Clear history
(function(){
var btn=document.getElementById("clear-history-btn");
var toast=document.getElementById("toast-clear-history");
if(!btn) return;
btn.addEventListener("click",function(){
fetch("{% url 'clear_history' %}",{method:"POST",headers:{"X-CSRFToken":getCookie("csrftoken")}})
.then(r=>r.ok?r.json():Promise.reject())
.then(()=>{
const keys=["recent_searches","recent_entries","recent_viewed","recently_viewed","recent_results","recentSearches","recentEntries"];
try{ keys.forEach(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.style.opacity="1"; setTimeout(()=>toast.style.opacity="0",1500); }
}).catch(()=>{});
});
})();
// Theme select + Save
window.saveThemeFromSelect=function(){
var sel=document.getElementById('theme-select');
if(!sel) return false;
var name=sel.value;
setTheme(name);
// persist to session
var f=document.createElement('form');
f.method='POST'; f.action="{% url 'set_theme' %}";
var csrf=document.createElement('input'); csrf.type='hidden'; csrf.name='csrfmiddlewaretoken'; csrf.value=getCookie('csrftoken');
var t=document.createElement('input'); t.type='hidden'; t.name='theme'; t.value=name;
f.appendChild(csrf); f.appendChild(t); document.body.appendChild(f); f.submit();
return false;
};
// Swatch instant preview
document.querySelectorAll('.swatch').forEach(btn=>{
btn.addEventListener('click',()=>{
const name=btn.getAttribute('data-theme');
setTheme(name);
var sel=document.getElementById('theme-select');
if(sel) sel.value=name;
});
});
// Highlight toggle
(async function(){
const toggle=document.getElementById("highlightHitsToggle");
if(!toggle) return;
try{
const res=await fetch("{% url 'api_get_prefs' %}");
const data=await res.json();
if(typeof data.highlight_search_hits!=="undefined") toggle.checked=!!data.highlight_search_hits;
}catch(e){}
toggle.addEventListener("change",()=>{
const form=new FormData();
form.append("enabled", toggle.checked ? "true" : "false");
fetch("{% url 'api_set_highlight_hits' %}", { method:"POST", body:form, headers:{ "X-CSRFToken": getCookie("csrftoken") }})
.catch(()=>alert("Could not save the setting. Please try again."));
});
})();
})();
</script>
{% endblock %}