Files
mkd/components/building/OverviewView.tsx

148 lines
9.6 KiB
TypeScript
Raw Permalink Normal View History

2026-02-04 00:17:04 +05:00
import React, { useState } from 'react';
import { Building } from '../../types';
import { generateBuildingAudit } from '../../services/geminiService';
import {
Users,
Wallet,
Activity,
TrendingDown,
Bot,
Sparkles,
CheckCircle2,
Calendar
} from 'lucide-react';
type TabType = 'overview' | 'finance' | 'inspections' | 'residents' | 'passport' | 'accounts';
export const OverviewView: React.FC<{ building: Building, onNavigate: (tab: TabType) => void }> = ({ building, onNavigate }) => {
const [auditResult, setAuditResult] = useState<string | null>(null);
const [loadingAudit, setLoadingAudit] = useState(false);
const handleAudit = async () => {
setLoadingAudit(true);
const res = await generateBuildingAudit(building);
setAuditResult(res);
setLoadingAudit(false);
};
return (
<div className="space-y-6 animate-fade-in">
{/* Main Info Card */}
<div className="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
<div className="h-48 w-full bg-slate-200 relative">
<img src={building.imageUrl} alt="Building" className="w-full h-full object-cover" />
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-6">
<div className="flex justify-between items-end text-white">
<div>
<h2 className="text-2xl font-bold">{building.passport.address}</h2>
<p className="text-sm opacity-90">{building.passport.general.constructionYear} г. {building.passport.general.floors} эт. {building.passport.general.totalArea} м²</p>
</div>
<div className="text-right">
<p className="text-sm opacity-75">NPS</p>
<p className={`text-3xl font-black ${building.nps >= 0 ? 'text-emerald-400' : 'text-red-400'}`}>{building.nps > 0 ? '+' : ''}{building.nps}</p>
</div>
</div>
</div>
</div>
<div className="p-4 grid grid-cols-2 md:grid-cols-4 gap-4 divide-x divide-slate-100">
<div className="text-center px-2 cursor-pointer hover:bg-slate-50 transition-colors rounded" onClick={() => onNavigate('residents')}>
<p className="text-slate-400 text-xs font-bold uppercase">Жителей</p>
<p className="text-xl font-black text-slate-800 flex items-center justify-center gap-1">
<Users className="w-4 h-4 text-slate-400"/> {building.accounts.reduce((sum, acc) => {
const count = acc.registered?.length || acc.registeredCount || 0;
return sum + (typeof count === 'number' ? count : 0);
}, 0)}
</p>
</div>
<div className="text-center px-2 cursor-pointer hover:bg-slate-50 transition-colors rounded" onClick={() => onNavigate('finance')}>
<p className="text-slate-400 text-xs font-bold uppercase">Баланс</p>
<p className={`text-xl font-black flex items-center justify-center gap-1 ${building.financials.balance >= 0 ? 'text-emerald-600' : 'text-red-600'}`}>
<Wallet className="w-4 h-4 text-slate-400"/> {(building.financials.balance / 1000).toFixed(0)}k
</p>
</div>
<div className="text-center px-2 cursor-pointer hover:bg-slate-50 transition-colors rounded" onClick={() => onNavigate('inspections')}>
<p className="text-slate-400 text-xs font-bold uppercase">Состояние</p>
<p className="text-xl font-black text-slate-800 flex items-center justify-center gap-1">
<Activity className="w-4 h-4 text-amber-500"/> Удовл.
</p>
</div>
<div className="text-center px-2 cursor-pointer hover:bg-slate-50 transition-colors rounded" onClick={() => onNavigate('passport')}>
<p className="text-slate-400 text-xs font-bold uppercase">Износ</p>
<p className="text-xl font-black text-slate-800 flex items-center justify-center gap-1">
<TrendingDown className="w-4 h-4 text-slate-400"/> 35%
</p>
</div>
</div>
</div>
{/* AI Audit Section */}
<div className="bg-gradient-to-br from-indigo-600 to-violet-700 rounded-2xl shadow-xl text-white p-6 relative overflow-hidden">
<div className="absolute top-0 right-0 p-8 opacity-10">
<Bot className="w-32 h-32" />
</div>
<h3 className="text-lg font-bold flex items-center gap-2 mb-2"><Sparkles className="w-5 h-5 text-yellow-300"/> Анализ Дома</h3>
<p className="text-indigo-100 text-sm mb-4 max-w-lg">Краткая сводка состояния дома, финансовых показателей и рекомендаций по эксплуатации на основе данных паспорта и истории осмотров.</p>
{!auditResult ? (
<button
onClick={handleAudit}
disabled={loadingAudit}
className="bg-white text-indigo-600 px-4 py-2 rounded-xl font-bold text-sm shadow-lg hover:bg-indigo-50 transition-transform active:scale-95 flex items-center gap-2 disabled:opacity-70"
>
{loadingAudit ? 'Анализирую...' : 'Сгенерировать отчет'}
</button>
) : (
<div className="bg-white/10 backdrop-blur-md rounded-xl p-4 text-sm animate-fade-in border border-white/20">
<div className="prose prose-invert prose-sm max-w-none">
<pre className="whitespace-pre-wrap font-sans">{auditResult}</pre>
</div>
<button onClick={() => setAuditResult(null)} className="mt-3 text-xs text-indigo-200 hover:text-white underline">Скрыть отчет</button>
</div>
)}
</div>
{/* Tasks Widget */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-5 rounded-xl border border-slate-200 shadow-sm">
<h3 className="font-bold text-slate-700 mb-4 flex items-center gap-2"><CheckCircle2 className="w-5 h-5 text-primary-500"/> Текущие задачи</h3>
<div className="space-y-3">
{building.tasks.map(task => (
<div key={task.id} className="flex items-start gap-3 p-2 hover:bg-slate-50 rounded-lg transition-colors cursor-pointer">
<div className={`mt-1 w-4 h-4 rounded-full border-2 ${task.status === 'done' ? 'bg-emerald-500 border-emerald-500' : 'border-slate-300'}`}></div>
<div className="flex-1">
<p className={`text-sm font-medium ${task.status === 'done' ? 'text-slate-400 line-through' : 'text-slate-800'}`}>{task.title}</p>
<p className="text-xs text-slate-500 flex items-center gap-2 mt-1">
<Calendar className="w-3 h-3"/> {task.deadline}
<span className={`px-1.5 py-0.5 rounded text-[10px] font-bold uppercase ${task.priority === 'high' ? 'bg-red-100 text-red-600' : 'bg-slate-100 text-slate-500'}`}>{task.priority}</span>
</p>
</div>
</div>
))}
<button className="w-full py-2 text-xs font-bold text-primary-600 border border-dashed border-primary-200 rounded-lg hover:bg-primary-50 mt-2">
+ Добавить задачу
</button>
</div>
</div>
<div className="bg-white p-5 rounded-xl border border-slate-200 shadow-sm">
<h3 className="font-bold text-slate-700 mb-4 flex items-center gap-2"><Calendar className="w-5 h-5 text-primary-500"/> План работ (Май)</h3>
<div className="space-y-4">
{building.annualPlan.map(plan => (
<div key={plan.id}>
<div className="flex justify-between text-xs mb-1">
<span className="font-bold text-slate-700">{plan.workName}</span>
<span className="text-slate-500">{plan.progress}%</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-2">
<div className={`h-2 rounded-full ${plan.progress === 100 ? 'bg-emerald-500' : 'bg-primary-500'}`} style={{ width: `${plan.progress}%` }}></div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
);
};