File size: 22,368 Bytes
07381d2 b94499a 07381d2 b94499a 07381d2 b94499a 07381d2 b94499a 07381d2 b94499a 07381d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard — NSFWStudio</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<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=Inter:opsz,wght@14..32,300..800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['Inter','system-ui','sans-serif'], mono: ['JetBrains Mono','monospace'] },
colors: {
bg: 'oklch(0.07 0.006 285)', surface: 'oklch(0.12 0.02 285)', 'surface-2': 'oklch(0.16 0.025 285)', 'surface-3': 'oklch(0.20 0.028 285)',
fg: 'oklch(0.97 0.005 285)', muted: 'oklch(0.55 0.02 285)', primary: 'oklch(0.70 0.28 350)', 'primary-foreground': 'oklch(0.98 0.005 285)',
secondary: 'oklch(0.75 0.15 215)', accent: 'oklch(0.80 0.18 85)', border: 'oklch(0.22 0.02 285)', ring: 'oklch(0.70 0.28 350)',
destructive: 'oklch(0.55 0.22 25)', success: 'oklch(0.65 0.2 145)',
},
},
},
}
</script>
<style>
body { background-color: oklch(0.07 0.006 285); color: oklch(0.97 0.005 285); font-family: 'Inter', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
.glass { background: oklch(0.14 0.015 285 / 0.6); backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); border: 1px solid oklch(0.25 0.02 285 / 0.5); }
.gradient-text { background: linear-gradient(135deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
::selection { background: oklch(0.70 0.28 350 / 0.3); color: white; }
body::after { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:0.025; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat:repeat; background-size:256px 256px; }
.no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.tabular-nums { font-variant-numeric: tabular-nums; }
.card-3d { transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), box-shadow 0.4s ease; }
.card-3d:hover { transform: perspective(1000px) rotateY(-3deg) rotateX(2deg) translateZ(15px); box-shadow: 0 20px 40px rgba(0,0,0,0.35), 0 0 0 1px oklch(0.70 0.28 350 / 0.2); }
.hover-lift { transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease; }
.hover-lift:hover { transform: translateY(-6px) scale(1.02); box-shadow: 0 15px 30px rgba(0,0,0,0.3); }
.gradient-border-animated { position: relative; isolation: isolate; }
.gradient-border-animated::before { content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1.5px; background: linear-gradient(270deg, oklch(0.70 0.28 350), oklch(0.75 0.15 215), oklch(0.80 0.18 85), oklch(0.70 0.28 350)); background-size: 300% 100%; -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask-composite: xor; mask-composite: exclude; opacity: 0; transition: opacity 0.3s ease; }
.gradient-border-animated:hover::before { opacity: 1; }
@keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.animate-slide-up { animation: slideUp 0.5s ease-out forwards; }
.glow-ring:hover { box-shadow: 0 0 0 2px oklch(0.70 0.28 350 / 0.3), 0 0 20px oklch(0.70 0.28 350 / 0.1); }
</style>
</head>
<body class="antialiased">
<div id="root"></div>
<script type="text/babel">
const { useState } = React;
const Icon = ({ name, size = 20, className = "", strokeWidth = 2 }) => {
const ref = React.useRef(null);
React.useEffect(() => { if (ref.current) { const existing = ref.current.querySelector('svg'); if (existing) existing.remove(); const svg = lucide.createElement(lucide.icons[name]); if (svg) { svg.setAttribute('width',size); svg.setAttribute('height',size); svg.setAttribute('stroke-width',strokeWidth); ref.current.appendChild(svg); } } }, [name, size, className, strokeWidth]);
return <span ref={ref} className={`inline-flex items-center justify-center ${className}`} style={{ width: size, height: size }} />;
};
const navItems = [
{ name: 'Dashboard', icon: 'layout-dashboard', href: 'dashboard.html', active: true },
{ name: 'Generator', icon: 'sparkles', href: 'generator.html' },
{ name: 'Playground', icon: 'code', href: 'playground.html' },
{ name: 'Models', icon: 'box', href: 'models.html' },
{ name: 'Workflows', icon: 'workflow', href: 'workflows.html' },
{ name: 'Marketplace', icon: 'shopping-bag', href: 'marketplace.html' },
{ name: 'API Keys', icon: 'key', href: 'api-keys.html' },
{ divider: true },
{ name: 'Profile', icon: 'user', href: 'profile.html' },
{ name: 'Settings', icon: 'settings', href: 'settings.html' },
];
const Sidebar = ({ isOpen, setIsOpen }) => (
<>
{/* Mobile overlay */}
<div className={`fixed inset-0 z-40 lg:hidden transition-all ${isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setIsOpen(false)} />
</div>
<aside className={`fixed top-0 left-0 bottom-0 z-50 w-64 bg-surface border-r border-border flex flex-col transition-transform duration-300 lg:translate-x-0 ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}>
<div className="p-4 flex items-center gap-2.5 border-b border-border">
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center"><Icon name="sparkles" size={16} className="text-primary-foreground" /></div>
<span className="text-lg font-bold tracking-tight">NSFW<span className="text-muted">Studio</span></span>
</div>
<nav className="flex-1 p-3 space-y-1 overflow-y-auto no-scrollbar">
{navItems.map((item, i) => item.divider ? <div key={i} className="my-3 border-t border-border" /> : (
<a key={i} href={item.href} className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors ${item.active ? 'bg-primary/10 text-primary border border-primary/20' : 'text-muted hover:text-fg hover:bg-surface-2 border border-transparent'}`}>
<Icon name={item.icon} size={18} />{item.name}
</a>
))}
</nav>
<div className="p-4 border-t border-border">
<a href="documentation.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">
<Icon name="book-open" size={18} />Documentation
</a>
<a href="api-reference.html" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-muted hover:text-fg hover:bg-surface-2 transition-colors">
<Icon name="file-code" size={18} />API Reference
</a>
</div>
</aside>
</>
);
const App = () => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const stats = [
{ label: 'Generations', value: '12,847', change: '+14.2%', icon: 'image', color: 'text-primary', bg: 'bg-primary/10' },
{ label: 'Revenue', value: '$8,492', change: '+23.1%', icon: 'dollar-sign', color: 'text-success', bg: 'bg-success/10' },
{ label: 'Followers', value: '2,341', change: '+8.7%', icon: 'users', color: 'text-secondary', bg: 'bg-secondary/10' },
{ label: 'API Calls', value: '284K', change: '+31.5%', icon: 'activity', color: 'text-accent', bg: 'bg-accent/10' },
];
const [hoveredStat, setHoveredStat] = useState(null);
const recentGens = [
{ title: 'Crimson Seductress', model: 'EroticDream XL', time: '2m ago', status: 'completed', img: 'http://static.photos/cyberpunk/200x200/5' },
{ title: 'Dark Temptation', model: 'SinfulAnime XL', time: '5m ago', status: 'completed', img: 'http://static.photos/people/200x200/6' },
{ title: 'Velvet Dungeon', model: 'Fantasy Flesh XL', time: '8m ago', status: 'completed', img: 'http://static.photos/nature/200x200/7' },
{ title: 'Neon Curves', model: 'PhotoReal Erotica', time: '12m ago', status: 'completed', img: 'http://static.photos/technology/200x200/8' },
{ title: 'Shibari Art', model: 'DarkSeduction v3', time: '15m ago', status: 'processing', img: 'http://static.photos/people/200x200/9' },
];
const activity = [
{ text: 'New follower: @dark_pixel', time: '1m ago', icon: 'user-plus', color: 'text-secondary' },
{ text: 'Your model earned $49.00', time: '3m ago', icon: 'dollar-sign', color: 'text-success' },
{ text: 'Workflow "Auto Tag" completed', time: '7m ago', icon: 'check-circle', color: 'text-primary' },
{ text: 'Comment on "Neon Cityscape"', time: '10m ago', icon: 'message-circle', color: 'text-accent' },
{ text: 'API key rotation reminder', time: '1h ago', icon: 'key', color: 'text-muted' },
];
const topModels = [
{ name: 'DreamVision XL', runs: '45.2K', revenue: '$2,340', trend: '+12%' },
{ name: 'CyberPunk v3', runs: '28.1K', revenue: '$1,890', trend: '+8%' },
{ name: 'Anime Diffusion', runs: '19.7K', revenue: '$1,230', trend: '+21%' },
];
return (
<div className="min-h-screen bg-bg">
<Sidebar isOpen={sidebarOpen} setIsOpen={setSidebarOpen} />
<div className="lg:ml-64">
{/* Top bar */}
<header className="sticky top-0 z-30 glass border-b border-border">
<div className="flex items-center justify-between px-4 sm:px-6 h-16">
<div className="flex items-center gap-4">
<button onClick={() => setSidebarOpen(true)} className="lg:hidden p-2 rounded-lg hover:bg-surface-2 text-muted"><Icon name="menu" size={20} /></button>
<div className="hidden sm:flex items-center gap-2 px-4 py-2 rounded-xl bg-surface-2 border border-border text-muted text-sm w-72">
<Icon name="search" size={16} />
<span>Search anything...</span>
</div>
</div>
<div className="flex items-center gap-3">
<button className="p-2 rounded-lg hover:bg-surface-2 text-muted hover:text-fg transition-colors relative">
<Icon name="bell" size={20} />
<span className="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-primary" />
</button>
<a href="generator.html" className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:brightness-110 transition-all">+ New</a>
<a href="profile.html" className="w-9 h-9 rounded-lg overflow-hidden border border-border">
<img src="http://static.photos/people/100x100/10" alt="" className="w-full h-full object-cover" />
</a>
</div>
</div>
</header>
<main className="p-4 sm:p-6 lg:p-8">
<div className="mb-8">
<h1 className="text-2xl sm:text-3xl font-bold mb-1">Welcome back, <span className="gradient-text">Neon</span></h1>
<p className="text-muted">Here's what's happening with your creations today.</p>
</div>
{/* Stats */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
{stats.map((s, i) => (
<div key={i} className="rounded-2xl p-5 bg-surface border border-border hover:border-primary/30 transition-all duration-500 card-3d gradient-border-animated"
style={{animationDelay: `${i * 0.1}s`, animation: 'slideUp 0.5s ease-out forwards', opacity: 0}}
onMouseEnter={() => setHoveredStat(i)} onMouseLeave={() => setHoveredStat(null)}>
<div className="flex items-center justify-between mb-3">
<div className={`w-10 h-10 rounded-xl ${s.bg} flex items-center justify-center transition-transform duration-300 ${hoveredStat === i ? 'scale-110' : ''}`}><Icon name={s.icon} size={18} className={s.color} /></div>
<span className="text-xs font-bold text-success bg-success/10 px-2.5 py-1 rounded-full border border-success/20">{s.change}</span>
</div>
<div className="text-2xl font-bold text-fg tabular-nums">{s.value}</div>
<div className="text-xs text-muted mt-1">{s.label}</div>
</div>
))}
</div>
<div className="grid lg:grid-cols-3 gap-6">
{/* Recent Generations */}
<div className="lg:col-span-2 rounded-2xl p-6 bg-surface border border-border">
<div className="flex items-center justify-between mb-5">
<h2 className="text-lg font-semibold">Recent Generations</h2>
<a href="generator.html" className="text-sm text-primary hover:underline flex items-center gap-1">View all <Icon name="arrow-right" size={14} /></a>
</div>
<div className="space-y-3">
{recentGens.map((g, i) => (
<div key={i} className="flex items-center gap-4 p-3 rounded-xl hover:bg-surface-2/50 transition-colors cursor-pointer group">
<img src={g.img} alt="" className="w-12 h-12 rounded-xl object-cover border border-border" />
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-fg truncate">{g.title}</div>
<div className="text-xs text-muted">{g.model} · {g.time}</div>
</div>
<span className={`px-2.5 py-1 rounded-full text-xs font-semibold ${g.status === 'completed' ? 'bg-success/10 text-success border border-success/20' : 'bg-primary/10 text-primary border border-primary/20'}`}>
{g.status}
</span>
</div>
))}
</div>
</div>
{/* Right column */}
<div className="space-y-6">
{/* Activity */}
<div className="rounded-2xl p-6 bg-surface border border-border">
<h2 className="text-lg font-semibold mb-4">Activity</h2>
<div className="space-y-4">
{activity.map((a, i) => (
<div key={i} className="flex items-start gap-3">
<div className={`mt-0.5 ${a.color}`}><Icon name={a.icon} size={16} /></div>
<div className="flex-1 min-w-0">
<div className="text-sm text-fg/90">{a.text}</div>
<div className="text-xs text-muted mt-0.5">{a.time}</div>
</div>
</div>
))}
</div>
</div>
{/* Top Models */}
<div className="rounded-2xl p-6 bg-surface border border-border">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold">Top Models</h2>
<a href="models.html" className="text-xs text-primary hover:underline">See all</a>
</div>
<div className="space-y-3">
{topModels.map((m, i) => (
<div key={i} className="flex items-center justify-between p-3 rounded-xl hover:bg-surface-2/50 transition-colors cursor-pointer">
<div className="flex items-center gap-3">
<span className="w-6 h-6 rounded-full bg-surface-2 flex items-center justify-center text-xs font-bold text-muted">{i+1}</span>
<div>
<div className="text-sm font-medium text-fg">{m.name}</div>
<div className="text-xs text-muted">{m.runs} runs</div>
</div>
</div>
<div className="text-right">
<div className="text-sm font-semibold text-fg">{m.revenue}</div>
<div className="text-xs text-success">{m.trend}</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* Quick Actions */}
<div className="mt-8 grid grid-cols-2 sm:grid-cols-4 gap-3">
{[
{ label: 'Generate Image', icon: 'image', color: 'text-primary', bg: 'bg-primary/10', href: 'generator.html' },
{ label: 'Create Workflow', icon: 'workflow', color: 'text-secondary', bg: 'bg-secondary/10', href: 'workflows.html' },
{ label: 'Train Model', icon: 'box', color: 'text-success', bg: 'bg-success/10', href: 'models.html' },
{ label: 'Explore Marketplace', icon: 'shopping-bag', color: 'text-accent', bg: 'bg-accent/10', href: 'marketplace.html' },
].map((a, i) => (
<a key={i} href={a.href} className="group flex flex-col items-center gap-3 p-5 rounded-2xl bg-surface border border-border hover:border-surface-3 transition-all hover:-translate-y-0.5">
<div className={`w-12 h-12 rounded-xl ${a.bg} flex items-center justify-center group-hover:scale-110 transition-transform`}><Icon name={a.icon} size={22} className={a.color} /></div>
<span className="text-sm font-medium text-fg">{a.label}</span>
</a>
))}
</div>
</main>
</div>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html> |