Illustrations/web/templates/settings/home.html

303 lines
12 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 wraps the tiles and the Save button. A hidden input carries the choice. #}
<form method="post" action="{% url 'set_theme' %}" class="cc-form" onsubmit="return saveTheme()">
{% csrf_token %}
<input type="hidden" id="theme-hidden" name="theme" value="{{ request.session.theme|default:'classic' }}">
<div class="swatch-grid">
{% for t in available_themes %}
<button type="button" class="swatch" data-theme="{{ t }}" aria-label="Preview {{ t|capfirst }}">
<span class="swatch-name">{{ t|capfirst }}</span>
</button>
{% endfor %}
</div>
<div class="cc-actions">
<button class="btn btn-primary">Save</button>
</div>
</form>
</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>
</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>
/* (unchanged styles from your file) */
.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);}
/* Windows 95 swatch */
/* Windows 95 swatch */
.swatch[data-theme="win95"] {
background: linear-gradient(to bottom, #000080 0 30%, #c0c0c0 30% 100%);
border: 2px solid #808080;
border-top-color: #fff; /* top highlight */
border-left-color: #fff; /* left highlight */
border-right-color: #404040;
border-bottom-color: #404040;
border-radius: 0; /* blocky, no rounded corners */
box-shadow: inset 1px 1px 0 #dfdfdf, inset -1px -1px 0 #000;
font-family: "MS Sans Serif", sans-serif;
position: relative;
overflow: hidden;
}
/* Hide the default theme label pill */
.swatch[data-theme="win95"] .swatch-name {
display: none;
}
.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{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 v='{{ APP_VERSION }}';
var link=document.getElementById('theme-css');
if(link) link.href='{% static "themes/" %}'+name+'.css?v='+v; // << cache-busted preview
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'); }
var hidden=document.getElementById('theme-hidden'); if(hidden) hidden.value=name;
}
// 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(()=>{});
});
})();
// Save from tiles
window.saveTheme = function(){
// Hidden input already holds current pick; just submit
return true;
};
// Swatch instant preview + set hidden value
document.querySelectorAll('.swatch').forEach(btn=>{
btn.addEventListener('click',()=>{
const name=btn.getAttribute('data-theme');
setTheme(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 %}