This commit introduces the foundational UI for the admin dashboard. It includes: - A new `maud`-based layout component for consistent page structure. - A navigation sidebar and header. - Basic views for dashboard overview, user management, library settings, and about page. - Integration with Tailwind CSS and Phosphor Icons for styling. - Added `maud` and `axum-core` as dependencies.
115 lines
6.3 KiB
Rust
115 lines
6.3 KiB
Rust
use maud::{html, Markup, DOCTYPE};
|
|
use axum::{Form, response::Redirect};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct LoginPayload {
|
|
pub username: String,
|
|
pub password: String,
|
|
}
|
|
|
|
pub async fn login_form() -> Markup {
|
|
render_login(None)
|
|
}
|
|
|
|
pub async fn login_post(Form(payload): Form<LoginPayload>) -> Result<Redirect, Markup> {
|
|
if payload.username == "admin" && payload.password == "admin" {
|
|
Ok(Redirect::to("/admin"))
|
|
} else {
|
|
Err(render_login(Some("Invalid username or password.")))
|
|
}
|
|
}
|
|
|
|
fn render_login(error: Option<&str>) -> Markup {
|
|
html! {
|
|
(DOCTYPE)
|
|
html lang="en" {
|
|
head {
|
|
meta charset="utf-8";
|
|
meta name="viewport" content="width=device-width, initial-scale=1";
|
|
title { "SoundSonic Admin - Login" }
|
|
script src="https://cdn.tailwindcss.com" {}
|
|
script {
|
|
(maud::PreEscaped(r##"
|
|
tailwind.config = {
|
|
darkMode: 'class',
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {"50":"#eff6ff","100":"#dbeafe","200":"#bfdbfe","300":"#93c5fd","400":"#60a5fa","500":"#3b82f6","600":"#2563eb","700":"#1d4ed8","800":"#1e40af","900":"#1e3a8a","950":"#172554"}
|
|
},
|
|
fontFamily: { sans: ['Inter', 'sans-serif'] }
|
|
}
|
|
}
|
|
}
|
|
"##))
|
|
}
|
|
link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet";
|
|
script src="https://unpkg.com/@phosphor-icons/web" {}
|
|
style {
|
|
(maud::PreEscaped(r##"
|
|
body { font-family: 'Inter', sans-serif; background-color: #0f172a; color: #f8fafc; }
|
|
.glass-card {
|
|
background: rgba(30, 41, 59, 0.6);
|
|
backdrop-filter: blur(16px);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
|
|
}
|
|
"##))
|
|
}
|
|
}
|
|
body class="min-h-screen flex items-center justify-center relative overflow-hidden antialiased selection:bg-primary-500 selection:text-white" {
|
|
// Background Glows
|
|
div class="absolute top-[20%] left-[20%] w-[30%] h-[30%] rounded-full bg-primary-900/30 blur-[120px] pointer-events-none" {}
|
|
div class="absolute bottom-[20%] right-[20%] w-[30%] h-[30%] rounded-full bg-blue-900/20 blur-[120px] pointer-events-none" {}
|
|
|
|
div class="w-full max-w-sm glass-card rounded-2xl p-8 z-10 relative" {
|
|
|
|
div class="flex flex-col items-center mb-8" {
|
|
div class="w-16 h-16 rounded-full bg-gradient-to-tr from-primary-500 to-indigo-600 flex items-center justify-center border-4 border-black/40 shadow-xl shadow-primary-500/20 mb-4" {
|
|
i class="ph-fill ph-waves text-3xl text-white" {}
|
|
}
|
|
h1 class="text-2xl font-bold text-white tracking-tight" { "SoundSonic Admin" }
|
|
p class="text-slate-400 text-sm mt-1" { "Sign in to access control center" }
|
|
}
|
|
|
|
@if let Some(msg) = error {
|
|
div class="bg-rose-500/10 border border-rose-500/20 rounded-lg p-3 mb-6 flex items-start gap-3" {
|
|
i class="ph-fill ph-warning-circle text-rose-400 text-lg mt-0.5" {}
|
|
p class="text-sm text-rose-200" { (msg) }
|
|
}
|
|
}
|
|
|
|
form action="/admin/login" method="POST" class="space-y-5" {
|
|
div {
|
|
label for="username" class="block text-sm font-medium text-slate-300 mb-1.5" { "Username" }
|
|
div class="relative" {
|
|
div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none" {
|
|
i class="ph ph-user text-slate-500 text-lg" {}
|
|
}
|
|
input type="text" name="username" id="username" required class="block w-full pl-10 pr-4 py-2.5 bg-black/40 border border-white/10 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500/50 focus:border-primary-500 transition-all sm:text-sm" placeholder="admin" {}
|
|
}
|
|
}
|
|
|
|
div {
|
|
div class="flex justify-between items-center mb-1.5" {
|
|
label for="password" class="block text-sm font-medium text-slate-300" { "Password" }
|
|
}
|
|
div class="relative" {
|
|
div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none" {
|
|
i class="ph ph-lock-key text-slate-500 text-lg" {}
|
|
}
|
|
input type="password" name="password" id="password" required class="block w-full pl-10 pr-4 py-2.5 bg-black/40 border border-white/10 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500/50 focus:border-primary-500 transition-all sm:text-sm" placeholder="••••••••" {}
|
|
}
|
|
}
|
|
|
|
button type="submit" class="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm shadow-primary-900/20 text-sm font-medium text-white bg-primary-600 hover:bg-primary-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors mt-2" {
|
|
"Sign In"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|