diff --git a/public/images/dashboard-preview-light.webp b/public/images/dashboard-preview-light.webp index 77acca2..d3ebf36 100644 Binary files a/public/images/dashboard-preview-light.webp and b/public/images/dashboard-preview-light.webp differ diff --git a/public/images/preview-lancamentos-light.webp b/public/images/preview-lancamentos-light.webp index 45a09f0..391a3e7 100644 Binary files a/public/images/preview-lancamentos-light.webp and b/public/images/preview-lancamentos-light.webp differ diff --git a/src/app/(landing-page)/page.tsx b/src/app/(landing-page)/page.tsx index c625f7c..54699c8 100644 --- a/src/app/(landing-page)/page.tsx +++ b/src/app/(landing-page)/page.tsx @@ -12,6 +12,7 @@ import { RiFlashlightLine, RiGitBranchLine, RiGithubFill, + RiInformationLine, RiLayoutGridLine, RiLineChartLine, RiLockLine, @@ -29,89 +30,135 @@ import { import { headers } from "next/headers"; import Image from "next/image"; import Link from "next/link"; +import type { ComponentType } from "react"; import { AnimateOnScroll } from "@/features/landing/components/animate-on-scroll"; import { MobileNav } from "@/features/landing/components/mobile-nav"; import { SetupTabs } from "@/features/landing/components/setup-tabs"; import { AnimatedThemeToggler } from "@/shared/components/animated-theme-toggler"; import { Logo } from "@/shared/components/logo"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/shared/components/ui/alert"; import { Badge } from "@/shared/components/ui/badge"; import { Button } from "@/shared/components/ui/button"; import { Card, CardContent } from "@/shared/components/ui/card"; +import { DotPattern } from "@/shared/components/ui/dot-pattern"; import { getOptionalUserSession } from "@/shared/lib/auth/server"; -const mainFeatures = [ +async function fetchGitHubStats() { + try { + const res = await fetch( + "https://api.github.com/repos/felipegcoutinho/openmonetis", + { next: { revalidate: 3600 } }, + ); + if (!res.ok) return { stars: 200, forks: 60 }; + const data = await res.json(); + return { + stars: data.stargazers_count as number, + forks: data.forks_count as number, + }; + } catch { + return { stars: 200, forks: 60 }; + } +} + +const navbarActionClassName = + "border-black/10 bg-transparent text-black/75 shadow-none hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-black/20 data-[state=open]:bg-black/10 data-[state=open]:text-black"; + +type FeatureItem = { + icon: ComponentType<{ className?: string; style?: React.CSSProperties }>; + title: string; + description: string; + colorVar: string; +}; + +const mainFeatures: FeatureItem[] = [ { icon: RiWalletLine, title: "Contas e transações", description: "Registre suas contas bancárias, cartões e dinheiro. Adicione receitas, despesas e transferências. Organize por categorias. Extratos detalhados por conta.", + colorVar: "var(--data-9)", }, { icon: RiPercentLine, title: "Parcelamentos avançados", description: "Controle completo de compras parceladas. Antecipe parcelas com cálculo automático de desconto. Veja análise consolidada de todas as parcelas em aberto.", + colorVar: "var(--data-4)", }, { icon: RiRobot2Line, title: "Insights com IA", description: "Análises financeiras geradas por IA (Claude, GPT, Gemini). Insights personalizados sobre seus padrões de gastos e recomendações inteligentes.", + colorVar: "var(--data-8)", }, { icon: RiBarChartBoxLine, title: "Relatórios e gráficos", description: "Dashboard com 20+ widgets interativos. Relatórios detalhados por categoria. Gráficos de evolução e comparativos. Exportação em PDF e Excel.", + colorVar: "var(--data-5)", }, { icon: RiBankCard2Line, title: "Faturas de cartão", description: "Cadastre seus cartões e acompanhe as faturas por período. Veja o que ainda não foi fechado. Controle limites, vencimentos e fechamentos.", + colorVar: "var(--data-1)", }, { icon: RiTeamLine, title: "Gestão colaborativa", description: "Compartilhe pagadores com permissões granulares (admin/viewer). Notificações automáticas por e-mail. Colabore em lançamentos compartilhados.", + colorVar: "var(--data-3)", }, ]; -const extraFeatures = [ +const extraFeatures: FeatureItem[] = [ { icon: RiPieChartLine, title: "Categorias e orçamentos", description: "Crie categorias personalizadas e defina orçamentos mensais com indicadores visuais.", + colorVar: "var(--data-7)", }, { icon: RiFileTextLine, title: "Anotações e tarefas", description: "Notas de texto e listas de tarefas com checkboxes. Arquivamento para manter histórico.", + colorVar: "var(--data-6)", }, { icon: RiCalendarLine, title: "Calendário financeiro", description: "Visualize transações em calendário mensal. Nunca perca prazos de pagamentos.", + colorVar: "var(--data-2)", }, { icon: RiDownloadCloudLine, title: "Importação em massa", description: "Lance múltiplos lançamentos de uma vez", + colorVar: "var(--data-9)", }, { icon: RiEyeOffLine, title: "Modo privacidade", description: "Oculte valores sensíveis com um clique. Tema dark/light. Calculadora integrada.", + colorVar: "var(--data-4)", }, { icon: RiFlashlightLine, title: "Performance otimizada", description: "Sistema rápido e com alta performance", + colorVar: "var(--data-5)", }, ]; @@ -136,12 +183,75 @@ const screenshotSections = [ }, ]; -const companionBanks = ["Nubank", "Itaú", "Inter", "Mercado Pago", "Outros"]; +const companionBanks = [ + { name: "Nubank", logo: "/logos/nubank.png" }, + { name: "Itaú", logo: "/logos/itau.png" }, + { name: "Inter", logo: "/logos/interpj.png" }, + { name: "Mercado Pago", logo: "/logos/mercadopago.png" }, + { name: "Outros", logo: null }, +]; + +const stackItems = [ + { + icon: RiCodeSSlashLine, + title: "Frontend", + subtitle: "Next.js 16, TypeScript, Tailwind CSS, shadcn/ui", + description: "Interface moderna e responsiva com React 19 e App Router", + colorVar: "var(--data-3)", + }, + { + icon: RiDatabase2Line, + title: "Backend", + subtitle: "PostgreSQL 18, Drizzle ORM, Better Auth", + description: "Banco relacional robusto com type-safe ORM", + colorVar: "var(--data-9)", + }, + { + icon: RiShieldCheckLine, + title: "Segurança", + subtitle: "Better Auth com OAuth (Google) e autenticação por email", + description: "Sessões seguras e proteção de rotas por middleware", + colorVar: "var(--data-1)", + }, + { + icon: RiDeviceLine, + title: "Deploy", + subtitle: + "Docker com multi-stage build, health checks e volumes persistentes", + description: "Fácil de rodar localmente ou em qualquer servidor", + colorVar: "var(--data-5)", + }, +]; + +const whoIsItForItems = [ + { + icon: RiTimeLine, + title: "Tem disciplina de registrar gastos", + description: + "Não se importa em dedicar alguns minutos por dia ou semana para manter tudo atualizado", + colorVar: "var(--data-4)", + }, + { + icon: RiLockLine, + title: "Quer controle total sobre seus dados", + description: + "Prefere hospedar seus próprios dados ao invés de depender de serviços terceiros", + colorVar: "var(--data-9)", + }, + { + icon: RiLineChartLine, + title: "Gosta de entender exatamente onde o dinheiro vai", + description: + "Quer visualizar padrões de gastos e tomar decisões informadas", + colorVar: "var(--data-3)", + }, +]; export default async function Page() { - const [session, headersList] = await Promise.all([ + const [session, headersList, githubStats] = await Promise.all([ getOptionalUserSession(), headers(), + fetchGitHubStats(), ]); const hostname = headersList.get("host")?.replace(/:\d+$/, ""); const publicDomain = process.env.PUBLIC_DOMAIN?.replace( @@ -153,62 +263,62 @@ export default async function Page() { return (