feat(ntfy): add admin Notifications settings page with test button
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full bg-gray-50">
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
@@ -12,6 +12,101 @@
|
||||
<%= javascript_importmap_tags %>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=Outfit:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--accent: #38bdf8;
|
||||
--accent-deep: #0ea5e9;
|
||||
}
|
||||
.font-display { font-family: 'Syne', sans-serif; }
|
||||
.font-body { font-family: 'Outfit', sans-serif; }
|
||||
.font-mono-tech { font-family: 'JetBrains Mono', ui-monospace, monospace; }
|
||||
|
||||
body { font-family: 'Outfit', sans-serif; }
|
||||
|
||||
/* Login immersive background */
|
||||
.login-stage {
|
||||
position: relative;
|
||||
background: radial-gradient(ellipse 80% 60% at 50% 0%, #0f172a 0%, #020617 55%, #000 100%);
|
||||
overflow: hidden;
|
||||
}
|
||||
.login-stage::before {
|
||||
content: "";
|
||||
position: absolute; inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(56, 189, 248, 0.07) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(56, 189, 248, 0.07) 1px, transparent 1px);
|
||||
background-size: 46px 46px;
|
||||
-webkit-mask-image: radial-gradient(ellipse at center, black 20%, transparent 72%);
|
||||
mask-image: radial-gradient(ellipse at center, black 20%, transparent 72%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.login-stage::after {
|
||||
content: "";
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 12% 18%, rgba(56, 189, 248, 0.14), transparent 42%),
|
||||
radial-gradient(circle at 88% 82%, rgba(14, 165, 233, 0.11), transparent 42%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.login-card {
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.09);
|
||||
box-shadow: 0 0 0 1px rgba(56, 189, 248, 0.08), 0 30px 80px -20px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.beacon-ring {
|
||||
animation: beacon-pulse 2.4s ease-out infinite;
|
||||
}
|
||||
.beacon-ring.delay-1 { animation-delay: 0.8s; }
|
||||
.beacon-ring.delay-2 { animation-delay: 1.6s; }
|
||||
@keyframes beacon-pulse {
|
||||
0% { transform: scale(0.6); opacity: 0.55; }
|
||||
80% { opacity: 0; }
|
||||
100% { transform: scale(1.8); opacity: 0; }
|
||||
}
|
||||
|
||||
.field-input {
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
color: #e2e8f0;
|
||||
transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
|
||||
}
|
||||
.field-input::placeholder { color: #64748b; }
|
||||
.field-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.18);
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
}
|
||||
|
||||
.btn-accent {
|
||||
background: linear-gradient(135deg, #38bdf8 0%, #0ea5e9 100%);
|
||||
box-shadow: 0 10px 30px -10px rgba(56, 189, 248, 0.55), inset 0 1px 0 rgba(255,255,255,0.25);
|
||||
transition: transform .15s ease, box-shadow .2s ease, filter .2s ease;
|
||||
}
|
||||
.btn-accent:hover { transform: translateY(-1px); filter: brightness(1.08); box-shadow: 0 16px 40px -10px rgba(56, 189, 248, 0.7), inset 0 1px 0 rgba(255,255,255,0.3); }
|
||||
.btn-accent:active { transform: translateY(0); }
|
||||
|
||||
@keyframes fade-up {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.fade-up { animation: fade-up .5s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
||||
.fade-up.d-1 { animation-delay: .06s; }
|
||||
.fade-up.d-2 { animation-delay: .14s; }
|
||||
.fade-up.d-3 { animation-delay: .22s; }
|
||||
.fade-up.d-4 { animation-delay: .30s; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.beacon-ring, .fade-up { animation: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="h-full">
|
||||
@@ -54,6 +149,11 @@
|
||||
<i class="fas fa-vial w-6 h-6 shrink-0 flex items-center justify-center"></i>
|
||||
API Tester
|
||||
<% end %>
|
||||
|
||||
<%= link_to admin_notifications_path, class: "group flex gap-x-3 rounded-md p-3 text-sm leading-6 font-semibold transition-all duration-200 #{current_page?(admin_notifications_path) ? 'bg-gray-700 text-white' : 'text-gray-300 hover:text-white hover:bg-gray-700'}" do %>
|
||||
<i class="fas fa-bell w-6 h-6 shrink-0 flex items-center justify-center"></i>
|
||||
Notifications
|
||||
<% end %>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mt-auto">
|
||||
@@ -115,9 +215,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<!-- Login page without sidebar -->
|
||||
<div class="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<%= yield %>
|
||||
<!-- Login stage (not logged in) — flash IS rendered here -->
|
||||
<div class="login-stage min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="relative w-full max-w-md">
|
||||
<% if flash[:alert] %>
|
||||
<div class="fade-up mb-6 flex items-start gap-3 rounded-xl border border-red-400/30 bg-red-500/10 p-4 backdrop-blur-md">
|
||||
<i class="fas fa-exclamation-circle mt-0.5 text-red-400"></i>
|
||||
<p class="text-sm font-medium text-red-100"><%= flash[:alert] %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if flash[:notice] %>
|
||||
<div class="fade-up mb-6 flex items-start gap-3 rounded-xl border border-emerald-400/30 bg-emerald-500/10 p-4 backdrop-blur-md">
|
||||
<i class="fas fa-check-circle mt-0.5 text-emerald-400"></i>
|
||||
<p class="text-sm font-medium text-emerald-100"><%= flash[:notice] %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user