feat(balance): подпись аккаунта «хостер / имя» как в фильтрах VPS

- Вынесен accountSelectLabel в src/lib/account-select-label.js
- Модалка списания, таблица баланса и журнал на BalancePage
- VpsPage переведён на общий модуль

Made-with: Cursor
This commit is contained in:
Denozordec
2026-03-23 21:06:52 +07:00
parent 524cbba5e7
commit f4517f03fc
3 changed files with 27 additions and 11 deletions
+13
View File
@@ -0,0 +1,13 @@
/**
* Подпись аккаунта в select и списках: при выборе одного хостера (scopedProviderId) — только имя аккаунта,
* иначе «хостер / аккаунт» (как в фильтрах VPS).
* @param {{ name?: string, providerId?: string }} account
* @param {Map<string, { name?: string }>} providerById
* @param {string} [scopedProviderId]
*/
export function accountSelectLabel(account, providerById, scopedProviderId) {
const name = account.name?.trim() || '—'
if (scopedProviderId) return name
const providerName = providerById.get(account.providerId)?.name ?? '—'
return `${providerName} / ${name}`
}
+13 -3
View File
@@ -8,6 +8,7 @@ import { EmptyState } from '../components/EmptyState'
import { PageHeader } from '../components/PageHeader'
import { ConvertedAmount } from '../components/ConvertedAmount'
import { noBrowserSuggestProps } from '../lib/noBrowserSuggestProps'
import { accountSelectLabel } from '../lib/account-select-label'
const emptyForm = {
type: 'daily_debit',
@@ -24,6 +25,11 @@ export function BalancePage({ db, actions, settings, ratesData }) {
const [error, setError] = useState('')
const [isModalOpen, setIsModalOpen] = useState(false)
const providerById = useMemo(
() => new Map(db.providers.map((p) => [p.id, p])),
[db.providers],
)
const vpsOptions = useMemo(
() => db.vps.filter((vps) => !form.providerAccountId || vps.providerAccountId === form.providerAccountId),
[db.vps, form.providerAccountId],
@@ -94,7 +100,7 @@ export function BalancePage({ db, actions, settings, ratesData }) {
<tbody>
{accountBalances.map((account) => (
<tr key={account.id}>
<td>{account.name}</td>
<td>{accountSelectLabel(account, providerById, '')}</td>
<td>{billingModeLabel(account.billingMode)}</td>
<td>
<ConvertedAmount
@@ -146,7 +152,11 @@ export function BalancePage({ db, actions, settings, ratesData }) {
</span>
</td>
<td>
<div>{account?.name || '-'}</div>
<div>
{account
? accountSelectLabel(account, providerById, '')
: '-'}
</div>
<div className="text-secondary">{vps?.dns || vps?.ip || '-'}</div>
</td>
<td>
@@ -216,7 +226,7 @@ export function BalancePage({ db, actions, settings, ratesData }) {
<option value="">Выберите аккаунт</option>
{db.providerAccounts.map((account) => (
<option key={account.id} value={account.id}>
{account.name}
{accountSelectLabel(account, providerById, '')}
</option>
))}
</select>
+1 -8
View File
@@ -29,6 +29,7 @@ import { ProjectSuggestInput } from '../components/ProjectSuggestInput'
import { noBrowserSuggestProps } from '../lib/noBrowserSuggestProps'
import { getPaidUntilDate as computePaidUntilForHealth } from '../lib/paid-until'
import { billmanagerSyncableAccounts } from '../lib/billmanager-ui'
import { accountSelectLabel } from '../lib/account-select-label'
const emptyForm = {
ip: '',
@@ -78,14 +79,6 @@ function loadFilterPresets() {
}
}
/** Подпись аккаунта в select: при фильтре по одному хостеру — только имя, иначе «хостер / аккаунт». */
function accountSelectLabel(account, providerById, scopedProviderId) {
const name = account.name?.trim() || '—'
if (scopedProviderId) return name
const providerName = providerById.get(account.providerId)?.name ?? '—'
return `${providerName} / ${name}`
}
function vpsMonthlyEstimateInBase(item, baseCurrency, ratesData, providerById) {
if (item.status !== 'active') return 0
const tariffType = item.tariffType || (Number(item.dailyRate || 0) > 0 ? 'daily' : 'monthly')