Files
mkd/components/finance/InvoiceRegistry.tsx

102 lines
6.7 KiB
TypeScript
Raw Normal View History

2026-02-04 00:17:04 +05:00
import React from 'react';
import { Invoice, InvoiceStatus } from '../../types';
import { FileText, FileSearch, AlertCircle, FileCheck, Check, X, MessageSquareQuote, CalendarPlus } from 'lucide-react';
const StatusBadge: React.FC<{ status: InvoiceStatus }> = ({ status }) => {
const config: Record<InvoiceStatus, { label: string, color: string, bg: string }> = {
draft: { label: 'Черновик', color: 'text-slate-500', bg: 'bg-slate-100' },
pending_approval: { label: 'На согл.', color: 'text-amber-600', bg: 'bg-amber-50' },
clarification: { label: 'Уточнение', color: 'text-purple-600', bg: 'bg-purple-50' },
approved: { label: 'Согласован', color: 'text-blue-600', bg: 'bg-blue-50' },
scheduled: { label: 'В графике', color: 'text-indigo-600', bg: 'bg-indigo-50' },
paid: { label: 'Оплачен', color: 'text-emerald-600', bg: 'bg-emerald-50' },
rejected: { label: 'Отказ', color: 'text-red-600', bg: 'bg-red-50' },
overdue: { label: 'Просрочен', color: 'text-white', bg: 'bg-red-500' },
};
const s = config[status] || config.draft;
return (
<span className={`px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider ${s.bg} ${s.color}`}>
{s.label}
</span>
);
};
export const InvoiceRegistry: React.FC<{ invoices: Invoice[], onUpdateStatus: (id: string, s: InvoiceStatus, extra?: any) => void }> = ({ invoices, onUpdateStatus }) => {
// Show only "inbox" invoices: draft, pending, clarification, approved
const registryInvoices = invoices.filter(i => ['draft', 'pending_approval', 'clarification', 'approved', 'rejected'].includes(i.status));
return (
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden animate-fade-in">
<div className="p-4 bg-slate-50 border-b border-slate-200 flex justify-between items-center">
<h3 className="font-black text-slate-700 text-[10px] uppercase tracking-widest">Реестр согласования счетов</h3>
<div className="flex gap-2">
<button className="p-1.5 bg-white border border-slate-200 rounded-lg text-slate-400"><FileSearch className="w-4 h-4"/></button>
</div>
</div>
<div className="divide-y divide-slate-100">
{registryInvoices.length === 0 && <div className="p-10 text-center text-slate-400 text-sm">Новых счетов нет</div>}
{registryInvoices.map(inv => (
<div key={inv.id} className="p-4 flex flex-col md:flex-row md:items-center justify-between hover:bg-slate-50 transition-colors group gap-4">
<div className="flex gap-4 items-center flex-1">
<div className={`p-3 rounded-xl ${inv.status === 'approved' ? 'bg-blue-50 text-blue-600' : 'bg-slate-50 text-slate-400'}`}>
<FileText className="w-5 h-5"/>
</div>
<div>
<div className="flex items-center gap-2 mb-1">
<StatusBadge status={inv.status}/>
{inv.priority === 'high' && <span className="text-[9px] font-black text-red-500 uppercase">Срочно</span>}
</div>
<p className="text-sm font-bold text-slate-800">{inv.contractorName}</p>
<p className="text-[10px] text-slate-500 font-medium">{inv.address} {inv.serviceName}</p>
</div>
</div>
<div className="flex items-center justify-between md:justify-end gap-6">
<div className="text-right">
<p className="text-sm font-black text-slate-900">{inv.amount.toLocaleString()} </p>
<p className="text-[10px] text-slate-400 font-medium">{inv.date}</p>
</div>
<div className="flex gap-2">
{inv.status === 'pending_approval' || inv.status === 'clarification' ? (
<>
<button
onClick={() => onUpdateStatus(inv.id, 'approved')}
className="p-2 bg-emerald-50 text-emerald-600 rounded-xl hover:bg-emerald-100 border border-emerald-100"
title="Согласовать"
>
<Check className="w-4 h-4"/>
</button>
<button
onClick={() => onUpdateStatus(inv.id, 'clarification')}
className="p-2 bg-purple-50 text-purple-600 rounded-xl hover:bg-purple-100 border border-purple-100"
title="На уточнение"
>
<MessageSquareQuote className="w-4 h-4"/>
</button>
<button
onClick={() => onUpdateStatus(inv.id, 'rejected')}
className="p-2 bg-red-50 text-red-600 rounded-xl hover:bg-red-100 border border-red-100"
title="Отказать"
>
<X className="w-4 h-4"/>
</button>
</>
) : inv.status === 'approved' ? (
<button
onClick={() => onUpdateStatus(inv.id, 'scheduled', { scheduledDate: new Date().toISOString().split('T')[0] })}
className="px-3 py-2 bg-indigo-600 text-white rounded-xl hover:bg-indigo-700 text-[10px] font-black uppercase flex items-center gap-1.5 shadow-lg shadow-indigo-500/20"
>
<CalendarPlus className="w-4 h-4"/> В график
</button>
) : null}
</div>
</div>
</div>
))}
</div>
</div>
);
};