139 lines
5.7 KiB
TypeScript
Executable File
139 lines
5.7 KiB
TypeScript
Executable File
|
||
import React, { useState, useEffect, useMemo } from 'react';
|
||
import {
|
||
Briefcase,
|
||
ShoppingCart,
|
||
FileText,
|
||
Monitor,
|
||
QrCode,
|
||
Wrench,
|
||
BookOpen,
|
||
Calendar,
|
||
Newspaper
|
||
} from 'lucide-react';
|
||
import { MOCK_OFFICE_REQUESTS } from '../constants';
|
||
import { OfficeRequest } from '../types';
|
||
|
||
// Modular Imports
|
||
import { OfficeSummary } from './office/OfficeSummary';
|
||
import { SupplyRegistry } from './office/SupplyRegistry';
|
||
import { DocumentFlow } from './office/DocumentFlow';
|
||
import { FacilityManagement } from './office/FacilityManagement';
|
||
import { RepairRequests } from './office/RepairRequests';
|
||
import { KnowledgeBase } from './office/KnowledgeBase';
|
||
import { MeetingsAndRooms } from './office/MeetingsAndRooms';
|
||
import { CompanyNewsRegistry } from './office/CompanyNewsRegistry';
|
||
import { allowedSubsForSection } from '../constants/permissions';
|
||
|
||
type Tab = 'dashboard' | 'supply' | 'docs' | 'facility' | 'repair' | 'knowledge' | 'meetings' | 'news';
|
||
|
||
const OFFICE_TABS: Tab[] = ['dashboard', 'supply', 'docs', 'facility', 'repair', 'knowledge', 'meetings', 'news'];
|
||
const SUBTAB_KEY = 'mkd_subTab_office';
|
||
|
||
interface OfficeModuleProps {
|
||
allowedPermissions?: string[] | null;
|
||
}
|
||
|
||
export const OfficeModule: React.FC<OfficeModuleProps> = ({ allowedPermissions }) => {
|
||
const visibleTabs = useMemo(() => {
|
||
const allowed = allowedSubsForSection(allowedPermissions ?? [], 'office');
|
||
if (allowed === 'all') return OFFICE_TABS;
|
||
return OFFICE_TABS.filter((t) => allowed.includes(t));
|
||
}, [allowedPermissions]);
|
||
|
||
const [activeTab, setActiveTab] = useState<Tab>(() => {
|
||
const s = localStorage.getItem(SUBTAB_KEY);
|
||
return (s && OFFICE_TABS.includes(s as Tab)) ? s as Tab : 'dashboard';
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (visibleTabs.length > 0 && !visibleTabs.includes(activeTab)) {
|
||
setActiveTab(visibleTabs[0]);
|
||
}
|
||
}, [visibleTabs, activeTab]);
|
||
|
||
useEffect(() => {
|
||
if (visibleTabs.includes(activeTab)) localStorage.setItem(SUBTAB_KEY, activeTab);
|
||
}, [activeTab, visibleTabs]);
|
||
|
||
const [requests, setRequests] = useState<OfficeRequest[]>(MOCK_OFFICE_REQUESTS);
|
||
|
||
return (
|
||
<div className="animate-fade-in pb-20">
|
||
<div className="flex justify-between items-center mb-6 px-1">
|
||
<div>
|
||
<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 gap-2">
|
||
<button
|
||
className="p-2.5 bg-white border border-slate-200 text-slate-400 rounded-xl hover:text-primary-600 transition-all shadow-sm"
|
||
title="Сканировать QR"
|
||
onClick={() => alert("Камера активирована для инвентаризации")}
|
||
>
|
||
<QrCode className="w-5 h-5"/>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Sheet Selector */}
|
||
<div className="flex p-1 bg-slate-200/50 rounded-2xl mb-6 overflow-x-auto no-scrollbar gap-1">
|
||
{[
|
||
{ id: 'dashboard', label: 'Сводка', icon: Briefcase },
|
||
{ id: 'supply', label: 'Закупки', icon: ShoppingCart },
|
||
{ id: 'docs', label: 'Корреспонденция', icon: FileText },
|
||
{ id: 'facility', label: 'Имущество', icon: Monitor },
|
||
{ id: 'repair', label: 'Ремонт техники', icon: Wrench },
|
||
{ id: 'knowledge', label: 'База знаний', icon: BookOpen },
|
||
{ id: 'meetings', label: 'Совещания', icon: Calendar },
|
||
{ id: 'news', label: 'Новости', icon: Newspaper },
|
||
].filter((tab) => visibleTabs.includes(tab.id as Tab)).map((tab) => (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => setActiveTab(tab.id as Tab)}
|
||
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 ${activeTab === tab.id ? 'bg-white text-primary-600 shadow-sm border border-white' : 'text-slate-500 hover:text-slate-700'}`}
|
||
>
|
||
<tab.icon className="w-3.5 h-3.5"/> {tab.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Dynamic Sheet Content */}
|
||
<div className="min-h-[280px] sm:min-h-[360px] md:min-h-[500px]">
|
||
{(() => {
|
||
try {
|
||
switch (activeTab) {
|
||
case 'dashboard':
|
||
return <OfficeSummary requests={requests} />;
|
||
case 'supply':
|
||
return <SupplyRegistry requests={requests} />;
|
||
case 'docs':
|
||
return <DocumentFlow />;
|
||
case 'facility':
|
||
return <FacilityManagement />;
|
||
case 'repair':
|
||
return <RepairRequests />;
|
||
case 'knowledge':
|
||
return <KnowledgeBase />;
|
||
case 'meetings':
|
||
return <MeetingsAndRooms />;
|
||
case 'news':
|
||
return <CompanyNewsRegistry />;
|
||
default:
|
||
return <OfficeSummary requests={requests} />;
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка рендеринга вкладки:', error);
|
||
return (
|
||
<div className="text-center py-8 text-red-600">
|
||
<p className="font-bold">Ошибка загрузки вкладки</p>
|
||
<p className="text-sm text-slate-500 mt-2">Попробуйте перезагрузить страницу</p>
|
||
</div>
|
||
);
|
||
}
|
||
})()}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|