refactor: Update BillingManager UI components for improved styling and user experience, including badge colors, button styles, and status selection method.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m3s

This commit is contained in:
2025-12-06 03:00:35 +07:00
parent d60eb96d00
commit e086d04131
+59 -38
View File
@@ -426,27 +426,32 @@ function BillingManager() {
if (days < 0) { if (days < 0) {
return ( return (
<span className="badge bg-danger"> <span className="badge bg-red-lt text-red">
Просрочено {Math.abs(days)} дн. <IconAlertTriangle size={12} className="me-1" />
{Math.abs(days)} дн. назад
</span> </span>
); );
} else if (days === 0) { } else if (days === 0) {
return <span className="badge bg-danger">Сегодня!</span>; return (
<span className="badge bg-red-lt text-red">
Сегодня
</span>
);
} else if (days <= 7) { } else if (days <= 7) {
return ( return (
<span className="badge bg-warning text-dark"> <span className="badge bg-yellow-lt text-yellow">
{days} дн. {days} дн.
</span> </span>
); );
} else if (days <= 30) { } else if (days <= 30) {
return ( return (
<span className="badge bg-blue-lt text-blue"> <span className="badge bg-azure-lt text-azure">
{days} дн. {days} дн.
</span> </span>
); );
} else { } else {
return ( return (
<span className="text-muted small"> <span className="text-muted">
{days} дн. {days} дн.
</span> </span>
); );
@@ -495,7 +500,7 @@ function BillingManager() {
<div className="card"> <div className="card">
<div className="card-body py-3"> <div className="card-body py-3">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<span className="avatar avatar-md me-3 bg-primary-lt text-primary border-0"> <span className="avatar avatar-md me-3 bg-blue-lt text-blue border-0">
<IconCurrencyDollar size={24} /> <IconCurrencyDollar size={24} />
</span> </span>
<div> <div>
@@ -512,7 +517,7 @@ function BillingManager() {
<div className="card"> <div className="card">
<div className="card-body py-3"> <div className="card-body py-3">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<span className={`avatar avatar-md me-3 ${overduePayments.length > 0 ? 'bg-danger-lt text-danger' : 'bg-success-lt text-success'} border-0`}> <span className={`avatar avatar-md me-3 ${overduePayments.length > 0 ? 'bg-red-lt text-red' : 'bg-green-lt text-green'} border-0`}>
<IconAlertTriangle size={24} /> <IconAlertTriangle size={24} />
</span> </span>
<div> <div>
@@ -529,7 +534,7 @@ function BillingManager() {
<div className="card"> <div className="card">
<div className="card-body py-3"> <div className="card-body py-3">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<span className={`avatar avatar-md me-3 ${urgentPayments.length > 0 ? 'bg-warning-lt text-warning' : 'bg-blue-lt text-blue'} border-0`}> <span className={`avatar avatar-md me-3 ${urgentPayments.length > 0 ? 'bg-yellow-lt text-yellow' : 'bg-azure-lt text-azure'} border-0`}>
<IconClock size={24} /> <IconClock size={24} />
</span> </span>
<div> <div>
@@ -568,21 +573,21 @@ function BillingManager() {
</div> </div>
{/* Быстрые фильтры */} {/* Быстрые фильтры */}
<div className="btn-group btn-group-sm"> <div className="d-flex gap-1">
<button <button
className={`btn ${filterUrgency === '' ? 'btn-primary' : 'btn-outline-primary'}`} className={`btn btn-sm ${filterUrgency === '' ? 'btn-primary' : 'btn-outline-secondary'}`}
onClick={() => { setFilterUrgency(''); setCurrentPage(1); }} onClick={() => { setFilterUrgency(''); setCurrentPage(1); }}
> >
Все Все
</button> </button>
<button <button
className={`btn ${filterUrgency === 'overdue' ? 'btn-danger' : 'btn-outline-danger'}`} className={`btn btn-sm ${filterUrgency === 'overdue' ? 'btn-red' : 'btn-outline-secondary'}`}
onClick={() => { setFilterUrgency('overdue'); setCurrentPage(1); }} onClick={() => { setFilterUrgency('overdue'); setCurrentPage(1); }}
> >
Просрочено Просрочено
</button> </button>
<button <button
className={`btn ${filterUrgency === 'urgent' ? 'btn-warning' : 'btn-outline-warning'}`} className={`btn btn-sm ${filterUrgency === 'urgent' ? 'btn-yellow' : 'btn-outline-secondary'}`}
onClick={() => { setFilterUrgency('urgent'); setCurrentPage(1); }} onClick={() => { setFilterUrgency('urgent'); setCurrentPage(1); }}
> >
Срочно Срочно
@@ -694,14 +699,16 @@ function BillingManager() {
<UrgencyBadge days={days} /> <UrgencyBadge days={days} />
</td> </td>
<td className="text-center"> <td className="text-center">
<span className={`badge ${item.status === 'active' ? 'bg-success-lt text-success' : 'bg-secondary-lt text-secondary'}`}> {item.status === 'active' ? (
{item.status === 'active' ? '●' : '○'} <span className="badge bg-green-lt text-green">Активен</span>
</span> ) : (
<span className="badge bg-secondary-lt text-secondary">Неактивен</span>
)}
</td> </td>
<td> <td>
<div className="btn-list gap-1 justify-content-end"> <div className="btn-list gap-1 justify-content-end flex-nowrap">
<button <button
className="btn btn-ghost-success btn-icon btn-sm" className="btn btn-icon btn-sm btn-outline-success"
onClick={() => { onClick={() => {
setPaymentDraft({ setPaymentDraft({
serverId: item.id, serverId: item.id,
@@ -717,14 +724,14 @@ function BillingManager() {
<IconCreditCard size={16} /> <IconCreditCard size={16} />
</button> </button>
<button <button
className="btn btn-ghost-primary btn-icon btn-sm" className="btn btn-icon btn-sm btn-outline-primary"
onClick={() => handleEditItem(item)} onClick={() => handleEditItem(item)}
title="Редактировать" title="Редактировать"
> >
<IconEdit size={16} /> <IconEdit size={16} />
</button> </button>
<button <button
className="btn btn-ghost-danger btn-icon btn-sm" className="btn btn-icon btn-sm btn-outline-danger"
onClick={() => handleDeleteItem(item)} onClick={() => handleDeleteItem(item)}
title="Удалить" title="Удалить"
> >
@@ -830,9 +837,9 @@ function BillingManager() {
</td> </td>
<td className="text-muted small">{r.note || '—'}</td> <td className="text-muted small">{r.note || '—'}</td>
<td> <td>
<div className="btn-list gap-1 justify-content-end"> <div className="btn-list gap-1 justify-content-end flex-nowrap">
<button <button
className="btn btn-ghost-primary btn-icon btn-sm" className="btn btn-icon btn-sm btn-outline-secondary"
onClick={() => { onClick={() => {
setPaymentDraft({ setPaymentDraft({
serverId: r.server.id, serverId: r.server.id,
@@ -849,7 +856,7 @@ function BillingManager() {
<IconEdit size={14} /> <IconEdit size={14} />
</button> </button>
<button <button
className="btn btn-ghost-danger btn-icon btn-sm" className="btn btn-icon btn-sm btn-outline-danger"
onClick={() => { onClick={() => {
setPaymentToDelete({ serverId: r.server.id, index: r._index, data: r }); setPaymentToDelete({ serverId: r.server.id, index: r._index, data: r });
setShowDeletePaymentModal(true); setShowDeletePaymentModal(true);
@@ -1077,21 +1084,35 @@ function BillingForm({ item, onItemChange, servers }) {
<div className="col-12"> <div className="col-12">
<div className="form-label">Статус</div> <div className="form-label">Статус</div>
<div className="btn-group w-100"> <div className="d-flex gap-2">
<button <label className="form-selectgroup-item flex-fill">
type="button" <input
className={`btn ${item.status === 'active' ? 'btn-success' : 'btn-outline-success'}`} type="radio"
onClick={() => onItemChange({ ...item, status: 'active' })} name="status"
> value="active"
Активен className="form-selectgroup-input"
</button> checked={item.status === 'active'}
<button onChange={() => onItemChange({ ...item, status: 'active' })}
type="button" />
className={`btn ${item.status === 'inactive' ? 'btn-secondary' : 'btn-outline-secondary'}`} <span className="form-selectgroup-label">
onClick={() => onItemChange({ ...item, status: 'inactive' })} <IconCheck size={16} className="me-1" />
> Активен
Неактивен </span>
</button> </label>
<label className="form-selectgroup-item flex-fill">
<input
type="radio"
name="status"
value="inactive"
className="form-selectgroup-input"
checked={item.status === 'inactive'}
onChange={() => onItemChange({ ...item, status: 'inactive' })}
/>
<span className="form-selectgroup-label">
<IconX size={16} className="me-1" />
Неактивен
</span>
</label>
</div> </div>
</div> </div>