sidebar
This commit is contained in:
parent
27807fc9d6
commit
9d579385d7
@ -24,7 +24,7 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Channel Partner Dashboard</title>
|
||||
<title>Cloudtopiaa reseller Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@ -31,7 +31,7 @@ import ResellerBilling from './pages/reseller/Billing';
|
||||
import ResellerSupport from './pages/reseller/Support';
|
||||
import ResellerReports from './pages/reseller/Reports';
|
||||
import ResellerTraining from './pages/reseller/Training';
|
||||
import ResellerLayout from './components/reseller/layout/ResellerLayout';
|
||||
import ResellerLayout from './components/Layout/ResellerLayout';
|
||||
import CookieConsent from './components/CookieConsent';
|
||||
import './index.css';
|
||||
|
||||
|
||||
92
src/components/Layout/ResellerLayout.tsx
Normal file
92
src/components/Layout/ResellerLayout.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import ResellerSidebar from './ResellerSidebar';
|
||||
|
||||
interface ResellerLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ResellerLayout: React.FC<ResellerLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-emerald-50 to-teal-50 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900">
|
||||
<div className="flex h-screen">
|
||||
{/* Sidebar */}
|
||||
<div className="flex-shrink-0">
|
||||
<ResellerSidebar />
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{/* Top Navigation Bar */}
|
||||
<div className="bg-white/90 dark:bg-slate-900/95 backdrop-blur-xl border-b border-slate-200/50 dark:border-slate-700/50 sticky top-0 z-40 shadow-sm">
|
||||
<div className="flex items-center justify-between px-8 py-5 h-20">
|
||||
{/* Left Side - Logo and Title */}
|
||||
<div className="flex items-center space-x-5">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-emerald-500 via-teal-500 to-cyan-500 rounded-xl flex items-center justify-center flex-shrink-0 shadow-lg">
|
||||
<div className="w-5 h-5 bg-white rounded-md shadow-sm"></div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-bold text-slate-900 dark:text-white leading-tight tracking-tight">
|
||||
Reseller Portal
|
||||
</h1>
|
||||
<p className="text-sm font-medium text-slate-500 dark:text-slate-400 leading-tight">
|
||||
Cloud Services Management
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Search, Notifications, User */}
|
||||
<div className="flex items-center space-x-6">
|
||||
{/* Search Bar */}
|
||||
<div className="relative flex-shrink-0">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search anything..."
|
||||
className="w-72 pl-12 pr-6 py-3 bg-slate-50 dark:bg-slate-800/80 border border-slate-200/50 dark:border-slate-600/50 rounded-xl text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-emerald-500/50 focus:border-emerald-500/50 transition-all duration-200 backdrop-blur-sm"
|
||||
/>
|
||||
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
|
||||
<svg className="w-5 h-5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Notifications */}
|
||||
<button className="relative p-3 text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100/80 dark:hover:bg-slate-700/50 rounded-xl transition-all duration-200 flex-shrink-0 group">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-5 5v-5zM4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 004.5 19.5z" />
|
||||
</svg>
|
||||
<span className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border-2 border-white dark:border-slate-900"></span>
|
||||
</button>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="w-px h-8 bg-slate-200 dark:bg-slate-700"></div>
|
||||
|
||||
{/* User Menu */}
|
||||
<div className="flex items-center space-x-4 flex-shrink-0 group cursor-pointer">
|
||||
<div className="text-right flex flex-col justify-center">
|
||||
<p className="text-sm font-semibold text-slate-900 dark:text-white leading-tight">John Reseller</p>
|
||||
<p className="text-xs font-medium text-slate-500 dark:text-slate-400 leading-tight">Tech Solutions Inc</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-emerald-500 via-teal-500 to-cyan-500 rounded-xl flex items-center justify-center flex-shrink-0 shadow-lg group-hover:shadow-xl transition-all duration-200">
|
||||
<span className="text-white text-sm font-bold">JR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Content */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="p-6">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResellerLayout;
|
||||
163
src/components/Layout/ResellerSidebar.tsx
Normal file
163
src/components/Layout/ResellerSidebar.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useAppSelector, useAppDispatch } from '../../store/hooks';
|
||||
import {
|
||||
Home,
|
||||
Users,
|
||||
Cloud,
|
||||
CreditCard,
|
||||
Headphones,
|
||||
BarChart3,
|
||||
Wallet,
|
||||
BookOpen,
|
||||
ShoppingBag,
|
||||
Award,
|
||||
HelpCircle,
|
||||
Settings,
|
||||
Menu,
|
||||
X,
|
||||
Sun,
|
||||
Moon,
|
||||
LogOut,
|
||||
Building,
|
||||
Target,
|
||||
TrendingUp,
|
||||
Package
|
||||
} from 'lucide-react';
|
||||
import { RootState } from '../../store';
|
||||
import { toggleTheme } from '../../store/slices/themeSlice';
|
||||
import { logout } from '../../store/slices/authSlice';
|
||||
import { cn } from '../../utils/cn';
|
||||
|
||||
const resellerNavigation = [
|
||||
{ name: 'Dashboard', href: '/reseller-dashboard', icon: Home },
|
||||
{ name: 'Customers', href: '/reseller-dashboard/customers', icon: Users },
|
||||
{ name: 'Cloud Instances', href: '/reseller-dashboard/instances', icon: Cloud },
|
||||
{ name: 'Billing', href: '/reseller-dashboard/billing', icon: CreditCard },
|
||||
{ name: 'Support', href: '/reseller-dashboard/support', icon: Headphones },
|
||||
{ name: 'Reports', href: '/reseller-dashboard/reports', icon: BarChart3 },
|
||||
{ name: 'Wallet', href: '/reseller-dashboard/wallet', icon: Wallet },
|
||||
{ name: 'Training', href: '/reseller-dashboard/training', icon: BookOpen },
|
||||
{ name: 'Marketplace', href: '/reseller-dashboard/marketplace', icon: ShoppingBag },
|
||||
{ name: 'Certifications', href: '/reseller-dashboard/certifications', icon: Award },
|
||||
{ name: 'Knowledge Base', href: '/reseller-dashboard/knowledge-base', icon: HelpCircle },
|
||||
{ name: 'Settings', href: '/reseller-dashboard/settings', icon: Settings },
|
||||
];
|
||||
|
||||
const ResellerSidebar: React.FC = () => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
const location = useLocation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { theme } = useAppSelector((state: RootState) => state.theme);
|
||||
const { user } = useAppSelector((state: RootState) => state.auth);
|
||||
|
||||
const handleLogout = () => {
|
||||
dispatch(logout());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"flex flex-col h-full bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900 border-r border-slate-700/50 transition-all duration-300",
|
||||
isCollapsed ? "w-16" : "w-64"
|
||||
)}>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-slate-700/50">
|
||||
{!isCollapsed && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-8 h-8 bg-gradient-to-r from-emerald-500 to-teal-500 rounded-lg flex items-center justify-center">
|
||||
<Cloud className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<span className="text-lg font-semibold text-white">
|
||||
Reseller
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
className="p-1 rounded-md hover:bg-slate-700/50 transition-colors"
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<Menu className="w-6 h-6 text-slate-400" />
|
||||
) : (
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className={cn(
|
||||
"flex-1 py-4 space-y-1 overflow-y-auto",
|
||||
isCollapsed ? "px-3" : "px-2"
|
||||
)}>
|
||||
{resellerNavigation.map((item) => {
|
||||
const isActive = location.pathname === item.href;
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className={cn(
|
||||
"flex items-center text-sm font-medium rounded-xl transition-all duration-200 group",
|
||||
isCollapsed ? "px-2 py-3 justify-center" : "px-4 py-3",
|
||||
isActive
|
||||
? "bg-gradient-to-r from-emerald-500/20 to-teal-500/20 text-emerald-400 border border-emerald-500/30 shadow-lg"
|
||||
: "text-slate-300 hover:bg-slate-800/50 hover:text-white"
|
||||
)}
|
||||
>
|
||||
<item.icon className={cn(
|
||||
"flex-shrink-0 transition-colors duration-200",
|
||||
isCollapsed ? "w-6 h-6" : "w-5 h-5",
|
||||
isActive
|
||||
? "text-emerald-400"
|
||||
: "text-slate-400 group-hover:text-white"
|
||||
)} />
|
||||
{!isCollapsed && (
|
||||
<span className="ml-3">{item.name}</span>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* User Profile & Actions */}
|
||||
<div className="border-t border-slate-700/50 p-4 space-y-2">
|
||||
{/* Theme Toggle */}
|
||||
<button
|
||||
onClick={() => dispatch(toggleTheme())}
|
||||
className={cn(
|
||||
"w-full flex items-center rounded-xl text-sm font-medium transition-all duration-200",
|
||||
isCollapsed ? "justify-center px-2 py-3" : "px-4 py-3",
|
||||
"text-slate-300 hover:bg-slate-800/50 hover:text-white"
|
||||
)}
|
||||
title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<Sun className="w-6 h-6 text-amber-400" />
|
||||
) : (
|
||||
<Moon className="w-6 h-6 text-slate-400" />
|
||||
)}
|
||||
{!isCollapsed && (
|
||||
<span className="ml-3">
|
||||
{theme === 'dark' ? 'Light Mode' : 'Dark Mode'}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Logout */}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className={cn(
|
||||
"w-full flex items-center rounded-xl text-sm font-medium transition-all duration-200",
|
||||
isCollapsed ? "justify-center px-2 py-3" : "px-4 py-3",
|
||||
"text-red-400 hover:bg-red-500/10 hover:text-red-300"
|
||||
)}
|
||||
title="Logout"
|
||||
>
|
||||
<LogOut className="w-6 h-6" />
|
||||
{!isCollapsed && <span className="ml-3">Logout</span>}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResellerSidebar;
|
||||
@ -82,7 +82,7 @@ const Sidebar: React.FC = () => {
|
||||
className="p-1 rounded-md hover:bg-secondary-100 dark:hover:bg-secondary-800 transition-colors"
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<Menu className="w-5 h-5 text-secondary-600 dark:text-secondary-400" />
|
||||
<Menu className="w-6 h-6 text-secondary-600 dark:text-secondary-400" />
|
||||
) : (
|
||||
<X className="w-5 h-5 text-secondary-600 dark:text-secondary-400" />
|
||||
)}
|
||||
@ -90,7 +90,10 @@ const Sidebar: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 px-2 py-4 space-y-1 overflow-y-auto">
|
||||
<nav className={cn(
|
||||
"flex-1 py-4 space-y-1 overflow-y-auto",
|
||||
isCollapsed ? "px-3" : "px-2"
|
||||
)}>
|
||||
{navigation.map((item) => {
|
||||
const isActive = location.pathname === item.href;
|
||||
return (
|
||||
@ -98,14 +101,16 @@ const Sidebar: React.FC = () => {
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className={cn(
|
||||
"flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors group",
|
||||
"flex items-center text-sm font-medium rounded-md transition-colors group",
|
||||
isCollapsed ? "px-2 py-3 justify-center" : "px-3 py-2",
|
||||
isActive
|
||||
? "bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300"
|
||||
: "text-secondary-700 hover:bg-secondary-100 hover:text-secondary-900 dark:text-secondary-300 dark:hover:bg-secondary-800 dark:hover:text-white"
|
||||
)}
|
||||
>
|
||||
<item.icon className={cn(
|
||||
"flex-shrink-0 w-5 h-5 transition-colors",
|
||||
"flex-shrink-0 transition-colors",
|
||||
isCollapsed ? "w-6 h-6" : "w-5 h-5",
|
||||
isActive
|
||||
? "text-primary-600 dark:text-primary-400"
|
||||
: "text-secondary-500 group-hover:text-secondary-700 dark:text-secondary-400 dark:group-hover:text-white"
|
||||
@ -133,9 +138,9 @@ const Sidebar: React.FC = () => {
|
||||
title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<Sun className="w-5 h-5 text-secondary-600 dark:text-secondary-400" />
|
||||
<Sun className={cn(isCollapsed ? "w-6 h-6" : "w-5 h-5", "text-secondary-600 dark:text-secondary-400")} />
|
||||
) : (
|
||||
<Moon className="w-5 h-5 text-secondary-600 dark:text-secondary-400" />
|
||||
<Moon className={cn(isCollapsed ? "w-6 h-6" : "w-5 h-5", "text-secondary-600 dark:text-secondary-400")} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@ -175,7 +180,7 @@ const Sidebar: React.FC = () => {
|
||||
className="p-1 rounded-md hover:bg-secondary-100 dark:hover:bg-secondary-800 transition-colors"
|
||||
title="Logout"
|
||||
>
|
||||
<LogOut className="w-4 h-4 text-secondary-600 dark:text-secondary-400" />
|
||||
<LogOut className={cn(isCollapsed ? "w-5 h-5" : "w-4 h-4", "text-secondary-600 dark:text-secondary-400")} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -103,7 +103,7 @@ const ResellerDashboard: React.FC = () => {
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2">Welcome back, John! 👋</h1>
|
||||
<h1 className="text-3xl font-bold mb-2">Welcome back, John! </h1>
|
||||
<p className="text-emerald-100 text-lg">Here's what's happening with your cloud services business today.</p>
|
||||
</div>
|
||||
<div className="hidden lg:flex items-center space-x-4">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user