NALU/src/Nalu.Api/Pages/Index.cshtml
Ricardo Carneiro ea6cdb5395 Initial commit — NALU AI web platform
- ASP.NET Core 9 Razor Pages + Minimal API hybrid
- 14 validators (CPF, CEP, CNPJ, email, phone, name, yes-no, birthdate, handoff, cancel-intent, company-name, plate-br, postal-code, validate_reply)
- OAuth login (Google, Microsoft, GitHub) + cookie auth
- MongoDB usage tracking + CEP cache collection
- Stripe checkout with inline PriceData (no Price IDs)
- MCP server for Claude Code / Cursor integration
- Playground (10 calls/IP/day, no auth)
- Docs: Quickstart, API Reference, N8N, MCP, Créditos, Erros, Fluxos
- Credit system: 3 cr standard validators, 5 cr validate_reply
- SmartSuggestion: contextual re-ask via IA when obtained=false
- Per-IP rate limiting + daily cap + shared-IP abuse detection
- Lightbox for comic images
- Validadores page split: Brasileiros / Universais + Em breve

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:39:04 -03:00

396 lines
22 KiB
Plaintext

@page
@model IndexModel
@{
ViewData["Title"] = "Seu chatbot está gravando 'Bom Dia' como nome";
ViewData["Description"] = "NALU AI extrai dados reais de diálogos agente/usuário. CPF, nome, CEP, parcelas. A partir de R$ 0,0019 por validação.";
}
<!-- ── 1. HERO ─────────────────────────────────────────────────────────────── -->
<section class="bg-gradient-to-b from-slate-50 to-white pt-20 pb-16">
<div class="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<div class="inline-flex items-center gap-2 bg-nalu-50 text-nalu-700 text-xs font-semibold px-3 py-1 rounded-full mb-6">
13 validadores · MCP + REST · 3.000 créditos grátis
</div>
<h1 class="text-4xl sm:text-5xl font-extrabold text-gray-900 leading-tight mb-6">
Seu chatbot está gravando<br>
<span class="text-nalu-600">"Bom Dia"</span> como nome do cliente.
</h1>
<p class="text-xl text-gray-500 mb-8 max-w-2xl mx-auto">
NALU AI extrai o que o usuário <em>realmente</em> disse — nome, CPF, CEP, parcelas —
sem confundir saudação com dado. Integra em 30 segundos.
</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-3 mb-8">
<a href="/precos" class="bg-nalu-600 text-white font-semibold px-6 py-3 rounded-xl hover:bg-nalu-700 transition-colors text-base">
Começar grátis →
</a>
<a href="/playground" class="border border-gray-300 text-gray-700 font-medium px-6 py-3 rounded-xl hover:border-nalu-600 hover:text-nalu-600 transition-colors text-base">
Testar no playground
</a>
</div>
<ul class="flex flex-col sm:flex-row items-center justify-center gap-4 text-sm text-gray-600 mb-8">
<li class="flex items-center gap-2">✓ 3.000 créditos grátis por mês</li>
<li class="flex items-center gap-2">✓ Setup em 30 segundos</li>
<li class="flex items-center gap-2">✓ Funciona com n8n, Make, Claude Code, Cursor</li>
</ul>
<div class="text-center">
<span class="text-3xl font-extrabold text-nalu-600">R$ 0,0019</span>
<span class="text-gray-500 ml-2 text-base">por validação no plano Starter.</span>
<p class="text-sm text-gray-400 mt-1">Menos de 1 centavo para nunca mais gravar "Bom Dia" como nome.</p>
</div>
</div>
</section>
<!-- ── 2. BEFORE / AFTER ─────────────────────────────────────────────────────── -->
<section class="py-16 bg-white">
<div class="max-w-5xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-12">O problema (que todo mundo já teve)</h2>
<!-- Example 1: Bom Dia -->
<div class="grid md:grid-cols-2 gap-6 mb-10">
<div class="bg-red-50 border border-red-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-red-500 uppercase tracking-wide mb-3">BOT TRADICIONAL</div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">Agente:</span> Olá! Qual o seu nome?</div>
<div><span class="text-gray-400">Usuário:</span> Bom dia! Me chamo João Silva</div>
<div class="mt-3 text-red-600 font-semibold">❌ Gravou: "Bom Dia Me Chamo João Silva"</div>
</div>
</div>
<div class="bg-green-50 border border-green-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-green-600 uppercase tracking-wide mb-3">COM NALU AI (validate_name)</div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">extracted_value:</span> <span class="text-green-700 font-bold">"João Silva"</span></div>
<div><span class="text-gray-400">certain:</span> true</div>
<div><span class="text-gray-400">confidence:</span> "high"</div>
</div>
<div class="mt-3 text-xs text-gray-500">Custo: R$ 0,0039 (2 créditos)</div>
</div>
</div>
<!-- Example 2: Parcelas 48x -->
<div class="grid md:grid-cols-2 gap-6 mb-10">
<div class="bg-red-50 border border-red-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-red-500 uppercase tracking-wide mb-3">BOT TRADICIONAL</div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">Agente:</span> Posso parcelar em 20x de R$100. Topa?</div>
<div><span class="text-gray-400">Usuário:</span> Bora em 48?</div>
<div class="mt-3 text-red-600 font-semibold">❌ Bot entendeu: R$ 48,00</div>
</div>
</div>
<div class="bg-green-50 border border-green-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-green-600 uppercase tracking-wide mb-3">COM NALU AI (validate_reply) <span class="bg-nalu-100 text-nalu-700 text-xs px-2 py-0.5 rounded-full ml-1">5 créditos</span></div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">reply_type:</span> <span class="text-green-700 font-bold">counter_proposal</span></div>
<div><span class="text-gray-400">extracted_value:</span> "48 parcelas"</div>
<div><span class="text-gray-400">suggestion:</span> "Cliente propõe 48 parcelas..."</div>
</div>
<div class="mt-3 text-xs text-gray-500">Custo: R$ 0,0097 (5 créditos)</div>
</div>
</div>
<!-- Example 3: CEP -->
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-red-50 border border-red-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-red-500 uppercase tracking-wide mb-3">BOT TRADICIONAL</div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">Agente:</span> Qual seu CEP?</div>
<div><span class="text-gray-400">Usuário:</span> É o 01310-100</div>
<div class="mt-3 text-red-600 font-semibold">❌ Regex falhou: "É o 01310-100" não é só dígitos</div>
</div>
</div>
<div class="bg-green-50 border border-green-100 rounded-2xl p-6">
<div class="text-xs font-semibold text-green-600 uppercase tracking-wide mb-3">COM NALU AI (validate_cep)</div>
<div class="font-mono text-sm space-y-2">
<div><span class="text-gray-400">extracted_value:</span> <span class="text-green-700 font-bold">"01310-100"</span></div>
<div><span class="text-gray-400">cidade:</span> "São Paulo"</div>
<div><span class="text-gray-400">bairro:</span> "Bela Vista"</div>
</div>
<div class="mt-3 text-xs text-gray-500">Custo: R$ 0,0019 (1 crédito)</div>
</div>
</div>
</div>
</section>
<!-- ── 3. HOW IT WORKS ──────────────────────────────────────────────────────── -->
<section class="py-16 bg-slate-50">
<div class="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<h2 class="text-3xl font-bold mb-12">Como funciona</h2>
<div class="grid md:grid-cols-3 gap-8">
<div class="bg-white rounded-2xl p-6 shadow-sm">
<div class="text-3xl mb-4">📨</div>
<h3 class="font-bold text-lg mb-2">1. Envie o diálogo</h3>
<p class="text-gray-500 text-sm">Agente + resposta do usuário. Dois campos. Nada mais.</p>
</div>
<div class="bg-white rounded-2xl p-6 shadow-sm">
<div class="text-3xl mb-4">🧠</div>
<h3 class="font-bold text-lg mb-2">2. NALU extrai</h3>
<p class="text-gray-500 text-sm">Regras determinísticas primeiro. LLM só quando necessário. Resultado normalizado.</p>
</div>
<div class="bg-white rounded-2xl p-6 shadow-sm">
<div class="text-3xl mb-4">✅</div>
<h3 class="font-bold text-lg mb-2">3. Use o dado limpo</h3>
<p class="text-gray-500 text-sm"><code class="text-nalu-600">obtained: true</code> + valor validado. Sem regex, sem alucinação.</p>
</div>
</div>
</div>
</section>
<!-- ── 4. COMIC 1 PLACEHOLDER ─────────────────────────────────────────────── -->
<section class="py-8 bg-white">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<img src="/images/comic-1-pt.webp" alt="Quadrinho: bot gravando Bom Dia como nome" class="rounded-2xl w-full shadow-md" onerror="this.style.display='none'" />
</div>
</section>
<!-- ── 5. VALIDATOR CATALOG ───────────────────────────────────────────────── -->
<section class="py-16 bg-slate-50">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-4">13 validadores prontos</h2>
<p class="text-center text-gray-500 mb-10">Determinísticos + LLM leve + análise de contexto (70B)</p>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
@foreach (var v in Model.Validators)
{
<div class="bg-white border border-gray-100 rounded-xl p-5 hover:border-nalu-300 transition-colors @(v.IsNew ? "ring-2 ring-nalu-400" : "")">
<div class="flex items-start justify-between mb-2">
<div class="font-semibold text-gray-800 text-sm">@v.Icon @v.Name</div>
<div class="flex items-center gap-1">
@if (v.IsNew)
{
<span class="bg-nalu-500 text-white text-xs px-2 py-0.5 rounded-full font-semibold">NEW</span>
}
<span class="bg-gray-100 text-gray-500 text-xs px-2 py-0.5 rounded-full">@v.Credits cr</span>
</div>
</div>
<p class="text-xs text-gray-500 leading-relaxed">@v.Description</p>
</div>
}
</div>
<div class="text-center mt-8">
<a href="/validadores" class="text-nalu-600 font-medium text-sm hover:underline">Ver todos os validadores →</a>
</div>
</div>
</section>
<!-- ── 6. STATE MACHINE ───────────────────────────────────────────────────── -->
<section class="py-16 bg-white">
<div class="max-w-4xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-4">Máquina de estados inteligente</h2>
<p class="text-center text-gray-500 mb-10">Validadores podem ser encadeados em fluxos completos.</p>
<div class="bg-slate-50 rounded-2xl p-6 font-mono text-sm overflow-x-auto">
<pre class="text-gray-600 leading-relaxed">
[validate_name] → confirma identidade (2 créditos)
[validate_reply] → "20x de R$100, topa?" (5 créditos)
↓ reply_type: counter_proposal
→ extracted: 48 parcelas
→ agente avalia se pode oferecer 48x
↓ reply_type: confirmation → prossegue
↓ reply_type: rejection → oferece alternativa
↓ reply_type: handoff → transfere para humano
Custo total: 7 créditos = <span class="text-nalu-600 font-bold">R$ 0,0133</span> no Starter.
Mais barato que perder a venda.</pre>
</div>
</div>
</section>
<!-- ── 7. COMIC 2 PLACEHOLDER ─────────────────────────────────────────────── -->
<section class="py-8 bg-white">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<p class="text-center text-sm text-gray-500 mb-4">
Com o <strong>validate_reply</strong> do NALU AI, o bot entende que "48" no contexto
de uma oferta de parcelas é uma contraproposta — não um valor.
Custo por análise: <strong>R$ 0,0097</strong>. Menos que o cafezinho.
</p>
<img src="/images/comic-2-pt.webp" alt="Quadrinho: 48 parcelas vs R$48" class="rounded-2xl w-full shadow-md" onerror="this.style.display='none'" />
</div>
</section>
<!-- ── 8. CODE SNIPPETS ───────────────────────────────────────────────────── -->
<section class="py-16 bg-slate-900 text-white">
<div class="max-w-4xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-10">Integração em 30 segundos</h2>
<div x-data="{ tab: 'curl' }" class="space-y-4">
<div class="flex gap-2 text-sm font-medium overflow-x-auto pb-2">
<button onclick="showTab('curl')" id="tab-curl" class="tab-btn tab-active px-4 py-2 rounded-lg bg-slate-700 text-white">cURL</button>
<button onclick="showTab('reply')" id="tab-reply" class="tab-btn px-4 py-2 rounded-lg text-slate-400 hover:text-white">validate_reply</button>
<button onclick="showTab('js')" id="tab-js" class="tab-btn px-4 py-2 rounded-lg text-slate-400 hover:text-white">JavaScript</button>
<button onclick="showTab('csharp')" id="tab-csharp" class="tab-btn px-4 py-2 rounded-lg text-slate-400 hover:text-white">C#</button>
</div>
<div id="content-curl" class="tab-content bg-slate-800 rounded-xl p-6 text-sm font-mono text-slate-300 leading-relaxed overflow-x-auto">
<pre>curl https://api.naluai.com/v1/extract/name \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent_input": "Qual o seu nome?",
"user_input": "Bom dia! Me chamo João Silva",
"language": "pt-BR"
}'
# Resposta:
# {
# "obtained": true,
# "extracted_value": "João Silva",
# "confidence": "high",
# "certain": true
# }</pre>
</div>
<div id="content-reply" class="tab-content hidden bg-slate-800 rounded-xl p-6 text-sm font-mono text-slate-300 leading-relaxed overflow-x-auto">
<pre># validate_reply — Análise de contexto conversacional (5 créditos)
curl https://api.naluai.com/v1/extract/reply \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent_message": "Posso parcelar em 20x de R$100. Topa?",
"user_reply": "Bora em 48?"
}'
# Resposta:
# {
# "reply_type": "counter_proposal",
# "extracted_value": "48",
# "value_type": "quantity",
# "extracted_meaning": "48 parcelas, não R$48",
# "confidence": 0.95,
# "suggestion_to_agent": "Cliente propõe 48 parcelas..."
# }</pre>
</div>
<div id="content-js" class="tab-content hidden bg-slate-800 rounded-xl p-6 text-sm font-mono text-slate-300 leading-relaxed overflow-x-auto">
<pre>const res = await fetch('https://api.naluai.com/v1/extract/name', {
method: 'POST',
headers: {
'Authorization': 'Bearer SEU_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agent_input: 'Qual o seu nome?',
user_input: 'Bom dia! Me chamo João Silva'
})
});
const { obtained, extracted_value } = await res.json();
// obtained: true, extracted_value: "João Silva"</pre>
</div>
<div id="content-csharp" class="tab-content hidden bg-slate-800 rounded-xl p-6 text-sm font-mono text-slate-300 leading-relaxed overflow-x-auto">
<pre>using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer SEU_TOKEN");
var body = new {
agent_input = "Qual o seu nome?",
user_input = "Bom dia! Me chamo João Silva"
};
var resp = await client.PostAsJsonAsync(
"https://api.naluai.com/v1/extract/name", body);
var result = await resp.Content.ReadFromJsonAsync&lt;ExtractionResponse&gt;();
// result.ExtractedValue == "João Silva"</pre>
</div>
</div>
</div>
</section>
<!-- ── 9. PRICING SUMMARY ─────────────────────────────────────────────────── -->
<section class="py-16 bg-white">
<div class="max-w-5xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-4">Preços</h2>
<p class="text-center text-gray-500 mb-10">
<span class="text-2xl font-bold text-nalu-600">R$ 0,0019</span>
<span class="text-gray-500"> por validação no plano Starter.</span>
</p>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-10">
<div class="border border-gray-200 rounded-2xl p-6 text-center">
<div class="font-bold text-gray-900 mb-2">Free</div>
<div class="text-3xl font-extrabold text-gray-900 mb-1">R$ 0</div>
<div class="text-gray-500 text-sm mb-4">3.000 créditos/mês</div>
<a href="/precos" class="block border border-gray-300 rounded-lg py-2 text-sm font-medium hover:border-nalu-500 transition-colors">Começar grátis</a>
</div>
<div class="border-2 border-nalu-500 rounded-2xl p-6 text-center relative">
<div class="absolute -top-3 left-1/2 -translate-x-1/2 bg-nalu-500 text-white text-xs px-3 py-1 rounded-full font-semibold">Popular</div>
<div class="font-bold text-gray-900 mb-1">Starter</div>
<div class="text-2xl font-extrabold text-nalu-600 mb-0">R$ 0,0019</div>
<div class="text-xs text-gray-400 mb-1">por validação</div>
<div class="text-gray-400 text-xs mb-4">R$ 29/mês · 15.000 créditos</div>
<a href="/precos" class="block bg-nalu-600 text-white rounded-lg py-2 text-sm font-semibold hover:bg-nalu-700 transition-colors">Assinar →</a>
</div>
<div class="border border-gray-200 rounded-2xl p-6 text-center">
<div class="font-bold text-gray-900 mb-1">Indie</div>
<div class="text-2xl font-extrabold text-nalu-600 mb-0">R$ 0,0014</div>
<div class="text-xs text-gray-400 mb-1">por validação</div>
<div class="text-gray-400 text-xs mb-4">R$ 69/mês · 50.000 créditos</div>
<a href="/precos" class="block border border-gray-300 rounded-lg py-2 text-sm font-medium hover:border-nalu-500 transition-colors">Assinar →</a>
</div>
<div class="border border-gray-200 rounded-2xl p-6 text-center">
<div class="font-bold text-gray-900 mb-1">Pro</div>
<div class="text-2xl font-extrabold text-nalu-600 mb-0">R$ 0,0008</div>
<div class="text-xs text-gray-400 mb-1">por validação</div>
<div class="text-gray-400 text-xs mb-4">R$ 199/mês · 250.000 créditos</div>
<a href="/precos" class="block border border-gray-300 rounded-lg py-2 text-sm font-medium hover:border-nalu-500 transition-colors">Assinar →</a>
</div>
</div>
<!-- Callout -->
<div class="bg-blue-50 border border-blue-100 rounded-2xl p-6">
<div class="text-blue-700 font-bold mb-2">💡 Fazendo a conta</div>
<p class="text-blue-800 text-sm leading-relaxed">
Um chatbot de cobrança faz ~500 validações por mês.
Com o plano Starter, isso custa <strong>R$ 0,95</strong>.
Menos que um café. Para nunca mais perder um cliente
por um bot que confundiu 48 parcelas com R$ 48.
</p>
<p class="text-blue-600 font-semibold text-sm mt-2">
Qual o custo de perder uma venda por um erro do bot?
</p>
</div>
</div>
</section>
<!-- ── 10. FAQ ────────────────────────────────────────────────────────────── -->
<section class="py-16 bg-slate-50">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold text-center mb-10">FAQ</h2>
<div class="space-y-4">
@foreach (var faq in Model.Faqs)
{
<div class="bg-white rounded-xl border border-gray-100 p-5">
<div class="font-semibold text-gray-800 mb-2">@faq.Q</div>
<div class="text-gray-500 text-sm leading-relaxed">@faq.A</div>
</div>
}
</div>
</div>
</section>
<!-- ── 12. FINAL CTA ──────────────────────────────────────────────────────── -->
<section class="py-20 bg-nalu-600 text-white text-center">
<div class="max-w-2xl mx-auto px-4 sm:px-6">
<h2 class="text-3xl font-bold mb-4">Pare de perder dados (e clientes) por regex ruim.</h2>
<p class="text-nalu-100 mb-8 text-lg">3.000 créditos grátis por mês. Sem cartão. Setup em 30 segundos.</p>
<a href="/precos" class="bg-white text-nalu-600 font-bold px-8 py-4 rounded-xl hover:bg-nalu-50 transition-colors text-lg inline-block">
Começar grátis →
</a>
<p class="text-nalu-200 text-sm mt-4">A partir de R$ 0,0019 por validação. Menos que uma gota de café.</p>
</div>
</section>
@section Scripts {
<script>
function showTab(name) {
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
document.querySelectorAll('.tab-btn').forEach(el => {
el.classList.remove('bg-slate-700', 'text-white');
el.classList.add('text-slate-400');
});
document.getElementById('content-' + name).classList.remove('hidden');
var btn = document.getElementById('tab-' + name);
btn.classList.add('bg-slate-700', 'text-white');
btn.classList.remove('text-slate-400');
}
</script>
}