- Simplifica estrutura da sidebar combinando seções "Visão Geral" e "Gestão Financeira"
- Renomeia itens de relatórios para maior clareza ("Tendências", "Uso de Cartões")
- Aplica correções de formatação do Biome (ordenação de imports, quebras de linha)
- Remove código comentado não utilizado
- Adiciona migração 0014 do Drizzle
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
128 lines
3.4 KiB
TypeScript
128 lines
3.4 KiB
TypeScript
import { currencyFormatter } from "@/lib/lancamentos/formatting-helpers";
|
|
import { calculatePercentageChange } from "@/lib/utils/math";
|
|
import { buildPeriodRange, MONTH_NAMES, parsePeriod } from "@/lib/utils/period";
|
|
import type { DateRangeValidation } from "./types";
|
|
|
|
// Re-export for convenience
|
|
export { calculatePercentageChange };
|
|
|
|
/**
|
|
* Formats period string from "YYYY-MM" to "MMM/YYYY" format
|
|
* Example: "2025-01" -> "Jan/2025"
|
|
*
|
|
* @param period - Period in YYYY-MM format
|
|
* @returns Formatted period string
|
|
*/
|
|
export function formatPeriodLabel(period: string): string {
|
|
try {
|
|
const { year, month } = parsePeriod(period);
|
|
const monthName = MONTH_NAMES[month - 1];
|
|
|
|
// Capitalize first letter and take first 3 chars
|
|
const shortMonth =
|
|
monthName.charAt(0).toUpperCase() + monthName.slice(1, 3);
|
|
|
|
return `${shortMonth}/${year}`;
|
|
} catch {
|
|
return period; // Return original if parsing fails
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates an array of periods between start and end (inclusive)
|
|
* Alias for buildPeriodRange from period utils
|
|
*
|
|
* @param startPeriod - Start period in YYYY-MM format
|
|
* @param endPeriod - End period in YYYY-MM format
|
|
* @returns Array of period strings in chronological order
|
|
*/
|
|
export function generatePeriodRange(
|
|
startPeriod: string,
|
|
endPeriod: string,
|
|
): string[] {
|
|
return buildPeriodRange(startPeriod, endPeriod);
|
|
}
|
|
|
|
/**
|
|
* Validates that end date is >= start date and period is within limits
|
|
* Maximum allowed period: 24 months
|
|
*
|
|
* @param startPeriod - Start period in YYYY-MM format
|
|
* @param endPeriod - End period in YYYY-MM format
|
|
* @returns Validation result with error message if invalid
|
|
*/
|
|
export function validateDateRange(
|
|
startPeriod: string,
|
|
endPeriod: string,
|
|
): DateRangeValidation {
|
|
try {
|
|
// Parse periods to validate format
|
|
const start = parsePeriod(startPeriod);
|
|
const end = parsePeriod(endPeriod);
|
|
|
|
// Check if end is before start
|
|
if (
|
|
end.year < start.year ||
|
|
(end.year === start.year && end.month < start.month)
|
|
) {
|
|
return {
|
|
isValid: false,
|
|
error: "A data final deve ser maior ou igual à data inicial",
|
|
};
|
|
}
|
|
|
|
// Calculate number of months between periods
|
|
const monthsDiff =
|
|
(end.year - start.year) * 12 + (end.month - start.month) + 1;
|
|
|
|
// Check if period exceeds 24 months
|
|
if (monthsDiff > 24) {
|
|
return {
|
|
isValid: false,
|
|
error: "O período máximo permitido é de 24 meses",
|
|
};
|
|
}
|
|
|
|
return { isValid: true };
|
|
} catch (error) {
|
|
return {
|
|
isValid: false,
|
|
error:
|
|
error instanceof Error
|
|
? error.message
|
|
: "Formato de período inválido. Use YYYY-MM",
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Formats a number as Brazilian currency (R$ X.XXX,XX)
|
|
* Uses the shared currencyFormatter from formatting-helpers
|
|
*
|
|
* @param value - Numeric value to format
|
|
* @returns Formatted currency string
|
|
*/
|
|
export function formatCurrency(value: number): string {
|
|
return currencyFormatter.format(value);
|
|
}
|
|
|
|
/**
|
|
* Formats percentage change for display
|
|
* Format: "±X%" or "±X.X%" (one decimal if < 10%)
|
|
*
|
|
* @param change - Percentage change value
|
|
* @returns Formatted percentage string
|
|
*/
|
|
export function formatPercentageChange(change: number | null): string {
|
|
if (change === null) return "-";
|
|
|
|
const absChange = Math.abs(change);
|
|
const sign = change >= 0 ? "+" : "-";
|
|
|
|
// Use one decimal place if less than 10%
|
|
const formatted =
|
|
absChange < 10 ? absChange.toFixed(1) : Math.round(absChange).toString();
|
|
|
|
return `${sign}${formatted}%`;
|
|
}
|