From b2d896a8aa071cc1a90a770681e88060ee9f59bb Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 12 Oct 2025 22:09:59 -0300 Subject: [PATCH 1/3] feat: add WaitlistForm, thank you page and confetti effect --- package-lock.json | 10 +++++++ package.json | 1 + src/app/obrigado/page.tsx | 51 +++++++++++++++++++++++++++++++++ src/app/page.tsx | 1 + src/components/Hero.tsx | 9 ++++++ src/components/WaitlistForm.tsx | 50 ++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+) create mode 100644 src/app/obrigado/page.tsx create mode 100644 src/components/WaitlistForm.tsx diff --git a/package-lock.json b/package-lock.json index ecb4fa0..5daeaa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@headlessui/react": "^2.1.8", "@next/third-parties": "^14.2.13", + "canvas-confetti": "^1.9.3", "clsx": "^2.1.1", "framer-motion": "^11.11.9", "next": "^14.2.13", @@ -1514,6 +1515,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvas-confetti": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz", + "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index 01c714e..5ae388a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@headlessui/react": "^2.1.8", "@next/third-parties": "^14.2.13", + "canvas-confetti": "^1.9.3", "clsx": "^2.1.1", "framer-motion": "^11.11.9", "next": "^14.2.13", diff --git a/src/app/obrigado/page.tsx b/src/app/obrigado/page.tsx new file mode 100644 index 0000000..44d7656 --- /dev/null +++ b/src/app/obrigado/page.tsx @@ -0,0 +1,51 @@ +'use client' + +import React, { useEffect } from 'react' +import Link from 'next/link' +//@ts-ignore +import confetti from 'canvas-confetti' + +const ObrigadoPage = () => { + useEffect(() => { + const duration = 1 * 1000 // duração do efeito (1 segundo) + const end = Date.now() + duration + + const frame = () => { + confetti({ + particleCount: 4, // quantidade de partículas + spread: 60, // sobe mais reto + startVelocity: 90, // aumenta a força para subir mais alto + origin: { + x: Math.random(), // posição horizontal aleatória + y: 1, // começa do rodapé + }, + }) + + if (Date.now() < end) { + requestAnimationFrame(frame) + } + } + + frame() + }, []) + + return ( +
+
+

🎉 Bem-vindo à lista!

+

+ Parabéns pelo seu primeiro passo!

Em breve, novidades no seu email 🚀 +

+ + + Voltar para o início + +
+
+ ) +} + +export default ObrigadoPage diff --git a/src/app/page.tsx b/src/app/page.tsx index edfe481..7c4beeb 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,5 @@ import Hero from '@/components/Hero' +import WaitlistForm from '@/components/WaitlistForm' // eslint-disable-next-line @typescript-eslint/no-unused-vars import Testimonials from '@/components/Testimonials' // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index c07f5d6..283939a 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -1,8 +1,10 @@ +'use client' import React from 'react' import Image from 'next/image' import AppStoreButton from './AppStoreButton' import PlayStoreButton from './PlayStoreButton' +import WaitlistForm from './WaitlistForm' import { heroDetails } from '@/data/hero' @@ -23,10 +25,17 @@ const Hero: React.FC = () => { {heroDetails.heading}

{heroDetails.subheading}

+ + {/* FORMULÁRIO DA WAITLIST AQUI */} +
+ +
+
+ { + const [email, setEmail] = useState('') + const [loading, setLoading] = useState(false) + const router = useRouter() + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!email) return + setLoading(true) + + try { + // Futuramente integrar com mailchimp, formspree, etc + await new Promise(res => setTimeout(res, 1000)) + router.push('/obrigado') + } catch (err) { + console.error(err) + } finally { + setLoading(false) + } + } + + return ( +
+ setEmail(e.target.value)} + className="flex-1 px-4 py-3 rounded-lg bg-white/20 border border-white/30 focus:border-white focus:outline-none placeholder-white/60 text-white" + /> + +
+ ) +} + +export default WaitlistForm From 3495b2753ddb718b5a59fb57bab3c395cdda4f49 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 13 Oct 2025 18:35:27 -0300 Subject: [PATCH 2/3] fix: lint adjustments --- src/app/obrigado/page.tsx | 2 +- src/app/page.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/obrigado/page.tsx b/src/app/obrigado/page.tsx index 44d7656..5f795d9 100644 --- a/src/app/obrigado/page.tsx +++ b/src/app/obrigado/page.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import Link from 'next/link' -//@ts-ignore +// @ts-expect-error: ajuste de tipagem import confetti from 'canvas-confetti' const ObrigadoPage = () => { diff --git a/src/app/page.tsx b/src/app/page.tsx index 7c4beeb..edfe481 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,4 @@ import Hero from '@/components/Hero' -import WaitlistForm from '@/components/WaitlistForm' // eslint-disable-next-line @typescript-eslint/no-unused-vars import Testimonials from '@/components/Testimonials' // eslint-disable-next-line @typescript-eslint/no-unused-vars From 3703f68ab7bb6e8ed26c4bc1de245d01ed3cd61f Mon Sep 17 00:00:00 2001 From: Matheus Date: Tue, 14 Oct 2025 20:12:37 -0300 Subject: [PATCH 3/3] refactor: Applied useCallback to handleSubmit to keep a stable reference --- src/components/WaitlistForm.tsx | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/components/WaitlistForm.tsx b/src/components/WaitlistForm.tsx index 2d16a79..8c357dd 100644 --- a/src/components/WaitlistForm.tsx +++ b/src/components/WaitlistForm.tsx @@ -1,5 +1,5 @@ 'use client' -import { useState } from 'react' +import { useState, useCallback } from 'react' import { useRouter } from 'next/navigation' const WaitlistForm: React.FC = () => { @@ -7,21 +7,24 @@ const WaitlistForm: React.FC = () => { const [loading, setLoading] = useState(false) const router = useRouter() - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!email) return - setLoading(true) + const handleSubmit = useCallback( + async (e: React.FormEvent) => { + e.preventDefault() + if (!email) return + setLoading(true) - try { - // Futuramente integrar com mailchimp, formspree, etc - await new Promise(res => setTimeout(res, 1000)) - router.push('/obrigado') - } catch (err) { - console.error(err) - } finally { - setLoading(false) - } - } + try { + // Futuramente integrar com mailchimp, formspree, etc + await new Promise(res => setTimeout(res, 1000)) + router.push('/obrigado') + } catch (err) { + console.error(err) + } finally { + setLoading(false) + } + }, + [email, router], + ) return (