forked from elizaOS/eliza
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from romain-lfg/feat/dashboard-ui
feat(dashboard): implement modern dashboard UI
- Loading branch information
Showing
2 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
'use client'; | ||
|
||
import Link from 'next/link'; | ||
import { usePathname } from 'next/navigation'; | ||
import { | ||
IconHome, | ||
IconBriefcase, | ||
IconRobot, | ||
IconPlus, | ||
IconChartBar | ||
} from '@tabler/icons-react'; | ||
|
||
const navigation = [ | ||
{ name: 'Overview', href: '/dashboard', icon: IconHome }, | ||
{ name: 'Bounties', href: '/dashboard/bounties', icon: IconBriefcase }, | ||
{ name: 'VSA Profile', href: '/dashboard/profile', icon: IconRobot }, | ||
]; | ||
|
||
export default function DashboardLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
const pathname = usePathname(); | ||
|
||
return ( | ||
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black"> | ||
{/* Side Navigation */} | ||
<nav className="fixed inset-y-0 left-0 w-64 bg-gray-900/50 backdrop-blur-xl border-r border-white/[0.05]"> | ||
<div className="flex h-16 items-center px-6 border-b border-white/[0.05]"> | ||
<Link | ||
href="/" | ||
className="text-xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 text-transparent bg-clip-text hover:from-indigo-500 hover:to-purple-500" | ||
> | ||
LFG | ||
</Link> | ||
</div> | ||
<div className="px-2 py-4"> | ||
{navigation.map((item) => { | ||
const Icon = item.icon; | ||
const isActive = pathname === item.href; | ||
return ( | ||
<Link | ||
key={item.name} | ||
href={item.href} | ||
className={` | ||
group relative flex items-center gap-x-3 rounded-lg p-2 text-sm font-semibold leading-6 mb-1 | ||
${isActive | ||
? 'text-white bg-white/[0.06]' | ||
: 'text-gray-300 hover:text-white hover:bg-white/[0.06]' | ||
} | ||
`} | ||
> | ||
<Icon | ||
className={`h-5 w-5 shrink-0 ${isActive ? 'text-indigo-400' : 'text-gray-400 group-hover:text-white'}`} | ||
aria-hidden="true" | ||
/> | ||
{item.name} | ||
</Link> | ||
); | ||
})} | ||
</div> | ||
<div className="absolute bottom-4 left-0 right-0 px-4"> | ||
<button className="navbar-button w-full flex items-center justify-center gap-x-2"> | ||
<IconPlus className="h-5 w-5" /> | ||
Create Bounty | ||
</button> | ||
</div> | ||
</nav> | ||
|
||
{/* Main Content */} | ||
<main className="pl-64"> | ||
<div className="sticky top-0 z-40 h-16 bg-gray-900/50 backdrop-blur-xl border-b border-white/[0.05]"> | ||
<div className="flex h-16 items-center justify-between px-6"> | ||
<h1 className="text-lg font-semibold text-white"> | ||
{navigation.find((item) => item.href === pathname)?.name || 'Dashboard'} | ||
</h1> | ||
<div className="flex items-center gap-x-4"> | ||
<div className="flex items-center gap-x-2"> | ||
<div className="h-2 w-2 rounded-full bg-emerald-400"></div> | ||
<span className="text-sm text-gray-300">VSA Active</span> | ||
</div> | ||
<div className="text-sm text-gray-300"> | ||
0x742...3ab4 | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="p-6"> | ||
{children} | ||
</div> | ||
</main> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { IconBriefcase, IconCoin, IconCheck } from '@tabler/icons-react'; | ||
|
||
const stats = [ | ||
{ name: 'Active Bounties', value: '2', change: '+2 this week', icon: IconBriefcase }, | ||
{ name: 'Total Earnings', value: '1.8 ETH', change: '+0.5 ETH this month', icon: IconCoin }, | ||
{ name: 'Success Rate', value: '100%', change: '8 completed', icon: IconCheck }, | ||
]; | ||
|
||
const recentActivity = [ | ||
{ | ||
id: 1, | ||
type: 'match', | ||
title: 'VSA found a matching bounty', | ||
description: 'Smart Contract Security Review - 0.8 ETH', | ||
time: '2 hours ago', | ||
}, | ||
{ | ||
id: 2, | ||
type: 'completed', | ||
title: 'Bounty completed', | ||
description: 'Frontend Bug Fix - 0.3 ETH', | ||
time: '1 day ago', | ||
}, | ||
{ | ||
id: 3, | ||
type: 'payout', | ||
title: 'Payment received', | ||
description: 'API Integration - 0.7 ETH', | ||
time: '3 days ago', | ||
}, | ||
]; | ||
|
||
export default function DashboardPage() { | ||
return ( | ||
<div className="max-w-7xl mx-auto space-y-8"> | ||
{/* Stats */} | ||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> | ||
{stats.map((stat) => { | ||
const Icon = stat.icon; | ||
return ( | ||
<div | ||
key={stat.name} | ||
className="relative overflow-hidden rounded-2xl bg-gray-900/50 p-6 backdrop-blur-xl border border-white/[0.05] shadow-lg shadow-black/20" | ||
> | ||
<dt> | ||
<div className="absolute rounded-lg bg-gradient-to-r from-indigo-600/20 to-purple-600/20 p-3"> | ||
<Icon className="h-6 w-6 text-indigo-400" aria-hidden="true" /> | ||
</div> | ||
<p className="ml-16 truncate text-sm font-medium text-gray-300"> | ||
{stat.name} | ||
</p> | ||
</dt> | ||
<dd className="ml-16 flex items-baseline"> | ||
<p className="text-2xl font-semibold text-white">{stat.value}</p> | ||
<p className="ml-2 flex items-baseline text-sm font-medium text-emerald-400"> | ||
{stat.change} | ||
</p> | ||
</dd> | ||
</div> | ||
); | ||
})} | ||
</div> | ||
|
||
{/* Recent Activity */} | ||
<div className="rounded-2xl bg-gray-900/50 p-6 backdrop-blur-xl border border-white/[0.05] shadow-lg shadow-black/20"> | ||
<h2 className="text-base font-semibold text-white mb-6">Recent Activity</h2> | ||
<div className="flow-root"> | ||
<ul role="list" className="-mb-8"> | ||
{recentActivity.map((item, itemIdx) => ( | ||
<li key={item.id}> | ||
<div className="relative pb-8"> | ||
{itemIdx !== recentActivity.length - 1 ? ( | ||
<span className="absolute left-4 top-4 -ml-px h-full w-0.5 bg-white/[0.05]" aria-hidden="true" /> | ||
) : null} | ||
<div className="relative flex space-x-3"> | ||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-800 ring-8 ring-black/50"> | ||
<div className={`h-2.5 w-2.5 rounded-full ${ | ||
item.type === 'match' ? 'bg-indigo-400' : | ||
item.type === 'completed' ? 'bg-emerald-400' : 'bg-purple-400' | ||
}`} /> | ||
</div> | ||
<div className="flex min-w-0 flex-1 justify-between space-x-4"> | ||
<div> | ||
<p className="text-sm text-white">{item.title}</p> | ||
<p className="text-sm text-gray-300">{item.description}</p> | ||
</div> | ||
<div className="whitespace-nowrap text-right text-sm text-gray-300"> | ||
{item.time} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |