feat: setup admin layout and base routing
This commit is contained in:
parent
7fcfbda8cd
commit
222581c6a3
@ -1,122 +1,22 @@
|
||||
import { useState } from 'react'
|
||||
import reactLogo from './assets/react.svg'
|
||||
import viteLogo from './assets/vite.svg'
|
||||
import heroImg from './assets/hero.png'
|
||||
import './App.css'
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import Layout from './components/Layout';
|
||||
import Dashboard from './pages/Dashboard';
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<section id="center">
|
||||
<div className="hero">
|
||||
<img src={heroImg} className="base" width="170" height="179" alt="" />
|
||||
<img src={reactLogo} className="framework" alt="React logo" />
|
||||
<img src={viteLogo} className="vite" alt="Vite logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h1>Get started</h1>
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="counter"
|
||||
onClick={() => setCount((count) => count + 1)}
|
||||
>
|
||||
Count is {count}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<div className="ticks"></div>
|
||||
|
||||
<section id="next-steps">
|
||||
<div id="docs">
|
||||
<svg className="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#documentation-icon"></use>
|
||||
</svg>
|
||||
<h2>Documentation</h2>
|
||||
<p>Your questions, answered</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vite.dev/" target="_blank">
|
||||
<img className="logo" src={viteLogo} alt="" />
|
||||
Explore Vite
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://react.dev/" target="_blank">
|
||||
<img className="button-icon" src={reactLogo} alt="" />
|
||||
Learn more
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="social">
|
||||
<svg className="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#social-icon"></use>
|
||||
</svg>
|
||||
<h2>Connect with us</h2>
|
||||
<p>Join the Vite community</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/vitejs/vite" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#github-icon"></use>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chat.vite.dev/" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#discord-icon"></use>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://x.com/vite_js" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#x-icon"></use>
|
||||
</svg>
|
||||
X.com
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://bsky.app/profile/vite.dev" target="_blank">
|
||||
<svg
|
||||
className="button-icon"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use href="/icons.svg#bluesky-icon"></use>
|
||||
</svg>
|
||||
Bluesky
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="ticks"></div>
|
||||
<section id="spacer"></section>
|
||||
</>
|
||||
)
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="users" element={<div className="p-4">مدیریت کاربران</div>} />
|
||||
<Route path="products" element={<div className="p-4">مدیریت محصولات</div>} />
|
||||
<Route path="orders" element={<div className="p-4">مدیریت سفارشات</div>} />
|
||||
<Route path="coupons" element={<div className="p-4">کدهای تخفیف</div>} />
|
||||
<Route path="settings" element={<div className="p-4">تنظیمات سیستم</div>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
17
frontend/admin-panel/src/components/Layout.tsx
Normal file
17
frontend/admin-panel/src/components/Layout.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import Sidebar from './Sidebar';
|
||||
import Topbar from './Topbar';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div className="flex min-h-screen bg-gray-50 text-gray-900 font-vazir" dir="rtl">
|
||||
<Sidebar />
|
||||
<div className="flex-1 flex flex-col min-h-screen">
|
||||
<Topbar />
|
||||
<main className="flex-1 p-6 overflow-y-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
52
frontend/admin-panel/src/components/Sidebar.tsx
Normal file
52
frontend/admin-panel/src/components/Sidebar.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Home, Users, ShoppingBag, ShoppingCart, Tag, Settings, LogOut } from 'lucide-react';
|
||||
|
||||
const menuItems = [
|
||||
{ icon: Home, label: 'داشبورد', path: '/' },
|
||||
{ icon: Users, label: 'کاربران', path: '/users' },
|
||||
{ icon: ShoppingBag, label: 'محصولات', path: '/products' },
|
||||
{ icon: ShoppingCart, label: 'سفارشات', path: '/orders' },
|
||||
{ icon: Tag, label: 'تخفیفها', path: '/coupons' },
|
||||
{ icon: Settings, label: 'تنظیمات', path: '/settings' },
|
||||
];
|
||||
|
||||
export default function Sidebar() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<aside className="w-64 bg-white border-l border-gray-200 h-screen sticky top-0 flex flex-col font-vazir shadow-sm">
|
||||
<div className="h-16 flex items-center justify-center border-b border-gray-200">
|
||||
<h1 className="text-xl font-black text-purple-600">کانینا | ادمینپنل</h1>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 overflow-y-auto py-4 px-3 space-y-1">
|
||||
{menuItems.map((item) => {
|
||||
const isActive = location.pathname === item.path || (item.path !== '/' && location.pathname.startsWith(item.path));
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={`flex items-center gap-3 px-3 py-3 rounded-xl transition-all font-bold ${
|
||||
isActive
|
||||
? 'bg-purple-50 text-purple-600'
|
||||
: 'text-gray-500 hover:bg-gray-50 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-5 h-5" />
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="p-4 border-t border-gray-200">
|
||||
<button className="flex items-center gap-3 px-3 py-3 w-full text-red-500 hover:bg-red-50 rounded-xl transition-all font-bold">
|
||||
<LogOut className="w-5 h-5" />
|
||||
<span>خروج از حساب</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
34
frontend/admin-panel/src/components/Topbar.tsx
Normal file
34
frontend/admin-panel/src/components/Topbar.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Bell, Search, UserCircle } from 'lucide-react';
|
||||
|
||||
export default function Topbar() {
|
||||
return (
|
||||
<header className="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6 sticky top-0 z-10 shadow-sm font-vazir">
|
||||
<div className="flex-1 flex items-center max-w-xl">
|
||||
<div className="relative w-full">
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<Search className="w-5 h-5 text-gray-400" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="جستجو در پنل..."
|
||||
className="w-full bg-gray-50 border border-gray-200 text-gray-900 text-sm rounded-xl focus:ring-purple-500 focus:border-purple-500 block pr-10 p-2.5 outline-none font-bold"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button className="w-10 h-10 rounded-full flex items-center justify-center text-gray-500 hover:bg-gray-100 transition-all relative">
|
||||
<Bell className="w-5 h-5" />
|
||||
<span className="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full"></span>
|
||||
</button>
|
||||
<div className="flex items-center gap-3 pl-2 border-l border-gray-200 ml-2">
|
||||
<div className="text-left hidden md:block">
|
||||
<p className="text-sm font-bold text-gray-900">مدیر سیستم</p>
|
||||
<p className="text-xs font-medium text-gray-500">ادمین ارشد</p>
|
||||
</div>
|
||||
<UserCircle className="w-10 h-10 text-gray-300" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
40
frontend/admin-panel/src/pages/Dashboard.tsx
Normal file
40
frontend/admin-panel/src/pages/Dashboard.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { DollarSign, ShoppingCart, Users, Activity } from 'lucide-react';
|
||||
|
||||
export default function Dashboard() {
|
||||
const stats = [
|
||||
{ title: 'درآمد کل', value: '۴۵,۰۰۰,۰۰۰ تومان', icon: DollarSign, color: 'text-green-600', bg: 'bg-green-100' },
|
||||
{ title: 'سفارشات جدید', value: '۱۲', icon: ShoppingCart, color: 'text-blue-600', bg: 'bg-blue-100' },
|
||||
{ title: 'کاربران فعال', value: '۱,۰۲۴', icon: Users, color: 'text-purple-600', bg: 'bg-purple-100' },
|
||||
{ title: 'بازدید امروز', value: '۳۴۲', icon: Activity, color: 'text-orange-600', bg: 'bg-orange-100' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-black text-gray-900">داشبورد</h2>
|
||||
<p className="text-gray-500 font-medium mt-1">خلاصهای از وضعیت فروشگاه کانینا</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{stats.map((stat, i) => {
|
||||
const Icon = stat.icon;
|
||||
return (
|
||||
<div key={i} className="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 flex items-center gap-4">
|
||||
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center ${stat.bg}`}>
|
||||
<Icon className={`w-6 h-6 ${stat.color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-gray-500">{stat.title}</p>
|
||||
<p className="text-xl font-black text-gray-900 mt-1">{stat.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 min-h-[400px] flex items-center justify-center">
|
||||
<p className="text-gray-400 font-bold">بخش نمودارها (در فاز بعدی اضافه میشود)</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user