diff --git a/next/components/atoms/TypewriterConsole.js b/next/components/atoms/TypewriterConsole.js
new file mode 100644
index 00000000..b70acdb6
--- /dev/null
+++ b/next/components/atoms/TypewriterConsole.js
@@ -0,0 +1,84 @@
+import { useState, useEffect, useRef } from "react";
+import { Flex, Box } from "@chakra-ui/react";
+import ArrowIcon from "../../public/img/icons/arrowIcon";
+import Button from "../atoms/Button";
+import styles from "../../styles/typewriterConsole.module.css";
+
+export default function TypewriterConsole({
+ messages = [],
+ typingSpeed = 45,
+ deletingSpeed = 25,
+ pauseDuration = 2000,
+ maxWidth = "600px",
+ textBtn="",
+ onClickBtn=() => {},
+ isVariantBtn=false,
+ targetBtn="_self",
+ propsBtn={},
+}) {
+ const [displayText, setDisplayText] = useState("");
+ const messageIndexRef = useRef(0);
+ const isDeletingRef = useRef(false);
+ const charIndexRef = useRef(0);
+ const timeoutRef = useRef(null);
+
+ useEffect(() => {
+ if (!messages.length) return;
+
+ const tick = () => {
+ const currentMessage = messages[messageIndexRef.current];
+ const isDeleting = isDeletingRef.current;
+
+ if (!isDeleting) {
+ if (charIndexRef.current < currentMessage.length) {
+ charIndexRef.current += 1;
+ setDisplayText(currentMessage.slice(0, charIndexRef.current));
+ timeoutRef.current = setTimeout(tick, typingSpeed);
+ } else {
+ timeoutRef.current = setTimeout(() => {
+ isDeletingRef.current = true;
+ tick();
+ }, pauseDuration);
+ }
+ } else if (charIndexRef.current > 0) {
+ charIndexRef.current -= 1;
+ setDisplayText(currentMessage.slice(0, charIndexRef.current));
+ timeoutRef.current = setTimeout(tick, deletingSpeed);
+ } else {
+ isDeletingRef.current = false;
+ messageIndexRef.current = (messageIndexRef.current + 1) % messages.length;
+ timeoutRef.current = setTimeout(tick, typingSpeed);
+ }
+ };
+
+ timeoutRef.current = setTimeout(tick, typingSpeed);
+
+ return () => {
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
+ };
+ }, [messages, typingSpeed, deletingSpeed, pauseDuration]);
+
+ return (
+
+
+ {displayText}
+
+
+
+
+ );
+}
diff --git a/next/components/molecules/Footer.js b/next/components/molecules/Footer.js
index fcaae41c..cb84fed5 100644
--- a/next/components/molecules/Footer.js
+++ b/next/components/molecules/Footer.js
@@ -8,7 +8,7 @@ import Link from "../atoms/Link";
import { isMobileMod } from "../../hooks/useCheckMobile.hook"
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
-import { triggerGAEvent } from "../../utils";
+import { triggerGAEvent, trackNavigateToChatbotLp } from "../../utils";
import Display from "../atoms/Text/Display";
import LabelText from "../atoms/Text/LabelText";
import BodyText from "../atoms/Text/BodyText";
@@ -54,18 +54,21 @@ function SocialLink({ href, icon }) {
)
}
-function FooterLink(props) {
+function FooterLink({ href, onClick, children, ...rest }) {
return (
+ onClick={onClick}
+ {...rest}
+ >
+ {children}
+
)
}
@@ -84,7 +87,8 @@ function TextFooterSimple({children, ...props}) {
export default function Footer({ template, ocult = false }) {
const { t } = useTranslation('common');
- const { locale } = useRouter();
+ const { locale, pathname: pagePath } = useRouter();
+ const isMobile = isMobileMod();
function handlerTriggerEvent(event, value) {
triggerGAEvent(event, value)
@@ -216,6 +220,17 @@ export default function Footer({ template, ocult = false }) {
"/bdpro"}>
{t('footer.products.DBPro')}
+ trackNavigateToChatbotLp({
+ value: "footer",
+ placement: "footer_products",
+ pagePath,
+ isMobile,
+ })}
+ >
+ {t('footer.products.chatbot')}
+
{locale === 'pt' && (
{t('footer.products.DBEdu')}
@@ -224,7 +239,7 @@ export default function Footer({ template, ocult = false }) {
{locale === 'pt' && (
-
+
handlerTriggerEvent("navigating_to_services", "footer")}
@@ -264,7 +279,7 @@ export default function Footer({ template, ocult = false }) {
)}
-
+
-
+
{t('footer.institutional.aboutUs')}
diff --git a/next/components/molecules/Menu.js b/next/components/molecules/Menu.js
index fab2ea34..230df662 100644
--- a/next/components/molecules/Menu.js
+++ b/next/components/molecules/Menu.js
@@ -32,7 +32,7 @@ import { ControlledInputSimple } from "../atoms/ControlledInput";
import Link from "../atoms/Link";
import Button from "../atoms/Button";
import HelpWidget from "../atoms/HelpWidget";
-import { triggerGAEvent, triggerGAEventWithData, hasBDProSubscription, hasChatbotSubscription } from "../../utils";
+import { triggerGAEvent, triggerGAEventWithData, hasBDProSubscription, hasChatbotSubscription, trackNavigateToChatbotLp } from "../../utils";
import LabelText from "../atoms/Text/LabelText";
import BodyText from "../atoms/Text/BodyText";
@@ -68,13 +68,29 @@ function trackMenuOpenChatbot({
});
}
-function handleMenuLinkClick(href) {
+function navigateToChatbotLp(router, { menuPlacement, pagePath }) {
+ trackNavigateToChatbotLp({
+ value: "menu",
+ placement: menuPlacement,
+ pagePath,
+ });
+ router.push("/chatbot-lp");
+}
+
+function handleMenuLinkClick(href, { pagePath, placement } = {}) {
if (href === "/services") {
triggerGAEvent("navigating_to_services", "menu");
}
if (href === "/search") {
triggerGAEvent("navigating_to_data", "menu");
}
+ if (href === "/chatbot-lp") {
+ trackNavigateToChatbotLp({
+ value: "menu",
+ placement,
+ pagePath,
+ });
+ }
}
function MenuDrawer({ userData, isOpen, onClose, links, hasChatbotAccess, isUserPro }) {
@@ -157,7 +173,10 @@ function MenuDrawer({ userData, isOpen, onClose, links, hasChatbotAccess, isUser
letterSpacing="0.1px"
fontWeight="400"
href={c.href}
- onClick={() => handleMenuLinkClick(c.href)}
+ onClick={() => handleMenuLinkClick(c.href, {
+ pagePath,
+ placement: "mobile_drawer_solutions",
+ })}
>
{c.icon && c.icon} {c.name}
@@ -176,7 +195,7 @@ function MenuDrawer({ userData, isOpen, onClose, links, hasChatbotAccess, isUser
letterSpacing="0.1px"
fontWeight="400"
href={elm}
- onClick={() => handleMenuLinkClick(elm)}
+ onClick={() => handleMenuLinkClick(elm, { pagePath, placement: "mobile_drawer" })}
>
{key}
@@ -203,8 +222,8 @@ function MenuDrawer({ userData, isOpen, onClose, links, hasChatbotAccess, isUser
fontSize="20px"
fontFamily="Roboto"
fontWeight="400"
- href={hasChatbotAccess ? "/chatbot" : "/prices"}
- onClick={() => {
+ href={hasChatbotAccess ? "/chatbot" : "#"}
+ onClick={(e) => {
onClose();
if (hasChatbotAccess) {
trackMenuOpenChatbot({
@@ -214,6 +233,12 @@ function MenuDrawer({ userData, isOpen, onClose, links, hasChatbotAccess, isUser
isUserPro,
pagePath,
});
+ } else {
+ e.preventDefault();
+ navigateToChatbotLp(router, {
+ menuPlacement: "mobile_drawer",
+ pagePath,
+ });
}
}}
>
@@ -391,8 +416,8 @@ function MenuDrawerUser({ userData, isOpen, onClose, isUserPro, haveInterprisePl
gap="6px"
color="#71757A"
fontWeight="400"
- href={hasChatbotAccess ? "/chatbot" : "/prices"}
- onClick={() => {
+ href={hasChatbotAccess ? "/chatbot" : "#"}
+ onClick={(e) => {
onClose()
if (hasChatbotAccess) {
trackMenuOpenChatbot({
@@ -402,6 +427,12 @@ function MenuDrawerUser({ userData, isOpen, onClose, isUserPro, haveInterprisePl
isUserPro,
pagePath,
})
+ } else {
+ e.preventDefault()
+ navigateToChatbotLp(router, {
+ menuPlacement: "mobile_drawer_user",
+ pagePath,
+ })
}
}}
>
@@ -784,7 +815,10 @@ function DesktopLinks({
href={url}
padding="10px 0"
gap="16px"
- onClick={() => handleMenuLinkClick(url)}
+ onClick={() => handleMenuLinkClick(url, {
+ pagePath,
+ placement: "desktop_solutions_dropdown",
+ })}
onMouseEnter={setFlag.on}
onMouseLeave={setFlag.off}
>
@@ -867,7 +901,7 @@ function DesktopLinks({
fontWeight="400"
href={v}
target={v.startsWith("https") ? "_blank" : null}
- onClick={() => handleMenuLinkClick(v)}
+ onClick={() => handleMenuLinkClick(v, { pagePath, placement: "desktop" })}
>
{k}
@@ -991,7 +1025,10 @@ function DesktopLinks({
: {
type: "button",
onClick: () => {
- router.push("/prices");
+ navigateToChatbotLp(router, {
+ menuPlacement: "desktop_header_right",
+ pagePath,
+ });
},
})}
minWidth="auto"
@@ -1152,6 +1189,10 @@ export default function MenuNav({ simpleTemplate = false, userTemplate = false }
name: [t('exclusive_data')],
href: "/bdpro"
},
+ {
+ name: [t('chatbot_lp')],
+ href: "/chatbot-lp"
+ },
{
name: [t('courses')],
href: "https://info.basedosdados.org/bd-edu-cursos"
@@ -1180,6 +1221,10 @@ export default function MenuNav({ simpleTemplate = false, userTemplate = false }
{
name: [t('exclusive_data')],
href: "/en/bdpro"
+ },
+ {
+ name: [t('chatbot_lp')],
+ href: "/chatbot-lp"
}
],
[t('resources')]: [
@@ -1202,6 +1247,10 @@ export default function MenuNav({ simpleTemplate = false, userTemplate = false }
{
name: [t('exclusive_data')],
href: "/es/bdpro"
+ },
+ {
+ name: [t('chatbot_lp')],
+ href: "/chatbot-lp"
}
],
[t('resources')]: [
diff --git a/next/components/organisms/componentsUserPage/PlansAndPayment.js b/next/components/organisms/componentsUserPage/PlansAndPayment.js
index 7584a085..b8c688ca 100644
--- a/next/components/organisms/componentsUserPage/PlansAndPayment.js
+++ b/next/components/organisms/componentsUserPage/PlansAndPayment.js
@@ -225,6 +225,17 @@ export default function PlansAndPayment ({ userData }) {
}
}, [plan, plans, userData, chatbotSubscriptionInfo])
+ useEffect(() => {
+ if (!plans || plan !== "") return
+ if (query.checkout !== "chatbot") return
+ if (hasChatbotSubscription(userData)) return
+
+ const planId = plans.bd_chatbot_year?._id
+ if (planId) {
+ setPlan(planId)
+ }
+ }, [query.checkout, plans, userData, plan])
+
useEffect(() => {
const planSelected = cookies.get('plan_selected');
if (planSelected && plans) {
diff --git a/next/content/chatbot/FAQ/pt/available-data-chatbot.md b/next/content/chatbot/FAQ/pt/available-data-chatbot.md
new file mode 100644
index 00000000..f9cb7ebf
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/available-data-chatbot.md
@@ -0,0 +1,9 @@
+---
+question: Quais dados estão disponíveis para consulta?
+categories: []
+keywords: []
+order: 3
+id: available-data-chatbot
+---
+
+O chatbot está conectado a mais de 740 tabelas que compõem o datalake público da BD. É uma fonte preciosa e em constante expansão, com dados sobre temas diversos: economia (CNPJ, RAIS, Caged), saúde (PNS, SINAN), educação (SAEB, Censo Educacional), demografia (Censo 2022, PNAD), meio ambiente e muito mais.
diff --git a/next/content/chatbot/FAQ/pt/chatbot-no-answer.md b/next/content/chatbot/FAQ/pt/chatbot-no-answer.md
new file mode 100644
index 00000000..369bc96b
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/chatbot-no-answer.md
@@ -0,0 +1,9 @@
+---
+question: O que fazer se o chatbot não encontrar uma resposta?
+categories: []
+keywords: []
+order: 9
+id: chatbot-no-answer
+---
+
+Para perguntas complexas, recomendamos dividir o pedido em perguntas menores e mais simples. Outra dica útil é mencionar o nome específico de uma coluna (como "município") ou pedir explicitamente para o chatbot buscar em um conjunto de dados que você já conheça.
diff --git a/next/content/chatbot/FAQ/pt/free-trial-chatbot.md b/next/content/chatbot/FAQ/pt/free-trial-chatbot.md
new file mode 100644
index 00000000..ee580ae3
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/free-trial-chatbot.md
@@ -0,0 +1,9 @@
+---
+question: Posso testar o chatbot gratuitamente?
+categories: []
+keywords: []
+order: 5
+id: free-trial-chatbot
+---
+
+Sim. Oferecemos um período de teste gratuito de 7 dias para você conhecer a ferramenta. Após esse período, o chatbot faz parte de um plano de assinatura que garante perguntas ilimitadas e nos ajuda a manter nosso datalake público gratuito.
diff --git a/next/content/chatbot/FAQ/pt/how-download-response-data.md b/next/content/chatbot/FAQ/pt/how-download-response-data.md
new file mode 100644
index 00000000..c4122c7c
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/how-download-response-data.md
@@ -0,0 +1,9 @@
+---
+question: Como posso baixar os dados da resposta?
+categories: []
+keywords: []
+order: 6
+id: how-download-response-data
+---
+
+Toda análise do chatbot é acompanhada por uma consulta SQL. Você pode copiar esse código e utilizá-lo no BigQuery (ferramenta do Google) para extrair a tabela completa e criar suas próprias visualizações ou relatórios.
diff --git a/next/content/chatbot/FAQ/pt/programming-sql-not-required.md b/next/content/chatbot/FAQ/pt/programming-sql-not-required.md
new file mode 100644
index 00000000..c35d6e9e
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/programming-sql-not-required.md
@@ -0,0 +1,9 @@
+---
+question: Preciso saber programar ou conhecer SQL para usar o Chatbot?
+categories: []
+keywords: []
+order: 2
+id: programming-sql-not-required
+---
+
+Não. O Chatbot foi desenhado para que qualquer pessoa explore dados públicos usando linguagem natural. O próprio processo de extrair os dados utilizados em uma resposta não exige conhecimento prévio de SQL: o chatbot gera a consulta e permite que você vá direto aos dados de que precisa.
diff --git a/next/content/chatbot/FAQ/pt/sql-bigquery-download-steps.md b/next/content/chatbot/FAQ/pt/sql-bigquery-download-steps.md
new file mode 100644
index 00000000..097caf14
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/sql-bigquery-download-steps.md
@@ -0,0 +1,20 @@
+---
+question: Como usar a consulta SQL para baixar os dados da resposta?
+categories: []
+keywords: []
+order: 8
+id: sql-bigquery-download-steps
+---
+
+O Chatbot sempre devolve uma consulta SQL junto com a resposta. Você pode copiar essa consulta e usar o [Google BigQuery](https://cloud.google.com/bigquery) — ferramenta de consulta e visualização de dados do Google — para extrair uma tabela com os dados utilizados na análise.
+
+Para isso, siga estes passos:
+
+1. Acesse o site do [Google BigQuery](https://cloud.google.com/bigquery).
+2. Se for direcionado à página inicial do produto, clique em algo como **Teste no Console** ou **Console** para abrir a interface de consultas.
+3. Faça login com uma conta Google e crie um projeto, se ainda não tiver um.
+4. Depois de criar o projeto, abra **Consulta SQL** (ou equivalente no console) para acessar o editor.
+5. Cole a consulta fornecida pelo chatbot e execute.
+6. Após executar, você pode salvar os resultados no formato preferido ou abrir com outras ferramentas do Google para visualização.
+
+Se tiver problema nesse fluxo, nossa equipe e comunidade podem ajudar no [Discord da Base dos Dados](https://discord.gg/huKWpsVYx4).
diff --git a/next/content/chatbot/FAQ/pt/technical-support-chatbot.md b/next/content/chatbot/FAQ/pt/technical-support-chatbot.md
new file mode 100644
index 00000000..88c60342
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/technical-support-chatbot.md
@@ -0,0 +1,9 @@
+---
+question: Existe algum suporte técnico?
+categories: []
+keywords: []
+order: 7
+id: technical-support-chatbot
+---
+
+Sim. Assinantes contam com suporte prioritário por e-mail e Discord. Você também pode enviar dúvidas e sugestões diretamente para nossa comunidade e equipe técnica.
diff --git a/next/content/chatbot/FAQ/pt/use-results-own-reports.md b/next/content/chatbot/FAQ/pt/use-results-own-reports.md
new file mode 100644
index 00000000..954a881f
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/use-results-own-reports.md
@@ -0,0 +1,9 @@
+---
+question: Posso usar os resultados em meus próprios relatórios e gráficos?
+categories: []
+keywords: []
+order: 4
+id: use-results-own-reports
+---
+
+Sim. Além da análise em texto, você recebe o código para baixar os dados via BigQuery. Isso dá flexibilidade para você levar as informações para o Excel, Google Sheets ou ferramentas de visualização e criar seus próprios mapas e dashboards.
diff --git a/next/content/chatbot/FAQ/pt/what-is-bd-chatbot.md b/next/content/chatbot/FAQ/pt/what-is-bd-chatbot.md
new file mode 100644
index 00000000..6e4ae665
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/what-is-bd-chatbot.md
@@ -0,0 +1,9 @@
+---
+question: O que é o Chatbot da BD?
+categories: []
+keywords: []
+order: 1
+id: what-is-bd-chatbot
+---
+
+É uma interface de consulta que utiliza linguagem natural para que qualquer pessoa explore o datalake público da Base dos Dados. Ele utiliza o modelo Gemini, do Google, para transformar suas perguntas em consultas de dados precisas.
diff --git a/next/content/chatbot/FAQ/pt/why-beta-version.md b/next/content/chatbot/FAQ/pt/why-beta-version.md
new file mode 100644
index 00000000..b43def4f
--- /dev/null
+++ b/next/content/chatbot/FAQ/pt/why-beta-version.md
@@ -0,0 +1,9 @@
+---
+question: Por que a ferramenta está em versão Beta?
+categories: []
+keywords: []
+order: 10
+id: why-beta-version
+---
+
+A versão Beta significa que o chatbot está em constante aprimoramento. Por isso, seu feedback pelos botões de curtir e descurtir (👍/👎) em cada resposta é fundamental para calibrarmos o modelo e melhorarmos a precisão dos resultados.
diff --git a/next/pages/bdpro.js b/next/pages/bdpro.js
index fcca41c9..8a51c677 100644
--- a/next/pages/bdpro.js
+++ b/next/pages/bdpro.js
@@ -481,10 +481,10 @@ function PricingSection({ t }) {
textAlign="center"
margin="0 auto"
>
- {t('prices:comparePlans')}
+ {t("prices:comparePlans")}
-
-
+
+
);
diff --git a/next/pages/chatbot-lp.js b/next/pages/chatbot-lp.js
new file mode 100644
index 00000000..ecdfd163
--- /dev/null
+++ b/next/pages/chatbot-lp.js
@@ -0,0 +1,943 @@
+import { useState, useRef, useEffect } from "react";
+import { useRouter } from "next/router";
+import Link from "next/link";
+import cookies from "js-cookie";
+import {
+ Box,
+ Stack,
+ VStack,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ Text,
+ Skeleton,
+} from "@chakra-ui/react";
+import Head from "next/head";
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
+import { useTranslation } from 'next-i18next';
+import { MainPageTemplate } from "../components/templates/main";
+import { withPages } from "../hooks/pages.hook";
+import { isMobileMod } from "../hooks/useCheckMobile.hook";
+import { triggerGAEventWithData, getUserFromCookie, hasChatbotSubscription } from "../utils";
+
+import { getAllFAQs } from "./api/faqs";
+
+import Display from "../components/atoms/Text/Display";
+import TitleText from "../components/atoms/Text/TitleText";
+import BodyText from "../components/atoms/Text/BodyText";
+import LabelText from "../components/atoms/Text/LabelText";
+import QuestionsBox from "../components/molecules/QuestionsBox";
+import TypewriterConsole from "../components/atoms/TypewriterConsole";
+import Button from "../components/atoms/Button";
+import Toggle from "../components/atoms/Toggle";
+import CheckIcon from "../public/img/icons/checkIcon";
+
+export async function getStaticProps({ locale }) {
+ const faqs = await getAllFAQs(locale, "chatbot/FAQ");
+ const pagesProps = await withPages();
+ return {
+ props: {
+ ...pagesProps,
+ ...(await serverSideTranslations(locale, ["common", "menu", "chatbot", "prices"])),
+ faqs,
+ },
+ revalidate: 30,
+ };
+}
+
+function filterChatbotPlans(edges) {
+ return edges.filter((item) => {
+ const name = item.node.productName?.toLowerCase() || "";
+ const slug = item.node.productSlug?.toLowerCase() || "";
+ const isConsumerChatbot =
+ (name.includes("chatbot") || slug.includes("chatbot")) &&
+ !name.includes("empresas");
+ return (
+ isConsumerChatbot &&
+ item.node.isActive === true
+ );
+ });
+}
+
+async function fetchChatbotPlans() {
+ const result = await fetch("/api/stripe/getPlans", { method: "GET" }).then(
+ (res) => res.json()
+ );
+
+ if (!result.success) return null;
+
+ const chatbotPlans = filterChatbotPlans(result.data);
+
+ function findByInterval(interval, amount) {
+ return (
+ chatbotPlans.find(
+ (item) =>
+ item.node.interval === interval &&
+ item.node.amount === amount
+ )?.node ?? null
+ );
+ }
+
+ return {
+ month: findByInterval("month", 30),
+ year: findByInterval("year", 326),
+ };
+}
+
+function ChatbotPricingCardSkeleton() {
+ const skeletonProps = {
+ startColor: "#F0F0F0",
+ endColor: "#F3F3F3",
+ borderRadius: "6px",
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {[0, 1, 2, 3].map((i) => (
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+function ChatbotPricingCard({ highlightedFeature }) {
+ const { t } = useTranslation(["chatbot", "prices"]);
+ const router = useRouter();
+ const { locale } = router;
+
+ const [toggleAnual, setToggleAnual] = useState(true);
+ const [plans, setPlans] = useState(null);
+ const [username, setUsername] = useState(null);
+ const [hasSubscribed, setHasSubscribed] = useState(true);
+ const [isBDChatbot, setIsBDChatbot] = useState({ isCurrentPlan: false });
+ const [isLoading, setIsLoading] = useState(true);
+
+ const features = t("plans.chatbot.features", {
+ returnObjects: true,
+ ns: "prices",
+ });
+
+ const selectedPlan = toggleAnual ? plans?.year : plans?.month;
+ const totalPrice = selectedPlan?.amount ?? (toggleAnual ? 326 : 30);
+ const displayPrice = toggleAnual
+ ? Math.ceil(totalPrice / 12)
+ : totalPrice;
+
+ const planInterval = toggleAnual ? "year" : "month";
+ const isCurrentPlan =
+ isBDChatbot.isCurrentPlan && isBDChatbot.planInterval === planInterval;
+
+ useEffect(() => {
+ async function loadData() {
+ setIsLoading(true);
+
+ let user = null;
+ if (cookies.get("userBD")) {
+ user = JSON.parse(cookies.get("userBD"));
+ }
+
+ const promises = [fetchChatbotPlans()];
+
+ if (user) {
+ const reg = new RegExp("(?<=:).*");
+ const match = reg.exec(user.id);
+ if (match) {
+ const [id] = match;
+ promises.push(
+ fetch(`/api/user/getAlreadySubscribed?p=${btoa(id)}`)
+ .then((res) => res.json())
+ .then(setHasSubscribed)
+ .catch(() => setHasSubscribed(false))
+ );
+ } else {
+ setHasSubscribed(false);
+ }
+
+ const internalNodes =
+ user?.internalSubscription?.edges?.map((e) => e?.node) || [];
+ const chatbotNode = internalNodes.find((n) =>
+ (n?.stripeSubscription || "").toLowerCase().includes("chatbot")
+ );
+
+ setUsername(user?.username);
+ setIsBDChatbot({
+ isCurrentPlan: !!chatbotNode,
+ planInterval: chatbotNode?.planInterval,
+ });
+ } else {
+ setHasSubscribed(false);
+ }
+
+ const [chatbotPlans] = await Promise.all(promises);
+ if (chatbotPlans) setPlans(chatbotPlans);
+ setIsLoading(false);
+ }
+
+ loadData();
+ }, []);
+
+ const handleSubscribe = () => {
+ triggerGAEventWithData("bd_chatbot_card_price", {
+ plan_interval: planInterval,
+ is_free_trial: !hasSubscribed,
+ source: "chatbot_lp",
+ });
+
+ if (selectedPlan?._id) {
+ cookies.set("plan_selected", selectedPlan._id, { expires: 1, path: "/" });
+ }
+
+ if (username === null) {
+ router.push("/user/login");
+ return;
+ }
+
+ router.push(`/user/${username}?plans_and_payment`);
+ };
+
+ const buttonLabel = isCurrentPlan
+ ? t("currentPlan", { ns: "prices" })
+ : hasSubscribed
+ ? t("subscribe", { ns: "prices" })
+ : t("startFreeTrial", { ns: "prices" });
+
+ if (isLoading) {
+ return ;
+ }
+
+ return (
+
+
+ setToggleAnual(!toggleAnual)}
+ />
+
+ {t("annualDiscount", { ns: "prices" })}
+
+ {t("save20", { ns: "prices" })}
+
+
+
+
+
+ {t("pricingCard.title")}
+
+
+
+
+ R$ {displayPrice}
+
+ {t("perMonth", { ns: "prices" })}
+
+
+
+ {toggleAnual &&
+ t("annualBillingMessage", {
+ ns: "prices",
+ price: totalPrice.toLocaleString("pt-BR", {
+ style: "currency",
+ currency: "BRL",
+ minimumFractionDigits: 0,
+ }),
+ })}
+
+
+
+
+ {features.map((feature, index) => {
+ const isHighlighted = highlightedFeature === index;
+
+ return (
+
+
+
+ {feature}
+
+
+ );
+ })}
+
+
+
+ {isCurrentPlan ? (
+
+ {buttonLabel}
+
+ ) : (
+
+ {buttonLabel}
+
+ )}
+
+
+ {t("readThe", { ns: "prices" })}
+
+
+ {t("termsOfService", { ns: "prices" })}
+
+
+
+
+
+ );
+}
+
+function WhyChatbotSection() {
+ const { t, ready } = useTranslation("chatbot");
+ const [activeIndex, setActiveIndex] = useState(0);
+ const isMobile = isMobileMod();
+
+ if (!ready) return null;
+
+ let items = t("why.items", { returnObjects: true });
+ if (!Array.isArray(items) && items && typeof items === "object") {
+ items = Object.values(items);
+ }
+ if (!Array.isArray(items)) return null;
+
+ return (
+
+
+ {t("why.title")}
+
+
+
+
+ setActiveIndex(index)}
+ width="100%"
+ >
+ {items.map((elm, index) => (
+
+
+
+ {elm.title}
+
+ +
+
+
+
+
+
+ {elm.content}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
+
+function clearPresentationHashFromUrl() {
+ window.history.replaceState(
+ null,
+ "",
+ `${window.location.pathname}${window.location.search}`
+ );
+}
+
+function scrollToPresentation() {
+ const el = document.getElementById("presentation");
+ if (!el) return;
+
+ el.scrollIntoView({ behavior: "smooth", block: "start" });
+ window.history.replaceState(null, "", `#${"presentation"}`);
+
+ window.setTimeout(clearPresentationHashFromUrl, 1500);
+}
+
+function scrollToPricing() {
+ const el = document.getElementById("chatbot-pricing");
+ if (!el) return;
+
+ el.scrollIntoView({ behavior: "smooth", block: "start" });
+}
+
+function VideoPlayer({ src }) {
+ const videoRef = useRef(null);
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ const video = videoRef.current;
+ if (!video) return;
+
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ setIsVisible(entry.isIntersecting);
+ },
+ { threshold: 0.1 }
+ );
+
+ observer.observe(video);
+
+ return () => {
+ observer.unobserve(video);
+ };
+ }, []);
+
+ useEffect(() => {
+ const video = videoRef.current;
+ if (!video) return;
+
+ if (isVisible) {
+ video.play().catch((error) => {
+ console.error("Video play failed:", error);
+ });
+ } else {
+ video.pause();
+ }
+ }, [isVisible]);
+
+ return (
+
+ );
+}
+
+function Hero({ t }) {
+ const router = useRouter();
+ const prompts = t("hero.prompts", { returnObjects: true });
+ const [hasChatbotAccess, setHasChatbotAccess] = useState(false);
+
+ useEffect(() => {
+ let cancelled = false;
+
+ async function checkAccess() {
+ const token = cookies.get("token");
+ const userRaw = cookies.get("userBD");
+
+ if (!token || !userRaw || userRaw === "undefined") {
+ if (!cancelled) setHasChatbotAccess(false);
+ return;
+ }
+
+ try {
+ JSON.parse(userRaw);
+ } catch {
+ if (!cancelled) setHasChatbotAccess(false);
+ return;
+ }
+
+ try {
+ const params = new URLSearchParams({ p: btoa(token) });
+ const res = await fetch(`/api/user/validateToken?${params}`);
+ const data = await res.json();
+
+ if (!cancelled) {
+ setHasChatbotAccess(Boolean(res.ok && data.success && data.has_chatbot_access));
+ }
+ } catch {
+ if (!cancelled) {
+ setHasChatbotAccess(hasChatbotSubscription(getUserFromCookie()));
+ }
+ }
+ }
+
+ checkAccess();
+
+ return () => {
+ cancelled = true;
+ };
+ }, []);
+
+ const heroPromptButtonText = hasChatbotAccess
+ ? t("hero.buttonPrompt.textStart")
+ : t("hero.buttonPrompt.textSubscribe");
+
+ const handleHeroPromptClick = () => {
+ if (hasChatbotAccess) {
+ router.push("/chatbot");
+ return;
+ }
+
+ scrollToPricing();
+ };
+
+ return (
+
+
+
+
+ {t("hero.title.prefix")}{" "}
+
+ {t("hero.title.highlight1")}
+ {" "}
+ {t("hero.title.middle")}{" "}
+
+ {t("hero.title.highlight2")}
+ {" "}
+ {t("hero.title.suffix")}
+
+
+
+
+
+ );
+}
+
+function Presentation({ t }) {
+ const isMobile = isMobileMod();
+
+ return (
+
+
+ {t("presentation.title")}
+
+
+
+ {t("presentation.description")}
+
+
+
+
+
+
+ );
+}
+
+function FAQ({ t, faqs }) {
+ const isMobile = isMobileMod();
+
+ return (
+
+
+ {t('faq.title')}
+
+
+
+ {faqs && faqs.map((elm, i) => (
+
+ ))}
+
+
+ );
+}
+
+export default function ChatbotLPPage({ faqs }) {
+ const { t } = useTranslation(['chatbot', 'prices']);
+
+ useEffect(() => {
+ if (
+ typeof window === "undefined" ||
+ window.location.hash !== `#${"presentation"}`
+ ) {
+ return;
+ }
+
+ const el = document.getElementById("presentation");
+ if (el) {
+ el.scrollIntoView({ behavior: "smooth", block: "start" });
+ }
+
+ const timeout = window.setTimeout(clearPresentationHashFromUrl, 1500);
+
+ return () => window.clearTimeout(timeout);
+ }, []);
+
+ return (
+
+
+ {t('head.pageTitle')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/next/pages/chatbot.js b/next/pages/chatbot.js
index 08fe0c6f..7a8723bb 100644
--- a/next/pages/chatbot.js
+++ b/next/pages/chatbot.js
@@ -15,6 +15,7 @@ import ChatWindow from "../components/organisms/chatbot/ChatWindow";
import Display from "../components/atoms/Text/Display";
import useChatbot from "../hooks/useChatbot";
import { ChatbotProvider } from "../context/ChatbotContext";
+import { redirectToChatbotCheckout } from "../utils";
function getGreetingFirstNameFromCookie() {
try {
@@ -76,7 +77,7 @@ function ChatbotAccessGate({ children }) {
return;
}
if (!data.has_chatbot_access) {
- router.replace("/prices");
+ await redirectToChatbotCheckout(router);
return;
}
setCanEnter(true);
diff --git a/next/pages/user/login.js b/next/pages/user/login.js
index 252ca60c..dfd2fd6b 100644
--- a/next/pages/user/login.js
+++ b/next/pages/user/login.js
@@ -43,7 +43,6 @@ export async function getStaticProps({ locale }) {
export default function Login() {
const router = useRouter();
const { t } = useTranslation('user');
- const { query } = useRouter()
const [formData, setFormData] = useState({ email: "", password: "" })
const [errors, setErrors] = useState({ email: "", password: "", login: ""})
const [showPassword, setShowPassword] = useState(true)
diff --git a/next/public/locales/en/chatbot.json b/next/public/locales/en/chatbot.json
new file mode 100644
index 00000000..ac3a034d
--- /dev/null
+++ b/next/public/locales/en/chatbot.json
@@ -0,0 +1,62 @@
+{
+ "head": {
+ "pageTitle": "Chatbot – Data Basis",
+ "metaTitle": "Chatbot – Data Basis"
+ },
+ "hero": {
+ "title": {
+ "prefix": "Talk to",
+ "highlight1": "the largest collection",
+ "middle": "of",
+ "highlight2": "public data",
+ "suffix": "in Brazil."
+ },
+ "prompts": [
+ "What is the gender pay gap in São Paulo?",
+ "Help me with analysis ideas using SAEB data",
+ "How many active companies are there in Serra da Saudade (MG)?",
+ "Who received the most votes for president in the Itaquera neighborhood in 2022?"
+ ],
+ "buttonPrompt": {
+ "textSubscribe": "Subscribe now",
+ "textStart": "Get started"
+ },
+ "buttonScroll": {
+ "text": "Demo"
+ }
+ },
+ "presentation": {
+ "title": "How does our product work?",
+ "description": "Discover the AI tool that helps you create analyses in seconds and guides you through the entire Data Basis public datalake"
+ },
+ "why": {
+ "title": "Why will the Data Basis Chatbot revolutionize the way you work with data?",
+ "items": [
+ {
+ "title": "Access to Brazil's Largest Public Datalake",
+ "content": "Browse terabytes of data across more than 740 tables on diverse topics (education, economy, health, labor market, demography, environment).",
+ "highlightFeature": 0
+ },
+ {
+ "title": "Advanced AI Engine with Unlimited Questions",
+ "content": "Powered by Gemini, Google's state-of-the-art AI model, to turn as many questions as you want into precise queries.",
+ "highlightFeature": 1
+ },
+ {
+ "title": "Natural Language Interface",
+ "content": "Lets anyone explore the Data Basis public datalake through simple questions, with no programming required."
+ },
+ {
+ "title": "Transparency and Flexibility",
+ "content": "Including the SQL query lets you validate insights and download data to build your own visualizations and reports in other tools.",
+ "highlightFeature": 2
+ }
+ ]
+ },
+ "pricingCard": {
+ "title": "Try it free and create analyses in seconds with the power of AI"
+ },
+ "faq": {
+ "title": "Frequently Asked Questions – FAQ"
+ }
+}
diff --git a/next/public/locales/en/common.json b/next/public/locales/en/common.json
index 72862639..be0aa7e8 100644
--- a/next/public/locales/en/common.json
+++ b/next/public/locales/en/common.json
@@ -33,7 +33,8 @@
"publicDatalake": "Public datalake",
"dataPackages": "Packages",
"DBPro": "DB Pro",
- "DBEdu": "DB Edu"
+ "DBEdu": "DB Edu",
+ "chatbot": "Chatbot"
},
"services": {
"title": "SERVICES",
@@ -41,7 +42,7 @@
"arquiteturaDeDados": "Data Architecture",
"portalDeDados": "Data Portal",
"painelGerencial": "Management Panel",
- "chatbot": "Chatbot",
+ "chatbot": "Custom Chatbot",
"formacao": "Training"
},
"resources": {
diff --git a/next/public/locales/en/menu.json b/next/public/locales/en/menu.json
index 5da0430d..c0d1c5b3 100644
--- a/next/public/locales/en/menu.json
+++ b/next/public/locales/en/menu.json
@@ -34,5 +34,6 @@
"DBPro": "DB Pro",
"DBEnterprise": "DB Enterprise",
"openChatbot": "Chatbot",
- "chatbotNewBadge": "Beta"
+ "chatbotNewBadge": "Beta",
+ "chatbot_lp": "Chatbot"
}
diff --git a/next/public/locales/es/chatbot.json b/next/public/locales/es/chatbot.json
new file mode 100644
index 00000000..85238286
--- /dev/null
+++ b/next/public/locales/es/chatbot.json
@@ -0,0 +1,62 @@
+{
+ "head": {
+ "pageTitle": "Chatbot – Base de los Datos",
+ "metaTitle": "Chatbot – Base de los Datos"
+ },
+ "hero": {
+ "title": {
+ "prefix": "Conversa con el",
+ "highlight1": "mayor acervo",
+ "middle": "de",
+ "highlight2": "datos públicos",
+ "suffix": "de Brasil."
+ },
+ "prompts": [
+ "¿Cuál es la disparidad salarial por género en São Paulo?",
+ "Ayúdame con ideas de análisis con los datos del SAEB",
+ "¿Cuántas empresas activas hay en Serra da Saudade (MG)?",
+ "¿Quién recibió más votos para presidente en el barrio de Itaquera en 2022?"
+ ],
+ "buttonPrompt": {
+ "textSubscribe": "Suscríbete ya",
+ "textStart": "Empieza ya"
+ },
+ "buttonScroll": {
+ "text": "Demostración"
+ }
+ },
+ "presentation": {
+ "title": "¿Cómo funciona nuestro producto?",
+ "description": "Conoce la herramienta de IA que te ayudará a crear análisis en segundos y te guiará por todo el datalake público de Base de los Datos"
+ },
+ "why": {
+ "title": "¿Por qué el Chatbot de Base de los Datos revolucionará tu trabajo con datos?",
+ "items": [
+ {
+ "title": "Acceso al mayor datalake público de Brasil",
+ "content": "Navega por terabytes de datos en más de 740 tablas sobre diversos temas (educación, economía, salud, mercado de trabajo, demografía, medio ambiente).",
+ "highlightFeature": 0
+ },
+ {
+ "title": "Motor de IA avanzado con preguntas ilimitadas",
+ "content": "Utiliza Gemini, el modelo de inteligencia artificial de última generación de Google, para transformar todas las preguntas que quieras en consultas precisas.",
+ "highlightFeature": 1
+ },
+ {
+ "title": "Interfaz de lenguaje natural",
+ "content": "Permite que cualquier persona explore el datalake público de Base de los Datos mediante preguntas simples, sin lenguaje de programación."
+ },
+ {
+ "title": "Transparencia y flexibilidad",
+ "content": "La inclusión de la consulta SQL permite validar los insights y descargar los datos para crear tus propias visualizaciones e informes en otras herramientas.",
+ "highlightFeature": 2
+ }
+ ]
+ },
+ "pricingCard": {
+ "title": "Pruébalo gratis y crea análisis en segundos con el poder de la IA"
+ },
+ "faq": {
+ "title": "Preguntas frecuentes – FAQ"
+ }
+}
diff --git a/next/public/locales/es/common.json b/next/public/locales/es/common.json
index 132608ca..befaf26b 100644
--- a/next/public/locales/es/common.json
+++ b/next/public/locales/es/common.json
@@ -33,7 +33,8 @@
"publicDatalake": "Datalake público",
"dataPackages": "Paquetes",
"DBPro": "BD Pro",
- "DBEdu": "BD Edu"
+ "DBEdu": "BD Edu",
+ "chatbot": "Chatbot"
},
"services": {
"title": "SERVICIOS",
@@ -41,7 +42,7 @@
"arquiteturaDeDados": "Arquitectura de Datos",
"portalDeDados": "Portal de Datos",
"painelGerencial": "Panel de Gestión",
- "chatbot": "Chatbot",
+ "chatbot": "Chatbot personalizado",
"formacao": "Formación"
},
"resources": {
diff --git a/next/public/locales/es/menu.json b/next/public/locales/es/menu.json
index e7e7108f..a3fa7624 100644
--- a/next/public/locales/es/menu.json
+++ b/next/public/locales/es/menu.json
@@ -34,5 +34,6 @@
"DBPro": "BD Pro",
"DBEnterprise": "BD Empresas",
"openChatbot": "Chatbot",
- "chatbotNewBadge": "Beta"
+ "chatbotNewBadge": "Beta",
+ "chatbot_lp": "Chatbot"
}
diff --git a/next/public/locales/pt/chatbot.json b/next/public/locales/pt/chatbot.json
new file mode 100644
index 00000000..a5ce43e2
--- /dev/null
+++ b/next/public/locales/pt/chatbot.json
@@ -0,0 +1,62 @@
+{
+ "head": {
+ "pageTitle": "Chatbot LP - Base dos Dados",
+ "metaTitle": "Chatbot LP - Base dos Dados"
+ },
+ "hero": {
+ "title": {
+ "prefix": "Converse com o",
+ "highlight1": "maior acervo",
+ "middle": "de",
+ "highlight2": "dados públicos",
+ "suffix": "do Brasil."
+ },
+ "prompts": [
+ "Qual é a disparidade salarial por gênero em São Paulo?",
+ "Me ajude com ideias de análises com os dados do SAEB",
+ "Quantas empresas ativas existem em Serra da Saudade (MG)?",
+ "Quem teve mais votos para presidente no bairro de Itaquera em 2022?"
+ ],
+ "buttonPrompt": {
+ "textSubscribe": "Assine já",
+ "textStart": "Comece já"
+ },
+ "buttonScroll": {
+ "text": "Demonstração"
+ }
+ },
+ "presentation": {
+ "title": "Como funciona nosso produto?",
+ "description": "Conheça a ferramenta de IA que vai te ajudar a criar análises em segundos e te guiar por todo o datalake público da BD"
+ },
+ "why": {
+ "title": "Por que o Chatbot da BD vai revolucionar seu trabalho com dados?",
+ "items": [
+ {
+ "title": "Acesso ao Maior Datalake Público do Brasil",
+ "content": "Navega por terabytes de dados em mais de 740 tabelas sobre diversos temas (educação, economia, saúde, mercado de trabalho, demografia, meio ambiente).",
+ "highlightFeature": 0
+ },
+ {
+ "title": "Motor de IA Avançado com perguntas ilimitadas",
+ "content": "Utiliza o Gemini, modelo de inteligência artificial de última geração do Google, para transformar quantas perguntas quiser em consultas precisas.",
+ "highlightFeature": 1
+ },
+ {
+ "title": "Interface de Linguagem Natural",
+ "content": "Permite que qualquer pessoa explore o datalake público da BD Brasil através de perguntas simples, sem linguagem de programação."
+ },
+ {
+ "title": "Transparência e Flexibilidade",
+ "content": "A inclusão da consulta SQL permite que o usuário valide os insights e baixe os dados para criar suas próprias visualizações e relatórios em outras ferramentas.",
+ "highlightFeature": 2
+ }
+ ]
+ },
+ "pricingCard": {
+ "title": "Teste gratuitamente e crie análises em segundos com o poder da IA"
+ },
+ "faq": {
+ "title": "Dúvidas Frequentes - FAQ"
+ }
+}
diff --git a/next/public/locales/pt/common.json b/next/public/locales/pt/common.json
index 5da34631..46de1db2 100644
--- a/next/public/locales/pt/common.json
+++ b/next/public/locales/pt/common.json
@@ -33,7 +33,8 @@
"publicDatalake": "Datalake público",
"dataPackages": "Pacotes",
"DBPro": "BD Pro",
- "DBEdu": "BD Edu"
+ "DBEdu": "BD Edu",
+ "chatbot": "Chatbot"
},
"services": {
"title": "SERVIÇOS",
@@ -41,7 +42,7 @@
"arquiteturaDeDados": "Arquitetura de Dados",
"portalDeDados": "Portal de Dados",
"painelGerencial": "Painel Gerencial",
- "chatbot": "Chatbot",
+ "chatbot": "Chatbot Personalizado",
"formacao": "Formação"
},
"resources": {
diff --git a/next/public/locales/pt/menu.json b/next/public/locales/pt/menu.json
index 96759459..bc7c15c3 100644
--- a/next/public/locales/pt/menu.json
+++ b/next/public/locales/pt/menu.json
@@ -34,5 +34,6 @@
"DBPro": "BD Pro",
"DBEnterprise": "BD Empresas",
"openChatbot": "Chatbot",
- "chatbotNewBadge": "Beta"
+ "chatbotNewBadge": "Beta",
+ "chatbot_lp": "Chatbot"
}
diff --git a/next/styles/typewriterConsole.module.css b/next/styles/typewriterConsole.module.css
new file mode 100644
index 00000000..54476ce7
--- /dev/null
+++ b/next/styles/typewriterConsole.module.css
@@ -0,0 +1,52 @@
+.console {
+ position: relative;
+ width: 100%;
+ height: 180px;
+ border-radius: 14px;
+ background-color: #FFFFFF;
+ padding: 32px 32px 70px;
+ box-shadow: 0px 1.6px 16px rgba(100, 96, 103, 0.16);
+}
+
+.text {
+ flex: 1;
+ min-width: 0;
+ min-height: 38px;
+ font-size: 16px;
+ line-height: 24px;
+ font-family: Roboto, sans-serif;
+ font-weight: 400;
+ color: #252A32;
+ text-align: left;
+}
+
+.cursor {
+ display: inline-block;
+ width: 2px;
+ height: 1em;
+ background-color: #252A32;
+ margin-left: 1px;
+ vertical-align: text-bottom;
+ animation: blink 1s step-end infinite;
+}
+
+@keyframes blink {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0;
+ }
+}
+
+@media (max-width: 768px) {
+ .text {
+ font-size: 14px;
+ line-height: 20px;
+ min-height: 26px;
+ text-align: left;
+ white-space: normal;
+ }
+}
diff --git a/next/utils.js b/next/utils.js
index 0a7b9fa1..b8578424 100644
--- a/next/utils.js
+++ b/next/utils.js
@@ -220,6 +220,31 @@ export function triggerGAEventWithData(category, data) {
window.dataLayer.push(eventData);
}
+const CHATBOT_LP_DESKTOP_PLACEMENTS = new Set([
+ "desktop_header_right",
+ "desktop_solutions_dropdown",
+]);
+
+export function trackNavigateToChatbotLp({
+ value,
+ placement,
+ pagePath,
+ isMobile,
+}) {
+ if (typeof window === "undefined") return;
+
+ const user = getUserFromCookie();
+
+ triggerGAEventWithData("navigating_to_chatbot_lp", {
+ value,
+ menu_placement: placement,
+ is_mobile: isMobile ?? !CHATBOT_LP_DESKTOP_PLACEMENTS.has(placement),
+ is_logged_in: Boolean(user?.username),
+ is_bd_pro: hasBDProSubscription(user),
+ page_path: pagePath || window.location.pathname,
+ });
+}
+
export function cleanString(string) {
const newString = string.trim()
const returnString = newString.replace(/\s+/g, ' ')
@@ -273,3 +298,72 @@ export function getSubscriptionType(user) {
if (user?.isSubscriber) return "unknown"
return "none"
}
+
+function filterConsumerChatbotPlans(edges) {
+ return (edges || []).filter((item) => {
+ const name = item?.node?.productName?.toLowerCase() || ""
+ const slug = item?.node?.productSlug?.toLowerCase() || ""
+ const isConsumerChatbot =
+ (name.includes("chatbot") || slug.includes("chatbot")) &&
+ !name.includes("empresas")
+ return isConsumerChatbot && item?.node?.isActive === true
+ })
+}
+
+export async function fetchChatbotPlan(interval = "year") {
+ const amounts = { month: 30, year: 326 }
+ const amount = amounts[interval] || amounts.year
+
+ try {
+ const result = await fetch("/api/stripe/getPlans", { method: "GET" }).then((res) =>
+ res.json()
+ )
+ if (!result?.success) return null
+
+ const chatbotPlans = filterConsumerChatbotPlans(result.data)
+ return (
+ chatbotPlans.find(
+ (item) => item?.node?.interval === interval && item?.node?.amount === amount
+ )?.node ?? null
+ )
+ } catch {
+ return null
+ }
+}
+
+export function getUserFromCookie() {
+ try {
+ const raw = cookies.get("userBD")
+ if (!raw || raw === "undefined") return null
+ return JSON.parse(raw)
+ } catch {
+ return null
+ }
+}
+
+export async function redirectToChatbotCheckout(router, { interval = "year" } = {}) {
+ const plan = await fetchChatbotPlan(interval)
+
+ if (plan?._id) {
+ cookies.set("plan_selected", plan._id, { expires: 1, path: "/" })
+ }
+
+ const user = getUserFromCookie()
+
+ if (!user?.username) {
+ if (typeof window !== "undefined") {
+ localStorage.setItem("previousPath", window.location.href)
+ }
+ return router.push("/user/login")
+ }
+
+ const query = { plans_and_payment: "" }
+ if (!plan?._id) {
+ query.checkout = "chatbot"
+ }
+
+ return router.push({
+ pathname: `/user/${user.username}`,
+ query,
+ })
+}