feat(data-grid): add sorting function support for DataGridColumn and update vps tariff calculations
Docker / build (push) Failing after 22s
Docker / build (push) Failing after 22s
Enhanced DataGridColumn interface to include a sorting function option. Updated the frame-data-grid component to utilize the new sorting function. Introduced a new function for calculating comparable daily rates for VPS tariffs, improving the accuracy of monthly burn calculations. Updated the VpsPage to implement the new sorting function and daily rate logic.
This commit is contained in:
@@ -8,6 +8,8 @@ export interface DataGridColumn<T> {
|
||||
icon?: LucideIcon
|
||||
sortable?: boolean
|
||||
sortValue?: (row: T) => string | number
|
||||
/** TanStack sortingFn; для числовых sortValue — `'basic'`. */
|
||||
sortingFn?: 'auto' | 'alphanumeric' | 'basic' | 'text' | 'datetime'
|
||||
headerTitle?: string
|
||||
className?: string
|
||||
headerClassName?: string
|
||||
|
||||
@@ -418,6 +418,7 @@ export function columnDefFromDataGrid<T>(
|
||||
accessorFn: c.sortValue
|
||||
? (row: T) => c.sortValue!(row)
|
||||
: (row: T) => (row as Record<string, unknown>)[c.key] as string | number,
|
||||
sortingFn: c.sortingFn ?? 'auto',
|
||||
}
|
||||
: {}),
|
||||
header: Icon
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
vpsTariffComparableDailyRate,
|
||||
vpsTariffMonthlyBurn,
|
||||
vpsTariffRateAmount,
|
||||
} from './format'
|
||||
|
||||
describe('vpsTariffComparableDailyRate', () => {
|
||||
it('нормализует месячный тариф к суточной стоимости', () => {
|
||||
expect(
|
||||
vpsTariffComparableDailyRate({
|
||||
tariffType: 'monthly',
|
||||
monthlyRate: 75,
|
||||
dailyRate: null,
|
||||
}),
|
||||
).toBeCloseTo(75 / 30, 6)
|
||||
})
|
||||
|
||||
it('оставляет суточный тариф как есть', () => {
|
||||
expect(
|
||||
vpsTariffComparableDailyRate({
|
||||
tariffType: 'daily',
|
||||
dailyRate: 5.59,
|
||||
monthlyRate: 167.7,
|
||||
}),
|
||||
).toBeCloseTo(5.59, 6)
|
||||
})
|
||||
|
||||
it('сортирует 5,59/сутки дороже 75/мес', () => {
|
||||
const daily = vpsTariffComparableDailyRate({
|
||||
tariffType: 'daily',
|
||||
dailyRate: 5.59,
|
||||
})
|
||||
const monthly = vpsTariffComparableDailyRate({
|
||||
tariffType: 'monthly',
|
||||
monthlyRate: 75,
|
||||
})
|
||||
expect(daily).toBeGreaterThan(monthly)
|
||||
})
|
||||
|
||||
it('для daily без dailyRate берёт monthly/30', () => {
|
||||
expect(
|
||||
vpsTariffComparableDailyRate({
|
||||
tariffType: 'daily',
|
||||
dailyRate: 0,
|
||||
monthlyRate: 90,
|
||||
}),
|
||||
).toBeCloseTo(3, 6)
|
||||
})
|
||||
})
|
||||
|
||||
describe('vpsTariffMonthlyBurn', () => {
|
||||
it('даёт месячный эквивалент суточного тарифа', () => {
|
||||
expect(
|
||||
vpsTariffMonthlyBurn({ tariffType: 'daily', dailyRate: 5.59 }),
|
||||
).toBeCloseTo(5.59 * 30, 6)
|
||||
})
|
||||
|
||||
it('для месячного возвращает monthlyRate', () => {
|
||||
expect(
|
||||
vpsTariffMonthlyBurn({ tariffType: 'monthly', monthlyRate: 75 }),
|
||||
).toBeCloseTo(75, 6)
|
||||
})
|
||||
})
|
||||
|
||||
describe('vpsTariffRateAmount', () => {
|
||||
it('для отображения берёт ставку периода без нормализации', () => {
|
||||
expect(
|
||||
vpsTariffRateAmount({ tariffType: 'daily', dailyRate: 5.59, monthlyRate: 167 }),
|
||||
).toBe(5.59)
|
||||
expect(
|
||||
vpsTariffRateAmount({ tariffType: 'monthly', monthlyRate: 75, dailyRate: 2.5 }),
|
||||
).toBe(75)
|
||||
})
|
||||
})
|
||||
@@ -151,16 +151,34 @@ export function vpsTariffRateAmount(vps: {
|
||||
return monthly ?? 0
|
||||
}
|
||||
|
||||
/** Месячный burn-rate для сортировки и отчётов. */
|
||||
/**
|
||||
* Сопоставимая суточная стоимость (единый знаменатель для сортировки).
|
||||
* Месячный тариф → / 30; суточный → dailyRate (fallback: monthly/30).
|
||||
*/
|
||||
export function vpsTariffComparableDailyRate(vps: {
|
||||
tariffType?: string | null
|
||||
dailyRate?: number | string | null
|
||||
monthlyRate?: number | string | null
|
||||
}): number {
|
||||
const daily = tariffRateNumber(vps.dailyRate)
|
||||
const monthly = tariffRateNumber(vps.monthlyRate)
|
||||
if (vps.tariffType === 'daily') {
|
||||
if (daily != null && daily > 0) return daily
|
||||
if (monthly != null && monthly > 0) return monthly / 30
|
||||
return 0
|
||||
}
|
||||
if (monthly != null && monthly > 0) return monthly / 30
|
||||
if (daily != null && daily > 0) return daily
|
||||
return 0
|
||||
}
|
||||
|
||||
/** Месячный burn-rate для сортировки и отчётов (= comparable daily × 30). */
|
||||
export function vpsTariffMonthlyBurn(vps: {
|
||||
tariffType?: string | null
|
||||
dailyRate?: number | string | null
|
||||
monthlyRate?: number | string | null
|
||||
}): number {
|
||||
const daily = tariffRateNumber(vps.dailyRate) ?? 0
|
||||
const monthly = tariffRateNumber(vps.monthlyRate) ?? 0
|
||||
if (vps.tariffType === 'daily') return daily * 30
|
||||
return monthly
|
||||
return vpsTariffComparableDailyRate(vps) * 30
|
||||
}
|
||||
|
||||
const ENVIRONMENT_LABELS: Record<string, string> = {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { toast } from 'sonner'
|
||||
import { snapshotQueryOptions, ratesQueryOptions } from '@/queries/snapshot'
|
||||
import { api, ApiError } from '@/lib/api-client'
|
||||
import type { VpsFormValues } from '@/lib/schemas'
|
||||
import { normalizeRatesPayload, effectiveVpsTariffCurrency, formatInProviderCurrency, vpsStatusLabel, tariffTypeLabel, vpsTariffRateAmount, vpsTariffMonthlyBurn } from '@/lib/format'
|
||||
import { normalizeRatesPayload, effectiveVpsTariffCurrency, formatInProviderCurrency, vpsStatusLabel, tariffTypeLabel, vpsTariffRateAmount, vpsTariffComparableDailyRate, convertWithProviderRate } from '@/lib/format'
|
||||
import { PageShell } from '@/components/page-shell'
|
||||
import { PageHeader } from '@/components/page-header'
|
||||
import { Button } from '@cfdm/ui/components/button'
|
||||
@@ -405,6 +405,7 @@ function VpsPage() {
|
||||
key: 'specs',
|
||||
header: 'Ресурсы',
|
||||
icon: CpuIcon,
|
||||
sortingFn: 'basic',
|
||||
sortValue: (v) => v.vcpu,
|
||||
cell: (v) => (
|
||||
<span className="tabular-nums text-muted-foreground">
|
||||
@@ -431,7 +432,21 @@ function VpsPage() {
|
||||
key: 'tariff',
|
||||
header: 'Тариф',
|
||||
icon: CreditCardIcon,
|
||||
sortValue: (v) => vpsTariffMonthlyBurn(v),
|
||||
// Единый знаменатель: суточная стоимость в базовой валюте (5,59/сутки > 75/мес).
|
||||
sortingFn: 'basic',
|
||||
sortValue: (v) => {
|
||||
const provider = providerById.get(v.providerId)
|
||||
const daily = vpsTariffComparableDailyRate(v)
|
||||
if (!(daily > 0)) return 0
|
||||
const currency = effectiveVpsTariffCurrency(v, provider)
|
||||
return convertWithProviderRate(
|
||||
daily,
|
||||
currency,
|
||||
provider,
|
||||
snapshot?.settings ?? null,
|
||||
ratesData,
|
||||
).value
|
||||
},
|
||||
cell: (v) => {
|
||||
const provider = providerById.get(v.providerId)
|
||||
const currency = effectiveVpsTariffCurrency(v, provider)
|
||||
|
||||
Reference in New Issue
Block a user