fix(web): VPS — корректная валюта тарифа и конвертация курсов в таблице

Раньше в колонке тарифа вызывался formatInBaseCurrency с ratesData=null,
поэтому 0.8 EUR отображались как «0,80 ₽» (без конвертации, но в базовой
валюте). Теперь:
- ratesData грузится через ratesQueryOptions (как на dashboard/reports);
- валюта тарифа определяется через effectiveVpsTariffCurrency (приоритет
  baseCurrency провайдера, затем vps.currency);
- форматирование через formatInProviderCurrency — учитывает курсы
  провайдера (usdRate/eurRate) и глобальные курсы, при их отсутствии
  показывает сумму в исходной валюте, а не в базовой.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-06-26 22:33:44 +07:00
co-authored by Cursor
parent ee5d5b228b
commit 7ef42ee7e8
+18 -13
View File
@@ -4,9 +4,10 @@ import { useState, useMemo } from 'react'
import { PlusIcon, PencilIcon, Trash2Icon } from 'lucide-react' import { PlusIcon, PencilIcon, Trash2Icon } from 'lucide-react'
import { toast } from 'sonner' import { toast } from 'sonner'
import { snapshotQueryOptions } from '@/queries/snapshot' import { snapshotQueryOptions, ratesQueryOptions } from '@/queries/snapshot'
import { api, ApiError } from '@/lib/api-client' import { api, ApiError } from '@/lib/api-client'
import { vpsSchema, type VpsFormValues } from '@/lib/schemas' import { vpsSchema, type VpsFormValues } from '@/lib/schemas'
import { normalizeRatesPayload, effectiveVpsTariffCurrency, formatInProviderCurrency } from '@/lib/format'
import { PageShell } from '@/components/page-shell' import { PageShell } from '@/components/page-shell'
import { PageHeader } from '@/components/page-header' import { PageHeader } from '@/components/page-header'
import { Button } from '@cfdm/ui/components/button' import { Button } from '@cfdm/ui/components/button'
@@ -30,7 +31,7 @@ import {
} from '@/components/vps-filters' } from '@/components/vps-filters'
import type { Vps } from '@/types/entities' import type { Vps } from '@/types/entities'
import { vpsStatusLabel, tariffTypeLabel, formatInBaseCurrency } from '@/lib/format' import { vpsStatusLabel, tariffTypeLabel } from '@/lib/format'
import { providerByIdMap, accountSelectLabel } from '@/lib/billmanager' import { providerByIdMap, accountSelectLabel } from '@/lib/billmanager'
export const Route = createFileRoute('/_auth/vps')({ export const Route = createFileRoute('/_auth/vps')({
@@ -54,6 +55,10 @@ function VpsPage() {
const [filters, setFilters] = useState<VpsFiltersState>(buildDefaultVpsFilters()) const [filters, setFilters] = useState<VpsFiltersState>(buildDefaultVpsFilters())
const [presets, setPresets] = useState<VpsFilterPreset[]>(() => loadFilterPresets()) const [presets, setPresets] = useState<VpsFilterPreset[]>(() => loadFilterPresets())
const settings = snapshot?.settings[0]
const { data: rawRates } = useQuery(ratesQueryOptions(settings?.ratesUrl))
const ratesData = normalizeRatesPayload(rawRates) ?? rawRates ?? null
const createMutation = useMutation({ const createMutation = useMutation({
mutationFn: (record: VpsFormValues) => api.create('vps', record as unknown as Vps), mutationFn: (record: VpsFormValues) => api.create('vps', record as unknown as Vps),
onSuccess: () => { onSuccess: () => {
@@ -195,17 +200,17 @@ function VpsPage() {
{ {
key: 'tariff', key: 'tariff',
header: 'Тариф', header: 'Тариф',
cell: (v) => ( cell: (v) => {
<span className="tabular-nums"> const provider = providerById.get(v.providerId)
{formatInBaseCurrency( const currency = effectiveVpsTariffCurrency(v, provider)
v.tariffType === 'daily' ? Number(v.dailyRate || 0) * 30 : Number(v.monthlyRate || 0), const amount = v.tariffType === 'daily' ? Number(v.dailyRate || 0) * 30 : Number(v.monthlyRate || 0)
v.currency, return (
snapshot?.settings ?? [], <span className="tabular-nums">
null, {formatInProviderCurrency(amount, currency, provider, snapshot?.settings ?? [], ratesData)}
)} <span className="ml-1 text-xs text-muted-foreground">{tariffTypeLabel(v.tariffType)}</span>
<span className="ml-1 text-xs text-muted-foreground">{tariffTypeLabel(v.tariffType)}</span> </span>
</span> )
), },
}, },
{ {
key: 'actions', key: 'actions',