mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
Update AdminPanel.tsx
This commit is contained in:
@@ -1,77 +1,107 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
// Icons
|
||||
const TicketIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
// Icons - smaller versions for mobile
|
||||
const TicketIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 010 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 010-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const CogIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const CogIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const PhoneIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const PhoneIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const WheelIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const WheelIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const TariffIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const TariffIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.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>
|
||||
)
|
||||
|
||||
const ServerIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const ServerIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const AdminIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const AdminIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const ChartIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
const ChartIcon = ({ className = "w-8 h-8" }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
interface AdminCardProps {
|
||||
const ChevronRightIcon = () => (
|
||||
<svg className="w-4 h-4 text-dark-500 group-hover:text-dark-300 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
interface AdminSection {
|
||||
to: string
|
||||
icon: React.ReactNode
|
||||
mobileIcon: React.ReactNode
|
||||
title: string
|
||||
description: string
|
||||
color: string
|
||||
bgColor: string
|
||||
textColor: string
|
||||
}
|
||||
|
||||
function AdminCard({ to, icon, title, description, color }: AdminCardProps) {
|
||||
// Mobile compact card
|
||||
function MobileAdminCard({ to, mobileIcon, title, bgColor, textColor }: AdminSection) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className={`block p-6 bg-dark-800 rounded-xl border border-dark-700 hover:border-${color}-500/50 hover:bg-dark-750 transition-all duration-200 group`}
|
||||
className="flex flex-col items-center justify-center p-3 bg-dark-800/80 rounded-2xl border border-dark-700/50 hover:border-dark-600 active:scale-95 transition-all duration-150"
|
||||
>
|
||||
<div className={`w-14 h-14 rounded-xl bg-${color}-500/20 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}>
|
||||
<div className={`text-${color}-400`}>
|
||||
<div className={`w-12 h-12 rounded-xl ${bgColor} flex items-center justify-center mb-2`}>
|
||||
<div className={textColor}>
|
||||
{mobileIcon}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-xs font-medium text-dark-200 text-center leading-tight">{title}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
// Desktop card
|
||||
function DesktopAdminCard({ to, icon, title, description, bgColor, textColor }: AdminSection) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className="group flex items-center gap-4 p-4 bg-dark-800/80 rounded-2xl border border-dark-700/50 hover:border-dark-600 hover:bg-dark-800 transition-all duration-200"
|
||||
>
|
||||
<div className={`w-14 h-14 rounded-xl ${bgColor} flex items-center justify-center shrink-0 group-hover:scale-105 transition-transform`}>
|
||||
<div className={textColor}>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-dark-100 mb-1">{title}</h3>
|
||||
<p className="text-sm text-dark-400">{description}</p>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-base font-semibold text-dark-100 mb-0.5">{title}</h3>
|
||||
<p className="text-sm text-dark-400 truncate">{description}</p>
|
||||
</div>
|
||||
<ChevronRightIcon />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -79,75 +109,103 @@ function AdminCard({ to, icon, title, description, color }: AdminCardProps) {
|
||||
export default function AdminPanel() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const adminSections = [
|
||||
const adminSections: AdminSection[] = [
|
||||
{
|
||||
to: '/admin/dashboard',
|
||||
icon: <ChartIcon />,
|
||||
mobileIcon: <ChartIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.dashboard'),
|
||||
description: t('admin.panel.dashboardDesc'),
|
||||
color: 'success'
|
||||
color: 'success',
|
||||
bgColor: 'bg-emerald-500/20',
|
||||
textColor: 'text-emerald-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/tickets',
|
||||
icon: <TicketIcon />,
|
||||
mobileIcon: <TicketIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.tickets'),
|
||||
description: t('admin.panel.ticketsDesc'),
|
||||
color: 'warning'
|
||||
color: 'warning',
|
||||
bgColor: 'bg-amber-500/20',
|
||||
textColor: 'text-amber-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/settings',
|
||||
icon: <CogIcon />,
|
||||
mobileIcon: <CogIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.settings'),
|
||||
description: t('admin.panel.settingsDesc'),
|
||||
color: 'accent'
|
||||
color: 'accent',
|
||||
bgColor: 'bg-blue-500/20',
|
||||
textColor: 'text-blue-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/apps',
|
||||
icon: <PhoneIcon />,
|
||||
mobileIcon: <PhoneIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.apps'),
|
||||
description: t('admin.panel.appsDesc'),
|
||||
color: 'success'
|
||||
color: 'success',
|
||||
bgColor: 'bg-teal-500/20',
|
||||
textColor: 'text-teal-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/wheel',
|
||||
icon: <WheelIcon />,
|
||||
mobileIcon: <WheelIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.wheel'),
|
||||
description: t('admin.panel.wheelDesc'),
|
||||
color: 'error'
|
||||
color: 'error',
|
||||
bgColor: 'bg-rose-500/20',
|
||||
textColor: 'text-rose-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/tariffs',
|
||||
icon: <TariffIcon />,
|
||||
mobileIcon: <TariffIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.tariffs'),
|
||||
description: t('admin.panel.tariffsDesc'),
|
||||
color: 'info'
|
||||
color: 'info',
|
||||
bgColor: 'bg-cyan-500/20',
|
||||
textColor: 'text-cyan-400'
|
||||
},
|
||||
{
|
||||
to: '/admin/servers',
|
||||
icon: <ServerIcon />,
|
||||
mobileIcon: <ServerIcon className="w-6 h-6" />,
|
||||
title: t('admin.nav.servers'),
|
||||
description: t('admin.panel.serversDesc'),
|
||||
color: 'purple'
|
||||
color: 'purple',
|
||||
bgColor: 'bg-purple-500/20',
|
||||
textColor: 'text-purple-400'
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="p-3 bg-warning-500/20 rounded-xl">
|
||||
<AdminIcon />
|
||||
{/* Header - compact on mobile */}
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2.5 sm:p-3 bg-amber-500/20 rounded-xl">
|
||||
<AdminIcon className="w-6 h-6 sm:w-8 sm:h-8 text-amber-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-dark-100">{t('admin.panel.title')}</h1>
|
||||
<p className="text-dark-400">{t('admin.panel.subtitle')}</p>
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-dark-100">{t('admin.panel.title')}</h1>
|
||||
<p className="text-sm text-dark-400 hidden sm:block">{t('admin.panel.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid of admin sections */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{/* Mobile: Compact 2-column grid */}
|
||||
<div className="grid grid-cols-3 gap-3 sm:hidden">
|
||||
{adminSections.map((section) => (
|
||||
<AdminCard key={section.to} {...section} />
|
||||
<MobileAdminCard key={section.to} {...section} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tablet/Desktop: List style */}
|
||||
<div className="hidden sm:grid sm:grid-cols-1 lg:grid-cols-2 gap-3">
|
||||
{adminSections.map((section) => (
|
||||
<DesktopAdminCard key={section.to} {...section} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user