Files
mkd/components/objects/PerformanceCard.tsx

108 lines
5.6 KiB
TypeScript
Raw Permalink Normal View History

2026-02-04 00:17:04 +05:00
import React, { useMemo } from 'react';
import { DomaApplication } from '../../types';
import { LayoutGrid, Building2, Inbox, AlertTriangle, ChevronRight, Users, Trash2, Pencil } from 'lucide-react';
const UNASSIGNED_MANAGER = 'Не назначен';
interface Props {
title: string;
subtitle?: string;
applications: DomaApplication[];
onClick: () => void;
type: 'district' | 'building';
buildingCount?: number;
staffCount?: number;
onDelete?: () => void;
onViewStaff?: () => void;
onEdit?: () => void;
}
export const PerformanceCard: React.FC<Props> = ({ title, subtitle, applications, onClick, type, buildingCount, staffCount, onDelete, onViewStaff, onEdit }) => {
const stats = useMemo(() => {
const now = new Date();
const active = applications.filter(a => a.status === 'new' || a.status === 'in_progress');
const overdue = active.filter(a => a.deadlineAt && new Date(a.deadlineAt) < now);
const newApps = applications.filter(a => a.status === 'new');
const performance = active.length > 0 ? Math.round((1 - (overdue.length / active.length)) * 100) : 100;
return { overdue, newApps, performance };
}, [applications]);
const perfColor = stats.performance > 85 ? 'text-emerald-500' : stats.performance > 60 ? 'text-amber-500' : 'text-red-500';
const Icon = type === 'district' ? LayoutGrid : Building2;
return (
<div
onClick={onClick}
className="bg-white p-4 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow cursor-pointer active:scale-[0.99] flex gap-4 items-center group relative"
style={{ position: 'relative' }}
>
<div className="p-3 bg-primary-50 rounded-lg text-primary-600 hidden sm:block">
<Icon className="w-6 h-6"/>
</div>
<div className="flex-1 min-w-0 pr-12">
<h4 className="font-bold text-slate-800 truncate">{title}</h4>
{(subtitle != null && subtitle !== '') && (
<p className={`text-xs truncate ${subtitle === UNASSIGNED_MANAGER ? 'text-red-600 font-semibold' : 'text-slate-500'}`}>{subtitle}</p>
)}
<div className="flex flex-wrap gap-x-4 gap-y-1 mt-2 text-xs">
{type === 'district' && (
<div className="flex items-center gap-1.5 text-slate-500">
<Building2 className="w-3.5 h-3.5" /> {buildingCount} домов
</div>
)}
<div className="flex items-center gap-1.5 text-slate-500">
<Inbox className="w-3.5 h-3.5 text-blue-500" /> <span className="font-bold text-slate-700">{stats.newApps.length}</span> новых
</div>
<div className="flex items-center gap-1.5 text-slate-500">
<AlertTriangle className="w-3.5 h-3.5 text-red-500" /> <span className="font-bold text-slate-700">{stats.overdue.length}</span> просрочено
</div>
</div>
</div>
<div className="text-right flex items-center gap-4">
{type === 'district' && onViewStaff && (
<div className="flex items-center gap-2">
{staffCount !== undefined && (
<span className="text-sm font-bold text-slate-600 bg-slate-100 px-2 py-1 rounded-lg">
{staffCount}
</span>
)}
<button onClick={(e) => { e.stopPropagation(); onViewStaff(); }} className="p-2 bg-slate-50 hover:bg-primary-50 text-slate-400 hover:text-primary-600 rounded-lg transition-colors border border-slate-100">
<Users className="w-5 h-5"/>
</button>
</div>
)}
<div className="hidden md:block">
<p className={`text-2xl font-black ${perfColor}`}>{stats.performance}%</p>
<p className="text-[10px] text-slate-400 font-bold uppercase">Успеваемость</p>
</div>
<ChevronRight className="w-5 h-5 text-slate-300 group-hover:text-primary-400 transition-colors" />
</div>
<div className="absolute top-2 right-2 flex items-center gap-1 z-30">
{type === 'district' && onEdit && (
<button
onClick={(e) => { e.stopPropagation(); e.preventDefault(); onEdit(); }}
className="p-1.5 bg-white hover:bg-primary-50 text-slate-400 hover:text-primary-600 rounded-full transition-all border border-slate-200 shadow-md hover:shadow-lg"
title="Редактировать участок"
type="button"
>
<Pencil className="w-3.5 h-3.5" />
</button>
)}
{onDelete && (
<button
onClick={(e) => { e.stopPropagation(); e.preventDefault(); onDelete(); }}
className="p-1.5 bg-white hover:bg-red-50 text-slate-400 hover:text-red-500 rounded-full transition-all border border-slate-200 shadow-md hover:shadow-lg"
title="Удалить"
type="button"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
)}
</div>
</div>
);
};