refactor(quieter): tone down AdminPanel + AdminDashboard

Per /impeccable audit findings: admin overview pages concentrated
AI-aesthetic tells. Apply the 'quieter' refinement — strip the loudness,
keep the structure, preserve state-conveying motion.

AdminPanel.tsx
  • Hero h1 — drop bg-clip-text + 3-stop gradient. Solid text-dark-50
    (the absolute ban applied; type weight + size carry hierarchy now).
  • 'Online' dot — drop the 10px-radius glow shadow; the dot + pulse
    animation alone signal liveness.
  • GlassCard — drop the 3D perspective-tilt mouse-tracking (decorative
    motion that didn't convey state), drop the staggered enter
    animation (no orchestrated page-load), drop the top hairline-glow
    gradient, drop the inner shine overlay + drop-shadow-sm on the
    count badge. Keep backdrop-blur on the card body itself — the
    component is literally named GlassCard, the glass identity is the
    point. Hover state stays.
  • Stats-bar pills (2 places) — drop backdrop-blur. Tiny data cells
    don't need glass.
  • Search empty-state icon container — drop backdrop-blur. Decorative
    on a non-interactive surface.

AdminDashboard.tsx
  • Strip backdrop-blur from all 9 stat / node / panel cards. They
    were using it as ambient noise; solid bg-dark-800/{30,50} reads
    better and renders faster.
  • Daily-sales progress bar — drop the from-accent-600 to-accent-400
    gradient (and hover gradient swap). Solid bg-accent-500, hover
    bg-accent-400. Width still animates (functional state change).
  • Animate-pulse on the connected-node status dot kept — it conveys
    'live' state, which is exactly what motion is for in product UI.
This commit is contained in:
c0mrade
2026-05-26 19:11:17 +03:00
parent 9afa7f131f
commit b81346dbb2
2 changed files with 23 additions and 63 deletions

View File

@@ -178,7 +178,7 @@ function StatCard({ title, value, subtitle, icon, color, trend }: StatCardProps)
};
return (
<div className="rounded-xl border border-dark-700 bg-dark-800/50 p-5 backdrop-blur transition-colors hover:border-dark-600">
<div className="rounded-xl border border-dark-700 bg-dark-800/50 p-5 transition-colors hover:border-dark-600">
<div className="mb-3 flex items-start justify-between">
<div className={`rounded-lg p-2.5 ${colorClasses[color]}`}>{icon}</div>
{trend && (
@@ -230,7 +230,7 @@ function NodeCard({ node, onRestart, onToggle, isLoading }: NodeCardProps) {
return (
<div
className={`rounded-xl border bg-dark-800/50 backdrop-blur ${node.is_disabled ? 'border-dark-700' : node.is_connected ? 'border-success-500/30' : 'border-error-500/30'} p-4 transition-colors hover:border-dark-600`}
className={`rounded-xl border bg-dark-800/50 ${node.is_disabled ? 'border-dark-700' : node.is_connected ? 'border-success-500/30' : 'border-error-500/30'} p-4 transition-colors hover:border-dark-600`}
>
<div className="mb-3 flex items-start justify-between">
<div className="flex items-center gap-3">
@@ -499,7 +499,7 @@ export default function AdminDashboard() {
</div>
{/* Nodes Section */}
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5 backdrop-blur">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5">
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="rounded-lg bg-accent-500/20 p-2.5 text-accent-400">
@@ -568,7 +568,7 @@ export default function AdminDashboard() {
{/* Revenue and Subscriptions */}
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
{/* Revenue Chart */}
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5 backdrop-blur">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5">
<div className="mb-4 flex items-center gap-3">
<div className="rounded-lg bg-warning-500/20 p-2.5 text-warning-400">
<ChartBarIcon />
@@ -602,7 +602,7 @@ export default function AdminDashboard() {
</div>
{/* Subscription Stats */}
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5 backdrop-blur">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5">
<div className="mb-4 flex items-center gap-3">
<div className="rounded-lg bg-accent-500/20 p-2.5 text-accent-400">
<SparklesIcon />
@@ -701,7 +701,7 @@ export default function AdminDashboard() {
{/* Tariff Stats */}
{stats?.tariff_stats && stats.tariff_stats.tariffs.length > 0 && (
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5 backdrop-blur">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-5">
<div className="mb-4 flex items-center gap-3">
<div className="rounded-lg bg-success-500/20 p-2.5 text-success-400">
<TagIcon />
@@ -778,7 +778,7 @@ export default function AdminDashboard() {
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
{/* Top Referrers */}
{referrers && (referrers.by_earnings.length > 0 || referrers.by_invited.length > 0) && (
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 backdrop-blur sm:p-5">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 sm:p-5">
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-2 sm:gap-3">
<div className="rounded-lg bg-accent-500/20 p-2 text-accent-400 sm:p-2.5">
@@ -918,7 +918,7 @@ export default function AdminDashboard() {
{/* Top Campaigns */}
{campaigns && campaigns.campaigns.length > 0 && (
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 backdrop-blur sm:p-5">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 sm:p-5">
<div className="mb-4 flex items-center gap-2 sm:gap-3">
<div className="rounded-lg bg-warning-500/20 p-2 text-warning-400 sm:p-2.5">
<MegaphoneIcon />
@@ -981,7 +981,7 @@ export default function AdminDashboard() {
{/* Recent Payments */}
{payments && payments.payments.length > 0 && (
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 backdrop-blur sm:p-5">
<div className="rounded-xl border border-dark-700 bg-dark-800/30 p-4 sm:p-5">
<div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="rounded-lg bg-success-500/20 p-2 text-success-400 sm:p-2.5">

View File

@@ -690,7 +690,7 @@ const StatsBar = memo(function StatsBar({ systemInfo, dashboardStats, loading }:
<div
key={i}
className={cn(
'flex min-w-0 flex-1 items-center gap-2 rounded-xl border border-dark-700/50 bg-dark-800/40 px-3 py-2 backdrop-blur-lg transition-all duration-200',
'flex min-w-0 flex-1 items-center gap-2 rounded-xl border border-dark-700/50 bg-dark-800/40 px-3 py-2 transition-all duration-200',
'light:border-champagne-300/50 light:bg-champagne-100/60',
loading && 'animate-pulse',
)}
@@ -740,30 +740,9 @@ const GlassCard = memo(function GlassCard({ section, index, searchTerm }: GlassC
const { t } = useTranslation();
const hasPermission = usePermissionStore((state) => state.hasPermission);
const [hoveredItem, setHoveredItem] = useState<number | null>(null);
const cardRef = useRef<HTMLDivElement>(null);
const [tilt, setTilt] = useState({ x: 0, y: 0 });
const tiltRef = useRef({ x: 0, y: 0 });
const rafRef = useRef(0);
useEffect(() => () => cancelAnimationFrame(rafRef.current), []);
const onMouseMove = useCallback((e: React.MouseEvent) => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
tiltRef.current = {
x: ((e.clientY - rect.top) / rect.height - 0.5) * -2.5,
y: ((e.clientX - rect.left) / rect.width - 0.5) * 2.5,
};
cancelAnimationFrame(rafRef.current);
rafRef.current = requestAnimationFrame(() => {
setTilt({ ...tiltRef.current });
});
}, []);
const onMouseLeave = useCallback(() => {
cancelAnimationFrame(rafRef.current);
setTilt({ x: 0, y: 0 });
}, []);
// 3D mouse-tracking tilt removed: decorative motion that didn't convey state.
// Hover styling alone signals interactivity.
void index;
const visibleItems = useMemo(
() =>
@@ -796,31 +775,14 @@ const GlassCard = memo(function GlassCard({ section, index, searchTerm }: GlassC
if (visibleItems.length === 0) return null;
return (
<div
ref={cardRef}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
className="group/card relative overflow-hidden rounded-2xl border border-dark-700/50 bg-dark-800/30 backdrop-blur-xl transition-all duration-300 hover:border-dark-600/80 hover:shadow-lg light:border-champagne-300/50 light:bg-champagne-100/40 light:hover:border-champagne-400/60"
style={{
transform: `perspective(800px) rotateX(${tilt.x}deg) rotateY(${tilt.y}deg)`,
animation: `adminCardEnter 0.5s cubic-bezier(0.22, 1, 0.36, 1) ${index * 60}ms both`,
}}
>
{/* Top glow line */}
<div
className="absolute left-0 right-0 top-0 h-px opacity-50 transition-all duration-300 group-hover/card:h-0.5 group-hover/card:opacity-100"
style={{ background: section.gradient }}
/>
<div className="group/card relative overflow-hidden rounded-2xl border border-dark-700/50 bg-dark-800/30 backdrop-blur-xl transition-colors duration-200 hover:border-dark-600/80 light:border-champagne-300/50 light:bg-champagne-100/40 light:hover:border-champagne-400/60">
{/* Header */}
<div className="flex items-center gap-2.5 border-b border-dark-700/30 px-3.5 py-2.5 light:border-champagne-300/30">
<div
className="relative flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-lg shadow-md"
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg"
style={{ background: section.gradient }}
>
{/* Shine overlay */}
<div className="absolute inset-0 bg-gradient-to-br from-transparent via-transparent to-white/25" />
<span className="relative text-xs font-bold text-white drop-shadow-sm" aria-hidden="true">
<span className="text-xs font-bold text-dark-50" aria-hidden="true">
{visibleItems.length}
</span>
</div>
@@ -836,16 +798,13 @@ const GlassCard = memo(function GlassCard({ section, index, searchTerm }: GlassC
key={item.to}
to={item.to}
className={cn(
'group/item flex items-center gap-2.5 rounded-xl border border-transparent px-2 py-1.5 transition-all duration-150',
'group/item flex items-center gap-2.5 rounded-xl border border-transparent px-2 py-1.5 transition-colors duration-150',
hoveredItem === i
? 'border-dark-600/50 bg-dark-700/30 light:border-champagne-400/40 light:bg-champagne-200/50'
: 'hover:border-dark-600/50 hover:bg-dark-700/30 light:hover:border-champagne-400/40 light:hover:bg-champagne-200/50',
)}
onMouseEnter={() => setHoveredItem(i)}
onMouseLeave={() => setHoveredItem(null)}
style={{
animation: `adminItemEnter 0.35s cubic-bezier(0.22, 1, 0.36, 1) ${index * 60 + i * 20}ms both`,
}}
>
{/* Icon */}
<div
@@ -978,7 +937,7 @@ export default function AdminPanel() {
<div
key={i}
className={cn(
'flex items-center gap-2 rounded-xl border border-dark-700/50 bg-dark-800/40 px-2.5 py-2 backdrop-blur-lg',
'flex items-center gap-2 rounded-xl border border-dark-700/50 bg-dark-800/40 px-2.5 py-2',
'light:border-champagne-300/50 light:bg-champagne-100/60',
loading && 'animate-pulse',
)}
@@ -1003,12 +962,13 @@ export default function AdminPanel() {
{/* Hero + Search */}
<div className="flex shrink-0 flex-wrap items-center gap-3">
<h1 className="bg-gradient-to-r from-dark-50 via-dark-300 to-accent-400 bg-clip-text text-lg font-extrabold tracking-tight text-transparent light:from-champagne-900 light:via-champagne-600 light:to-accent-600 sm:text-xl">
<h1 className="text-lg font-bold tracking-tight text-dark-50 light:text-champagne-900 sm:text-xl">
{t('admin.panel.title')}
</h1>
<div className="flex items-center gap-1.5 text-xs text-dark-400 light:text-champagne-500">
<div
className="h-1.5 w-1.5 rounded-full bg-success-400 shadow-[0_0_10px_rgba(var(--color-success-400),0.6)]"
<span
aria-hidden="true"
className="h-1.5 w-1.5 rounded-full bg-success-400"
style={{ animation: 'adminPulse 2s ease-in-out infinite' }}
/>
{t('admin.panel.statsOnline')}
@@ -1060,7 +1020,7 @@ export default function AdminPanel() {
role="status"
aria-live="polite"
>
<div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-dark-700/50 bg-dark-800/40 text-dark-500 backdrop-blur-lg [&>svg]:h-6 [&>svg]:w-6">
<div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-dark-700/50 bg-dark-800/40 text-dark-500 [&>svg]:h-6 [&>svg]:w-6">
{icons.search}
</div>
<h3 className="text-sm font-semibold text-dark-200 light:text-champagne-800">