Feat: Implement Admin Dashboard UI
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.
This commit is contained in:
114
src/admin/views/login.rs
Normal file
114
src/admin/views/login.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user