feat: Add currency support to billing data and enhance BillingManager with exchange rate fetching and formatting functions
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m47s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m47s
This commit is contained in:
@@ -7,9 +7,11 @@
|
|||||||
"provider": "VDSINA",
|
"provider": "VDSINA",
|
||||||
"loginUrl": "cp.vdsina.com",
|
"loginUrl": "cp.vdsina.com",
|
||||||
"monthlyCost": 12.00,
|
"monthlyCost": 12.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2026-04-25",
|
"nextPaymentDate": "2026-04-25",
|
||||||
"lastPaymentDate": "2025-05-05",
|
"lastPaymentDate": "2025-05-05",
|
||||||
"lastPaymentAmount": 12.00,
|
"lastPaymentAmount": 12.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -21,9 +23,11 @@
|
|||||||
"provider": "Macloud",
|
"provider": "Macloud",
|
||||||
"loginUrl": "cp.macloud.ru",
|
"loginUrl": "cp.macloud.ru",
|
||||||
"monthlyCost": 7.00,
|
"monthlyCost": 7.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-09-02",
|
"nextPaymentDate": "2025-09-02",
|
||||||
"lastPaymentDate": "2025-02-03",
|
"lastPaymentDate": "2025-02-03",
|
||||||
"lastPaymentAmount": 7.00,
|
"lastPaymentAmount": 7.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -35,9 +39,11 @@
|
|||||||
"provider": "Hosting VDS",
|
"provider": "Hosting VDS",
|
||||||
"loginUrl": "my.hosting-vds.com/",
|
"loginUrl": "my.hosting-vds.com/",
|
||||||
"monthlyCost": 11.95,
|
"monthlyCost": 11.95,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-12-21",
|
"nextPaymentDate": "2025-12-21",
|
||||||
"lastPaymentDate": "2025-06-13",
|
"lastPaymentDate": "2025-06-13",
|
||||||
"lastPaymentAmount": 10.00,
|
"lastPaymentAmount": 10.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -49,9 +55,11 @@
|
|||||||
"provider": "Waicore",
|
"provider": "Waicore",
|
||||||
"loginUrl": "my.waicore.com/billmgr",
|
"loginUrl": "my.waicore.com/billmgr",
|
||||||
"monthlyCost": 9.60,
|
"monthlyCost": 9.60,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2026-04-25",
|
"nextPaymentDate": "2026-04-25",
|
||||||
"lastPaymentDate": "2025-04-25",
|
"lastPaymentDate": "2025-04-25",
|
||||||
"lastPaymentAmount": 9.60,
|
"lastPaymentAmount": 9.60,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -63,9 +71,11 @@
|
|||||||
"provider": "IHOR",
|
"provider": "IHOR",
|
||||||
"loginUrl": "billing.ihor-hosting.ru/billmgr",
|
"loginUrl": "billing.ihor-hosting.ru/billmgr",
|
||||||
"monthlyCost": 10.00,
|
"monthlyCost": 10.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-09-10",
|
"nextPaymentDate": "2025-09-10",
|
||||||
"lastPaymentDate": "2025-06-25",
|
"lastPaymentDate": "2025-06-25",
|
||||||
"lastPaymentAmount": 10.00,
|
"lastPaymentAmount": 10.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
}
|
}
|
||||||
|
|||||||
+142
-17
@@ -37,6 +37,14 @@ function BillingManager() {
|
|||||||
const [selectedItem, setSelectedItem] = useState(null);
|
const [selectedItem, setSelectedItem] = useState(null);
|
||||||
const [editingItem, setEditingItem] = useState({});
|
const [editingItem, setEditingItem] = useState({});
|
||||||
|
|
||||||
|
// Состояние для курсов валют
|
||||||
|
const [exchangeRates, setExchangeRates] = useState({
|
||||||
|
USD: 1,
|
||||||
|
EUR: 1,
|
||||||
|
RUB: 1
|
||||||
|
});
|
||||||
|
const [ratesLoading, setRatesLoading] = useState(false);
|
||||||
|
|
||||||
// Новый элемент для добавления
|
// Новый элемент для добавления
|
||||||
const [newItem, setNewItem] = useState({
|
const [newItem, setNewItem] = useState({
|
||||||
hostName: '',
|
hostName: '',
|
||||||
@@ -45,17 +53,45 @@ function BillingManager() {
|
|||||||
provider: '',
|
provider: '',
|
||||||
loginUrl: '',
|
loginUrl: '',
|
||||||
monthlyCost: 0,
|
monthlyCost: 0,
|
||||||
|
monthlyCostCurrency: 'USD',
|
||||||
nextPaymentDate: '',
|
nextPaymentDate: '',
|
||||||
lastPaymentDate: '',
|
lastPaymentDate: '',
|
||||||
lastPaymentAmount: 0,
|
lastPaymentAmount: 0,
|
||||||
|
lastPaymentCurrency: 'USD',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
notes: ''
|
notes: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchBillingData();
|
fetchBillingData();
|
||||||
|
fetchExchangeRates();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const fetchExchangeRates = async () => {
|
||||||
|
setRatesLoading(true);
|
||||||
|
try {
|
||||||
|
// Используем бесплатный API для получения курсов валют
|
||||||
|
const response = await axios.get('https://api.exchangerate-api.com/v4/latest/USD');
|
||||||
|
const rates = response.data.rates;
|
||||||
|
|
||||||
|
setExchangeRates({
|
||||||
|
USD: 1,
|
||||||
|
EUR: rates.EUR || 1,
|
||||||
|
RUB: rates.RUB || 1
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при загрузке курсов валют:', error);
|
||||||
|
// Используем примерные курсы если API недоступен
|
||||||
|
setExchangeRates({
|
||||||
|
USD: 1,
|
||||||
|
EUR: 0.85,
|
||||||
|
RUB: 95
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setRatesLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchBillingData = async () => {
|
const fetchBillingData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -75,9 +111,11 @@ function BillingManager() {
|
|||||||
"provider": "VDSINA",
|
"provider": "VDSINA",
|
||||||
"loginUrl": "cp.vdsina.com",
|
"loginUrl": "cp.vdsina.com",
|
||||||
"monthlyCost": 12.00,
|
"monthlyCost": 12.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2026-04-25",
|
"nextPaymentDate": "2026-04-25",
|
||||||
"lastPaymentDate": "2025-05-05",
|
"lastPaymentDate": "2025-05-05",
|
||||||
"lastPaymentAmount": 12.00,
|
"lastPaymentAmount": 12.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -89,9 +127,11 @@ function BillingManager() {
|
|||||||
"provider": "Macloud",
|
"provider": "Macloud",
|
||||||
"loginUrl": "cp.macloud.ru",
|
"loginUrl": "cp.macloud.ru",
|
||||||
"monthlyCost": 7.00,
|
"monthlyCost": 7.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-09-02",
|
"nextPaymentDate": "2025-09-02",
|
||||||
"lastPaymentDate": "2025-02-03",
|
"lastPaymentDate": "2025-02-03",
|
||||||
"lastPaymentAmount": 7.00,
|
"lastPaymentAmount": 7.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -103,9 +143,11 @@ function BillingManager() {
|
|||||||
"provider": "Hosting VDS",
|
"provider": "Hosting VDS",
|
||||||
"loginUrl": "my.hosting-vds.com/",
|
"loginUrl": "my.hosting-vds.com/",
|
||||||
"monthlyCost": 11.95,
|
"monthlyCost": 11.95,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-12-21",
|
"nextPaymentDate": "2025-12-21",
|
||||||
"lastPaymentDate": "2025-06-13",
|
"lastPaymentDate": "2025-06-13",
|
||||||
"lastPaymentAmount": 10.00,
|
"lastPaymentAmount": 10.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -117,9 +159,11 @@ function BillingManager() {
|
|||||||
"provider": "Waicore",
|
"provider": "Waicore",
|
||||||
"loginUrl": "my.waicore.com/billmgr",
|
"loginUrl": "my.waicore.com/billmgr",
|
||||||
"monthlyCost": 9.60,
|
"monthlyCost": 9.60,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2026-04-25",
|
"nextPaymentDate": "2026-04-25",
|
||||||
"lastPaymentDate": "2025-04-25",
|
"lastPaymentDate": "2025-04-25",
|
||||||
"lastPaymentAmount": 9.60,
|
"lastPaymentAmount": 9.60,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
},
|
},
|
||||||
@@ -131,9 +175,11 @@ function BillingManager() {
|
|||||||
"provider": "IHOR",
|
"provider": "IHOR",
|
||||||
"loginUrl": "billing.ihor-hosting.ru/billmgr",
|
"loginUrl": "billing.ihor-hosting.ru/billmgr",
|
||||||
"monthlyCost": 10.00,
|
"monthlyCost": 10.00,
|
||||||
|
"monthlyCostCurrency": "USD",
|
||||||
"nextPaymentDate": "2025-09-10",
|
"nextPaymentDate": "2025-09-10",
|
||||||
"lastPaymentDate": "2025-06-25",
|
"lastPaymentDate": "2025-06-25",
|
||||||
"lastPaymentAmount": 10.00,
|
"lastPaymentAmount": 10.00,
|
||||||
|
"lastPaymentCurrency": "USD",
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
}
|
}
|
||||||
@@ -247,8 +293,14 @@ function BillingManager() {
|
|||||||
const totalPages = Math.ceil(filteredData.length / pageSize);
|
const totalPages = Math.ceil(filteredData.length / pageSize);
|
||||||
|
|
||||||
// Статистика
|
// Статистика
|
||||||
const totalMonthlyCost = billingData.reduce((sum, item) => sum + item.monthlyCost, 0);
|
const totalMonthlyCost = billingData.reduce((sum, item) => {
|
||||||
const totalLastPayments = billingData.reduce((sum, item) => sum + item.lastPaymentAmount, 0);
|
const currency = item.monthlyCostCurrency || 'USD';
|
||||||
|
return sum + convertToRUB(item.monthlyCost, currency);
|
||||||
|
}, 0);
|
||||||
|
const totalLastPayments = billingData.reduce((sum, item) => {
|
||||||
|
const currency = item.lastPaymentCurrency || 'USD';
|
||||||
|
return sum + convertToRUB(item.lastPaymentAmount, currency);
|
||||||
|
}, 0);
|
||||||
const activeProviders = [...new Set(billingData.map(item => item.provider))].length;
|
const activeProviders = [...new Set(billingData.map(item => item.provider))].length;
|
||||||
const activeNodes = billingData.filter(item => item.status === 'active').length;
|
const activeNodes = billingData.filter(item => item.status === 'active').length;
|
||||||
|
|
||||||
@@ -274,13 +326,26 @@ function BillingManager() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCurrency(amount) {
|
function formatCurrency(amount, currency = 'USD') {
|
||||||
return new Intl.NumberFormat('en-US', {
|
return new Intl.NumberFormat('en-US', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'USD'
|
currency: currency
|
||||||
}).format(amount);
|
}).format(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertToRUB(amount, currency) {
|
||||||
|
const rate = exchangeRates[currency] || 1;
|
||||||
|
return amount * rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCurrencyInRUB(amount, currency) {
|
||||||
|
const rubAmount = convertToRUB(amount, currency);
|
||||||
|
return new Intl.NumberFormat('ru-RU', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'RUB'
|
||||||
|
}).format(rubAmount);
|
||||||
|
}
|
||||||
|
|
||||||
function formatPurpose(purpose) {
|
function formatPurpose(purpose) {
|
||||||
const purposeMap = {
|
const purposeMap = {
|
||||||
'relay': 'Relay VPS',
|
'relay': 'Relay VPS',
|
||||||
@@ -375,7 +440,7 @@ function BillingManager() {
|
|||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div className="h3 mb-0 fw-bold">
|
<div className="h3 mb-0 fw-bold">
|
||||||
{formatCurrency(totalLastPayments)}
|
{formatCurrencyInRUB(totalLastPayments, 'USD')}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted lh-1">всего платежей</div>
|
<div className="text-muted lh-1">всего платежей</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -390,7 +455,7 @@ function BillingManager() {
|
|||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div className="h3 mb-0 fw-bold">
|
<div className="h3 mb-0 fw-bold">
|
||||||
{formatCurrency(totalMonthlyCost)}
|
{formatCurrencyInRUB(totalMonthlyCost, 'USD')}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted lh-1">суммарных трат</div>
|
<div className="text-muted lh-1">суммарных трат</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -492,7 +557,10 @@ function BillingManager() {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{providers.map(provider => {
|
{providers.map(provider => {
|
||||||
const providerItems = billingData.filter(item => item.provider === provider);
|
const providerItems = billingData.filter(item => item.provider === provider);
|
||||||
const totalCost = providerItems.reduce((sum, item) => sum + item.monthlyCost, 0);
|
const totalCost = providerItems.reduce((sum, item) => {
|
||||||
|
const currency = item.monthlyCostCurrency || 'USD';
|
||||||
|
return sum + convertToRUB(item.monthlyCost, currency);
|
||||||
|
}, 0);
|
||||||
const servers = providerItems.map(item => formatPurpose(item.purpose)).join(', ');
|
const servers = providerItems.map(item => formatPurpose(item.purpose)).join(', ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -509,7 +577,7 @@ function BillingManager() {
|
|||||||
<IconExternalLink className="icon ms-1" size={14} />
|
<IconExternalLink className="icon ms-1" size={14} />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{formatCurrency(totalCost)}</td>
|
<td>{formatCurrencyInRUB(totalCost, 'USD')}</td>
|
||||||
<td>
|
<td>
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
<span className="avatar avatar-sm me-1">
|
<span className="avatar avatar-sm me-1">
|
||||||
@@ -569,7 +637,14 @@ function BillingManager() {
|
|||||||
<tr key={item.id}>
|
<tr key={item.id}>
|
||||||
<td>{item.hostName}</td>
|
<td>{item.hostName}</td>
|
||||||
<td>{formatDate(item.lastPaymentDate)}</td>
|
<td>{formatDate(item.lastPaymentDate)}</td>
|
||||||
<td>{formatCurrency(item.lastPaymentAmount)}</td>
|
<td>
|
||||||
|
<div>
|
||||||
|
<div>{formatCurrency(item.lastPaymentAmount, item.lastPaymentCurrency || 'USD')}</div>
|
||||||
|
<small className="text-muted">
|
||||||
|
{formatCurrencyInRUB(item.lastPaymentAmount, item.lastPaymentCurrency || 'USD')}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
@@ -764,8 +839,8 @@ function AddBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
placeholder="example.com"
|
placeholder="example.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-4">
|
||||||
<label className="form-label">Ежемесячная стоимость ($)</label>
|
<label className="form-label">Ежемесячная стоимость</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -775,6 +850,19 @@ function AddBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<label className="form-label">Валюта</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={item.monthlyCostCurrency || 'USD'}
|
||||||
|
onChange={(e) => handleChange('monthlyCostCurrency', e.target.value)}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="RUB">RUB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label">Следующий платеж</label>
|
<label className="form-label">Следующий платеж</label>
|
||||||
<input
|
<input
|
||||||
@@ -794,8 +882,8 @@ function AddBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
onChange={(e) => handleChange('lastPaymentDate', e.target.value)}
|
onChange={(e) => handleChange('lastPaymentDate', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-4">
|
||||||
<label className="form-label">Сумма последнего платежа ($)</label>
|
<label className="form-label">Сумма последнего платежа</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -804,6 +892,18 @@ function AddBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
onChange={(e) => handleChange('lastPaymentAmount', parseFloat(e.target.value) || 0)}
|
onChange={(e) => handleChange('lastPaymentAmount', parseFloat(e.target.value) || 0)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<label className="form-label">Валюта</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={item.lastPaymentCurrency || 'USD'}
|
||||||
|
onChange={(e) => handleChange('lastPaymentCurrency', e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="RUB">RUB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label">Статус</label>
|
<label className="form-label">Статус</label>
|
||||||
<select
|
<select
|
||||||
@@ -932,8 +1032,8 @@ function EditBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
placeholder="example.com"
|
placeholder="example.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-4">
|
||||||
<label className="form-label">Ежемесячная стоимость ($)</label>
|
<label className="form-label">Ежемесячная стоимость</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -943,6 +1043,19 @@ function EditBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<label className="form-label">Валюта</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={item.monthlyCostCurrency || 'USD'}
|
||||||
|
onChange={(e) => handleChange('monthlyCostCurrency', e.target.value)}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="RUB">RUB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label">Следующий платеж</label>
|
<label className="form-label">Следующий платеж</label>
|
||||||
<input
|
<input
|
||||||
@@ -962,8 +1075,8 @@ function EditBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
onChange={(e) => handleChange('lastPaymentDate', e.target.value)}
|
onChange={(e) => handleChange('lastPaymentDate', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-4">
|
||||||
<label className="form-label">Сумма последнего платежа ($)</label>
|
<label className="form-label">Сумма последнего платежа</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -972,6 +1085,18 @@ function EditBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
|
|||||||
onChange={(e) => handleChange('lastPaymentAmount', parseFloat(e.target.value) || 0)}
|
onChange={(e) => handleChange('lastPaymentAmount', parseFloat(e.target.value) || 0)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<label className="form-label">Валюта</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={item.lastPaymentCurrency || 'USD'}
|
||||||
|
onChange={(e) => handleChange('lastPaymentCurrency', e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="RUB">RUB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label">Статус</label>
|
<label className="form-label">Статус</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
Reference in New Issue
Block a user