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,51 +1,54 @@
"use server";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { cartoes, contas } from "@/db/schema";
import { type ActionResult, handleActionError } from "@/lib/actions/helpers";
import { revalidateForEntity } from "@/lib/actions/helpers";
import {
dayOfMonthSchema,
noteSchema,
optionalDecimalSchema,
uuidSchema,
type ActionResult,
handleActionError,
revalidateForEntity,
} from "@/lib/actions/helpers";
import { getUser } from "@/lib/auth/server";
import { db } from "@/lib/db";
import {
dayOfMonthSchema,
noteSchema,
optionalDecimalSchema,
uuidSchema,
} from "@/lib/schemas/common";
import { formatDecimalForDb } from "@/lib/utils/currency";
import { normalizeFilePath } from "@/lib/utils/string";
import { db } from "@/lib/db";
import { getUser } from "@/lib/auth/server";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
const cardBaseSchema = z.object({
name: z
.string({ message: "Informe o nome do cartão." })
.trim()
.min(1, "Informe o nome do cartão."),
brand: z
.string({ message: "Informe a bandeira." })
.trim()
.min(1, "Informe a bandeira."),
status: z
.string({ message: "Informe o status do cartão." })
.trim()
.min(1, "Informe o status do cartão."),
closingDay: dayOfMonthSchema,
dueDay: dayOfMonthSchema,
note: noteSchema,
limit: optionalDecimalSchema,
logo: z
.string({ message: "Selecione um logo." })
.trim()
.min(1, "Selecione um logo."),
contaId: uuidSchema("Conta"),
name: z
.string({ message: "Informe o nome do cartão." })
.trim()
.min(1, "Informe o nome do cartão."),
brand: z
.string({ message: "Informe a bandeira." })
.trim()
.min(1, "Informe a bandeira."),
status: z
.string({ message: "Informe o status do cartão." })
.trim()
.min(1, "Informe o status do cartão."),
closingDay: dayOfMonthSchema,
dueDay: dayOfMonthSchema,
note: noteSchema,
limit: optionalDecimalSchema,
logo: z
.string({ message: "Selecione um logo." })
.trim()
.min(1, "Selecione um logo."),
contaId: uuidSchema("Conta"),
});
const createCardSchema = cardBaseSchema;
const updateCardSchema = cardBaseSchema.extend({
id: uuidSchema("Cartão"),
id: uuidSchema("Cartão"),
});
const deleteCardSchema = z.object({
id: uuidSchema("Cartão"),
id: uuidSchema("Cartão"),
});
type CardCreateInput = z.infer<typeof createCardSchema>;
@@ -53,113 +56,113 @@ type CardUpdateInput = z.infer<typeof updateCardSchema>;
type CardDeleteInput = z.infer<typeof deleteCardSchema>;
async function assertAccountOwnership(userId: string, contaId: string) {
const account = await db.query.contas.findFirst({
columns: { id: true },
where: and(eq(contas.id, contaId), eq(contas.userId, userId)),
});
const account = await db.query.contas.findFirst({
columns: { id: true },
where: and(eq(contas.id, contaId), eq(contas.userId, userId)),
});
if (!account) {
throw new Error("Conta vinculada não encontrada.");
}
if (!account) {
throw new Error("Conta vinculada não encontrada.");
}
}
export async function createCardAction(
input: CardCreateInput
input: CardCreateInput,
): Promise<ActionResult> {
try {
const user = await getUser();
const data = createCardSchema.parse(input);
try {
const user = await getUser();
const data = createCardSchema.parse(input);
await assertAccountOwnership(user.id, data.contaId);
await assertAccountOwnership(user.id, data.contaId);
const logoFile = normalizeFilePath(data.logo);
const logoFile = normalizeFilePath(data.logo);
await db.insert(cartoes).values({
name: data.name,
brand: data.brand,
status: data.status,
closingDay: data.closingDay,
dueDay: data.dueDay,
note: data.note ?? null,
limit: formatDecimalForDb(data.limit),
logo: logoFile,
contaId: data.contaId,
userId: user.id,
});
await db.insert(cartoes).values({
name: data.name,
brand: data.brand,
status: data.status,
closingDay: data.closingDay,
dueDay: data.dueDay,
note: data.note ?? null,
limit: formatDecimalForDb(data.limit),
logo: logoFile,
contaId: data.contaId,
userId: user.id,
});
revalidateForEntity("cartoes");
revalidateForEntity("cartoes");
return { success: true, message: "Cartão criado com sucesso." };
} catch (error) {
return handleActionError(error);
}
return { success: true, message: "Cartão criado com sucesso." };
} catch (error) {
return handleActionError(error);
}
}
export async function updateCardAction(
input: CardUpdateInput
input: CardUpdateInput,
): Promise<ActionResult> {
try {
const user = await getUser();
const data = updateCardSchema.parse(input);
try {
const user = await getUser();
const data = updateCardSchema.parse(input);
await assertAccountOwnership(user.id, data.contaId);
await assertAccountOwnership(user.id, data.contaId);
const logoFile = normalizeFilePath(data.logo);
const logoFile = normalizeFilePath(data.logo);
const [updated] = await db
.update(cartoes)
.set({
name: data.name,
brand: data.brand,
status: data.status,
closingDay: data.closingDay,
dueDay: data.dueDay,
note: data.note ?? null,
limit: formatDecimalForDb(data.limit),
logo: logoFile,
contaId: data.contaId,
})
.where(and(eq(cartoes.id, data.id), eq(cartoes.userId, user.id)))
.returning();
const [updated] = await db
.update(cartoes)
.set({
name: data.name,
brand: data.brand,
status: data.status,
closingDay: data.closingDay,
dueDay: data.dueDay,
note: data.note ?? null,
limit: formatDecimalForDb(data.limit),
logo: logoFile,
contaId: data.contaId,
})
.where(and(eq(cartoes.id, data.id), eq(cartoes.userId, user.id)))
.returning();
if (!updated) {
return {
success: false,
error: "Cartão não encontrado.",
};
}
if (!updated) {
return {
success: false,
error: "Cartão não encontrado.",
};
}
revalidateForEntity("cartoes");
revalidateForEntity("cartoes");
return { success: true, message: "Cartão atualizado com sucesso." };
} catch (error) {
return handleActionError(error);
}
return { success: true, message: "Cartão atualizado com sucesso." };
} catch (error) {
return handleActionError(error);
}
}
export async function deleteCardAction(
input: CardDeleteInput
input: CardDeleteInput,
): Promise<ActionResult> {
try {
const user = await getUser();
const data = deleteCardSchema.parse(input);
try {
const user = await getUser();
const data = deleteCardSchema.parse(input);
const [deleted] = await db
.delete(cartoes)
.where(and(eq(cartoes.id, data.id), eq(cartoes.userId, user.id)))
.returning({ id: cartoes.id });
const [deleted] = await db
.delete(cartoes)
.where(and(eq(cartoes.id, data.id), eq(cartoes.userId, user.id)))
.returning({ id: cartoes.id });
if (!deleted) {
return {
success: false,
error: "Cartão não encontrado.",
};
}
if (!deleted) {
return {
success: false,
error: "Cartão não encontrado.",
};
}
revalidateForEntity("cartoes");
revalidateForEntity("cartoes");
return { success: true, message: "Cartão removido com sucesso." };
} catch (error) {
return handleActionError(error);
}
return { success: true, message: "Cartão removido com sucesso." };
} catch (error) {
return handleActionError(error);
}
}