Initial commit MKD fixes
This commit is contained in:
113
components/AdminModule.tsx
Executable file
113
components/AdminModule.tsx
Executable file
@@ -0,0 +1,113 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { UserCog, Plug, DatabaseBackup, Upload, Shield, ShieldCheck, Trash2, Building2, Bot, Users, Briefcase } from 'lucide-react';
|
||||
import { allowedSubsForSection } from '../constants/permissions';
|
||||
import { UsersSection } from './admin/UsersSection';
|
||||
import { IntegrationsSection } from './admin/IntegrationsSection';
|
||||
import { AISection } from './admin/AISection';
|
||||
import { BackupsSection } from './admin/BackupsSection';
|
||||
import { DataImportSection } from './admin/DataImportSection';
|
||||
import { DataCleanupSection } from './admin/DataCleanupSection';
|
||||
import { PermissionsSection } from './admin/PermissionsSection';
|
||||
import { SecuritySection } from './admin/SecuritySection';
|
||||
import { CompanySection } from './admin/CompanySection';
|
||||
import { ResponsibilityZonesSection } from './admin/ResponsibilityZonesSection';
|
||||
import { PositionsSection } from './admin/PositionsSection';
|
||||
|
||||
export type AdminSectionId = 'users' | 'permissions' | 'integrations' | 'ai' | 'company' | 'positions' | 'responsibility-zones' | 'backups' | 'data-import' | 'data-cleanup' | 'security';
|
||||
|
||||
const ADMIN_SECTIONS: Array<{
|
||||
id: AdminSectionId;
|
||||
label: string;
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
component: React.ComponentType;
|
||||
}> = [
|
||||
{ id: 'users', label: 'Пользователи', icon: UserCog, component: UsersSection },
|
||||
{ id: 'permissions', label: 'Права и шаблоны', icon: Shield, component: PermissionsSection },
|
||||
{ id: 'integrations', label: 'Интеграции', icon: Plug, component: IntegrationsSection },
|
||||
{ id: 'ai', label: 'ИИ', icon: Bot, component: AISection },
|
||||
{ id: 'company', label: 'О компании', icon: Building2, component: CompanySection },
|
||||
{ id: 'positions', label: 'Должности', icon: Briefcase, component: PositionsSection },
|
||||
{ id: 'responsibility-zones', label: 'Зоны ответственности', icon: Users, component: ResponsibilityZonesSection },
|
||||
{ id: 'backups', label: 'Резервные копии', icon: DatabaseBackup, component: BackupsSection },
|
||||
{ id: 'data-import', label: 'Загрузка данных', icon: Upload, component: DataImportSection },
|
||||
{ id: 'data-cleanup', label: 'Очистка данных', icon: Trash2, component: DataCleanupSection },
|
||||
{ id: 'security', label: 'Безопасность', icon: ShieldCheck, component: SecuritySection },
|
||||
];
|
||||
|
||||
const ADMIN_SUBTAB_KEY = 'mkd_subTab_admin';
|
||||
|
||||
export type AdminModuleProps = {
|
||||
allowedPermissions?: string[] | null;
|
||||
};
|
||||
|
||||
export const AdminModule: React.FC<AdminModuleProps> = ({ allowedPermissions }) => {
|
||||
const visibleSectionIds = useMemo(() => {
|
||||
const allowed = allowedSubsForSection(allowedPermissions ?? [], 'admin');
|
||||
return allowed === 'all' ? ADMIN_SECTIONS.map((s) => s.id) : allowed;
|
||||
}, [allowedPermissions]);
|
||||
|
||||
const visibleSections = useMemo(
|
||||
() => ADMIN_SECTIONS.filter((s) => visibleSectionIds.includes(s.id)),
|
||||
[visibleSectionIds]
|
||||
);
|
||||
|
||||
const [activeSection, setActiveSection] = useState<AdminSectionId>(() => {
|
||||
const s = localStorage.getItem(ADMIN_SUBTAB_KEY);
|
||||
const id = s as AdminSectionId;
|
||||
const allowed = allowedSubsForSection(allowedPermissions ?? [], 'admin');
|
||||
const ids = allowed === 'all' ? ADMIN_SECTIONS.map((x) => x.id) : allowed;
|
||||
if (id && ids.includes(id)) return id;
|
||||
return (ids[0] as AdminSectionId) ?? 'users';
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!visibleSectionIds.includes(activeSection)) {
|
||||
setActiveSection((visibleSectionIds[0] as AdminSectionId) ?? 'users');
|
||||
}
|
||||
}, [visibleSectionIds, activeSection]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(ADMIN_SUBTAB_KEY, activeSection);
|
||||
}, [activeSection]);
|
||||
|
||||
const ActiveComponent = ADMIN_SECTIONS.find((s) => s.id === activeSection)?.component ?? UsersSection;
|
||||
|
||||
if (visibleSections.length === 0) {
|
||||
return (
|
||||
<div className="animate-fade-in pb-20">
|
||||
<p className="text-slate-500 text-sm">Нет доступа к разделам панели управления.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in pb-20">
|
||||
<div className="mb-6 px-1">
|
||||
<h2 className="text-xl font-bold text-slate-800 leading-none">Панель управления</h2>
|
||||
<p className="text-[10px] text-slate-400 font-black uppercase tracking-widest mt-1">
|
||||
Пользователи, интеграции, данные компании, резервные копии и безопасность
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex p-1 bg-slate-200/50 rounded-2xl mb-6 overflow-x-auto no-scrollbar gap-1">
|
||||
{visibleSections.map((section) => (
|
||||
<button
|
||||
key={section.id}
|
||||
onClick={() => setActiveSection(section.id)}
|
||||
className={`flex-shrink-0 min-w-[7rem] flex items-center justify-center gap-2 py-2.5 px-4 text-[10px] font-black uppercase tracking-wider whitespace-nowrap rounded-xl transition-all ${
|
||||
activeSection === section.id
|
||||
? 'bg-white text-primary-600 shadow-sm border border-white'
|
||||
: 'text-slate-500 hover:text-slate-700'
|
||||
}`}
|
||||
>
|
||||
<section.icon className="w-3.5 h-3.5" /> {section.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm p-6">
|
||||
<ActiveComponent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user