Files
openmonetis/lib/utils/calculator.ts
Felipe Coutinho a7f63fb77a refactor: migrate from ESLint to Biome and extract SQL queries to data.ts
- Replace ESLint with Biome for linting and formatting
- Configure Biome with tabs, double quotes, and organized imports
- Move all SQL/Drizzle queries from page.tsx files to data.ts files
- Create new data.ts files for: ajustes, dashboard, relatorios/categorias
- Update existing data.ts files: extrato, fatura (add lancamentos queries)
- Remove all drizzle-orm imports from page.tsx files
- Update README.md with new tooling info

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:15:37 +00:00

169 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export type Operator = "add" | "subtract" | "multiply" | "divide";
export const OPERATOR_SYMBOLS: Record<Operator, string> = {
add: "+",
subtract: "-",
multiply: "×",
divide: "÷",
};
export function formatNumber(value: number): string {
if (!Number.isFinite(value)) {
return "Erro";
}
const rounded = Number(Math.round(value * 1e10) / 1e10);
return rounded.toString();
}
export function formatLocaleValue(rawValue: string): string {
if (rawValue === "Erro") {
return rawValue;
}
const isNegative = rawValue.startsWith("-");
const unsignedValue = isNegative ? rawValue.slice(1) : rawValue;
if (unsignedValue === "") {
return isNegative ? "-0" : "0";
}
const hasDecimalSeparator = unsignedValue.includes(".");
const [integerPartRaw, decimalPartRaw] = unsignedValue.split(".");
const integerPart = integerPartRaw || "0";
const decimalPart = hasDecimalSeparator ? (decimalPartRaw ?? "") : undefined;
const numericInteger = Number(integerPart);
const formattedInteger = Number.isFinite(numericInteger)
? numericInteger.toLocaleString("pt-BR")
: integerPart;
if (decimalPart === undefined) {
return `${isNegative ? "-" : ""}${formattedInteger}`;
}
return `${isNegative ? "-" : ""}${formattedInteger},${decimalPart}`;
}
export function performOperation(
a: number,
b: number,
operator: Operator,
): number {
switch (operator) {
case "add":
return a + b;
case "subtract":
return a - b;
case "multiply":
return a * b;
case "divide":
return b === 0 ? Infinity : a / b;
default:
return b;
}
}
// Trata colagem de valores com formatação brasileira (ponto para milhar, vírgula para decimal)
// e variações simples em formato internacional.
export function normalizeClipboardNumber(rawValue: string): string | null {
const trimmed = rawValue.trim();
if (!trimmed) {
return null;
}
const match = trimmed.match(/-?[\d.,\s]+/);
if (!match) {
return null;
}
let extracted = match[0].replace(/\s+/g, "");
if (!extracted) {
return null;
}
const isNegative = extracted.startsWith("-");
if (isNegative) {
extracted = extracted.slice(1);
}
extracted = extracted.replace(/[^\d.,]/g, "");
if (!extracted) {
return null;
}
const countOccurrences = (char: string) =>
(extracted.match(new RegExp(`\\${char}`, "g")) ?? []).length;
const hasComma = extracted.includes(",");
const hasDot = extracted.includes(".");
let decimalSeparator: "," | "." | null = null;
if (hasComma && hasDot) {
decimalSeparator =
extracted.lastIndexOf(",") > extracted.lastIndexOf(".") ? "," : ".";
} else if (hasComma) {
const commaCount = countOccurrences(",");
if (commaCount > 1) {
decimalSeparator = null;
} else {
const digitsAfterComma =
extracted.length - extracted.lastIndexOf(",") - 1;
decimalSeparator =
digitsAfterComma > 0 && digitsAfterComma <= 2 ? "," : null;
}
} else if (hasDot) {
const dotCount = countOccurrences(".");
if (dotCount > 1) {
decimalSeparator = null;
} else {
const digitsAfterDot = extracted.length - extracted.lastIndexOf(".") - 1;
const decimalCandidate = extracted.slice(extracted.lastIndexOf(".") + 1);
const allZeros = /^0+$/.test(decimalCandidate);
const shouldTreatAsDecimal =
digitsAfterDot > 0 &&
digitsAfterDot <= 3 &&
!(digitsAfterDot === 3 && allZeros);
decimalSeparator = shouldTreatAsDecimal ? "." : null;
}
}
let integerPart = extracted;
let decimalPart = "";
if (decimalSeparator) {
const decimalIndex = extracted.lastIndexOf(decimalSeparator);
integerPart = extracted.slice(0, decimalIndex);
decimalPart = extracted.slice(decimalIndex + 1);
}
integerPart = integerPart.replace(/[^\d]/g, "");
decimalPart = decimalPart.replace(/[^\d]/g, "");
if (!integerPart) {
integerPart = "0";
}
let normalized = integerPart;
if (decimalPart) {
normalized = `${integerPart}.${decimalPart}`;
}
if (isNegative && Number(normalized) !== 0) {
normalized = `-${normalized}`;
}
if (!/^(-?\d+(\.\d+)?)$/.test(normalized)) {
return null;
}
const numericValue = Number(normalized);
if (!Number.isFinite(numericValue)) {
return null;
}
return normalized;
}