246 lines
5.7 KiB
HTML
246 lines
5.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
|
<meta name="description" content="FreeRADIUS Web Manager">
|
|
<meta name="author" content="Simon Cloutier">
|
|
<meta property="og:title" content="FreeRADIUS Manager">
|
|
<meta property="og:description" content="Manage FreeRADIUS MAC authentication visually">
|
|
<meta property="og:type" content="website">
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>{% block title %}FreeRADIUS Manager{% endblock %}</title>
|
|
<style>
|
|
:root {
|
|
--bg: #ffffff;
|
|
--fg: #000000;
|
|
--cell-bg: #f5f5f5;
|
|
--th-bg: #e0e0e0;
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
--bg: #121212;
|
|
--fg: #e0e0e0;
|
|
--cell-bg: #1e1e1e;
|
|
--th-bg: #2c2c2c;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--bg);
|
|
color: var(--fg);
|
|
font-family: sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
nav {
|
|
background-color: var(--th-bg);
|
|
padding: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
nav a {
|
|
margin-right: 10px;
|
|
text-decoration: none;
|
|
padding: 5px 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
color: var(--fg);
|
|
}
|
|
|
|
nav a.active {
|
|
background-color: var(--cell-bg);
|
|
}
|
|
|
|
.content {
|
|
padding: 20px;
|
|
}
|
|
|
|
#theme-toggle {
|
|
margin-left: auto;
|
|
cursor: pointer;
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
border: 1px solid #ccc;
|
|
background-color: var(--cell-bg);
|
|
color: var(--fg);
|
|
}
|
|
|
|
.styled-table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin-bottom: 2rem;
|
|
background-color: var(--bg);
|
|
color: var(--fg);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.styled-table th,
|
|
.styled-table td {
|
|
border: 1px solid #444;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
.styled-table input,
|
|
.styled-table select {
|
|
background-color: var(--cell-bg);
|
|
color: var(--fg);
|
|
border: 1px solid #666;
|
|
padding: 4px;
|
|
border-radius: 4px;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
.styled-table input:focus,
|
|
.styled-table select:focus {
|
|
background-color: #555;
|
|
outline: none;
|
|
}
|
|
|
|
.styled-table thead {
|
|
background-color: var(--th-bg);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
}
|
|
|
|
.styled-table tbody tr:hover {
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
.icon-button {
|
|
background-color: var(--cell-bg);
|
|
color: var(--fg);
|
|
border: 1px solid #666;
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
margin: 2px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: background-color 0.2s, transform 0.2s;
|
|
}
|
|
|
|
.icon-button:hover {
|
|
background-color: #555;
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.icon-button.pulse {
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { box-shadow: 0 0 0 0 rgba(50, 205, 50, 0.4); }
|
|
70% { box-shadow: 0 0 0 10px rgba(50, 205, 50, 0); }
|
|
100% { box-shadow: 0 0 0 0 rgba(50, 205, 50, 0); }
|
|
}
|
|
|
|
.page-title {
|
|
margin-bottom: 1rem;
|
|
color: var(--fg);
|
|
}
|
|
|
|
.merged-cell {
|
|
border: none;
|
|
}
|
|
|
|
.fade-in {
|
|
animation: fadeIn 0.5s ease-in;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(-10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
#toast-container {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.toast {
|
|
background-color: #444;
|
|
color: #fff;
|
|
padding: 10px 16px;
|
|
margin-top: 10px;
|
|
border-radius: 4px;
|
|
box-shadow: 0 0 10px rgba(0,0,0,0.2);
|
|
opacity: 0.95;
|
|
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
|
}
|
|
|
|
@keyframes fadein {
|
|
from { opacity: 0; right: 0; }
|
|
to { opacity: 0.95; }
|
|
}
|
|
|
|
@keyframes fadeout {
|
|
from { opacity: 0.95; }
|
|
to { opacity: 0; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<a href="/" {% if request.path == '/' %}class="active"{% endif %}>Home</a>
|
|
<a href="/user/user_list" {% if request.path.startswith('/user') %}class="active"{% endif %}>User List</a>
|
|
<a href="/group/groups" {% if request.path.startswith('/group') %}class="active"{% endif %}>Group List</a>
|
|
<a href="/stats" {% if request.path.startswith('/stats') %}class="active"{% endif %}>Stats</a>
|
|
<button id="theme-toggle" onclick="toggleTheme()">🌓</button>
|
|
</nav>
|
|
|
|
<div class="content">
|
|
{% block content %}{% endblock %}
|
|
</div>
|
|
|
|
<div id="toast-container"></div>
|
|
|
|
<script>
|
|
function showToast(message) {
|
|
const toast = document.createElement('div');
|
|
toast.textContent = message;
|
|
toast.style.cssText = `
|
|
position: fixed;
|
|
bottom: 1rem;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: var(--cell-bg, #333);
|
|
color: var(--fg, #fff);
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
|
z-index: 9999;
|
|
font-weight: bold;
|
|
`;
|
|
document.body.appendChild(toast);
|
|
setTimeout(() => toast.remove(), 3000);
|
|
}
|
|
function toggleTheme() {
|
|
const current = document.body.dataset.theme || 'light';
|
|
const next = current === 'light' ? 'dark' : 'light';
|
|
document.body.dataset.theme = next;
|
|
localStorage.setItem('theme', next);
|
|
}
|
|
|
|
window.onload = () => {
|
|
document.body.dataset.theme = localStorage.getItem('theme') || 'light';
|
|
};
|
|
</script>
|
|
|
|
<script>
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
showToast("{{ message }}");
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|