Initial commit MKD fixes
This commit is contained in:
117
components/PRModule.tsx
Executable file
117
components/PRModule.tsx
Executable file
@@ -0,0 +1,117 @@
|
||||
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Megaphone,
|
||||
PartyPopper,
|
||||
MessageCircle,
|
||||
FileText,
|
||||
AlertCircle,
|
||||
Plus,
|
||||
Printer,
|
||||
LayoutDashboard,
|
||||
Share2,
|
||||
ClipboardList,
|
||||
Image as ImageIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
// Modular Imports
|
||||
import { PRSummary } from './pr/PRSummary';
|
||||
import { EventsRegistry } from './pr/EventsRegistry';
|
||||
import { ResidentReports } from './pr/ResidentReports';
|
||||
import { NegativeResolution } from './pr/NegativeResolution';
|
||||
import { PRFeedbackFeed } from './pr/PRFeedbackFeed';
|
||||
import { SMMManager } from './pr/SMMManager';
|
||||
import { NPSSurveysManager } from './pr/NPSSurveysManager';
|
||||
import { WorkPhotosDirectory } from './pr/WorkPhotosDirectory';
|
||||
import { allowedSubsForSection } from '../constants/permissions';
|
||||
|
||||
type Tab = 'summary' | 'smm' | 'events' | 'feedback' | 'reports' | 'photos' | 'nps' | 'negative';
|
||||
|
||||
const PR_TABS: Tab[] = ['summary', 'smm', 'events', 'feedback', 'reports', 'photos', 'nps', 'negative'];
|
||||
const SUBTAB_KEY = 'mkd_subTab_pr';
|
||||
|
||||
interface PRModuleProps {
|
||||
allowedPermissions?: string[] | null;
|
||||
}
|
||||
|
||||
export const PRModule: React.FC<PRModuleProps> = ({ allowedPermissions }) => {
|
||||
const visibleTabs = useMemo(() => {
|
||||
const allowed = allowedSubsForSection(allowedPermissions ?? [], 'pr');
|
||||
if (allowed === 'all') return PR_TABS;
|
||||
return PR_TABS.filter((t) => allowed.includes(t));
|
||||
}, [allowedPermissions]);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<Tab>(() => {
|
||||
const s = localStorage.getItem(SUBTAB_KEY);
|
||||
return (s && PR_TABS.includes(s as Tab)) ? s as Tab : 'summary';
|
||||
});
|
||||
|
||||
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]);
|
||||
|
||||
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">PR и Лояльность (NPS)</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="Печать аналитики"
|
||||
>
|
||||
<Printer className="w-5 h-5"/>
|
||||
</button>
|
||||
<button
|
||||
className="bg-primary-600 text-white p-2.5 rounded-xl shadow-lg shadow-primary-500/30 flex items-center gap-2 px-4 text-xs font-black uppercase tracking-wider active:scale-95 transition-all"
|
||||
onClick={() => alert("Форма сбора NPS / Создание мероприятия")}
|
||||
>
|
||||
<Plus className="w-4 h-4" /> Создать
|
||||
</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: 'summary', label: 'Сводка', icon: LayoutDashboard },
|
||||
{ id: 'smm', label: 'SMM', icon: Share2 },
|
||||
{ id: 'events', label: 'Мероприятия', icon: PartyPopper },
|
||||
{ id: 'feedback', label: 'Отзывы', icon: MessageCircle },
|
||||
{ id: 'reports', label: 'Отчеты жителям', icon: FileText },
|
||||
{ id: 'photos', label: 'Фото отчёты', icon: ImageIcon },
|
||||
{ id: 'nps', label: 'NPS опросы', icon: ClipboardList },
|
||||
{ id: 'negative', label: 'Негатив', icon: AlertCircle },
|
||||
].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]">
|
||||
{activeTab === 'summary' && <PRSummary onNavigate={setActiveTab} />}
|
||||
{activeTab === 'smm' && <SMMManager />}
|
||||
{activeTab === 'events' && <EventsRegistry onNavigate={setActiveTab} />}
|
||||
{activeTab === 'feedback' && <PRFeedbackFeed />}
|
||||
{activeTab === 'reports' && <ResidentReports />}
|
||||
{activeTab === 'photos' && <WorkPhotosDirectory />}
|
||||
{activeTab === 'nps' && <NPSSurveysManager />}
|
||||
{activeTab === 'negative' && <NegativeResolution />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user