Ctrl K
ComunidadGuías…

src/services/creditPricing.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { Database } from '~/types/database.types'
import { formatNumber } from '~/services/formatLocale'

export type CreditMetricType = Database['public']['Enums']['credit_metric_type']

export interface CreditPricingStep {
  type: CreditMetricType
  step_min: number
  step_max: number
  price_per_unit: number
  unit_factor: number
  org_id?: string | null
}

type Translate = (key: string, values?: Record<string, string | number>) => string

export const creditPricingMetricOrder: CreditMetricType[] = ['mau', 'bandwidth', 'storage', 'build_time']

const creditPricingUnitLabelKeys: Record<CreditMetricType, string> = {
  mau: 'credits-pricing-unit-per-mau',
  bandwidth: 'credits-pricing-unit-per-gib',
  storage: 'credits-pricing-unit-per-gib',
  build_time: 'credits-pricing-unit-per-minute',
}

function getMetricOrder(metric: CreditMetricType) {
  const index = creditPricingMetricOrder.indexOf(metric)
  return index === -1 ? Number.MAX_SAFE_INTEGER : index
}

function isOpenEndedTier(step: Pick<CreditPricingStep, 'step_max'>) {
  return !Number.isFinite(step.step_max) || step.step_max >= Number.MAX_SAFE_INTEGER
}

function toBilledUnits(step: Pick<CreditPricingStep, 'unit_factor'>, rawValue: number) {
  const factor = step.unit_factor || 1
  return Math.ceil(rawValue / factor)
}

function formatCreditTierAmount(metric: CreditMetricType, billedUnits: number, t: Translate) {
  const formattedAmount = formatNumber(billedUnits, {
    maximumFractionDigits: 0,
    notation: metric === 'mau' ? 'compact' : 'standard',
    compactDisplay: 'short',
  })

  if ((metric === 'bandwidth' || metric === 'storage') && billedUnits >= 1024 && billedUnits % 1024 === 0)
    return `${formatNumber(billedUnits / 1024, { maximumFractionDigits: 0 })} TB`

  if (metric === 'bandwidth' || metric === 'storage')
    return `${formattedAmount} GiB`

  if (metric === 'build_time')
    return t('minutes-short', { minutes: formattedAmount })

  return formattedAmount
}

export function sortCreditPricingSteps(steps: CreditPricingStep[]) {
  return [...steps].sort((left, right) => {
    const metricOrderDiff = getMetricOrder(left.type) - getMetricOrder(right.type)
    if (metricOrderDiff !== 0)
      return metricOrderDiff

    if (left.step_min !== right.step_min)
      return left.step_min - right.step_min

    return left.step_max - right.step_max
  })
}

export function getFirstTierCreditUnitPricing(steps: CreditPricingStep[]) {
  return sortCreditPricingSteps(steps).reduce<Partial<Record<CreditMetricType, number>>>((pricing, step) => {
    pricing[step.type] ??= step.price_per_unit

    return pricing
  }, {})
}

export function formatCreditPriceValue(pricePerUnit: number) {
  return formatNumber(pricePerUnit, {
    style: 'currency',
    currency: 'USD',
    minimumFractionDigits: 2,
    maximumFractionDigits: 4,
  })
}

export function formatCreditPricingPrice(
  metric: CreditMetricType,
  pricePerUnit: number,
  t: Translate,
) {
  return t('credits-pricing-price', {
    price: formatCreditPriceValue(pricePerUnit),
    unit: t(creditPricingUnitLabelKeys[metric]),
  })
}

export function formatCreditPricingTierLabel(
  step: Pick<CreditPricingStep, 'type' | 'step_min' | 'step_max' | 'unit_factor'>,
  t: Translate,
) {
  const minUnits = toBilledUnits(step, step.step_min)
  const maxUnits = toBilledUnits(step, step.step_max)
  const openEnded = isOpenEndedTier(step)

  if (step.step_min === 0) {
    return t('credits-pricing-tier-first', {
      to: formatCreditTierAmount(step.type, maxUnits, t),
    })
  }

  if (openEnded) {
    return t('credits-pricing-tier-over', {
      from: formatCreditTierAmount(step.type, minUnits, t),
    })
  }

  return t('credits-pricing-tier-range', {
    from: formatCreditTierAmount(step.type, minUnits, t),
    to: formatCreditTierAmount(step.type, maxUnits, t),
  })
}