Skip to content

Commit

Permalink
Merge pull request #7 from romain-lfg/feat/dashboard-ui
Browse files Browse the repository at this point in the history
feat(dashboard): implement modern dashboard UI
  • Loading branch information
Okulon authored Feb 6, 2025
2 parents c6cea70 + ec68dc5 commit 8ac2e2d
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 0 deletions.
95 changes: 95 additions & 0 deletions frontend/app/dashboard/layout.tsx
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>
);
}
100 changes: 100 additions & 0 deletions frontend/app/dashboard/page.tsx
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>
);
}

0 comments on commit 8ac2e2d

Please sign in to comment.