feat: adição de novos ícones SVG e configuração do ambiente

- Adicionados ícones SVG para ChatGPT, Claude, Gemini e OpenRouter
- Implementados ícones para modos claro e escuro do ChatGPT
- Criado script de inicialização para PostgreSQL com extensão pgcrypto
- Adicionado script de configuração de ambiente que faz backup do .env
- Configurado tsconfig.json para TypeScript com opções de compilação
This commit is contained in:
Felipe Coutinho
2025-11-15 15:49:36 -03:00
commit ea0b8618e0
441 changed files with 53569 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
"use server";
import { categorias } from "@/db/schema";
import {
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)),
});
const createCategorySchema = categoryBaseSchema;
const updateCategorySchema = categoryBaseSchema.extend({
id: uuidSchema("Categoria"),
});
const deleteCategorySchema = z.object({
id: uuidSchema("Categoria"),
});
type CategoryCreateInput = z.infer<typeof createCategorySchema>;
type CategoryUpdateInput = z.infer<typeof updateCategorySchema>;
type CategoryDeleteInput = z.infer<typeof deleteCategorySchema>;
export async function createCategoryAction(
input: CategoryCreateInput
): Promise<ActionResult> {
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,
});
revalidateForEntity("categorias");
return { success: true, message: "Categoria criada com sucesso." };
} catch (error) {
return handleActionError(error);
}
}
export async function updateCategoryAction(
input: CategoryUpdateInput
): Promise<ActionResult> {
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)),
});
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.`,
};
}
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.",
};
}
revalidateForEntity("categorias");
return { success: true, message: "Categoria atualizada com sucesso." };
} catch (error) {
return handleActionError(error);
}
}
export async function deleteCategoryAction(
input: CategoryDeleteInput
): Promise<ActionResult> {
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)),
});
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.`,
};
}
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.",
};
}
revalidateForEntity("categorias");
return { success: true, message: "Categoria removida com sucesso." };
} catch (error) {
return handleActionError(error);
}
}