Files

170 lines
9.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Announcement Manager</title>
<style>
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
body {{ font-family: system-ui, -apple-system, sans-serif; background: #f1f5f9; color: #1e293b; min-height: 100vh; }}
.header {{ background: #fff; border-bottom: 1px solid #e2e8f0; padding: 1rem 2rem; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 10; }}
.header h1 {{ font-size: 1.25rem; font-weight: 600; }}
.header-actions {{ display: flex; gap: 0.75rem; align-items: center; }}
.header-actions .count {{ font-size: 0.85rem; color: #64748b; }}
.main {{ padding: 2rem; max-width: 1400px; margin: 0 auto; }}
.toolbar {{ display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; margin-bottom: 1.25rem; padding: 1rem 1.25rem; background: #fff; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,.06); border: 1px solid #e2e8f0; }}
.toolbar form {{ display: flex; gap: 0.5rem; align-items: center; }}
.toolbar input[type=date] {{ padding: 0.45rem 0.7rem; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.9rem; }}
.toolbar .clear {{ font-size: 0.85rem; color: #64748b; text-decoration: none; }}
.toolbar .clear:hover {{ color: #1e293b; text-decoration: underline; }}
.btn {{ padding: 0.45rem 1rem; border: none; border-radius: 6px; cursor: pointer; font-size: 0.9rem; font-weight: 500; display: inline-flex; align-items: center; gap: 0.35rem; text-decoration: none; }}
.btn-primary {{ background: #2563eb; color: #fff; }}
.btn-primary:hover {{ background: #1d4ed8; }}
.btn-ghost {{ background: transparent; color: #64748b; border: 1px solid #e2e8f0; }}
.btn-ghost:hover {{ background: #f8fafc; color: #1e293b; }}
.btn-danger {{ background: #ef4444; color: #fff; }}
.btn-danger:hover {{ background: #dc2626; }}
.btn-sm {{ padding: 0.3rem 0.65rem; font-size: 0.8rem; }}
.table-wrap {{ background: #fff; border-radius: 10px; border: 1px solid #e2e8f0; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.06); }}
table {{ width: 100%; border-collapse: collapse; }}
th {{ padding: 0.85rem 1rem; text-align: left; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: #64748b; background: #f8fafc; border-bottom: 1px solid #e2e8f0; }}
td {{ padding: 0.85rem 1rem; border-bottom: 1px solid #f1f5f9; font-size: 0.9rem; vertical-align: middle; }}
tr:last-child td {{ border-bottom: none; }}
tr:hover td {{ background: #f8fafc; }}
.cell-id {{ color: #94a3b8; font-size: 0.8rem; font-family: monospace; vertical-align: top; }}
.cell-text {{ max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }}
.cell-date {{ font-size: 0.85rem; color: #64748b; white-space: nowrap; font-variant-numeric: tabular-nums; }}
.cell-actions {{ white-space: nowrap; }}
.flag {{ display: inline-block; padding: 0.2rem 0.55rem; border-radius: 999px; background: #dbeafe; color: #1e40af; font-size: 0.75rem; font-weight: 500; white-space: nowrap; }}
.class-pill {{ display: inline-block; padding: 0.2rem 0.55rem; border-radius: 999px; background: #f0fdf4; color: #166534; font-size: 0.75rem; font-weight: 500; white-space: nowrap; }}
.class-pill.all {{ background: #f1f5f9; color: #64748b; font-style: italic; }}
.field-hint {{ font-size: 0.8rem; color: #94a3b8; font-weight: 400; }}
.empty {{ padding: 3rem 1rem; text-align: center; color: #94a3b8; font-size: 0.95rem; }}
.pagination {{ display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 1rem; padding: 1rem 1.25rem; background: #fff; border-top: 1px solid #e2e8f0; }}
.pagination-info {{ font-size: 0.9rem; color: #64748b; padding: 0.25rem 0; }}
.pagination-pages {{ display: flex; gap: 0.4rem; align-items: center; }}
.page-btn {{ display: inline-flex; align-items: center; justify-content: center; min-width: 2.5rem; height: 2.5rem; padding: 0 0.75rem; border-radius: 8px; font-size: 0.9rem; font-weight: 500; text-decoration: none; color: #475569; background: #f8fafc; border: 1px solid #e2e8f0; }}
.page-btn:hover {{ background: #f1f5f9; border-color: #cbd5e1; }}
.page-btn.active {{ background: #2563eb; color: #fff; font-weight: 600; border-color: #2563eb; }}
.page-btn.disabled {{ color: #cbd5e1; cursor: default; background: #f8fafc; border-color: #f1f5f9; }}
.page-btn.dots {{ color: #94a3b8; border: none; min-width: auto; padding: 0 0.3rem; background: transparent; }}
dialog {{ margin: auto; border: none; border-radius: 12px; padding: 0; box-shadow: 0 20px 60px rgba(0,0,0,.2); max-width: 520px; width: 90vw; }}
dialog::backdrop {{ background: rgba(0,0,0,.4); }}
dialog form {{ display: flex; flex-direction: column; }}
.dialog-header {{ padding: 1.25rem 1.5rem; border-bottom: 1px solid #e2e8f0; font-size: 1.1rem; font-weight: 600; }}
.dialog-body {{ padding: 1.25rem 1.5rem; display: flex; flex-direction: column; gap: 1rem; }}
.dialog-body label {{ display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.85rem; font-weight: 500; }}
.dialog-body input, .dialog-body textarea {{ padding: 0.5rem 0.65rem; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.9rem; font-family: inherit; }}
.dialog-body textarea {{ min-height: 80px; resize: vertical; }}
.dialog-footer {{ padding: 1rem 1.5rem; border-top: 1px solid #e2e8f0; display: flex; justify-content: flex-end; gap: 0.5rem; }}
.flag-checkboxes {{ display: flex; gap: 0.75rem; flex-wrap: wrap; }}
.flag-checkboxes label {{ display: flex; flex-direction: row; align-items: center; gap: 0.3rem; font-size: 0.85rem; font-weight: 400; cursor: pointer; }}
</style>
</head>
<body>
<header class="header">
<h1>Announcement Manager</h1>
<div class="header-actions">
<span class="count">{total_count} total</span>
<button class="btn btn-primary" onclick="document.getElementById('create-dialog').showModal()">+ New</button>
</div>
</header>
<div class="main">
<div class="toolbar">
<form action="/" method="GET">
<input type="date" name="filter_date" value="{filter_date}">
<button class="btn btn-ghost btn-sm" type="submit">Filter</button>
</form>
<a class="clear" href="/">Clear</a>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>ID</th>
<th>Author</th>
<th>Text</th>
<th>Start</th>
<th>End</th>
<th>For</th>
<th>Flags</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
{pagination}
</div>
</div>
<dialog id="create-dialog">
<form action="/api/announcements" method="POST">
<div class="dialog-header">New Announcement</div>
<div class="dialog-body">
<label>
Author
<input name="author" placeholder="e.g. School office" required>
</label>
<label>
Text
<textarea name="text_content" placeholder="Announcement text&hellip;"></textarea>
</label>
<label>
Start date
<input type="date" name="start_date" required>
</label>
<label>
End date
<input type="date" name="end_date" required>
</label>
<label>
Classes
<input id="classes-input" name="classes" placeholder="e.g. C2c, A1" autocomplete="off">
<span class="field-hint">Leave empty for all classes. Separate multiple with commas.</span>
</label>
<label id="flags-section" style="gap:0.5rem">
Flags
<div class="flag-checkboxes">
{flags_checkboxes}
</div>
</label>
</div>
<div class="dialog-footer">
<button class="btn btn-ghost" type="button" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn btn-primary" type="submit">Create</button>
</div>
</form>
</dialog>
<script>
document.addEventListener('DOMContentLoaded', function() {{
var classesInput = document.getElementById('classes-input');
var flagsSection = document.getElementById('flags-section');
function toggleFlags() {{
if (classesInput.value.trim() !== '') {{
flagsSection.style.display = 'none';
var cbs = flagsSection.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < cbs.length; i++) {{ cbs[i].checked = false; }}
}} else {{
flagsSection.style.display = '';
}}
}}
classesInput.addEventListener('input', toggleFlags);
toggleFlags();
}});
</script>
</body>
</html>