forked from git.gladyson/openmonetis
- 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>
86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
/**
|
|
* Categoria grouping and sorting helpers for lancamentos
|
|
*/
|
|
|
|
import type { SelectOption } from "@/components/lancamentos/types";
|
|
|
|
/**
|
|
* Capitalizes the first letter of a string
|
|
*/
|
|
function capitalize(value: string): string {
|
|
return value.length > 0
|
|
? value[0]?.toUpperCase().concat(value.slice(1))
|
|
: value;
|
|
}
|
|
|
|
/**
|
|
* Group label for categorias
|
|
*/
|
|
type CategoriaGroup = {
|
|
label: string;
|
|
options: SelectOption[];
|
|
};
|
|
|
|
/**
|
|
* Normalizes category group labels (Despesa -> Despesas, Receita -> Receitas)
|
|
*/
|
|
function normalizeCategoryGroupLabel(value: string): string {
|
|
const lower = value.toLowerCase();
|
|
if (lower === "despesa") {
|
|
return "Despesas";
|
|
}
|
|
if (lower === "receita") {
|
|
return "Receitas";
|
|
}
|
|
return capitalize(value);
|
|
}
|
|
|
|
/**
|
|
* Groups and sorts categoria options by their group property
|
|
* @param categoriaOptions - Array of categoria select options
|
|
* @returns Array of grouped and sorted categoria options
|
|
*/
|
|
export function groupAndSortCategorias(
|
|
categoriaOptions: SelectOption[],
|
|
): CategoriaGroup[] {
|
|
// Group categorias by their group property
|
|
const groups = categoriaOptions.reduce<Record<string, SelectOption[]>>(
|
|
(acc, option) => {
|
|
const key = option.group ?? "Outros";
|
|
if (!acc[key]) {
|
|
acc[key] = [];
|
|
}
|
|
acc[key].push(option);
|
|
return acc;
|
|
},
|
|
{},
|
|
);
|
|
|
|
// Define preferred order (Despesa first, then Receita, then others)
|
|
const preferredOrder = ["Despesa", "Receita"];
|
|
const orderedKeys = [
|
|
...preferredOrder.filter((key) => groups[key]?.length),
|
|
...Object.keys(groups).filter((key) => !preferredOrder.includes(key)),
|
|
];
|
|
|
|
// Map to final structure with normalized labels and sorted options
|
|
return orderedKeys.map((key) => ({
|
|
label: normalizeCategoryGroupLabel(key),
|
|
options: groups[key]
|
|
.slice()
|
|
.sort((a, b) =>
|
|
a.label.localeCompare(b.label, "pt-BR", { sensitivity: "base" }),
|
|
),
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Filters secondary pagador options to exclude the primary pagador
|
|
*/
|
|
export function filterSecondaryPagadorOptions(
|
|
allOptions: SelectOption[],
|
|
primaryPagadorId?: string,
|
|
): SelectOption[] {
|
|
return allOptions.filter((option) => option.value !== primaryPagadorId);
|
|
}
|