Files
mkd/components/finance/PaymentStatusModal.tsx
2026-02-04 00:17:04 +05:00

170 lines
6.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 { X, Wallet, Calendar, Ban, Clock } from 'lucide-react';
export type PaymentStatusAction = 'paid' | 'postponed' | 'cancelled';
export interface PaymentStatusModalPayload {
paymentDate?: string;
paymentRef?: string;
isCash?: boolean;
postponedDate?: string;
cancelReason?: string;
}
interface PaymentStatusModalProps {
action: PaymentStatusAction;
invoiceLabel?: string;
/** По умолчанию отметить «Оплата наличными» */
defaultIsCash?: boolean;
onConfirm: (payload: PaymentStatusModalPayload) => void;
onCancel: () => void;
}
export const PaymentStatusModal: React.FC<PaymentStatusModalProps> = ({
action,
invoiceLabel,
defaultIsCash = false,
onConfirm,
onCancel
}) => {
const today = new Date().toISOString().split('T')[0];
const [paymentDate, setPaymentDate] = useState(today);
const [paymentRef, setPaymentRef] = useState('');
const [isCash, setIsCash] = useState(defaultIsCash);
const [postponedDate, setPostponedDate] = useState('');
const [cancelReason, setCancelReason] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (action === 'paid') {
onConfirm({ paymentDate, paymentRef: paymentRef.trim() || undefined, isCash });
} else if (action === 'postponed') {
if (!postponedDate) {
alert('Укажите дату переноса');
return;
}
onConfirm({ postponedDate });
} else {
if (!cancelReason.trim()) {
alert('Укажите причину отмены');
return;
}
onConfirm({ cancelReason: cancelReason.trim() });
}
};
const title =
action === 'paid'
? 'Оплата счета'
: action === 'postponed'
? 'Перенос на другую дату'
: 'Отмена / отказ';
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50">
<div className="bg-white rounded-2xl shadow-xl max-w-md w-full">
<div className="p-4 border-b border-slate-200 flex justify-between items-center">
<h3 className="font-bold text-slate-800">{title}</h3>
<button type="button" onClick={onCancel} className="p-2 rounded-lg hover:bg-slate-100 text-slate-500">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSubmit} className="p-4 space-y-4">
{invoiceLabel && (
<p className="text-sm text-slate-600">{invoiceLabel}</p>
)}
{action === 'paid' && (
<>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Дата оплаты</label>
<div className="relative">
<Calendar className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="date"
value={paymentDate}
onChange={(e) => setPaymentDate(e.target.value)}
className="w-full pl-10 pr-3 py-2 border border-slate-200 rounded-xl text-sm"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Номер платежки</label>
<input
type="text"
value={paymentRef}
onChange={(e) => setPaymentRef(e.target.value)}
className="w-full px-3 py-2 border border-slate-200 rounded-xl text-sm"
placeholder="Номер платёжного поручения"
/>
</div>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={isCash}
onChange={(e) => setIsCash(e.target.checked)}
className="rounded border-slate-300"
/>
<span className="text-sm font-medium text-slate-700">Оплата наличными</span>
</label>
</>
)}
{action === 'postponed' && (
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Перенос на дату *</label>
<div className="relative">
<Clock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="date"
value={postponedDate}
onChange={(e) => setPostponedDate(e.target.value)}
className="w-full pl-10 pr-3 py-2 border border-slate-200 rounded-xl text-sm"
/>
</div>
</div>
)}
{action === 'cancelled' && (
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Причина отмены / отказа *</label>
<textarea
value={cancelReason}
onChange={(e) => setCancelReason(e.target.value)}
rows={3}
className="w-full px-3 py-2 border border-slate-200 rounded-xl text-sm resize-none"
placeholder="Укажите причину"
/>
</div>
)}
<div className="flex gap-2 pt-2">
<button
type="submit"
className={`flex items-center gap-2 px-4 py-2 rounded-xl font-medium text-sm ${
action === 'paid'
? 'bg-emerald-600 text-white hover:bg-emerald-700'
: action === 'postponed'
? 'bg-amber-600 text-white hover:bg-amber-700'
: 'bg-red-600 text-white hover:bg-red-700'
}`}
>
{action === 'paid' && <Wallet className="w-4 h-4" />}
{action === 'postponed' && <Clock className="w-4 h-4" />}
{action === 'cancelled' && <Ban className="w-4 h-4" />}
{action === 'paid' ? 'Оплатить' : action === 'postponed' ? 'Перенести' : 'Отменить'}
</button>
<button
type="button"
onClick={onCancel}
className="px-4 py-2 border border-slate-200 rounded-xl font-medium text-sm text-slate-600 hover:bg-slate-50"
>
Отмена
</button>
</div>
</form>
</div>
</div>
);
};