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

134
lib/schemas/common.ts Normal file
View File

@@ -0,0 +1,134 @@
import { z } from "zod";
/**
* Common Zod schemas for reuse across the application
*/
/**
* UUID schema with custom error message
*/
export const uuidSchema = (entityName: string = "ID") =>
z.string({ message: `${entityName} inválido.` }).uuid(`${entityName} inválido.`);
/**
* Decimal string schema - parses string with comma/period to number
*/
export const decimalSchema = z
.string()
.trim()
.transform((value) => value.replace(/\s/g, "").replace(",", "."))
.refine(
(value) => !Number.isNaN(Number.parseFloat(value)),
"Informe um valor numérico válido."
)
.transform((value) => Number.parseFloat(value));
/**
* Optional/nullable decimal string schema
*/
export const optionalDecimalSchema = z
.string()
.trim()
.optional()
.transform((value) =>
value && value.length > 0 ? value.replace(",", ".") : null
)
.refine(
(value) => value === null || !Number.isNaN(Number.parseFloat(value)),
"Informe um valor numérico válido."
)
.transform((value) => (value === null ? null : Number.parseFloat(value)));
/**
* Day of month schema (1-31)
*/
export const dayOfMonthSchema = z
.string({ message: "Informe o dia." })
.trim()
.min(1, "Informe o dia.")
.refine((value) => {
const parsed = Number.parseInt(value, 10);
return !Number.isNaN(parsed) && parsed >= 1 && parsed <= 31;
}, "Informe um dia entre 1 e 31.");
/**
* Period schema (YYYY-MM format)
*/
export const periodSchema = z
.string({ message: "Informe o período." })
.trim()
.regex(/^\d{4}-(0[1-9]|1[0-2])$/, "Período inválido.");
/**
* Optional period schema
*/
export const optionalPeriodSchema = z
.string()
.trim()
.regex(/^(\d{4})-(\d{2})$/, {
message: "Selecione um período válido.",
})
.optional();
/**
* Date string schema
*/
export const dateStringSchema = z
.string({ message: "Informe a data." })
.trim()
.refine((value) => !Number.isNaN(new Date(value).getTime()), {
message: "Data inválida.",
});
/**
* Optional date string schema
*/
export const optionalDateStringSchema = z
.string()
.trim()
.refine((value) => !value || !Number.isNaN(new Date(value).getTime()), {
message: "Informe uma data válida.",
})
.optional();
/**
* Note/observation schema (max 500 chars, trimmed, nullable)
*/
export const noteSchema = z
.string()
.trim()
.max(500, "A anotação deve ter no máximo 500 caracteres.")
.optional()
.transform((value) => (value && value.length > 0 ? value : null));
/**
* Optional string that becomes null if empty
*/
export const optionalStringToNull = z
.string()
.trim()
.optional()
.transform((value) => (value && value.length > 0 ? value : null));
/**
* Required non-empty string schema
*/
export const requiredStringSchema = (fieldName: string) =>
z
.string({ message: `Informe ${fieldName}.` })
.trim()
.min(1, `Informe ${fieldName}.`);
/**
* Amount schema with minimum value validation
*/
export const amountSchema = z.coerce
.number({ message: "Informe o valor." })
.min(0, "Informe um valor maior ou igual a zero.");
/**
* Positive amount schema
*/
export const positiveAmountSchema = z.coerce
.number({ message: "Informe o valor." })
.positive("Informe um valor maior que zero.");

69
lib/schemas/insights.ts Normal file
View File

@@ -0,0 +1,69 @@
import { z } from "zod";
/**
* Categorias de insights
*/
export const INSIGHT_CATEGORIES = {
behaviors: {
id: "behaviors",
title: "Comportamentos Observados",
icon: "RiEyeLine",
color: "blue",
},
triggers: {
id: "triggers",
title: "Gatilhos de Consumo",
icon: "RiFlashlightLine",
color: "amber",
},
recommendations: {
id: "recommendations",
title: "Recomendações Práticas",
icon: "RiLightbulbLine",
color: "green",
},
improvements: {
id: "improvements",
title: "Melhorias Sugeridas",
icon: "RiRocketLine",
color: "purple",
},
} as const;
export type InsightCategoryId = keyof typeof INSIGHT_CATEGORIES;
/**
* Schema para item individual de insight
*/
export const InsightItemSchema = z.object({
text: z.string().min(1),
});
/**
* Schema para categoria de insights
*/
export const InsightCategorySchema = z.object({
category: z.enum([
"behaviors",
"triggers",
"recommendations",
"improvements",
]),
items: z.array(InsightItemSchema).min(1).max(6),
});
/**
* Schema for complete insights response from AI
*/
export const InsightsResponseSchema = z.object({
month: z.string().regex(/^\d{4}-\d{2}$/), // YYYY-MM
generatedAt: z.string(), // ISO datetime
categories: z.array(InsightCategorySchema).length(4),
});
/**
* TypeScript types derived from schemas
*/
export type InsightItem = z.infer<typeof InsightItemSchema>;
export type InsightCategory = z.infer<typeof InsightCategorySchema>;
export type InsightsResponse = z.infer<typeof InsightsResponseSchema>;