mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 19:01:47 +00:00
feat: reformula landing page e experiência mobile
This commit is contained in:
@@ -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 (
|
||||
|
||||
107
src/features/landing/components/screenshot-tabs.tsx
Normal file
107
src/features/landing/components/screenshot-tabs.tsx
Normal file
@@ -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 (
|
||||
<Tabs defaultValue="lancamentos" className="w-full">
|
||||
<div className="flex flex-col gap-6">
|
||||
<TabsList className="w-full h-auto flex-wrap gap-1">
|
||||
{sections.map((s) => (
|
||||
<TabsTrigger
|
||||
key={s.value}
|
||||
value={s.value}
|
||||
className="flex-1 gap-1.5 lowercase"
|
||||
>
|
||||
<s.icon className="size-4" />
|
||||
{s.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
{sections.map((s) => (
|
||||
<TabsContent key={s.value} value={s.value} className="w-full mt-0">
|
||||
<div className="rounded-lg overflow-hidden border bg-card">
|
||||
<div className="flex items-center gap-1.5 px-3 h-8 border-b bg-muted/50">
|
||||
<div className="size-2.5 rounded-full bg-muted-foreground/20" />
|
||||
<div className="size-2.5 rounded-full bg-muted-foreground/20" />
|
||||
<div className="size-2.5 rounded-full bg-muted-foreground/20" />
|
||||
<div className="ml-2 flex-1 max-w-52 h-4 rounded bg-muted-foreground/10" />
|
||||
</div>
|
||||
<Image
|
||||
src={s.light}
|
||||
alt={`Preview ${s.label}`}
|
||||
width={1920}
|
||||
height={1080}
|
||||
className="w-full h-auto dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src={s.dark}
|
||||
alt={`Preview ${s.label}`}
|
||||
width={1920}
|
||||
height={1080}
|
||||
className="w-full h-auto hidden dark:block"
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
))}
|
||||
</div>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
292
src/features/landing/constants.ts
Normal file
292
src/features/landing/constants.ts
Normal file
@@ -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)",
|
||||
},
|
||||
];
|
||||
}
|
||||
59
src/features/landing/images.ts
Normal file
59
src/features/landing/images.ts
Normal file
@@ -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;
|
||||
19
src/features/landing/queries.ts
Normal file
19
src/features/landing/queries.ts
Normal file
@@ -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 };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user