Illustrations/web/templates/settings/home.html

327 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>
<!-- 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</h2>
</div>
<div class="cc-panel-body">
<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>
</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>
</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>
</div>
<div class="cc-panel-body">
<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>
</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 %}