diff --git a/public/fonts/america-bold.woff2 b/public/fonts/america-bold.woff2 deleted file mode 100644 index a5cdbf5..0000000 Binary files a/public/fonts/america-bold.woff2 and /dev/null differ diff --git a/public/fonts/america-medium.woff2 b/public/fonts/america-medium.woff2 deleted file mode 100644 index 275f1a9..0000000 Binary files a/public/fonts/america-medium.woff2 and /dev/null differ diff --git a/public/fonts/font_index.ts b/public/fonts/font_index.ts index c88370e..91ce81b 100644 --- a/public/fonts/font_index.ts +++ b/public/fonts/font_index.ts @@ -7,11 +7,6 @@ export const america = localFont({ weight: "400", style: "normal", }, - // { - // path: "./america-medium.woff2", - // weight: "500", - // style: "normal", - // }, ], display: "swap", variable: "--font-america", diff --git a/public/images/companion-preview-dark.webp b/public/images/companion-preview-dark.webp new file mode 100644 index 0000000..009bf5b Binary files /dev/null and b/public/images/companion-preview-dark.webp differ diff --git a/public/images/companion-preview-light.webp b/public/images/companion-preview-light.webp new file mode 100644 index 0000000..8000352 Binary files /dev/null and b/public/images/companion-preview-light.webp differ diff --git a/public/images/dashboard-preview-dark.webp b/public/images/dashboard-preview-dark.webp index 4283f20..5342e8a 100644 Binary files a/public/images/dashboard-preview-dark.webp and b/public/images/dashboard-preview-dark.webp differ diff --git a/public/images/dashboard-preview-light.webp b/public/images/dashboard-preview-light.webp index d3ebf36..04635ea 100644 Binary files a/public/images/dashboard-preview-light.webp and b/public/images/dashboard-preview-light.webp differ diff --git a/public/images/logo_small.png b/public/images/logo_small.png index 25a436a..cea70fe 100644 Binary files a/public/images/logo_small.png and b/public/images/logo_small.png differ diff --git a/public/images/openmonetis_companion.webp b/public/images/openmonetis_companion.webp deleted file mode 100644 index 2acda8f..0000000 Binary files a/public/images/openmonetis_companion.webp and /dev/null differ diff --git a/public/images/openmonetis_pwa.webp b/public/images/openmonetis_pwa.webp new file mode 100644 index 0000000..7af2099 Binary files /dev/null and b/public/images/openmonetis_pwa.webp differ diff --git a/public/images/preview-calendario-dark.webp b/public/images/preview-calendario-dark.webp index 1dbe73a..eed9842 100644 Binary files a/public/images/preview-calendario-dark.webp and b/public/images/preview-calendario-dark.webp differ diff --git a/public/images/preview-calendario-light.webp b/public/images/preview-calendario-light.webp index e37a246..06492fc 100644 Binary files a/public/images/preview-calendario-light.webp and b/public/images/preview-calendario-light.webp differ diff --git a/public/images/preview-cartao-dark.webp b/public/images/preview-cartao-dark.webp index 1cf246d..f212ef1 100644 Binary files a/public/images/preview-cartao-dark.webp and b/public/images/preview-cartao-dark.webp differ diff --git a/public/images/preview-cartao-light.webp b/public/images/preview-cartao-light.webp index 731d6f0..9c1d1cc 100644 Binary files a/public/images/preview-cartao-light.webp and b/public/images/preview-cartao-light.webp differ diff --git a/public/images/preview-lancamentos-dark.webp b/public/images/preview-lancamentos-dark.webp index d6516f4..cab1f3b 100644 Binary files a/public/images/preview-lancamentos-dark.webp and b/public/images/preview-lancamentos-dark.webp differ diff --git a/public/images/preview-lancamentos-light.webp b/public/images/preview-lancamentos-light.webp index 391a3e7..4686e53 100644 Binary files a/public/images/preview-lancamentos-light.webp and b/public/images/preview-lancamentos-light.webp differ diff --git a/public/images/preview-orcamentos-dark.webp b/public/images/preview-orcamentos-dark.webp new file mode 100644 index 0000000..26a177c Binary files /dev/null and b/public/images/preview-orcamentos-dark.webp differ diff --git a/public/images/preview-orcamentos-light.webp b/public/images/preview-orcamentos-light.webp new file mode 100644 index 0000000..7f11e01 Binary files /dev/null and b/public/images/preview-orcamentos-light.webp differ diff --git a/public/images/preview-parcelas-dark.webp b/public/images/preview-parcelas-dark.webp new file mode 100644 index 0000000..46999b9 Binary files /dev/null and b/public/images/preview-parcelas-dark.webp differ diff --git a/public/images/preview-parcelas-light.webp b/public/images/preview-parcelas-light.webp new file mode 100644 index 0000000..492c999 Binary files /dev/null and b/public/images/preview-parcelas-light.webp differ diff --git a/public/images/preview-pre-lancamentos-dark.webp b/public/images/preview-pre-lancamentos-dark.webp new file mode 100644 index 0000000..bf0e601 Binary files /dev/null and b/public/images/preview-pre-lancamentos-dark.webp differ diff --git a/public/images/preview-pre-lancamentos-light.webp b/public/images/preview-pre-lancamentos-light.webp new file mode 100644 index 0000000..3615e13 Binary files /dev/null and b/public/images/preview-pre-lancamentos-light.webp differ diff --git a/public/images/pwa-preview-dark.webp b/public/images/pwa-preview-dark.webp new file mode 100644 index 0000000..c015f1a Binary files /dev/null and b/public/images/pwa-preview-dark.webp differ diff --git a/public/images/pwa-preview-light.webp b/public/images/pwa-preview-light.webp new file mode 100644 index 0000000..64882fb Binary files /dev/null and b/public/images/pwa-preview-light.webp differ diff --git a/public/images/web-app-manifest-192x192.png b/public/images/web-app-manifest-192x192.png index 42d094d..a2fbcd1 100644 Binary files a/public/images/web-app-manifest-192x192.png and b/public/images/web-app-manifest-192x192.png differ diff --git a/public/images/web-app-manifest-512x512.png b/public/images/web-app-manifest-512x512.png index c6e4554..054ac3c 100644 Binary files a/public/images/web-app-manifest-512x512.png and b/public/images/web-app-manifest-512x512.png differ diff --git a/public/logos/insight-steps-ea3e04996dbca687a97a55c7018ec46d5c05d77d7871c547454582c617b59854.png b/public/logos/insight-steps-ea3e04996dbca687a97a55c7018ec46d5c05d77d7871c547454582c617b59854.png deleted file mode 100644 index 1438479..0000000 Binary files a/public/logos/insight-steps-ea3e04996dbca687a97a55c7018ec46d5c05d77d7871c547454582c617b59854.png and /dev/null differ diff --git a/src/app/(landing-page)/page.tsx b/src/app/(landing-page)/page.tsx index 54699c8..0504386 100644 --- a/src/app/(landing-page)/page.tsx +++ b/src/app/(landing-page)/page.tsx @@ -1,252 +1,38 @@ import { - RiBankCard2Line, - RiBarChartBoxLine, - RiCalendarLine, - RiCheckLine, - RiCodeSSlashLine, - RiDatabase2Line, - RiDeviceLine, - RiDownloadCloudLine, - RiEyeOffLine, - RiFileTextLine, - RiFlashlightLine, - RiGitBranchLine, + RiAndroidLine, RiGithubFill, - RiInformationLine, - RiLayoutGridLine, - RiLineChartLine, - RiLockLine, - RiNotification3Line, - RiPercentLine, - RiPieChartLine, - RiRobot2Line, RiShieldCheckLine, RiSmartphoneLine, - RiStarLine, - RiTeamLine, - RiTimeLine, - RiWalletLine, } from "@remixicon/react"; 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 { ScreenshotTabs } from "@/features/landing/components/screenshot-tabs"; import { SetupTabs } from "@/features/landing/components/setup-tabs"; +import { + companionBanks, + companionSteps, + extraFeatures, + getMetricsItems, + mainFeatures, + navbarActionClassName, + navLinks, + pwaHighlights, + stackItems, + whoIsItForItems, +} from "@/features/landing/constants"; +import { landingImages } from "@/features/landing/images"; +import { fetchGitHubStats } from "@/features/landing/queries"; 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"; -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: 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)", - }, -]; - -const screenshotSections = [ - { - title: "Lançamentos", - description: "Registre e organize todas as suas transações financeiras", - lightSrc: "/images/preview-lancamentos-light.webp", - darkSrc: "/images/preview-lancamentos-dark.webp", - }, - { - title: "Calendário", - description: "Visualize suas finanças no calendário mensal", - lightSrc: "/images/preview-calendario-light.webp", - darkSrc: "/images/preview-calendario-dark.webp", - }, - { - title: "Cartões", - description: "Acompanhe faturas, limites e vencimentos dos seus cartões", - lightSrc: "/images/preview-cartao-light.webp", - darkSrc: "/images/preview-cartao-dark.webp", - }, -]; - -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, githubStats] = await Promise.all([ getOptionalUserSession(), @@ -259,6 +45,7 @@ export default async function Page() { "", ).replace(/:\d+$/, ""); const isPublicDomain = !!(publicDomain && hostname === publicDomain); + const metricsItems = getMetricsItems(githubStats.stars, githubStats.forks); return (
@@ -273,13 +60,7 @@ export default async function Page() { {/* Center Navigation Links */}
@@ -817,7 +585,7 @@ export default async function Page() { {/* How to run Section */}
-
+
@@ -850,19 +618,19 @@ export default async function Page() {
{/* Who is this for Section */} -
+
-
+
Para quem é?

- Para quem funciona? + Feito para quem gosta de controle

- O openmonetis funciona melhor se você: + O OpenMonetis não é para todo mundo.

@@ -896,19 +664,6 @@ export default async function Page() { ))}
- - - - - Não é para todo mundo - - Se você não se encaixa nisso, provavelmente vai abandonar - depois de uma semana. Tudo certo! Existem outras ferramentas - com sincronização automática que podem funcionar melhor pra - você. - - -
@@ -917,7 +672,7 @@ export default async function Page() {
-
+

Pronto para testar?

diff --git a/src/app/apple-icon.png b/src/app/apple-icon.png index 3eed4df..45ae426 100644 Binary files a/src/app/apple-icon.png and b/src/app/apple-icon.png differ diff --git a/src/app/favicon.ico b/src/app/favicon.ico index 1ca4fc0..768b3f9 100644 Binary files a/src/app/favicon.ico and b/src/app/favicon.ico differ diff --git a/src/app/globals.css b/src/app/globals.css index 8b2e518..cc215e8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -51,7 +51,6 @@ --chart-9: var(--color-cyan-500); --chart-10: var(--color-lime-500); - /* Data palette — análoga quente (hue 0–120), família do primary */ --data-1: oklch(58% 0.22 18); /* vermelho-tijolo */ --data-2: oklch(64% 0.22 30); /* vermelho-laranja */ --data-3: oklch(69% 0.21 42); /* laranja (≈ primary) */ @@ -93,23 +92,23 @@ } .dark { - --background: oklch(22% 0.004 55); + --background: oklch(18% 0.004 55); --foreground: oklch(93% 0.008 80); - --card: oklch(25.5% 0.004 55); + --card: oklch(21.5% 0.004 55); --card-foreground: var(--foreground); - --popover: oklch(28% 0.004 55); + --popover: oklch(24% 0.004 55); --popover-foreground: var(--foreground); --primary: oklch(72.069% 0.18335 44.069); --primary-foreground: oklch(16% 0.004 60); - --secondary: oklch(29% 0.004 55); + --secondary: oklch(25% 0.004 55); --secondary-foreground: var(--foreground); - --muted: oklch(32% 0.0035 55); + --muted: oklch(28% 0.0035 55); --muted-foreground: oklch(73% 0.006 75); - --accent: oklch(33% 0.005 55); + --accent: oklch(29% 0.005 55); --accent-foreground: var(--foreground); --success: oklch(62% 0.16 150); @@ -121,7 +120,7 @@ --destructive: oklch(62% 0.2 28); --destructive-foreground: oklch(98% 0.005 30); - --border: oklch(35% 0.004 55); + --border: oklch(31% 0.004 55); --input: var(--border); --ring: var(--primary); @@ -136,8 +135,6 @@ --chart-9: var(--color-cyan-500); --chart-10: var(--color-lime-500); - /* Data palette — dark mode (ligeiramente mais vivos) */ - /* Data palette dark — ligeiramente mais vivos para contrastar no fundo escuro */ --data-1: oklch(66% 0.22 18); --data-2: oklch(72% 0.22 30); --data-3: oklch(76% 0.21 42); @@ -149,13 +146,13 @@ --data-9: oklch(69% 0.17 120); --data-10: oklch(63% 0.15 10); - --sidebar: oklch(19.5% 0.004 55); + --sidebar: oklch(15.5% 0.004 55); --sidebar-foreground: var(--foreground); --sidebar-primary: var(--primary); --sidebar-primary-foreground: var(--primary-foreground); - --sidebar-accent: oklch(30% 0.004 55); + --sidebar-accent: oklch(26% 0.004 55); --sidebar-accent-foreground: var(--foreground); - --sidebar-border: oklch(34% 0.004 55); + --sidebar-border: oklch(30% 0.004 55); --sidebar-ring: var(--primary); --radius: 0.625rem; diff --git a/src/app/icon0.svg b/src/app/icon0.svg deleted file mode 100644 index 5d0c397..0000000 --- a/src/app/icon0.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/icon1.png b/src/app/icon1.png index 7ffba2d..45ae426 100644 Binary files a/src/app/icon1.png and b/src/app/icon1.png differ diff --git a/src/features/landing/components/mobile-nav.tsx b/src/features/landing/components/mobile-nav.tsx index 70ba9a1..e98caa0 100644 --- a/src/features/landing/components/mobile-nav.tsx +++ b/src/features/landing/components/mobile-nav.tsx @@ -15,9 +15,10 @@ import { const navLinks = [ { href: "#telas", label: "Conheça as telas" }, { href: "#funcionalidades", label: "Funcionalidades" }, - { href: "#companion", label: "Companion" }, + { href: "#mobile", label: "Mobile" }, { href: "#stack", label: "Stack" }, { href: "#como-usar", label: "Como usar" }, + { href: "#para-quem-e", label: "Para quem é?" }, ]; interface MobileNavProps { @@ -26,7 +27,11 @@ interface MobileNavProps { triggerClassName?: string; } -export function MobileNav({ isPublicDomain, isLoggedIn, triggerClassName }: MobileNavProps) { +export function MobileNav({ + isPublicDomain, + isLoggedIn, + triggerClassName, +}: MobileNavProps) { const [open, setOpen] = useState(false); return ( diff --git a/src/features/landing/components/screenshot-tabs.tsx b/src/features/landing/components/screenshot-tabs.tsx new file mode 100644 index 0000000..1575a4d --- /dev/null +++ b/src/features/landing/components/screenshot-tabs.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { + RiArrowLeftRightLine, + RiAtLine, + RiBankCard2Line, + RiBarChart2Line, + RiCalendarEventLine, + RiSecurePaymentLine, +} from "@remixicon/react"; +import Image from "next/image"; +import { landingImages } from "@/features/landing/images"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/shared/components/ui/tabs"; + +const { screenshots } = landingImages; + +const sections = [ + { + value: "lancamentos", + label: "Lançamentos", + icon: RiArrowLeftRightLine, + ...screenshots.lancamentos, + }, + { + value: "pre-lancamentos", + label: "Pré-lançamentos", + icon: RiAtLine, + ...screenshots.preLancamentos, + }, + { + value: "orcamentos", + label: "Orçamentos", + icon: RiBarChart2Line, + ...screenshots.orcamentos, + }, + { + value: "parcelas", + label: "Análise de Parcelas", + icon: RiSecurePaymentLine, + ...screenshots.parcelas, + }, + { + value: "calendario", + label: "Calendário", + icon: RiCalendarEventLine, + ...screenshots.calendario, + }, + { + value: "cartoes", + label: "Cartões", + icon: RiBankCard2Line, + ...screenshots.cartoes, + }, +]; + +export function ScreenshotTabs() { + return ( + +
+ + {sections.map((s) => ( + + + {s.label} + + ))} + + + {sections.map((s) => ( + +
+
+
+
+
+
+
+ {`Preview + {`Preview +
+ + ))} +
+ + ); +} diff --git a/src/features/landing/constants.ts b/src/features/landing/constants.ts new file mode 100644 index 0000000..8bf4830 --- /dev/null +++ b/src/features/landing/constants.ts @@ -0,0 +1,292 @@ +import { + RiBankCard2Line, + RiBarChartBoxLine, + RiCalendarLine, + RiCheckLine, + RiCodeSSlashLine, + RiDatabase2Line, + RiDeviceLine, + RiDownloadCloudLine, + RiEyeOffLine, + RiFileTextLine, + RiFlashlightLine, + RiGitBranchLine, + RiLayoutGridLine, + RiLineChartLine, + RiLockLine, + RiNotification3Line, + RiPercentLine, + RiPieChartLine, + RiRobot2Line, + RiShieldCheckLine, + RiSmartphoneLine, + RiStarLine, + RiTeamLine, + RiTimeLine, + RiWalletLine, +} from "@remixicon/react"; +import type { ComponentType } from "react"; + +export type FeatureItem = { + icon: ComponentType<{ className?: string; style?: React.CSSProperties }>; + title: string; + description: string; + colorVar: string; +}; + +export 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"; + +export const navLinks = [ + { href: "#telas", label: "conheça as telas" }, + { href: "#funcionalidades", label: "funcionalidades" }, + { href: "#mobile", label: "mobile" }, + { href: "#stack", label: "stack" }, + { href: "#como-usar", label: "como usar" }, + { href: "#para-quem-e", label: "para quem é" }, +] as const; + +export 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)", + }, +]; + +export 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)", + }, +]; + +export const companionBanks = [ + { name: "Nubank", logo: "/logos/nubank.png" }, + { name: "Itaú", logo: "/logos/itau.png" }, + { name: "Inter", logo: "/logos/intermedium.png" }, + { name: "Mercado Pago", logo: "/logos/mercadopagocartao.png" }, +]; + +export const pwaHighlights: FeatureItem[] = [ + { + icon: RiSmartphoneLine, + title: "Instale direto da web", + description: "Adicione à tela inicial e abra como app, sem loja.", + colorVar: "var(--data-3)", + }, + { + icon: RiLayoutGridLine, + title: "Acesso rápido ao que importa", + description: "Dashboard, inbox e lançamentos a um toque.", + colorVar: "var(--data-9)", + }, + { + icon: RiFlashlightLine, + title: "Experiência mobile mais direta", + description: "Modo standalone com navegação limpa e fluida.", + colorVar: "var(--data-4)", + }, +]; + +export const pwaCompatList = [ + { + label: "Android", + description: + "Chrome e Edge — instale pelo banner ou pelo menu do navegador", + }, + { + label: "iOS / iPadOS", + description: "Safari — adicione à tela inicial pelo menu compartilhar", + }, + { + label: "Desktop", + description: "Chrome, Edge e outros — instale pela barra de endereço", + }, +] as const; + +export const companionSteps: FeatureItem[] = [ + { + icon: RiNotification3Line, + title: "Notificação bancária chega", + description: "O Companion intercepta automaticamente", + colorVar: "var(--data-1)", + }, + { + icon: RiSmartphoneLine, + title: "Dados extraídos e enviados", + description: "Valor, descrição e banco são identificados", + colorVar: "var(--data-4)", + }, + { + icon: RiCheckLine, + title: "Revise e confirme no OpenMonetis", + description: "Pré-lançamentos ficam na inbox para sua aprovação", + colorVar: "var(--data-9)", + }, +]; + +export const stackItems = [ + { + icon: RiCodeSSlashLine, + title: "Frontend", + subtitle: "Next.js, 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, 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)", + }, +]; + +export const whoIsItForItems: FeatureItem[] = [ + { + 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)", + }, + { + icon: RiTimeLine, + title: "Não é plug and play", + description: + "Você vai precisar configurar as coisas, conectar suas contas e ajustar o sistema para o seu jeito de usar.", + colorVar: "var(--data-4)", + }, +]; + +export function getMetricsItems(stars: number, forks: number) { + return [ + { + icon: RiLayoutGridLine, + value: "20+", + label: "Widgets no dashboard", + colorVar: "var(--data-9)", + }, + { + icon: RiShieldCheckLine, + value: "100%", + label: "Self-hosted", + colorVar: "var(--data-1)", + }, + { + icon: RiStarLine, + value: `${stars}`, + label: "Stars no GitHub", + colorVar: "var(--data-4)", + }, + { + icon: RiGitBranchLine, + value: `${forks}`, + label: "Forks no GitHub", + colorVar: "var(--data-3)", + }, + ]; +} diff --git a/src/features/landing/images.ts b/src/features/landing/images.ts new file mode 100644 index 0000000..c72d0fb --- /dev/null +++ b/src/features/landing/images.ts @@ -0,0 +1,59 @@ +/** + * Centraliza todos os assets de imagem da landing page. + * Para adicionar ou renomear uma imagem, altere apenas aqui. + * + * Convenção: + * - { light, dark } → imagem com variante de tema + * - string → imagem única (sem variante dark) + */ + +export const landingImages = { + /** Preview do dashboard no hero da página */ + hero: { + light: "/images/dashboard-preview-light.webp", + dark: "/images/dashboard-preview-dark.webp", + }, + + /** Mockup do app instalado como PWA */ + pwa: { + light: "/images/pwa-preview-light.webp", + dark: "/images/pwa-preview-dark.webp", + }, + + /** Mockup do Companion Android */ + companion: { + light: "/images/companion-preview-light.webp", + dark: "/images/companion-preview-dark.webp", + }, + + /** Screenshots usados nas abas da seção "Conheça as telas" */ + screenshots: { + lancamentos: { + light: "/images/preview-lancamentos-light.webp", + dark: "/images/preview-lancamentos-dark.webp", + }, + /** Ainda sem print próprio — usando lançamentos como placeholder */ + preLancamentos: { + light: "/images/preview-pre-lancamentos-light.webp", + dark: "/images/preview-pre-lancamentos-dark.webp", + }, + /** Ainda sem print próprio — usando lançamentos como placeholder */ + orcamentos: { + light: "/images/preview-orcamentos-light.webp", + dark: "/images/preview-orcamentos-dark.webp", + }, + /** Ainda sem print próprio — usando lançamentos como placeholder */ + parcelas: { + light: "/images/preview-parcelas-light.webp", + dark: "/images/preview-parcelas-dark.webp", + }, + calendario: { + light: "/images/preview-calendario-light.webp", + dark: "/images/preview-calendario-dark.webp", + }, + cartoes: { + light: "/images/preview-cartao-light.webp", + dark: "/images/preview-cartao-dark.webp", + }, + }, +} as const; diff --git a/src/features/landing/queries.ts b/src/features/landing/queries.ts new file mode 100644 index 0000000..9d2476b --- /dev/null +++ b/src/features/landing/queries.ts @@ -0,0 +1,19 @@ +export async function fetchGitHubStats(): Promise<{ + stars: number; + forks: number; +}> { + 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 }; + } +} diff --git a/src/features/payers/lib/avatar-options.ts b/src/features/payers/lib/avatar-options.ts index 1d9c330..0568851 100644 --- a/src/features/payers/lib/avatar-options.ts +++ b/src/features/payers/lib/avatar-options.ts @@ -4,12 +4,9 @@ import { DEFAULT_PAYER_AVATAR } from "@/shared/lib/payers/constants"; const AVATAR_DIRECTORY = path.join(process.cwd(), "public", "avatars"); const AVATAR_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".svg", ".webp"]); +let avatarOptionsPromise: Promise | null = null; -/** - * Loads available avatar files from the public/avatars directory - * @returns Array of unique avatar filenames sorted alphabetically - */ -export async function loadAvatarOptions() { +async function readAvatarOptions() { try { const files = await readdir(AVATAR_DIRECTORY, { withFileTypes: true }); @@ -28,3 +25,12 @@ export async function loadAvatarOptions() { return [DEFAULT_PAYER_AVATAR]; } } + +/** + * Loads available avatar files from the public/avatars directory + * @returns Array of unique avatar filenames sorted alphabetically + */ +export async function loadAvatarOptions() { + avatarOptionsPromise ??= readAvatarOptions(); + return avatarOptionsPromise; +} diff --git a/src/shared/components/logo.tsx b/src/shared/components/logo.tsx index 2f73958..bdab05c 100644 --- a/src/shared/components/logo.tsx +++ b/src/shared/components/logo.tsx @@ -26,7 +26,10 @@ export function Logo({ alt="OpenMonetis" width={32} height={32} - className={cn("object-contain", !colorIcon && "brightness-0 saturate-0")} + className={cn( + "object-contain", + !colorIcon && "brightness-0 saturate-0", + )} priority /> | null = null; -/** - * Loads available logo files from the public/logos directory - * @returns Array of logo filenames sorted alphabetically - */ -export async function loadLogoOptions() { +async function readLogoOptions() { try { const files = await readdir(LOGOS_DIRECTORY, { withFileTypes: true }); @@ -21,3 +18,8 @@ export async function loadLogoOptions() { return []; } } + +export async function loadLogoOptions() { + logoOptionsPromise ??= readLogoOptions(); + return logoOptionsPromise; +}