You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

531 lines
8.6 KiB

<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>pySonnerie - Interface de controle</title>
<style>
:root {
--bg: #f8f6ef;
--ink: #1f1c1a;
--panel: #fffef9;
--brand: #0f6f63;
--brand-strong: #0b544b;
--accent: #d66d2d;
--danger: #b13a2e;
--line: #d8d2c3;
--muted: #665e57;
--shadow: 0 12px 40px rgba(45, 37, 23, 0.12);
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
min-height: 100%;
}
body {
font-family: "Noto Sans", "DejaVu Sans", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
color: var(--ink);
background:
radial-gradient(circle at 15% -5%, #ffe8cc 0%, transparent 40%),
radial-gradient(circle at 100% 100%, #d5efe6 0%, transparent 40%),
var(--bg);
overflow-x: hidden;
}
h1,
h2,
h3 {
font-family: "Noto Sans", "DejaVu Sans", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
margin: 0 0 0.4rem;
}
.muted {
color: var(--muted);
}
.bg-orb {
position: fixed;
border-radius: 999px;
pointer-events: none;
filter: blur(4px);
z-index: -1;
}
.bg-orb-one {
width: 260px;
height: 260px;
background: rgba(214, 109, 45, 0.25);
top: -60px;
right: -40px;
}
.bg-orb-two {
width: 220px;
height: 220px;
background: rgba(15, 111, 99, 0.18);
bottom: -70px;
left: -40px;
}
.reveal {
animation: reveal-up 0.45s ease both;
}
.delay-1 {
animation-delay: 0.05s;
}
.delay-2 {
animation-delay: 0.11s;
}
.delay-3 {
animation-delay: 0.16s;
}
@keyframes reveal-up {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.flash-stack {
position: fixed;
top: 1rem;
right: 1rem;
display: grid;
gap: 0.5rem;
z-index: 50;
}
.flash {
padding: 0.7rem 0.95rem;
border-radius: 12px;
box-shadow: var(--shadow);
font-size: 0.9rem;
max-width: min(82vw, 420px);
opacity: 1;
transform: translateY(0);
transition: opacity 0.35s ease, transform 0.35s ease;
}
.flash.is-hiding {
opacity: 0;
transform: translateY(-6px);
}
.flash-success {
background: #e3f7ef;
border: 1px solid #9cdcbc;
}
.flash-error {
background: #fdeceb;
border: 1px solid #f2b2ad;
}
.flash-info {
background: #edf4ff;
border: 1px solid #bed4f9;
}
.auth-wrap {
min-height: 100vh;
display: grid;
place-items: center;
padding: 1rem;
}
.auth-card {
width: min(560px, 100%);
background: var(--panel);
border: 1px solid var(--line);
box-shadow: var(--shadow);
border-radius: 22px;
padding: 2rem;
}
.auth-card h1 {
margin-bottom: 2rem;
}
.auth-inline-error {
margin: -1rem 0 1rem;
padding: 0.65rem 0.75rem;
border-radius: 10px;
border: 1px solid #f2b2ad;
background: #fdeceb;
color: #7a1f16;
font-size: 0.9rem;
}
.form-grid {
display: grid;
gap: 0.9rem;
}
.form-grid > * {
min-width: 0;
}
label {
display: grid;
gap: 0.4rem;
font-size: 0.9rem;
}
input,
select,
button {
border-radius: 12px;
border: 1px solid var(--line);
font: inherit;
max-width: 100%;
}
input,
select {
display: block;
width: 100%;
background: #fff;
padding: 0.65rem 0.75rem;
}
select {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23665e57' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
padding-right: 2rem;
}
input[type="file"] {
min-width: 0;
}
button,
.small-button,
.ghost-link {
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.08s ease, background-color 0.2s ease;
}
button {
background: var(--brand);
color: #fff;
padding: 0.65rem 0.85rem;
border: 0;
}
button:hover,
.small-button:hover,
.ghost-link:hover {
transform: translateY(-1px);
}
button.danger {
background: var(--danger);
}
button.small {
padding: 0.45rem 0.6rem;
font-size: 0.82rem;
}
.topbar {
margin: 1.2rem;
padding: 1rem;
background: var(--panel);
border: 1px solid var(--line);
border-radius: 16px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.topbar-actions {
display: flex;
align-items: center;
gap: 0.65rem;
}
.ghost-link {
padding: 0.65rem 0.85rem;
border-radius: 12px;
border: 1px solid var(--line);
color: var(--ink);
}
.dashboard-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(12, minmax(0, 1fr));
padding: 0 1.2rem 1.2rem;
}
.panel {
background: var(--panel);
border: 1px solid var(--line);
border-radius: 16px;
padding: 1rem;
box-shadow: var(--shadow);
}
.panel:nth-child(1) {
grid-column: span 4;
}
.panel:nth-child(2) {
grid-column: span 8;
}
.panel:nth-child(3) {
grid-column: span 12;
}
.time-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.8rem;
}
.time-row > * {
min-width: 0;
}
.volume-row {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.volume-row input[type="range"] {
width: 100%;
accent-color: var(--brand);
}
.table-wrap {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
border-bottom: 1px solid var(--line);
text-align: left;
padding: 0.45rem;
font-size: 0.9rem;
}
.trigger-row {
cursor: pointer;
}
.trigger-row:hover {
background: #f7f2e6;
}
.inline-form {
display: inline-block;
margin-right: 0.35rem;
}
.upload-row {
display: flex;
flex-wrap: wrap;
gap: 0.7rem;
margin-bottom: 0.75rem;
}
.audio-select-row {
display: flex;
gap: 0.5rem;
align-items: stretch;
}
.audio-select-row select {
flex: 1;
min-width: 0;
}
.audio-select-row .btn-preview {
flex-shrink: 0;
padding: 0.65rem 0.75rem;
font-size: 1rem;
line-height: 1;
}
.audio-player-card {
border: 1px solid var(--line);
border-radius: 12px;
padding: 0.75rem;
margin-bottom: 0.8rem;
background: #fff;
}
#browser-audio-player {
width: 100%;
margin-top: 0.35rem;
}
.audio-time {
margin: 0.5rem 0 0;
font-size: 0.9rem;
color: var(--muted);
}
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(20, 18, 15, 0.45);
display: none;
align-items: center;
justify-content: center;
padding: 1rem;
z-index: 120;
}
.modal-backdrop.is-open {
display: flex;
}
.modal-card {
width: min(460px, 100%);
background: var(--panel);
border: 1px solid var(--line);
border-radius: 14px;
box-shadow: var(--shadow);
padding: 1rem;
}
.modal-actions {
margin-top: 0.9rem;
display: flex;
justify-content: flex-end;
gap: 0.6rem;
}
.audio-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.6rem;
}
.audio-list li {
border: 1px solid var(--line);
border-radius: 12px;
padding: 0.7rem;
display: flex;
justify-content: space-between;
gap: 0.8rem;
align-items: center;
background: #fff;
}
.audio-actions {
display: flex;
align-items: center;
gap: 0.45rem;
}
.small-button {
border: 1px solid var(--line);
border-radius: 10px;
padding: 0.45rem 0.6rem;
font-size: 0.82rem;
color: var(--ink);
background: #fff;
}
@media (max-width: 1080px) {
.panel:nth-child(1),
.panel:nth-child(2),
.panel:nth-child(3) {
grid-column: span 12;
}
}
@media (max-width: 640px) {
.topbar {
margin: 0.8rem;
flex-direction: column;
align-items: flex-start;
}
.dashboard-grid {
padding: 0 0.8rem 0.8rem;
}
.audio-list li {
flex-direction: column;
align-items: flex-start;
}
.time-row {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="bg-orb bg-orb-one"></div>
<div class="bg-orb bg-orb-two"></div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-stack">
{% for category, message in messages %}
<div class="flash flash-{{ category }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
<script>
(function () {
const stack = document.querySelector(".flash-stack");
if (!stack) return;
const flashes = Array.from(stack.querySelectorAll(".flash"));
flashes.forEach((flash, index) => {
const visibleDurationMs = 3600 + index * 250;
window.setTimeout(() => {
flash.classList.add("is-hiding");
window.setTimeout(() => {
flash.remove();
if (!stack.querySelector(".flash")) {
stack.remove();
}
}, 360);
}, visibleDurationMs);
});
})();
</script>
</body>
</html>