Files
finanzas/lib/utils.ts
renato97 712b06f118 feat: initial commit - finanzas app
Complete personal finance management application with:
- Dashboard with financial metrics and alerts
- Credit card management and payments
- Fixed and variable debt tracking
- Monthly budget planning
- Intelligent alert system
- Responsive design with Tailwind CSS

Tech stack: Next.js 14, TypeScript, Zustand, Recharts

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-29 00:00:32 +00:00

190 lines
4.9 KiB
TypeScript

import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { FixedDebt, VariableDebt, CardPayment } from './types'
/**
* Combina clases de Tailwind CSS usando clsx y tailwind-merge
* Permite combinar múltiples clases condicionalmente
*/
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs))
}
/**
* Formatea un número como moneda (pesos argentinos/USD)
* Ejemplo: 1500.50 -> "$ 1.500,50"
*/
export function formatCurrency(amount: number): string {
const formatter = new Intl.NumberFormat('es-AR', {
style: 'currency',
currency: 'ARS',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
return formatter.format(amount)
}
/**
* Formatea una fecha en formato legible en español
* Ejemplo: "28 de enero de 2026"
*/
export function formatDate(date: string | Date): string {
const d = typeof date === 'string' ? new Date(date) : date
const formatter = new Intl.DateTimeFormat('es-AR', {
day: 'numeric',
month: 'long',
year: 'numeric',
})
return formatter.format(d)
}
/**
* Formatea una fecha en formato corto
* Ejemplo: "28/01/2026"
*/
export function formatShortDate(date: string | Date): string {
const d = typeof date === 'string' ? new Date(date) : date
const formatter = new Intl.DateTimeFormat('es-AR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
})
return formatter.format(d)
}
/**
* Calcula los días hasta una fecha específica
* Retorna un número negativo si la fecha ya pasó
*/
export function getDaysUntil(date: string | Date): number {
const targetDate = typeof date === 'string' ? new Date(date) : date
const today = new Date()
// Reset hours to compare only dates
const target = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate())
const current = new Date(today.getFullYear(), today.getMonth(), today.getDate())
const diffTime = target.getTime() - current.getTime()
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
return diffDays
}
/**
* Obtiene la próxima fecha para un día específico del mes
* Si el día ya pasó este mes, devuelve el del mes siguiente
*/
export function getNextDateByDay(dayOfMonth: number): Date {
const today = new Date()
const currentYear = today.getFullYear()
const currentMonth = today.getMonth()
const currentDay = today.getDate()
let targetYear = currentYear
let targetMonth = currentMonth
// Si el día ya pasó este mes, ir al siguiente mes
if (currentDay > dayOfMonth) {
targetMonth += 1
if (targetMonth > 11) {
targetMonth = 0
targetYear += 1
}
}
// Ajustar si el día no existe en el mes objetivo (ej: 31 de febrero)
const lastDayOfMonth = new Date(targetYear, targetMonth + 1, 0).getDate()
const targetDay = Math.min(dayOfMonth, lastDayOfMonth)
return new Date(targetYear, targetMonth, targetDay)
}
/**
* Obtiene el nombre del mes en español
* El mes debe ser 1-12 (enero = 1)
*/
export function getMonthName(month: number): string {
const monthNames = [
'enero',
'febrero',
'marzo',
'abril',
'mayo',
'junio',
'julio',
'agosto',
'septiembre',
'octubre',
'noviembre',
'diciembre',
]
if (month < 1 || month > 12) {
throw new Error('El mes debe estar entre 1 y 12')
}
return monthNames[month - 1]
}
/**
* Calcula el total de deudas fijas no pagadas
*/
export function calculateTotalFixedDebts(debts: FixedDebt[]): number {
return debts
.filter((debt) => !debt.isPaid)
.reduce((total, debt) => total + debt.amount, 0)
}
/**
* Calcula el total de deudas variables no pagadas
*/
export function calculateTotalVariableDebts(debts: VariableDebt[]): number {
return debts
.filter((debt) => !debt.isPaid)
.reduce((total, debt) => total + debt.amount, 0)
}
/**
* Calcula el total de pagos de tarjeta
* Opcionalmente filtrados por cardId
*/
export function calculateCardPayments(
payments: CardPayment[],
cardId?: string
): number {
const filteredPayments = cardId
? payments.filter((payment) => payment.cardId === cardId)
: payments
return filteredPayments.reduce((total, payment) => total + payment.amount, 0)
}
/**
* Calcula la próxima fecha de cierre de tarjeta
* Si el día de cierre ya pasó este mes, devuelve el del mes siguiente
*/
export function calculateNextClosingDate(closingDay: number): Date {
return getNextDateByDay(closingDay)
}
/**
* Calcula la próxima fecha de vencimiento de tarjeta
* Si el día de vencimiento ya pasó este mes, devuelve el del mes siguiente
*/
export function calculateNextDueDate(dueDay: number): Date {
return getNextDateByDay(dueDay)
}
/**
* Calcula el porcentaje de utilización de una tarjeta de crédito
* Retorna un valor entre 0 y 100
*/
export function getCardUtilization(balance: number, limit: number): number {
if (limit <= 0) {
return 0
}
const utilization = (balance / limit) * 100
return Math.min(Math.max(utilization, 0), 100)
}