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:
@@ -1,41 +1,41 @@
|
||||
"use server";
|
||||
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { categorias } from "@/db/schema";
|
||||
import {
|
||||
type ActionResult,
|
||||
handleActionError,
|
||||
revalidateForEntity,
|
||||
type ActionResult,
|
||||
handleActionError,
|
||||
revalidateForEntity,
|
||||
} from "@/lib/actions/helpers";
|
||||
import { getUser } from "@/lib/auth/server";
|
||||
import { CATEGORY_TYPES } from "@/lib/categorias/constants";
|
||||
import { db } from "@/lib/db";
|
||||
import { uuidSchema } from "@/lib/schemas/common";
|
||||
import { normalizeIconInput } from "@/lib/utils/string";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
||||
const categoryBaseSchema = z.object({
|
||||
name: z
|
||||
.string({ message: "Informe o nome da categoria." })
|
||||
.trim()
|
||||
.min(1, "Informe o nome da categoria."),
|
||||
type: z.enum(CATEGORY_TYPES, {
|
||||
message: "Tipo de categoria inválido.",
|
||||
}),
|
||||
icon: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, "O ícone deve ter no máximo 100 caracteres.")
|
||||
.nullish()
|
||||
.transform((value) => normalizeIconInput(value)),
|
||||
name: z
|
||||
.string({ message: "Informe o nome da categoria." })
|
||||
.trim()
|
||||
.min(1, "Informe o nome da categoria."),
|
||||
type: z.enum(CATEGORY_TYPES, {
|
||||
message: "Tipo de categoria inválido.",
|
||||
}),
|
||||
icon: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, "O ícone deve ter no máximo 100 caracteres.")
|
||||
.nullish()
|
||||
.transform((value) => normalizeIconInput(value)),
|
||||
});
|
||||
|
||||
const createCategorySchema = categoryBaseSchema;
|
||||
const updateCategorySchema = categoryBaseSchema.extend({
|
||||
id: uuidSchema("Categoria"),
|
||||
id: uuidSchema("Categoria"),
|
||||
});
|
||||
const deleteCategorySchema = z.object({
|
||||
id: uuidSchema("Categoria"),
|
||||
id: uuidSchema("Categoria"),
|
||||
});
|
||||
|
||||
type CategoryCreateInput = z.infer<typeof createCategorySchema>;
|
||||
@@ -43,134 +43,134 @@ type CategoryUpdateInput = z.infer<typeof updateCategorySchema>;
|
||||
type CategoryDeleteInput = z.infer<typeof deleteCategorySchema>;
|
||||
|
||||
export async function createCategoryAction(
|
||||
input: CategoryCreateInput
|
||||
input: CategoryCreateInput,
|
||||
): Promise<ActionResult> {
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = createCategorySchema.parse(input);
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = createCategorySchema.parse(input);
|
||||
|
||||
await db.insert(categorias).values({
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
icon: data.icon,
|
||||
userId: user.id,
|
||||
});
|
||||
await db.insert(categorias).values({
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
icon: data.icon,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
revalidateForEntity("categorias");
|
||||
revalidateForEntity("categorias");
|
||||
|
||||
return { success: true, message: "Categoria criada com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
return { success: true, message: "Categoria criada com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateCategoryAction(
|
||||
input: CategoryUpdateInput
|
||||
input: CategoryUpdateInput,
|
||||
): Promise<ActionResult> {
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = updateCategorySchema.parse(input);
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = updateCategorySchema.parse(input);
|
||||
|
||||
// Buscar categoria antes de atualizar para verificar restrições
|
||||
const categoria = await db.query.categorias.findFirst({
|
||||
columns: { id: true, name: true },
|
||||
where: and(eq(categorias.id, data.id), eq(categorias.userId, user.id)),
|
||||
});
|
||||
// Buscar categoria antes de atualizar para verificar restrições
|
||||
const categoria = await db.query.categorias.findFirst({
|
||||
columns: { id: true, name: true },
|
||||
where: and(eq(categorias.id, data.id), eq(categorias.userId, user.id)),
|
||||
});
|
||||
|
||||
if (!categoria) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
if (!categoria) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
|
||||
// Bloquear edição das categorias protegidas
|
||||
const categoriasProtegidas = [
|
||||
"Transferência interna",
|
||||
"Saldo inicial",
|
||||
"Pagamentos",
|
||||
];
|
||||
if (categoriasProtegidas.includes(categoria.name)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `A categoria '${categoria.name}' é protegida e não pode ser editada.`,
|
||||
};
|
||||
}
|
||||
// Bloquear edição das categorias protegidas
|
||||
const categoriasProtegidas = [
|
||||
"Transferência interna",
|
||||
"Saldo inicial",
|
||||
"Pagamentos",
|
||||
];
|
||||
if (categoriasProtegidas.includes(categoria.name)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `A categoria '${categoria.name}' é protegida e não pode ser editada.`,
|
||||
};
|
||||
}
|
||||
|
||||
const [updated] = await db
|
||||
.update(categorias)
|
||||
.set({
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
icon: data.icon,
|
||||
})
|
||||
.where(and(eq(categorias.id, data.id), eq(categorias.userId, user.id)))
|
||||
.returning();
|
||||
const [updated] = await db
|
||||
.update(categorias)
|
||||
.set({
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
icon: data.icon,
|
||||
})
|
||||
.where(and(eq(categorias.id, data.id), eq(categorias.userId, user.id)))
|
||||
.returning();
|
||||
|
||||
if (!updated) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
if (!updated) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
|
||||
revalidateForEntity("categorias");
|
||||
revalidateForEntity("categorias");
|
||||
|
||||
return { success: true, message: "Categoria atualizada com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
return { success: true, message: "Categoria atualizada com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteCategoryAction(
|
||||
input: CategoryDeleteInput
|
||||
input: CategoryDeleteInput,
|
||||
): Promise<ActionResult> {
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = deleteCategorySchema.parse(input);
|
||||
try {
|
||||
const user = await getUser();
|
||||
const data = deleteCategorySchema.parse(input);
|
||||
|
||||
// Buscar categoria antes de deletar para verificar restrições
|
||||
const categoria = await db.query.categorias.findFirst({
|
||||
columns: { id: true, name: true },
|
||||
where: and(eq(categorias.id, data.id), eq(categorias.userId, user.id)),
|
||||
});
|
||||
// Buscar categoria antes de deletar para verificar restrições
|
||||
const categoria = await db.query.categorias.findFirst({
|
||||
columns: { id: true, name: true },
|
||||
where: and(eq(categorias.id, data.id), eq(categorias.userId, user.id)),
|
||||
});
|
||||
|
||||
if (!categoria) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
if (!categoria) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
|
||||
// Bloquear remoção das categorias protegidas
|
||||
const categoriasProtegidas = [
|
||||
"Transferência interna",
|
||||
"Saldo inicial",
|
||||
"Pagamentos",
|
||||
];
|
||||
if (categoriasProtegidas.includes(categoria.name)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `A categoria '${categoria.name}' é protegida e não pode ser removida.`,
|
||||
};
|
||||
}
|
||||
// Bloquear remoção das categorias protegidas
|
||||
const categoriasProtegidas = [
|
||||
"Transferência interna",
|
||||
"Saldo inicial",
|
||||
"Pagamentos",
|
||||
];
|
||||
if (categoriasProtegidas.includes(categoria.name)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `A categoria '${categoria.name}' é protegida e não pode ser removida.`,
|
||||
};
|
||||
}
|
||||
|
||||
const [deleted] = await db
|
||||
.delete(categorias)
|
||||
.where(and(eq(categorias.id, data.id), eq(categorias.userId, user.id)))
|
||||
.returning({ id: categorias.id });
|
||||
const [deleted] = await db
|
||||
.delete(categorias)
|
||||
.where(and(eq(categorias.id, data.id), eq(categorias.userId, user.id)))
|
||||
.returning({ id: categorias.id });
|
||||
|
||||
if (!deleted) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
if (!deleted) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Categoria não encontrada.",
|
||||
};
|
||||
}
|
||||
|
||||
revalidateForEntity("categorias");
|
||||
revalidateForEntity("categorias");
|
||||
|
||||
return { success: true, message: "Categoria removida com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
return { success: true, message: "Categoria removida com sucesso." };
|
||||
} catch (error) {
|
||||
return handleActionError(error);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user