Files
mkd/components/objects/PerformanceCard.tsx
2026-02-04 00:17:04 +05:00

108 lines
5.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, { 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>
);
};