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>
This commit is contained in:
Felipe Coutinho
2026-01-27 13:15:37 +00:00
parent 8ffe61c59b
commit a7f63fb77a
442 changed files with 66141 additions and 69292 deletions

View File

@@ -1,125 +1,127 @@
import { and, asc, eq, inArray, sum } from "drizzle-orm";
import {
categorias,
lancamentos,
orcamentos,
type Orcamento,
categorias,
lancamentos,
type Orcamento,
orcamentos,
} from "@/db/schema";
import { db } from "@/lib/db";
import { and, asc, eq, inArray, sum } from "drizzle-orm";
const toNumber = (value: string | number | null | undefined) => {
if (typeof value === "number") return value;
if (typeof value === "string") {
const parsed = Number.parseFloat(value);
return Number.isNaN(parsed) ? 0 : parsed;
}
return 0;
if (typeof value === "number") return value;
if (typeof value === "string") {
const parsed = Number.parseFloat(value);
return Number.isNaN(parsed) ? 0 : parsed;
}
return 0;
};
export type BudgetData = {
id: string;
amount: number;
spent: number;
period: string;
createdAt: string;
category: {
id: string;
name: string;
icon: string | null;
} | null;
id: string;
amount: number;
spent: number;
period: string;
createdAt: string;
category: {
id: string;
name: string;
icon: string | null;
} | null;
};
export type CategoryOption = {
id: string;
name: string;
icon: string | null;
id: string;
name: string;
icon: string | null;
};
export async function fetchBudgetsForUser(
userId: string,
selectedPeriod: string
userId: string,
selectedPeriod: string,
): Promise<{
budgets: BudgetData[];
categoriesOptions: CategoryOption[];
budgets: BudgetData[];
categoriesOptions: CategoryOption[];
}> {
const [budgetRows, categoryRows] = await Promise.all([
db.query.orcamentos.findMany({
where: and(
eq(orcamentos.userId, userId),
eq(orcamentos.period, selectedPeriod)
),
with: {
categoria: true,
},
}),
db.query.categorias.findMany({
columns: {
id: true,
name: true,
icon: true,
},
where: and(eq(categorias.userId, userId), eq(categorias.type, "despesa")),
orderBy: asc(categorias.name),
}),
]);
const [budgetRows, categoryRows] = await Promise.all([
db.query.orcamentos.findMany({
where: and(
eq(orcamentos.userId, userId),
eq(orcamentos.period, selectedPeriod),
),
with: {
categoria: true,
},
}),
db.query.categorias.findMany({
columns: {
id: true,
name: true,
icon: true,
},
where: and(eq(categorias.userId, userId), eq(categorias.type, "despesa")),
orderBy: asc(categorias.name),
}),
]);
const categoryIds = budgetRows
.map((budget: Orcamento) => budget.categoriaId)
.filter((id: string | null): id is string => Boolean(id));
const categoryIds = budgetRows
.map((budget: Orcamento) => budget.categoriaId)
.filter((id: string | null): id is string => Boolean(id));
let totalsByCategory = new Map<string, number>();
let totalsByCategory = new Map<string, number>();
if (categoryIds.length > 0) {
const totals = await db
.select({
categoriaId: lancamentos.categoriaId,
totalAmount: sum(lancamentos.amount).as("totalAmount"),
})
.from(lancamentos)
.where(
and(
eq(lancamentos.userId, userId),
eq(lancamentos.period, selectedPeriod),
eq(lancamentos.transactionType, "Despesa"),
inArray(lancamentos.categoriaId, categoryIds)
)
)
.groupBy(lancamentos.categoriaId);
if (categoryIds.length > 0) {
const totals = await db
.select({
categoriaId: lancamentos.categoriaId,
totalAmount: sum(lancamentos.amount).as("totalAmount"),
})
.from(lancamentos)
.where(
and(
eq(lancamentos.userId, userId),
eq(lancamentos.period, selectedPeriod),
eq(lancamentos.transactionType, "Despesa"),
inArray(lancamentos.categoriaId, categoryIds),
),
)
.groupBy(lancamentos.categoriaId);
totalsByCategory = new Map(
totals.map((row: { categoriaId: string | null; totalAmount: string | null }) => [
row.categoriaId ?? "",
Math.abs(toNumber(row.totalAmount)),
])
);
}
totalsByCategory = new Map(
totals.map(
(row: { categoriaId: string | null; totalAmount: string | null }) => [
row.categoriaId ?? "",
Math.abs(toNumber(row.totalAmount)),
],
),
);
}
const budgets = budgetRows
.map((budget: Orcamento) => ({
id: budget.id,
amount: toNumber(budget.amount),
spent: totalsByCategory.get(budget.categoriaId ?? "") ?? 0,
period: budget.period,
createdAt: budget.createdAt.toISOString(),
category: budget.categoria
? {
id: budget.categoria.id,
name: budget.categoria.name,
icon: budget.categoria.icon,
}
: null,
}))
.sort((a, b) =>
(a.category?.name ?? "").localeCompare(b.category?.name ?? "", "pt-BR", {
sensitivity: "base",
})
);
const budgets = budgetRows
.map((budget: Orcamento) => ({
id: budget.id,
amount: toNumber(budget.amount),
spent: totalsByCategory.get(budget.categoriaId ?? "") ?? 0,
period: budget.period,
createdAt: budget.createdAt.toISOString(),
category: budget.categoria
? {
id: budget.categoria.id,
name: budget.categoria.name,
icon: budget.categoria.icon,
}
: null,
}))
.sort((a, b) =>
(a.category?.name ?? "").localeCompare(b.category?.name ?? "", "pt-BR", {
sensitivity: "base",
}),
);
const categoriesOptions = categoryRows.map((category) => ({
id: category.id,
name: category.name,
icon: category.icon,
}));
const categoriesOptions = categoryRows.map((category) => ({
id: category.id,
name: category.name,
icon: category.icon,
}));
return { budgets, categoriesOptions };
return { budgets, categoriesOptions };
}