redesign: proper globals.css, new Sidebar, DashboardHeader with clock
All checks were successful
Build & Deploy Dashboard / deploy (push) Successful in 1m0s
All checks were successful
Build & Deploy Dashboard / deploy (push) Successful in 1m0s
This commit is contained in:
@@ -4,90 +4,37 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
body {
|
||||||
:root {
|
|
||||||
--background: 240 10% 4%;
|
|
||||||
--foreground: 210 40% 98%;
|
|
||||||
--card: 240 10% 4%;
|
|
||||||
--card-foreground: 210 40% 98%;
|
|
||||||
--border: 240 6% 12%;
|
|
||||||
--input: 240 6% 12%;
|
|
||||||
--primary: 210 40% 98%;
|
|
||||||
--primary-foreground: 240 10% 4%;
|
|
||||||
--secondary: 240 6% 10%;
|
|
||||||
--secondary-foreground: 210 40% 98%;
|
|
||||||
--muted: 240 6% 9%;
|
|
||||||
--muted-foreground: 215 16% 47%;
|
|
||||||
--accent: 240 6% 12%;
|
|
||||||
--accent-foreground: 210 40% 98%;
|
|
||||||
--destructive: 0 63% 31%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
--ring: 240 6% 12%;
|
|
||||||
--radius: 0.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
* { @apply border-border; }
|
|
||||||
body {
|
|
||||||
background-color: #080810;
|
background-color: #080810;
|
||||||
font-family: 'Inter', system-ui, sans-serif;
|
font-family: 'Inter', system-ui, sans-serif;
|
||||||
color: #f1f5f9;
|
color: #f1f5f9;
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar */
|
|
||||||
::-webkit-scrollbar { width: 4px; height: 4px; }
|
::-webkit-scrollbar { width: 4px; height: 4px; }
|
||||||
::-webkit-scrollbar-track { background: transparent; }
|
::-webkit-scrollbar-track { background: transparent; }
|
||||||
::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.3); border-radius: 2px; }
|
::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.3); border-radius: 2px; }
|
||||||
::-webkit-scrollbar-thumb:hover { background: rgba(99,102,241,0.5); }
|
::-webkit-scrollbar-thumb:hover { background: rgba(99,102,241,0.5); }
|
||||||
|
|
||||||
/* Card base */
|
|
||||||
.card {
|
.card {
|
||||||
background: rgba(255,255,255,0.03);
|
background: rgba(255,255,255,0.03);
|
||||||
border: 1px solid rgba(255,255,255,0.07);
|
border: 1px solid rgba(255,255,255,0.07);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
.card:hover {
|
.card:hover {
|
||||||
background: rgba(255,255,255,0.05);
|
background: rgba(255,255,255,0.05);
|
||||||
border-color: rgba(255,255,255,0.12);
|
border-color: rgba(255,255,255,0.12);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Legacy compat */
|
|
||||||
.glass-card {
|
|
||||||
background: rgba(255,255,255,0.03);
|
|
||||||
border: 1px solid rgba(255,255,255,0.07);
|
|
||||||
border-radius: 20px;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
}
|
|
||||||
.glass-card:hover {
|
|
||||||
background: rgba(255,255,255,0.05);
|
|
||||||
border-color: rgba(255,255,255,0.12);
|
|
||||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accent borders — short names */
|
.card-blue { border-top: 2px solid #3b82f6 !important; }
|
||||||
.card-blue { border-top: 2px solid #3b82f6; }
|
.card-violet { border-top: 2px solid #8b5cf6 !important; }
|
||||||
.card-violet { border-top: 2px solid #8b5cf6; }
|
.card-emerald { border-top: 2px solid #10b981 !important; }
|
||||||
.card-emerald { border-top: 2px solid #10b981; }
|
.card-amber { border-top: 2px solid #f59e0b !important; }
|
||||||
.card-amber { border-top: 2px solid #f59e0b; }
|
.card-cyan { border-top: 2px solid #06b6d4 !important; }
|
||||||
.card-cyan { border-top: 2px solid #06b6d4; }
|
.card-rose { border-top: 2px solid #f43f5e !important; }
|
||||||
.card-rose { border-top: 2px solid #f43f5e; }
|
|
||||||
|
|
||||||
/* Legacy accent names */
|
|
||||||
.card-accent-blue { border-top: 2px solid #3b82f6; }
|
|
||||||
.card-accent-violet { border-top: 2px solid #8b5cf6; }
|
|
||||||
.card-accent-emerald { border-top: 2px solid #10b981; }
|
|
||||||
.card-accent-amber { border-top: 2px solid #f59e0b; }
|
|
||||||
.card-accent-cyan { border-top: 2px solid #06b6d4; }
|
|
||||||
.card-accent-rose { border-top: 2px solid #f43f5e; }
|
|
||||||
|
|
||||||
/* Gradient text */
|
|
||||||
.gradient-text {
|
.gradient-text {
|
||||||
background: linear-gradient(135deg, #818cf8, #c084fc);
|
background: linear-gradient(135deg, #818cf8, #c084fc);
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
@@ -95,24 +42,14 @@
|
|||||||
background-clip: text;
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status indicators */
|
|
||||||
.status-online { background: #10b981; box-shadow: 0 0 6px rgba(16,185,129,0.5); }
|
|
||||||
.status-offline { background: #ef4444; box-shadow: 0 0 6px rgba(239,68,68,0.5); }
|
|
||||||
.status-checking { background: #6b7280; }
|
|
||||||
|
|
||||||
/* Pulsing status */
|
|
||||||
@keyframes ping-slow {
|
@keyframes ping-slow {
|
||||||
0%, 100% { opacity: 0.8; transform: scale(1); }
|
0%, 100% { opacity: 0.8; transform: scale(1); }
|
||||||
50% { opacity: 0.3; transform: scale(1.5); }
|
50% { opacity: 0.2; transform: scale(1.8); }
|
||||||
}
|
}
|
||||||
.ping-slow { animation: ping-slow 2s ease-in-out infinite; }
|
.ping-slow { animation: ping-slow 2s ease-in-out infinite; }
|
||||||
|
|
||||||
/* Glow effects */
|
.glass-card {
|
||||||
.glow-violet { box-shadow: 0 0 40px rgba(139,92,246,0.15); }
|
background: rgba(255,255,255,0.03);
|
||||||
.glow-blue { box-shadow: 0 0 40px rgba(59,130,246,0.15); }
|
border: 1px solid rgba(255,255,255,0.07);
|
||||||
|
border-radius: 20px;
|
||||||
/* Sidebar */
|
|
||||||
.sidebar {
|
|
||||||
background: #0a0a14;
|
|
||||||
border-right: 1px solid rgba(255,255,255,0.05);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { signOut, useSession } from "next-auth/react";
|
import { signOut } from "next-auth/react";
|
||||||
import { LayoutDashboard, Monitor, Bookmark, LogOut, Home } from "lucide-react";
|
import { LayoutDashboard, Monitor, Bookmark, LogOut, Home } from "lucide-react";
|
||||||
|
|
||||||
const NAV = [
|
const NAV = [
|
||||||
@@ -12,19 +12,19 @@ const NAV = [
|
|||||||
|
|
||||||
export function Sidebar() {
|
export function Sidebar() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { data: session } = useSession();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="w-[220px] flex-shrink-0 flex flex-col h-screen bg-[#0a0a14] border-r border-white/5">
|
<aside className="w-[220px] flex-shrink-0 flex flex-col h-screen" style={{ background: "#0a0a14", borderRight: "1px solid rgba(255,255,255,0.05)" }}>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="px-5 py-6 border-b border-white/5">
|
<div className="px-5 py-6" style={{ borderBottom: "1px solid rgba(255,255,255,0.05)" }}>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 flex items-center justify-center shadow-lg shadow-indigo-500/20">
|
<div className="w-9 h-9 rounded-xl flex items-center justify-center shadow-lg"
|
||||||
|
style={{ background: "linear-gradient(135deg,#6366f1,#8b5cf6)", boxShadow: "0 4px 20px rgba(99,102,241,0.3)" }}>
|
||||||
<Home className="w-4 h-4 text-white" />
|
<Home className="w-4 h-4 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-bold text-white">Digital Home</div>
|
<div className="text-sm font-bold text-white">Digital Home</div>
|
||||||
<div className="text-[10px] text-slate-500">Dashboard</div>
|
<div className="text-[10px]" style={{ color: "#475569" }}>Dashboard</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,13 +37,19 @@ export function Sidebar() {
|
|||||||
<Link
|
<Link
|
||||||
key={href}
|
key={href}
|
||||||
href={href}
|
href={href}
|
||||||
className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all ${
|
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all"
|
||||||
active
|
style={active ? {
|
||||||
? "bg-indigo-500/15 text-indigo-300 border border-indigo-500/20"
|
background: "rgba(99,102,241,0.15)",
|
||||||
: "text-slate-500 hover:text-slate-300 hover:bg-white/5 border border-transparent"
|
color: "#a5b4fc",
|
||||||
}`}
|
border: "1px solid rgba(99,102,241,0.25)",
|
||||||
|
} : {
|
||||||
|
color: "#475569",
|
||||||
|
border: "1px solid transparent",
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => { if (!active) { (e.currentTarget as HTMLElement).style.color = "#94a3b8"; (e.currentTarget as HTMLElement).style.background = "rgba(255,255,255,0.04)"; } }}
|
||||||
|
onMouseLeave={e => { if (!active) { (e.currentTarget as HTMLElement).style.color = "#475569"; (e.currentTarget as HTMLElement).style.background = "transparent"; } }}
|
||||||
>
|
>
|
||||||
<Icon className={`w-4 h-4 ${active ? "text-indigo-400" : ""}`} />
|
<Icon className="w-4 h-4" style={{ color: active ? "#818cf8" : "inherit" }} />
|
||||||
{label}
|
{label}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -51,19 +57,22 @@ export function Sidebar() {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* User */}
|
{/* User */}
|
||||||
<div className="px-3 py-4 border-t border-white/5">
|
<div className="px-3 py-4" style={{ borderTop: "1px solid rgba(255,255,255,0.05)" }}>
|
||||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl" style={{ background: "rgba(255,255,255,0.03)" }}>
|
<div className="flex items-center gap-3 px-3 py-2 rounded-xl" style={{ background: "rgba(255,255,255,0.03)" }}>
|
||||||
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 flex items-center justify-center text-xs font-bold text-white flex-shrink-0">
|
<div className="w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold text-white flex-shrink-0"
|
||||||
|
style={{ background: "linear-gradient(135deg,#6366f1,#8b5cf6)" }}>
|
||||||
D
|
D
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-xs font-medium text-slate-300 truncate">{session?.user?.name ?? "Daniil"}</div>
|
<div className="text-xs font-medium truncate" style={{ color: "#cbd5e1" }}>Daniil</div>
|
||||||
<div className="text-[10px] text-slate-600 truncate">Admin</div>
|
<div className="text-[10px] truncate" style={{ color: "#334155" }}>Admin</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => signOut({ callbackUrl: "/auth/signin" })}
|
onClick={() => signOut({ callbackUrl: "/auth/signin" })}
|
||||||
className="text-slate-600 hover:text-red-400 transition-colors"
|
|
||||||
title="Выйти"
|
title="Выйти"
|
||||||
|
style={{ color: "#334155" }}
|
||||||
|
onMouseEnter={e => (e.currentTarget.style.color = "#f87171")}
|
||||||
|
onMouseLeave={e => (e.currentTarget.style.color = "#334155")}
|
||||||
>
|
>
|
||||||
<LogOut className="w-3.5 h-3.5" />
|
<LogOut className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ export function DashboardHeader() {
|
|||||||
return () => clearInterval(i);
|
return () => clearInterval(i);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const hour = now.getHours();
|
const h = now.getHours();
|
||||||
const greeting = hour < 6 ? "Доброй ночи" : hour < 12 ? "Доброе утро" : hour < 17 ? "Добрый день" : "Добрый вечер";
|
const greeting = h < 6 ? "Доброй ночи" : h < 12 ? "Доброе утро" : h < 17 ? "Добрый день" : "Добрый вечер";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start justify-between pt-1">
|
<div className="flex items-start justify-between pt-1 pb-2">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-white">
|
<h1 className="text-2xl font-bold text-white">
|
||||||
{greeting}, <span className="gradient-text">Daniil</span> 👋
|
{greeting}, <span className="gradient-text">Daniil</span> 👋
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-slate-500 text-sm mt-0.5">
|
<p className="text-sm mt-0.5" style={{ color: "#475569" }}>
|
||||||
{DAYS[now.getDay()]}, {now.getDate()} {MONTHS[now.getMonth()]} {now.getFullYear()}
|
{DAYS[now.getDay()]}, {now.getDate()} {MONTHS[now.getMonth()]} {now.getFullYear()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,8 +28,8 @@ export function DashboardHeader() {
|
|||||||
<div className="text-2xl font-light text-white tabular-nums">
|
<div className="text-2xl font-light text-white tabular-nums">
|
||||||
{now.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" })}
|
{now.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" })}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-slate-600">
|
<div className="text-xs tabular-nums" style={{ color: "#334155" }}>
|
||||||
{now.toLocaleTimeString("ru-RU", { second: "2-digit" })} сек
|
{String(now.getSeconds()).padStart(2,"0")} сек
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user