ZERADO
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
<!-- src/views/pages/public/LandingPage.vue -->
|
||||
<template>
|
||||
<div class="min-h-screen bg-[var(--surface-ground)] text-[var(--text-color)]">
|
||||
<!-- TOPBAR -->
|
||||
@@ -5,46 +6,65 @@
|
||||
class="sticky top-0 z-40 border-b border-[var(--surface-border)] bg-[color-mix(in_srgb,var(--surface-card),transparent_12%)] backdrop-blur"
|
||||
>
|
||||
<div class="mx-auto max-w-7xl px-4 md:px-6 py-3 flex items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<button class="flex items-center gap-3 min-w-0" @click="scrollTo('top')">
|
||||
<div
|
||||
class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-card)] grid place-items-center shadow-sm"
|
||||
>
|
||||
<i class="pi pi-sparkles text-lg opacity-80" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="min-w-0 text-left">
|
||||
<div class="font-semibold leading-tight truncate">{{ brandName }}</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] truncate">Gestão clínica sem ruído.</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] truncate">
|
||||
Gestão clínica sem ruído.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<Button label="Entrar" icon="pi pi-sign-in" severity="secondary" outlined @click="go('/auth/login')" />
|
||||
<Button label="Começar" icon="pi pi-bolt" @click="goStart()" />
|
||||
<Button
|
||||
label="Entrar"
|
||||
icon="pi pi-sign-in"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="go('/auth/login')"
|
||||
/>
|
||||
<Button
|
||||
label="Começar"
|
||||
icon="pi pi-bolt"
|
||||
@click="goStart()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="relative overflow-hidden">
|
||||
<!-- blobs / noir glow -->
|
||||
<section id="top" class="relative overflow-hidden">
|
||||
<!-- background: noir grid + glow -->
|
||||
<div class="pointer-events-none absolute inset-0">
|
||||
<div class="hero-grid absolute inset-0 opacity-[0.35]" />
|
||||
<div class="absolute -top-28 -left-28 h-96 w-96 rounded-full blur-3xl opacity-60 bg-indigo-400/10" />
|
||||
<div class="absolute top-24 -right-24 h-[28rem] w-[28rem] rounded-full blur-3xl opacity-60 bg-emerald-400/10" />
|
||||
<div class="absolute top-20 -right-24 h-[28rem] w-[28rem] rounded-full blur-3xl opacity-60 bg-emerald-400/10" />
|
||||
<div class="absolute -bottom-40 left-1/3 h-[34rem] w-[34rem] rounded-full blur-3xl opacity-60 bg-fuchsia-400/10" />
|
||||
<div class="hero-noise absolute inset-0 opacity-[0.12]" />
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-4 md:px-6 pt-10 md:pt-16 pb-8 md:pb-14 relative">
|
||||
<div class="mx-auto max-w-7xl px-4 md:px-6 pt-10 md:pt-16 pb-10 md:pb-14 relative">
|
||||
<div class="grid grid-cols-12 gap-6 items-center">
|
||||
<div class="col-span-12 lg:col-span-7">
|
||||
<Chip class="mb-4" label="Para psicólogos e clínicas" icon="pi pi-shield" />
|
||||
<div class="flex flex-wrap items-center gap-2 mb-4">
|
||||
<Chip label="Para psicólogos e clínicas" icon="pi pi-shield" />
|
||||
<span class="text-xs text-[var(--text-color-secondary)]">
|
||||
• menos dispersão • mais presença • mais previsibilidade
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl md:text-5xl font-semibold leading-tight">
|
||||
Uma agenda inteligente, um prontuário organizado, um financeiro respirável.
|
||||
Um sistema que <span class="hero-underline">reduz ruído</span> — sem roubar seu método.
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 text-base md:text-lg text-[var(--text-color-secondary)] max-w-2xl">
|
||||
Centralize a rotina clínica em um lugar só: pacientes, sessões, lembretes e indicadores. Menos dispersão.
|
||||
Mais presença.
|
||||
<p class="mt-4 text-base md:text-lg text-[var(--text-color-secondary)] max-w-2xl leading-relaxed">
|
||||
Centralize a rotina clínica em um lugar só: pacientes, sessões, lembretes e indicadores.
|
||||
O objetivo não é “burocratizar”: é deixar o consultório respirável.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex flex-col sm:flex-row gap-2">
|
||||
@@ -62,6 +82,14 @@
|
||||
class="w-full sm:w-auto"
|
||||
@click="scrollTo('pricing')"
|
||||
/>
|
||||
<Button
|
||||
label="Como funciona"
|
||||
icon="pi pi-compass"
|
||||
severity="secondary"
|
||||
text
|
||||
class="w-full sm:w-auto"
|
||||
@click="scrollTo('how')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex flex-wrap gap-2">
|
||||
@@ -69,18 +97,25 @@
|
||||
<Tag severity="secondary" value="Controle de sessões" />
|
||||
<Tag severity="secondary" value="Financeiro integrado" />
|
||||
<Tag severity="secondary" value="Clínica / multi-profissional" />
|
||||
<Tag severity="secondary" value="Separação por tenant + RLS" />
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-xs text-[var(--text-color-secondary)]">
|
||||
“A diferença entre ter uma agenda e ter um sistema mora nos detalhes.”
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-5">
|
||||
<Card class="overflow-hidden">
|
||||
<Card class="overflow-hidden rounded-[2rem] border border-[var(--surface-border)]">
|
||||
<template #content>
|
||||
<div class="p-1">
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] p-4">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="font-semibold text-lg">Painel de hoje</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)]">Um recorte: o essencial, sem excesso.</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)]">
|
||||
Um recorte: o essencial, sem excesso.
|
||||
</div>
|
||||
</div>
|
||||
<i class="pi pi-chart-line opacity-70" />
|
||||
</div>
|
||||
@@ -92,7 +127,9 @@
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-card)] p-3">
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">Sessões</div>
|
||||
<div class="text-2xl font-semibold mt-1">6</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">com lembretes automáticos</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
com lembretes automáticos
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -100,18 +137,20 @@
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-card)] p-3">
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">Recebimentos</div>
|
||||
<div class="text-2xl font-semibold mt-1">R$ 840</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">visão clara do mês</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
visão clara do mês
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12">
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-card)] p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="min-w-0">
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">Prontuário</div>
|
||||
<div class="font-semibold mt-1">Anotações e histórico</div>
|
||||
<div class="font-semibold mt-1 truncate">Anotações e histórico</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
organizado por paciente, sessão e linha do tempo
|
||||
por paciente • por sessão • linha do tempo
|
||||
</div>
|
||||
</div>
|
||||
<i class="pi pi-file-edit opacity-70" />
|
||||
@@ -127,21 +166,40 @@
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<div class="mt-4 grid grid-cols-12 gap-3">
|
||||
<div class="col-span-12 sm:col-span-6">
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[color-mix(in_srgb,var(--surface-card),transparent_10%)] p-4">
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">Promessa</div>
|
||||
<div class="font-semibold mt-1">Organizar sem invadir.</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
Você define o método. O sistema remove ruído.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-12 sm:col-span-6">
|
||||
<div class="rounded-2xl border border-[var(--surface-border)] bg-[color-mix(in_srgb,var(--surface-card),transparent_10%)] p-4">
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">Arquitetura</div>
|
||||
<div class="font-semibold mt-1">Multi-tenant de verdade.</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
Menus + guards + RLS alinhados.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TRUST / VALUE STRIP -->
|
||||
<!-- VALUE STRIP -->
|
||||
<section class="mx-auto max-w-7xl px-4 md:px-6 pb-10">
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center"
|
||||
>
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-calendar opacity-80" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -156,12 +214,10 @@
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center"
|
||||
>
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-wallet opacity-80" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -176,18 +232,16 @@
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center"
|
||||
>
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-lock opacity-80" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold">Prontuário e controle de sessões</div>
|
||||
<div class="font-semibold">Prontuário e sessões</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
Registro clínico e histórico acessíveis, com backups e organização.
|
||||
Registro e histórico acessíveis, com organização e backup.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,40 +251,82 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-xs text-[var(--text-color-secondary)]">
|
||||
Inspirações de módulos comuns no mercado: agenda online, financeiro, prontuário/controle de sessões e gestão de
|
||||
clínica.
|
||||
Módulos: agenda, prontuário/sessões, financeiro e gestão multi-profissional.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section class="mx-auto max-w-7xl px-4 md:px-6 pb-12">
|
||||
<!-- HOW IT WORKS -->
|
||||
<section id="how" class="mx-auto max-w-7xl px-4 md:px-6 pb-12 scroll-mt-24">
|
||||
<div class="flex items-end justify-between gap-3 mb-4">
|
||||
<div>
|
||||
<div class="text-2xl md:text-3xl font-semibold">Recursos que sustentam a rotina</div>
|
||||
<div class="text-2xl md:text-3xl font-semibold">Como funciona</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
O foco é tirar o excesso de fricção sem invadir o que é do seu método.
|
||||
Três movimentos: preparar, atender, acompanhar — sem fricção.
|
||||
</div>
|
||||
</div>
|
||||
<Button label="Ver planos" severity="secondary" outlined icon="pi pi-arrow-down" @click="scrollTo('pricing')" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div v-for="f in features" :key="f.title" class="col-span-12 md:col-span-6 lg:col-span-4">
|
||||
<Card class="h-full">
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center"
|
||||
>
|
||||
<i :class="f.icon" class="opacity-80" />
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-sliders-h opacity-80" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="font-semibold">{{ f.title }}</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
{{ f.desc }}
|
||||
<div>
|
||||
<div class="font-semibold">1) Preparar</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1 leading-relaxed">
|
||||
Configure agenda, encaixes e regras. Se quiser, habilite autoagendamento (PRO).
|
||||
</div>
|
||||
<div v-if="f.pro" class="mt-2">
|
||||
<Tag severity="warning" value="PRO" />
|
||||
<div class="mt-2">
|
||||
<Tag severity="secondary" value="Bloqueios" />
|
||||
<Tag class="ml-2" severity="secondary" value="Encaixes" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-comments opacity-80" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold">2) Atender</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1 leading-relaxed">
|
||||
Sessão acontece. Registro fica onde precisa ficar: no prontuário, no tempo certo.
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Tag severity="secondary" value="Sessões" />
|
||||
<Tag class="ml-2" severity="secondary" value="Prontuário" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i class="pi pi-chart-bar opacity-80" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold">3) Acompanhar</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1 leading-relaxed">
|
||||
Financeiro e indicadores acompanham o movimento. Menos “cadê?”, mais previsibilidade.
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Tag severity="secondary" value="Recebimentos" />
|
||||
<Tag class="ml-2" severity="secondary" value="Indicadores" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,58 +340,71 @@
|
||||
<Accordion :activeIndex="0">
|
||||
<AccordionTab header="Como fica o fluxo na prática?">
|
||||
<div class="text-sm text-[var(--text-color-secondary)] leading-relaxed">
|
||||
Você abre a agenda, a sessão acontece, o registro fica no prontuário, e o financeiro acompanha o movimento.
|
||||
Você abre a agenda, a sessão acontece, o registro vai para o prontuário, e o financeiro acompanha.
|
||||
O sistema existe para manter o consultório respirando — não para virar uma burocracia nova.
|
||||
</div>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="E para clínica (multi-profissionais)?">
|
||||
<div class="text-sm text-[var(--text-color-secondary)] leading-relaxed">
|
||||
Perfis por função, agendas separadas, repasses e visão gerencial — quando você estiver pronto para crescer.
|
||||
Perfis por função, agendas separadas, visão gerencial e convites (Modelo B).
|
||||
Você cresce sem quebrar a estrutura.
|
||||
</div>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Privacidade e segurança">
|
||||
<div class="text-sm text-[var(--text-color-secondary)] leading-relaxed">
|
||||
Controle de acesso por conta, separação por clínica/tenant, e políticas de storage por usuário. (Os detalhes
|
||||
de conformidade você pode expor numa página própria de segurança/LGPD.)
|
||||
Separação por clínica/tenant, controle de acesso e políticas no banco (RLS).
|
||||
(Quando quiser, a gente cria uma página dedicada de LGPD/Segurança.)
|
||||
</div>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section class="mx-auto max-w-7xl px-4 md:px-6 pb-12">
|
||||
<div class="flex items-end justify-between gap-3 mb-4">
|
||||
<div>
|
||||
<div class="text-2xl md:text-3xl font-semibold">Recursos que sustentam a rotina</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
O foco é tirar fricção — sem invadir o que é do seu método.
|
||||
</div>
|
||||
</div>
|
||||
<Button label="Ver planos" severity="secondary" outlined icon="pi pi-arrow-down" @click="scrollTo('pricing')" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div v-for="f in features" :key="f.title" class="col-span-12 md:col-span-6 lg:col-span-4">
|
||||
<Card class="h-full rounded-[2rem]">
|
||||
<template #content>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="h-10 w-10 rounded-2xl border border-[var(--surface-border)] bg-[var(--surface-ground)] grid place-items-center">
|
||||
<i :class="f.icon" class="opacity-80" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="font-semibold">{{ f.title }}</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1 leading-relaxed">
|
||||
{{ f.desc }}
|
||||
</div>
|
||||
<div v-if="f.pro" class="mt-2">
|
||||
<Tag severity="warning" value="PRO" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRICING (dinâmico do SaaS) -->
|
||||
<section id="pricing" class="mx-auto max-w-7xl px-4 md:px-6 pb-14 scroll-mt-24">
|
||||
<div class="text-5xl md:text-4xl font-semibold text-center">Planos</div>
|
||||
<div class="text-2xl md:text-2xl text-[var(--text-color-secondary)] mt-1 text-center">
|
||||
<div class="text-3xl md:text-4xl font-semibold text-center">Planos</div>
|
||||
<div class="text-base md:text-lg text-[var(--text-color-secondary)] mt-2 text-center">
|
||||
Comece simples. Suba para PRO quando a agenda pedir automação.
|
||||
</div>
|
||||
|
||||
<!-- header conceitual + toggle -->
|
||||
<!-- toggle -->
|
||||
<div class="flex flex-col items-center text-center mt-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<AvatarGroup>
|
||||
<Avatar
|
||||
image="https://fqjltiegiezfetthbags.supabase.co/storage/v1/render/image/public/block.images/blocks/avatars/circle/avatar-m-1.png"
|
||||
shape="circle"
|
||||
/>
|
||||
<Avatar
|
||||
image="https://fqjltiegiezfetthbags.supabase.co/storage/v1/render/image/public/block.images/blocks/avatars/circle/avatar-f-21.png"
|
||||
shape="circle"
|
||||
/>
|
||||
<Avatar
|
||||
image="https://fqjltiegiezfetthbags.supabase.co/storage/v1/render/image/public/block.images/blocks/avatars/circle/avatar-f-1.png"
|
||||
shape="circle"
|
||||
/>
|
||||
<Avatar
|
||||
image="https://fqjltiegiezfetthbags.supabase.co/storage/v1/render/image/public/block.images/blocks/avatars/circle/avatar-m-3.png"
|
||||
shape="circle"
|
||||
/>
|
||||
</AvatarGroup>
|
||||
|
||||
<Divider layout="vertical" />
|
||||
<span class="text-sm text-[var(--text-color-secondary)] font-medium">Happy Customers</span>
|
||||
</div>
|
||||
|
||||
<div class="inline-flex items-center rounded-xl border border-[var(--surface-border)] bg-[var(--surface-50)] p-1">
|
||||
<div class="inline-flex items-center rounded-xl border border-[var(--surface-border)] bg-[color-mix(in_srgb,var(--surface-card),transparent_10%)] p-1">
|
||||
<Button
|
||||
label="Mensal"
|
||||
size="small"
|
||||
@@ -312,95 +421,132 @@
|
||||
@click="billingInterval = 'year'"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="billingInterval === 'year'" class="mt-2">
|
||||
<Tag severity="success" value="Economize até 20%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingPricing" class="mt-8 text-sm text-[var(--text-color-secondary)]">
|
||||
Carregando planos...
|
||||
</div>
|
||||
|
||||
<div v-else class="mt-8 grid grid-cols-12 gap-4">
|
||||
<div v-for="p in pricing" :key="p.plan_id" class="col-span-12 md:col-span-4">
|
||||
<Card
|
||||
class="h-full overflow-hidden transition-transform"
|
||||
:class="p.is_featured ? 'ring-1 ring-emerald-500/30 md:-translate-y-2 md:scale-[1.02]' : ''"
|
||||
>
|
||||
<template #content>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)]">
|
||||
{{ p.badge || 'Plano' }}
|
||||
</div>
|
||||
<div class="text-xl font-semibold">
|
||||
{{ p.public_name || p.plan_name || p.plan_key }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tag v-if="p.is_featured" severity="success" value="Popular" />
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-3xl font-semibold leading-none">
|
||||
{{ formatBRLFromCents(priceFor(p)) }}
|
||||
<span class="text-sm font-normal text-[var(--text-color-secondary)]">
|
||||
/{{ billingInterval === 'month' ? 'mês' : 'ano' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="billingInterval === 'year'"
|
||||
class="text-xs text-emerald-500 mt-1 font-medium"
|
||||
>
|
||||
Melhor custo-benefício
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-sm text-[var(--text-color-secondary)] min-h-[44px]">
|
||||
{{ p.public_description }}
|
||||
</div>
|
||||
|
||||
<Divider class="my-4" />
|
||||
|
||||
<ul v-if="p.bullets?.length" class="space-y-2 text-sm">
|
||||
<li v-for="b in p.bullets" :key="b.id" class="flex items-start gap-2">
|
||||
<i class="pi pi-check mt-1 text-emerald-500"></i>
|
||||
<span :class="b.highlight ? 'font-semibold' : 'text-[var(--text-color-secondary)]'">
|
||||
{{ b.text }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-else class="text-sm text-[var(--text-color-secondary)]">Benefícios em breve.</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<Button
|
||||
label="Começar"
|
||||
class="w-full"
|
||||
:severity="p.is_featured ? 'success' : 'secondary'"
|
||||
:outlined="!p.is_featured"
|
||||
icon="pi pi-arrow-right"
|
||||
@click="go(`/auth/signup?plan=${p.plan_key}&interval=${billingInterval}`)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
<div v-if="billingInterval === 'year'" class="mt-2">
|
||||
<Tag severity="success" value="Economize até 20%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-xs text-[var(--text-color-secondary)]">
|
||||
Dica: estes planos vêm do painel SaaS (vitrine pública) e podem mapear diretamente para entitlements (FREE/PRO)
|
||||
sem mexer no código.
|
||||
<div v-if="loadingPricing" class="mt-8">
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div v-for="i in 3" :key="i" class="col-span-12 md:col-span-4">
|
||||
<div class="rounded-[2rem] border border-[var(--surface-border)] bg-[var(--surface-card)] p-5">
|
||||
<div class="h-4 w-24 bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded mb-3 animate-pulse" />
|
||||
<div class="h-7 w-40 bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded mb-4 animate-pulse" />
|
||||
<div class="h-10 w-48 bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded mb-4 animate-pulse" />
|
||||
<div class="space-y-2">
|
||||
<div class="h-3 w-full bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded animate-pulse" />
|
||||
<div class="h-3 w-11/12 bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded animate-pulse" />
|
||||
<div class="h-3 w-10/12 bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded animate-pulse" />
|
||||
</div>
|
||||
<div class="h-10 w-full bg-[color-mix(in_srgb,var(--surface-200),transparent_40%)] rounded-xl mt-6 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="mt-8">
|
||||
<div v-if="!pricing.length" class="rounded-[2rem] border border-[var(--surface-border)] bg-[var(--surface-card)] p-6 text-center">
|
||||
<div class="text-lg font-semibold">Planos em preparação</div>
|
||||
<div class="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
Ainda não há itens visíveis na vitrine pública. Publique no painel SaaS para aparecer aqui.
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center gap-2 flex-wrap">
|
||||
<Button label="Entrar" severity="secondary" outlined icon="pi pi-sign-in" @click="go('/auth/login')" />
|
||||
<Button label="Criar conta" icon="pi pi-bolt" @click="goStart()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-12 gap-4">
|
||||
<div v-for="p in pricing" :key="p.plan_id" class="col-span-12 md:col-span-4">
|
||||
<div
|
||||
class="h-full rounded-[2rem] border border-[var(--surface-border)] bg-[var(--surface-card)] overflow-hidden transition-transform duration-300"
|
||||
:class="p.is_featured ? 'ring-1 ring-emerald-500/30 md:-translate-y-2 md:scale-[1.02]' : 'hover:-translate-y-1'"
|
||||
>
|
||||
<div class="relative p-5">
|
||||
<div v-if="p.is_featured" class="pointer-events-none absolute inset-0 opacity-70">
|
||||
<div class="absolute -top-20 -right-24 h-72 w-72 rounded-full bg-emerald-400/10 blur-3xl" />
|
||||
<div class="absolute -bottom-24 left-10 h-72 w-72 rounded-full bg-indigo-400/10 blur-3xl" />
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm text-[var(--text-color-secondary)]">
|
||||
{{ p.badge || 'Plano' }}
|
||||
</div>
|
||||
<div class="text-xl font-semibold truncate">
|
||||
{{ p.public_name || p.plan_name || p.plan_key }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tag v-if="p.is_featured" severity="success" value="Popular" />
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-3xl font-semibold leading-none">
|
||||
{{ formatBRLFromCents(priceFor(p)) }}
|
||||
<span class="text-sm font-normal text-[var(--text-color-secondary)]">
|
||||
/{{ billingInterval === 'month' ? 'mês' : 'ano' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="billingInterval === 'year'" class="text-xs text-emerald-500 mt-1 font-medium">
|
||||
Melhor custo-benefício
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-sm text-[var(--text-color-secondary)] min-h-[44px] leading-relaxed">
|
||||
{{ p.public_description || '—' }}
|
||||
</div>
|
||||
|
||||
<Divider class="my-4" />
|
||||
|
||||
<ul v-if="p.bullets?.length" class="space-y-2 text-sm">
|
||||
<li v-for="b in p.bullets" :key="b.id" class="flex items-start gap-2">
|
||||
<i class="pi pi-check mt-1 text-emerald-500"></i>
|
||||
<span :class="b.highlight ? 'font-semibold' : 'text-[var(--text-color-secondary)]'">
|
||||
{{ b.text }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-else class="text-sm text-[var(--text-color-secondary)]">
|
||||
Benefícios em breve.
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<Button
|
||||
label="Começar"
|
||||
class="w-full"
|
||||
:severity="p.is_featured ? 'success' : 'secondary'"
|
||||
:outlined="!p.is_featured"
|
||||
icon="pi pi-arrow-right"
|
||||
@click="go(`/auth/signup?plan=${encodeURIComponent(p.plan_key)}&interval=${billingInterval}`)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-xs text-[var(--text-color-secondary)]">
|
||||
Plano vem da vitrine SaaS — sem mexer no código.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-xs text-[var(--text-color-secondary)]">
|
||||
Dica: esses planos podem mapear diretamente para entitlements (FREE/PRO) e features (plan_features).
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="border-t border-[var(--surface-border)]">
|
||||
<div
|
||||
class="mx-auto max-w-7xl px-4 md:px-6 py-8 flex flex-col md:flex-row items-start md:items-center justify-between gap-4"
|
||||
>
|
||||
<div class="mx-auto max-w-7xl px-4 md:px-6 py-8 flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="font-semibold">{{ brandName }}</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">© {{ year }} — Todos os direitos reservados.</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)] mt-1">
|
||||
© {{ year }} — Todos os direitos reservados.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@@ -417,77 +563,31 @@ import { computed, ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
|
||||
import Button from 'primevue/button'
|
||||
import Card from 'primevue/card'
|
||||
import Divider from 'primevue/divider'
|
||||
import Tag from 'primevue/tag'
|
||||
import Chip from 'primevue/chip'
|
||||
import Accordion from 'primevue/accordion'
|
||||
import AccordionTab from 'primevue/accordiontab'
|
||||
import Avatar from 'primevue/avatar'
|
||||
import AvatarGroup from 'primevue/avatargroup'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const brandName = 'Psi Quasar' // ajuste para o nome final do produto
|
||||
const brandName = 'Agência PSI' // ajuste para o nome final do produto
|
||||
const year = computed(() => new Date().getFullYear())
|
||||
|
||||
function go(path) {
|
||||
function go (path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function scrollTo(id) {
|
||||
function scrollTo (id) {
|
||||
const el = document.getElementById(id)
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
|
||||
const featuredPlanKey = computed(() => {
|
||||
const list = Array.isArray(pricing.value) ? pricing.value : []
|
||||
const featured = list.find(p => p && p.is_featured && p.is_visible)
|
||||
return featured?.plan_key || null
|
||||
})
|
||||
|
||||
function goStart() {
|
||||
if (featuredPlanKey.value) {
|
||||
router.push(`/auth/signup?plan=${featuredPlanKey.value}&interval=${billingInterval.value}`)
|
||||
return
|
||||
}
|
||||
|
||||
router.push('/auth/signup')
|
||||
}
|
||||
|
||||
const features = ref([
|
||||
{
|
||||
title: 'Agenda inteligente',
|
||||
desc: 'Configure sua semana, encaixes, bloqueios e visão por dia/semana.',
|
||||
icon: 'pi pi-calendar'
|
||||
},
|
||||
{
|
||||
title: 'Autoagendamento (PRO)',
|
||||
desc: 'Página para o paciente confirmar, agendar e reagendar sem fricção.',
|
||||
icon: 'pi pi-globe',
|
||||
pro: true
|
||||
},
|
||||
{
|
||||
title: 'Prontuário e sessões',
|
||||
desc: 'Registro por paciente, histórico por sessão e organização por linha do tempo.',
|
||||
icon: 'pi pi-file-edit'
|
||||
},
|
||||
{
|
||||
title: 'Financeiro integrado',
|
||||
desc: 'Receitas, despesas e visão do mês conectadas ao que acontece na agenda.',
|
||||
icon: 'pi pi-wallet'
|
||||
},
|
||||
{
|
||||
title: 'Pacientes e tags',
|
||||
desc: 'Segmentação por grupos, etiquetas e filtros práticos para achar rápido.',
|
||||
icon: 'pi pi-users'
|
||||
},
|
||||
{
|
||||
title: 'Clínica / multi-profissional',
|
||||
desc: 'Múltiplos profissionais, agendas separadas, papéis e visão gerencial.',
|
||||
icon: 'pi pi-building'
|
||||
}
|
||||
{ title: 'Agenda inteligente', desc: 'Configure semana, encaixes, bloqueios e visão por dia/semana.', icon: 'pi pi-calendar' },
|
||||
{ title: 'Autoagendamento (PRO)', desc: 'Página para o paciente confirmar, agendar e reagendar sem fricção.', icon: 'pi pi-globe', pro: true },
|
||||
{ title: 'Prontuário e sessões', desc: 'Registro por paciente, histórico por sessão e linha do tempo.', icon: 'pi pi-file-edit' },
|
||||
{ title: 'Financeiro integrado', desc: 'Receitas e despesas conectadas ao que acontece na agenda.', icon: 'pi pi-wallet' },
|
||||
{ title: 'Pacientes e tags', desc: 'Segmentação por grupos, etiquetas e filtros para achar rápido.', icon: 'pi pi-users' },
|
||||
{ title: 'Clínica / multi-profissional', desc: 'Múltiplos profissionais, papéis, convites e visão gerencial.', icon: 'pi pi-building' }
|
||||
])
|
||||
|
||||
/** PRICING dinâmico do SaaS */
|
||||
@@ -495,29 +595,82 @@ const billingInterval = ref('year') // 'month' | 'year'
|
||||
const pricing = ref([])
|
||||
const loadingPricing = ref(false)
|
||||
|
||||
function formatBRLFromCents(cents) {
|
||||
const featuredPlanKey = computed(() => {
|
||||
const list = Array.isArray(pricing.value) ? pricing.value : []
|
||||
const featured = list.find(p => p && p.is_featured && p.is_visible)
|
||||
return featured?.plan_key || null
|
||||
})
|
||||
|
||||
function goStart () {
|
||||
if (featuredPlanKey.value) {
|
||||
router.push(`/auth/signup?plan=${encodeURIComponent(featuredPlanKey.value)}&interval=${billingInterval.value}`)
|
||||
return
|
||||
}
|
||||
router.push('/auth/signup')
|
||||
}
|
||||
|
||||
function formatBRLFromCents (cents) {
|
||||
if (cents == null) return '—'
|
||||
const v = Number(cents) / 100
|
||||
if (!Number.isFinite(v)) return '—'
|
||||
return v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })
|
||||
}
|
||||
|
||||
function priceFor(p) {
|
||||
return billingInterval.value === 'year' ? p.yearly_cents : p.monthly_cents
|
||||
function priceFor (p) {
|
||||
if (!p) return null
|
||||
const cents = billingInterval.value === 'year' ? p.yearly_cents : p.monthly_cents
|
||||
// fallback: se não existir anual, mostra mensal (e vice-versa)
|
||||
if (cents == null) return billingInterval.value === 'year' ? p.monthly_cents : p.yearly_cents
|
||||
return cents
|
||||
}
|
||||
|
||||
async function fetchPricing() {
|
||||
async function fetchPricing () {
|
||||
loadingPricing.value = true
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('v_public_pricing')
|
||||
.select('*')
|
||||
.eq('is_visible', true)
|
||||
.order('sort_order', { ascending: true })
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('v_public_pricing')
|
||||
.select('*')
|
||||
.eq('is_visible', true)
|
||||
.order('sort_order', { ascending: true })
|
||||
|
||||
loadingPricing.value = false
|
||||
|
||||
if (!error) pricing.value = data || []
|
||||
if (!error) pricing.value = data || []
|
||||
} finally {
|
||||
loadingPricing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchPricing)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero-grid {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(255,255,255,0.06) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(255,255,255,0.06) 1px, transparent 1px);
|
||||
background-size: 44px 44px;
|
||||
mask-image: radial-gradient(circle at 30% 20%, black 0%, transparent 65%);
|
||||
}
|
||||
|
||||
.hero-noise {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.35'/%3E%3C/svg%3E");
|
||||
background-size: 180px 180px;
|
||||
}
|
||||
|
||||
.hero-underline {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hero-underline::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -2%;
|
||||
right: -2%;
|
||||
bottom: 0.12em;
|
||||
height: 0.5em;
|
||||
background: linear-gradient(90deg, rgba(16,185,129,0.0), rgba(16,185,129,0.22), rgba(99,102,241,0.18), rgba(16,185,129,0.0));
|
||||
border-radius: 999px;
|
||||
z-index: -1;
|
||||
filter: blur(0.2px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user