diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..fe3c56e --- /dev/null +++ b/PLAN.md @@ -0,0 +1,806 @@ +--- + +📊 Análise e Sugestões para OpenSheets + +🎯 Resumo Executivo + +O OpenSheets é uma aplicação financeira bem estruturada com 184 componentes, 15 widgets de dashboard, e um design system coeso baseado em cores terracota. A análise identificou pontos fortes significativos e +oportunidades estratégicas para melhorias. + +--- + +✅ Pontos Fortes Identificados + +Arquitetura + +- ✨ Server-first com Next.js 15 App Router +- 🚀 Fetching paralelo otimizado (18+ requests simultâneos) +- 🔒 Modo privacidade bem implementado +- 🎨 Design system consistente (OKLCH color space) + +Componentes + +- 📦 40+ componentes shadcn/ui bem organizados +- ♻️ Alta reutilização de componentes +- 🎭 Sistema de skeletons completo +- 🌙 Suporte total a tema dark/light + +--- + +🚀 Sugestões de Novas Features + +1. Análise Preditiva e Forecasting 🔮 + +Prioridade: Alta | Complexidade: Média + +// Nova página: app/(dashboard)/previsoes/ + +Features: + +- Previsão de gastos mensais baseada em histórico +- Alerta de contas a vencer na próxima semana +- Projeção de saldo futuro considerando despesas recorrentes +- Machine learning simples para detectar padrões de gasto + +Componentes sugeridos: + +- ForecastChart - Gráfico de linha com projeções +- UpcomingBillsWidget - Widget de contas a vencer +- SavingsGoalTracker - Acompanhamento de metas de economia + +--- + +2. Metas Financeiras (Goals) 🎯 + +Prioridade: Alta | Complexidade: Média + +// Nova tabela no schema: +export const metas = pgTable("metas", { +id: uuid("id").primaryKey().defaultRandom(), +userId: uuid("user_id").notNull().references(() => user.id), +nome: text("nome").notNull(), +valorAlvo: numeric("valor_alvo", { precision: 12, scale: 2 }).notNull(), +valorAtual: numeric("valor_atual", { precision: 12, scale: 2 }).default("0"), +prazo: timestamp("prazo"), +categoriaId: uuid("categoria_id").references(() => categorias.id), +tipo: text("tipo").notNull(), // 'economia', 'quitacao_divida', 'compra' +}); + +Features: + +- Criar metas de economia (ex: "Viagem para Europa - R$ 10.000") +- Vincular transações às metas +- Dashboard de progresso visual +- Sugestões automáticas de quanto economizar mensalmente + +--- + +3. Relatórios Exportáveis 📄 + +Prioridade: Média | Complexidade: Baixa + +Formatos: + +- PDF com gráficos (usando jsPDF + html2canvas) +- Excel/CSV detalhado (usando xlsx) +- JSON para backup completo + +Tipos de relatório: + +- Extrato mensal consolidado +- Análise de gastos por categoria +- Comparativo período a período +- Resumo anual (imposto de renda) + +Código sugerido: +// lib/reports/generate-pdf-report.ts +import { jsPDF } from 'jspdf'; + +export async function generateMonthlyReport(userId: string, period: string) { +const data = await fetchMonthlyData(userId, period); +const doc = new jsPDF(); + + // Adicionar logo, gráficos, tabelas + doc.save(`relatorio-${period}.pdf`); + +} + +--- + +4. Modo Comparativo de Períodos 📊 + +Prioridade: Média | Complexidade: Baixa + +UI sugerida: + + +Features: + +- Comparar dois meses lado a lado +- Ver variação percentual por categoria +- Identificar onde economizou/gastou mais +- Gráficos de delta de gastos + +--- + +5. Tags/Etiquetas para Transações 🏷️ + +Prioridade: Baixa | Complexidade: Baixa + +export const tags = pgTable("tags", { +id: uuid("id").primaryKey(), +userId: uuid("user_id").notNull(), +nome: text("nome").notNull(), +cor: text("cor").notNull(), // hex color +}); + +export const lancamento_tags = pgTable("lancamento_tags", { +lancamentoId: uuid("lancamento_id").references(() => lancamentos.id), +tagId: uuid("tag_id").references(() => tags.id), +}); + +Use cases: + +- Tag "Trabalho" para despesas dedutíveis +- Tag "Emergência" para gastos não planejados +- Tag "Investimento" para rastrear aplicações +- Filtrar dashboard por tags + +--- + +6. Anexos e Comprovantes 📎 + +Prioridade: Média | Complexidade: Alta + +Implementação: + +- Upload de imagens/PDFs de notas fiscais +- Armazenamento em storage (S3-compatible ou local) +- OCR para extrair dados automaticamente (Tesseract.js) +- Galeria de comprovantes por transação + +export const anexos = pgTable("anexos", { +id: uuid("id").primaryKey(), +lancamentoId: uuid("lancamento_id").references(() => lancamentos.id), +arquivo: text("arquivo_url").notNull(), +tipo: text("tipo").notNull(), // 'imagem', 'pdf' +tamanho: integer("tamanho_bytes"), +}); + +--- + +7. Investimentos Tracking 💹 + +Prioridade: Baixa | Complexidade: Alta + +Escopo: + +- Registrar compra/venda de ações, FIIs, criptomoedas +- Importação de extratos de corretoras +- Gráfico de evolução patrimonial +- Cálculo de rentabilidade + +Nova seção no sidebar: +{ +title: "Investimentos", +icon: RiLineChartLine, +href: "/investimentos", +} + +--- + +8. Gamificação e Conquistas 🏆 + +Prioridade: Baixa | Complexidade: Média + +Conquistas sugeridas: + +- "Primeiro Mês no Azul" - Receitas > Despesas +- "Economista" - Gastou menos que orçamento 3 meses seguidos +- "Organizado" - Todas transações categorizadas +- "Disciplinado" - 30 dias sem gastos em categoria específica + +Implementação: +export const conquistas = pgTable("conquistas", { +id: uuid("id").primaryKey(), +codigo: text("codigo").notNull(), // 'primeiro_mes_azul' +nome: text("nome").notNull(), +descricao: text("descricao"), +icone: text("icone"), +}); + +export const usuario_conquistas = pgTable("usuario_conquistas", { +userId: uuid("user_id").references(() => user.id), +conquistaId: uuid("conquista_id").references(() => conquistas.id), +desbloqueadaEm: timestamp("desbloqueada_em").defaultNow(), +}); + +--- + +9. Notificações e Lembretes 🔔 + +Prioridade: Alta | Complexidade: Média + +Tipos de notificação: + +- Lembrete de fatura vencendo em 3 dias +- Orçamento atingindo 80% do limite +- Despesa incomum detectada (> 2x média da categoria) +- Cobrança recorrente não registrada este mês + +Implementação: + +- Cron job diário verificando condições +- Sistema de notificações in-app +- Opcional: Email notifications (Resend/Nodemailer) +- Web Push Notifications (service worker) + +--- + +10. Importação Automática de Extratos 🔄 + +Prioridade: Alta | Complexidade: Alta + +Métodos: + +1. Upload de OFX/CSV - Parser para formatos bancários +2. API Open Banking - Integração com Pluggy/Belvo +3. Email parsing - Ler extratos enviados por email +4. OCR de PDFs - Extrair dados de PDFs bancários + +Fluxo sugerido: +// app/(dashboard)/importacao/page.tsx + +1. Selecionar conta bancária de destino +2. Upload de arquivo ou conectar via API +3. Pré-visualização das transações +4. Matching automático com categorias (ML) +5. Revisão e confirmação +6. Importação em lote + +--- + +🎨 Melhorias de UI/UX + +1. Redesign do Diálogo de Transação 💳 + +Problema: Diálogo com muitos campos condicionais pode confundir usuários + +Solução: +// Wizard multi-step com progresso visual + + +{/_ Nome, valor, data _/} + + +{/_ Método, conta, condição _/} + + +{/_ Categoria, pagador, notas _/} + + + +Indicador de progresso: + +
+ 1}>1 + 2}>2 + 3 +
+ +--- + +2. Tabela Responsiva com Card View 📱 + +Problema: Tabelas complexas em mobile têm scroll horizontal + +Solução: +// components/lancamentos/table/lancamentos-responsive-view.tsx +export function LancamentosResponsiveView() { +const isMobile = useIsMobile(); + + if (isMobile) { + return ; + } + + return ; + +} + +// Card view para mobile +function LancamentoCard({ lancamento }) { +return ( + +
+
+

{lancamento.nome}

+

+{lancamento.categoria} +

+
+ +
+
+{lancamento.condicao} +{lancamento.formaPagamento} +
+
+); +} + +--- + +3. Dashboard Personalizável 🔧 + +Problema: Todos veem os mesmos 15 widgets + +Solução: +// lib/dashboard/widgets/user-widget-preferences.ts +export const widgetPreferences = pgTable("widget_preferences", { +userId: uuid("user_id").references(() => user.id), +widgetId: text("widget_id").notNull(), +ordem: integer("ordem").notNull(), +visivel: boolean("visivel").default(true), +tamanho: text("tamanho"), // 'small', 'medium', 'large' +}); + +// Drag-and-drop com dnd-kit +import { DndContext, closestCenter } from '@dnd-kit/core'; + + + + {widgets.map(widget => ( + + ))} + + + +Features: + +- Reordenar widgets via drag-and-drop +- Ocultar/mostrar widgets +- Redimensionar widgets (grid responsivo) +- Salvar preferências no banco + +--- + +4. Busca Global (Command Palette) ⌘K + +Problema: Navegar entre muitas páginas é lento + +Solução: +// components/command-palette.tsx +import { RiSearchLine } from '@remixicon/react'; + +export function CommandPalette() { +const [open, setOpen] = useState(false); + + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen(true); + } + }; + document.addEventListener('keydown', down); + return () => document.removeEventListener('keydown', down); + }, []); + + return ( + + + + + + + Nova Transação + + + + {recentTransactions.map(t => ( + {t.nome} + ))} + + + router.push('/dashboard')}> + Dashboard + + + + + ); + +} + +Ações rápidas: + +- Nova transação (Ctrl+K → "nova") +- Ver conta específica +- Buscar transação por nome/valor +- Navegar para qualquer página +- Executar ações (marcar como pago, editar, excluir) + +--- + +5. Onboarding Interativo 🎓 + +Problema: Novos usuários podem se sentir perdidos + +Solução: +// components/onboarding/onboarding-tour.tsx +import { Joyride } from 'react-joyride'; + +const steps = [ +{ +target: '.sidebar-nav', +content: 'Aqui você navega entre as diferentes seções', +}, +{ +target: '[data-tour="new-transaction"]', +content: 'Clique aqui para adicionar sua primeira transação', +}, +{ +target: '.month-picker', +content: 'Use isto para navegar entre meses', +}, +]; + +export function OnboardingTour() { +const { tourCompleted } = useUserPreferences(); + + return ( + + ); + +} + +Checklist inicial: + +- Criar primeira conta bancária +- Adicionar um cartão de crédito +- Registrar primeira transação +- Definir orçamento mensal +- Explorar dashboard + +--- + +6. Modo Compacto / Densidade Ajustável 📏 + +Problema: Algumas páginas têm muito espaço em branco + +Solução: +// Adicionar ao contexto de preferências +export const densitySettings = { +comfortable: { gap: 6, padding: 6, fontSize: 'text-base' }, +normal: { gap: 4, padding: 4, fontSize: 'text-sm' }, +compact: { gap: 2, padding: 2, fontSize: 'text-xs' }, +}; + +// Aplicar dinamicamente + +
+ +Controle: +// Em ajustes/page.tsx + + +--- + +7. Indicadores Visuais de Status 🚦 + +Problema: Difícil ver rapidamente status de contas/faturas + +Solução: +// components/status-indicator.tsx +export function StatusIndicator({ status }: { status: string }) { +const config = { +'em-dia': { color: 'green', icon: RiCheckLine, label: 'Em dia' }, +'vencendo': { color: 'yellow', icon: RiTimeLine, label: 'Vencendo' }, +'atrasado': { color: 'red', icon: RiAlertLine, label: 'Atrasado' }, +}; + + const { color, icon: Icon, label } = config[status]; + + return ( + + + {label} + + ); + +} + +Aplicar em: + +- Cards de faturas (verde = paga, amarelo = próxima, vermelho = vencida) +- Boletos (status de pagamento) +- Orçamentos (verde = dentro, amarelo = 80%, vermelho = estourou) + +--- + +8. Gráficos Interativos com Drill-Down 📊 + +Problema: Gráficos mostram dados mas não permitem explorar + +Solução: +// components/dashboard/interactive-category-chart.tsx + + { +// Ao clicar em fatia, abrir modal com transações daquela categoria +showCategoryDetails(data.categoryId); +}} +/> + + +// Modal de drill-down +function CategoryDetailsModal({ categoryId, period }) { +const transactions = useCategoryTransactions(categoryId, period); + + return ( + + + Detalhes - {categoryName} + + + + + + ); + +} + +--- + +9. Tema de Cores Personalizável 🎨 + +Problema: Apenas uma cor primária (terracota) + +Solução: +// lib/theme/color-themes.ts +export const colorThemes = { +terracotta: { primary: 'oklch(69.18% 0.18855 38.353)' }, +ocean: { primary: 'oklch(69.18% 0.18855 220)' }, +forest: { primary: 'oklch(69.18% 0.18855 140)' }, +sunset: { primary: 'oklch(69.18% 0.18855 25)' }, +lavender: { primary: 'oklch(69.18% 0.18855 280)' }, +}; + +// Em ajustes/page.tsx +export function ThemeColorPicker() { +const { colorTheme, setColorTheme } = useTheme(); + + return ( +
+ {Object.entries(colorThemes).map(([name, colors]) => ( +
+ ); + +} + +--- + +10. Breadcrumbs e Page Headers Consistentes 🗺️ + +Problema: Inconsistência em headers entre páginas + +Solução: +// components/page-header.tsx +interface PageHeaderProps { +title: string; +description?: string; +breadcrumbs?: { label: string; href?: string }[]; +actions?: React.ReactNode; +} + +export function PageHeader({ +title, +description, +breadcrumbs, +actions +}: PageHeaderProps) { +return ( +
+{breadcrumbs && ( + +{breadcrumbs.map((crumb, i) => ( + +{crumb.href ? ( + +{crumb.label} + +) : ( +{crumb.label} +)} + +))} + +)} +
+
+

{title}

+{description && ( +

{description}

+)} +
+{actions &&
{actions}
} +
+
+); +} + +// Uso: +Nova Transação +} +/> + +--- + +🔧 Melhorias Técnicas + +1. Error Boundary Global + +// app/error.tsx +'use client'; + +export default function Error({ +error, +reset, +}: { +error: Error & { digest?: string }; +reset: () => void; +}) { +return ( +
+

Algo deu errado!

+ +
+); +} + +2. Analytics e Telemetria + +// lib/analytics.ts +export function trackEvent(event: string, properties?: Record) { +// Posthog, Mixpanel, ou custom +console.log('[Analytics]', event, properties); +} + +// Uso: +trackEvent('transaction_created', { type: 'despesa', amount: 100 }); + +3. Rate Limiting para Actions + +// lib/rate-limit.ts +import { Ratelimit } from '@upstash/ratelimit'; + +const ratelimit = new Ratelimit({ +redis: Redis.fromEnv(), +limiter: Ratelimit.slidingWindow(10, '10 s'), +}); + +export async function checkRateLimit(userId: string) { +const { success } = await ratelimit.limit(userId); +if (!success) throw new Error('Rate limit exceeded'); +} + +--- + +📊 Priorização Sugerida + +🔥 Fase 1 - Quick Wins (1-2 semanas) + +1. ✅ Breadcrumbs e Page Headers consistentes +2. ✅ Command Palette (⌘K) +3. ✅ Indicadores visuais de status +4. ✅ Modo card para tabelas em mobile +5. ✅ Relatórios PDF básicos + +🚀 Fase 2 - Value Boost (1 mês) + +1. 🎯 Metas Financeiras +2. 🔮 Análise Preditiva +3. 🔔 Sistema de Notificações +4. 📊 Gráficos interativos com drill-down +5. 🎓 Onboarding interativo + +💎 Fase 3 - Diferenciais (2-3 meses) + +1. 🔄 Importação automática de extratos (OFX/CSV) +2. 📎 Anexos e comprovantes com OCR +3. 🎨 Temas personalizáveis +4. 🏆 Gamificação e conquistas +5. 💹 Tracking de investimentos + +--- + +🎯 Métricas de Sucesso + +Para medir o impacto das melhorias: + +1. Engajamento: + + + - Tempo médio na aplicação + - Frequência de uso (DAU/MAU) + - Transações criadas por usuário/mês + +2. Usabilidade: + + + - Taxa de conclusão de onboarding + - Tempo para criar primeira transação + - Taxa de erro em formulários + +3. Performance: + + + - LCP (Largest Contentful Paint) < 2.5s + - FID (First Input Delay) < 100ms + - CLS (Cumulative Layout Shift) < 0.1 + +4. Adoção de Features: + + + - % de usuários usando metas + - % de usuários que personalizam dashboard + - Taxa de uso do command palette + +--- + +📝 Conclusão + +O OpenSheets já possui uma base sólida com excelente arquitetura e design. As sugestões focam em: + +1. Melhorar a experiência mobile (responsividade avançada) +2. Adicionar inteligência (previsões, notificações, insights) +3. Aumentar a eficiência (command palette, importação automática) +4. Personalização (temas, dashboard, densidade) +5. Gamificação (metas, conquistas) para engajamento + +Próximos Passos Recomendados: + +1. Validar com usuários quais features têm maior demanda +2. Implementar quick wins da Fase 1 +3. A/B testing de novos designs +4. Iteração baseada em feedback