Files
mkd/components/building/OverviewView.tsx
2026-02-04 00:17:04 +05:00

148 lines
9.6 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
};